diff options
author | Dmitry Torokhov <dtor@insightbb.com> | 2007-05-01 00:24:54 -0400 |
---|---|---|
committer | Dmitry Torokhov <dtor@insightbb.com> | 2007-05-01 00:24:54 -0400 |
commit | bc95f3669f5e6f63cf0b84fe4922c3c6dd4aa775 (patch) | |
tree | 427fcf2a7287c16d4b5aa6cbf494d59579a6a8b1 /drivers | |
parent | 3d29cdff999c37b3876082278a8134a0642a02cd (diff) | |
parent | dc87c3985e9b442c60994308a96f887579addc39 (diff) | |
download | op-kernel-dev-bc95f3669f5e6f63cf0b84fe4922c3c6dd4aa775.zip op-kernel-dev-bc95f3669f5e6f63cf0b84fe4922c3c6dd4aa775.tar.gz |
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts:
drivers/usb/input/Makefile
drivers/usb/input/gtco.c
Diffstat (limited to 'drivers')
1666 files changed, 128759 insertions, 55507 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 3a718f5..920c975 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -72,7 +72,6 @@ obj-$(CONFIG_CPU_FREQ) += cpufreq/ obj-$(CONFIG_MMC) += mmc/ obj-$(CONFIG_NEW_LEDS) += leds/ obj-$(CONFIG_INFINIBAND) += infiniband/ -obj-$(CONFIG_IPATH_CORE) += infiniband/ obj-$(CONFIG_SGI_SN) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ diff --git a/drivers/acorn/char/Makefile b/drivers/acorn/char/Makefile index 2fa9a8b..d006c9f 100644 --- a/drivers/acorn/char/Makefile +++ b/drivers/acorn/char/Makefile @@ -2,5 +2,4 @@ # Makefile for the acorn character device drivers. # -obj-$(CONFIG_ARCH_ACORN) += i2c.o pcf8583.o obj-$(CONFIG_L7200_KEYB) += defkeymap-l7200.o keyb_l7200.o diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c deleted file mode 100644 index d276fd1..0000000 --- a/drivers/acorn/char/i2c.c +++ /dev/null @@ -1,368 +0,0 @@ -/* - * linux/drivers/acorn/char/i2c.c - * - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * ARM IOC/IOMD i2c driver. - * - * On Acorn machines, the following i2c devices are on the bus: - * - PCF8583 real time clock & static RAM - */ -#include <linux/capability.h> -#include <linux/init.h> -#include <linux/time.h> -#include <linux/miscdevice.h> -#include <linux/rtc.h> -#include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> -#include <linux/fs.h> - -#include <asm/hardware.h> -#include <asm/io.h> -#include <asm/hardware/ioc.h> -#include <asm/system.h> -#include <asm/uaccess.h> - -#include "pcf8583.h" - -extern int (*set_rtc)(void); - -static struct i2c_client *rtc_client; -static const unsigned char days_in_mon[] = - { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -#define CMOS_CHECKSUM (63) - -/* - * Acorn machines store the year in the static RAM at - * location 128. - */ -#define CMOS_YEAR (64 + 128) - -static inline int rtc_command(int cmd, void *data) -{ - int ret = -EIO; - - if (rtc_client) - ret = rtc_client->driver->command(rtc_client, cmd, data); - - return ret; -} - -/* - * Update the century + year bytes in the CMOS RAM, ensuring - * that the check byte is correctly adjusted for the change. - */ -static int rtc_update_year(unsigned int new_year) -{ - unsigned char yr[2], chk; - struct mem cmos_year = { CMOS_YEAR, sizeof(yr), yr }; - struct mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; - int ret; - - ret = rtc_command(MEM_READ, &cmos_check); - if (ret) - goto out; - ret = rtc_command(MEM_READ, &cmos_year); - if (ret) - goto out; - - chk -= yr[1] + yr[0]; - - yr[1] = new_year / 100; - yr[0] = new_year % 100; - - chk += yr[1] + yr[0]; - - ret = rtc_command(MEM_WRITE, &cmos_year); - if (ret == 0) - ret = rtc_command(MEM_WRITE, &cmos_check); - out: - return ret; -} - -/* - * Read the current RTC time and date, and update xtime. - */ -static void get_rtc_time(struct rtc_tm *rtctm, unsigned int *year) -{ - unsigned char ctrl, yr[2]; - struct mem rtcmem = { CMOS_YEAR, sizeof(yr), yr }; - int real_year, year_offset; - - /* - * Ensure that the RTC is running. - */ - rtc_command(RTC_GETCTRL, &ctrl); - if (ctrl & 0xc0) { - unsigned char new_ctrl = ctrl & ~0xc0; - - printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n", - ctrl, new_ctrl); - - rtc_command(RTC_SETCTRL, &new_ctrl); - } - - if (rtc_command(RTC_GETDATETIME, rtctm) || - rtc_command(MEM_READ, &rtcmem)) - return; - - real_year = yr[0]; - - /* - * The RTC year holds the LSB two bits of the current - * year, which should reflect the LSB two bits of the - * CMOS copy of the year. Any difference indicates - * that we have to correct the CMOS version. - */ - year_offset = rtctm->year_off - (real_year & 3); - if (year_offset < 0) - /* - * RTC year wrapped. Adjust it appropriately. - */ - year_offset += 4; - - *year = real_year + year_offset + yr[1] * 100; -} - -static int set_rtc_time(struct rtc_tm *rtctm, unsigned int year) -{ - unsigned char leap; - int ret; - - leap = (!(year % 4) && (year % 100)) || !(year % 400); - - if (rtctm->mon > 12 || rtctm->mon == 0 || rtctm->mday == 0) - return -EINVAL; - - if (rtctm->mday > (days_in_mon[rtctm->mon] + (rtctm->mon == 2 && leap))) - return -EINVAL; - - if (rtctm->hours >= 24 || rtctm->mins >= 60 || rtctm->secs >= 60) - return -EINVAL; - - /* - * The RTC's own 2-bit year must reflect the least - * significant two bits of the CMOS year. - */ - rtctm->year_off = (year % 100) & 3; - - ret = rtc_command(RTC_SETDATETIME, rtctm); - if (ret == 0) - ret = rtc_update_year(year); - - return ret; -} - -/* - * Set the RTC time only. Note that - * we do not touch the date. - */ -static int k_set_rtc_time(void) -{ - struct rtc_tm new_rtctm, old_rtctm; - unsigned long nowtime = xtime.tv_sec; - - if (rtc_command(RTC_GETDATETIME, &old_rtctm)) - return 0; - - new_rtctm.cs = xtime.tv_nsec / 10000000; - new_rtctm.secs = nowtime % 60; nowtime /= 60; - new_rtctm.mins = nowtime % 60; nowtime /= 60; - new_rtctm.hours = nowtime % 24; - - /* - * avoid writing when we're going to change the day - * of the month. We will retry in the next minute. - * This basically means that if the RTC must not drift - * by more than 1 minute in 11 minutes. - * - * [ rtc: 1/1/2000 23:58:00, real 2/1/2000 00:01:00, - * rtc gets set to 1/1/2000 00:01:00 ] - */ - if ((old_rtctm.hours == 23 && old_rtctm.mins == 59) || - (new_rtctm.hours == 23 && new_rtctm.mins == 59)) - return 1; - - return rtc_command(RTC_SETTIME, &new_rtctm); -} - -static int rtc_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - unsigned int year; - struct rtc_time rtctm; - struct rtc_tm rtc_raw; - - switch (cmd) { - case RTC_ALM_READ: - case RTC_ALM_SET: - break; - - case RTC_RD_TIME: - memset(&rtctm, 0, sizeof(struct rtc_time)); - get_rtc_time(&rtc_raw, &year); - rtctm.tm_sec = rtc_raw.secs; - rtctm.tm_min = rtc_raw.mins; - rtctm.tm_hour = rtc_raw.hours; - rtctm.tm_mday = rtc_raw.mday; - rtctm.tm_mon = rtc_raw.mon - 1; /* month starts at 0 */ - rtctm.tm_year = year - 1900; /* starts at 1900 */ - return copy_to_user((void *)arg, &rtctm, sizeof(rtctm)) - ? -EFAULT : 0; - - case RTC_SET_TIME: - if (!capable(CAP_SYS_TIME)) - return -EACCES; - - if (copy_from_user(&rtctm, (void *)arg, sizeof(rtctm))) - return -EFAULT; - rtc_raw.secs = rtctm.tm_sec; - rtc_raw.mins = rtctm.tm_min; - rtc_raw.hours = rtctm.tm_hour; - rtc_raw.mday = rtctm.tm_mday; - rtc_raw.mon = rtctm.tm_mon + 1; - year = rtctm.tm_year + 1900; - return set_rtc_time(&rtc_raw, year); - break; - - case RTC_EPOCH_READ: - return put_user(1900, (unsigned long *)arg); - - } - return -EINVAL; -} - -static const struct file_operations rtc_fops = { - .ioctl = rtc_ioctl, -}; - -static struct miscdevice rtc_dev = { - .minor = RTC_MINOR, - .name = "rtc", - .fops = &rtc_fops, -}; - -/* IOC / IOMD i2c driver */ - -#define FORCE_ONES 0xdc -#define SCL 0x02 -#define SDA 0x01 - -/* - * We must preserve all non-i2c output bits in IOC_CONTROL. - * Note also that we need to preserve the value of SCL and - * SDA outputs as well (which may be different from the - * values read back from IOC_CONTROL). - */ -static u_int force_ones; - -static void ioc_setscl(void *data, int state) -{ - u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); - u_int ones = force_ones; - - if (state) - ones |= SCL; - else - ones &= ~SCL; - - force_ones = ones; - - ioc_writeb(ioc_control | ones, IOC_CONTROL); -} - -static void ioc_setsda(void *data, int state) -{ - u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); - u_int ones = force_ones; - - if (state) - ones |= SDA; - else - ones &= ~SDA; - - force_ones = ones; - - ioc_writeb(ioc_control | ones, IOC_CONTROL); -} - -static int ioc_getscl(void *data) -{ - return (ioc_readb(IOC_CONTROL) & SCL) != 0; -} - -static int ioc_getsda(void *data) -{ - return (ioc_readb(IOC_CONTROL) & SDA) != 0; -} - -static struct i2c_algo_bit_data ioc_data = { - .setsda = ioc_setsda, - .setscl = ioc_setscl, - .getsda = ioc_getsda, - .getscl = ioc_getscl, - .udelay = 80, - .timeout = 100 -}; - -static int ioc_client_reg(struct i2c_client *client) -{ - if (client->driver->id == I2C_DRIVERID_PCF8583 && - client->addr == 0x50) { - struct rtc_tm rtctm; - unsigned int year; - struct timespec tv; - - rtc_client = client; - get_rtc_time(&rtctm, &year); - - tv.tv_nsec = rtctm.cs * 10000000; - tv.tv_sec = mktime(year, rtctm.mon, rtctm.mday, - rtctm.hours, rtctm.mins, rtctm.secs); - do_settimeofday(&tv); - set_rtc = k_set_rtc_time; - } - - return 0; -} - -static int ioc_client_unreg(struct i2c_client *client) -{ - if (client == rtc_client) { - set_rtc = NULL; - rtc_client = NULL; - } - - return 0; -} - -static struct i2c_adapter ioc_ops = { - .id = I2C_HW_B_IOC, - .algo_data = &ioc_data, - .client_register = ioc_client_reg, - .client_unregister = ioc_client_unreg, -}; - -static int __init i2c_ioc_init(void) -{ - int ret; - - force_ones = FORCE_ONES | SCL | SDA; - - ret = i2c_bit_add_bus(&ioc_ops); - - if (ret >= 0){ - ret = misc_register(&rtc_dev); - if(ret < 0) - i2c_del_adapter(&ioc_ops); - } - - return ret; -} - -__initcall(i2c_ioc_init); diff --git a/drivers/acorn/char/pcf8583.c b/drivers/acorn/char/pcf8583.c deleted file mode 100644 index 9b49f31..0000000 --- a/drivers/acorn/char/pcf8583.c +++ /dev/null @@ -1,284 +0,0 @@ -/* - * linux/drivers/acorn/char/pcf8583.c - * - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Driver for PCF8583 RTC & RAM chip - */ -#include <linux/module.h> -#include <linux/i2c.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/mc146818rtc.h> -#include <linux/init.h> -#include <linux/errno.h> -#include <linux/bcd.h> - -#include "pcf8583.h" - -static struct i2c_driver pcf8583_driver; - -static unsigned short ignore[] = { I2C_CLIENT_END }; -static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; -static unsigned short *forces[] = { NULL }; - -static struct i2c_client_address_data addr_data = { - .normal_i2c = normal_addr, - .probe = ignore, - .ignore = ignore, - .forces = forces, -}; - -#define set_ctrl(x, v) i2c_set_clientdata(x, (void *)(unsigned int)(v)) -#define get_ctrl(x) ((unsigned int)i2c_get_clientdata(x)) - -static int -pcf8583_attach(struct i2c_adapter *adap, int addr, int kind) -{ - struct i2c_client *c; - unsigned char buf[1], ad[1] = { 0 }; - struct i2c_msg msgs[2] = { - { - .addr = addr, - .flags = 0, - .len = 1, - .buf = ad, - }, { - .addr = addr, - .flags = I2C_M_RD, - .len = 1, - .buf = buf, - } - }; - - c = kmalloc(sizeof(*c), GFP_KERNEL); - if (!c) - return -ENOMEM; - - memset(c, 0, sizeof(*c)); - c->addr = addr; - c->adapter = adap; - c->driver = &pcf8583_driver; - - if (i2c_transfer(c->adapter, msgs, 2) == 2) - set_ctrl(c, buf[0]); - - return i2c_attach_client(c); -} - -static int -pcf8583_probe(struct i2c_adapter *adap) -{ - return i2c_probe(adap, &addr_data, pcf8583_attach); -} - -static int -pcf8583_detach(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(client); - return 0; -} - -static int -pcf8583_get_datetime(struct i2c_client *client, struct rtc_tm *dt) -{ - unsigned char buf[8], addr[1] = { 1 }; - struct i2c_msg msgs[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = addr, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = 6, - .buf = buf, - } - }; - int ret = -EIO; - - memset(buf, 0, sizeof(buf)); - - ret = i2c_transfer(client->adapter, msgs, 2); - if (ret == 2) { - dt->year_off = buf[4] >> 6; - dt->wday = buf[5] >> 5; - - buf[4] &= 0x3f; - buf[5] &= 0x1f; - - dt->cs = BCD_TO_BIN(buf[0]); - dt->secs = BCD_TO_BIN(buf[1]); - dt->mins = BCD_TO_BIN(buf[2]); - dt->hours = BCD_TO_BIN(buf[3]); - dt->mday = BCD_TO_BIN(buf[4]); - dt->mon = BCD_TO_BIN(buf[5]); - - ret = 0; - } - - return ret; -} - -static int -pcf8583_set_datetime(struct i2c_client *client, struct rtc_tm *dt, int datetoo) -{ - unsigned char buf[8]; - int ret, len = 6; - - buf[0] = 0; - buf[1] = get_ctrl(client) | 0x80; - buf[2] = BIN_TO_BCD(dt->cs); - buf[3] = BIN_TO_BCD(dt->secs); - buf[4] = BIN_TO_BCD(dt->mins); - buf[5] = BIN_TO_BCD(dt->hours); - - if (datetoo) { - len = 8; - buf[6] = BIN_TO_BCD(dt->mday) | (dt->year_off << 6); - buf[7] = BIN_TO_BCD(dt->mon) | (dt->wday << 5); - } - - ret = i2c_master_send(client, (char *)buf, len); - if (ret == len) - ret = 0; - - buf[1] = get_ctrl(client); - i2c_master_send(client, (char *)buf, 2); - - return ret; -} - -static int -pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl) -{ - *ctrl = get_ctrl(client); - return 0; -} - -static int -pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl) -{ - unsigned char buf[2]; - - buf[0] = 0; - buf[1] = *ctrl; - set_ctrl(client, *ctrl); - - return i2c_master_send(client, (char *)buf, 2); -} - -static int -pcf8583_read_mem(struct i2c_client *client, struct mem *mem) -{ - unsigned char addr[1]; - struct i2c_msg msgs[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = addr, - }, { - .addr = client->addr, - .flags = I2C_M_RD, - .len = mem->nr, - .buf = mem->data, - } - }; - - if (mem->loc < 8) - return -EINVAL; - - addr[0] = mem->loc; - - return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; -} - -static int -pcf8583_write_mem(struct i2c_client *client, struct mem *mem) -{ - unsigned char addr[1]; - struct i2c_msg msgs[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = addr, - }, { - .addr = client->addr, - .flags = I2C_M_NOSTART, - .len = mem->nr, - .buf = mem->data, - } - }; - - if (mem->loc < 8) - return -EINVAL; - - addr[0] = mem->loc; - - return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO; -} - -static int -pcf8583_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - switch (cmd) { - case RTC_GETDATETIME: - return pcf8583_get_datetime(client, arg); - - case RTC_SETTIME: - return pcf8583_set_datetime(client, arg, 0); - - case RTC_SETDATETIME: - return pcf8583_set_datetime(client, arg, 1); - - case RTC_GETCTRL: - return pcf8583_get_ctrl(client, arg); - - case RTC_SETCTRL: - return pcf8583_set_ctrl(client, arg); - - case MEM_READ: - return pcf8583_read_mem(client, arg); - - case MEM_WRITE: - return pcf8583_write_mem(client, arg); - - default: - return -EINVAL; - } -} - -static struct i2c_driver pcf8583_driver = { - .driver = { - .name = "PCF8583", - }, - .id = I2C_DRIVERID_PCF8583, - .attach_adapter = pcf8583_probe, - .detach_client = pcf8583_detach, - .command = pcf8583_command -}; - -static __init int pcf8583_init(void) -{ - return i2c_add_driver(&pcf8583_driver); -} - -static __exit void pcf8583_exit(void) -{ - i2c_del_driver(&pcf8583_driver); -} - -module_init(pcf8583_init); -module_exit(pcf8583_exit); - -MODULE_AUTHOR("Russell King"); -MODULE_DESCRIPTION("PCF8583 I2C RTC driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/acorn/char/pcf8583.h b/drivers/acorn/char/pcf8583.h deleted file mode 100644 index 847f7fd..0000000 --- a/drivers/acorn/char/pcf8583.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * linux/drivers/acorn/char/pcf8583.h - * - * Copyright (C) 2000 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -struct rtc_tm { - unsigned char cs; - unsigned char secs; - unsigned char mins; - unsigned char hours; - unsigned char mday; - unsigned char mon; - unsigned char year_off; - unsigned char wday; -}; - -struct mem { - unsigned int loc; - unsigned int nr; - unsigned char *data; -}; - -#define RTC_GETDATETIME 0 -#define RTC_SETTIME 1 -#define RTC_SETDATETIME 2 -#define RTC_GETCTRL 3 -#define RTC_SETCTRL 4 -#define MEM_READ 5 -#define MEM_WRITE 6 - -#define CTRL_STOP 0x80 -#define CTRL_HOLD 0x40 -#define CTRL_32KHZ 0x00 -#define CTRL_MASK 0x08 -#define CTRL_ALARMEN 0x04 -#define CTRL_ALARM 0x02 -#define CTRL_TIMER 0x01 diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 20eacc2..139f41f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -7,12 +7,14 @@ menu "ACPI (Advanced Configuration and Power Interface) Support" depends on !X86_VISWS depends on !IA64_HP_SIM depends on IA64 || X86 + depends on PM config ACPI bool "ACPI Support" depends on IA64 || X86 depends on PCI depends on PM + select PNP default y ---help--- Advanced Configuration and Power Interface (ACPI) support for @@ -83,8 +85,8 @@ config ACPI_PROCFS depends on ACPI default y ---help--- - Procfs interface for ACPI is made optional for back-compatible. - As the same functions are duplicated in sysfs interface + The Procfs interface for ACPI is made optional for backward compatibility. + As the same functions are duplicated in the sysfs interface and this proc interface will be removed some time later, it's marked as deprecated. ( /proc/acpi/debug_layer && debug_level are deprecated by @@ -132,15 +134,6 @@ config ACPI_VIDEO Note that this is an ref. implementation only. It may or may not work for your integrated video device. -config ACPI_HOTKEY - tristate "Generic Hotkey (EXPERIMENTAL)" - depends on EXPERIMENTAL - depends on X86 - default n - help - Experimental consolidated hotkey driver. - If you are unsure, say N. - config ACPI_FAN tristate "Fan" default y @@ -157,6 +150,7 @@ config ACPI_DOCK config ACPI_BAY tristate "Removable Drive Bay (EXPERIMENTAL)" depends on EXPERIMENTAL + depends on ACPI_DOCK help This driver adds support for ACPI controlled removable drive bays such as the IBM ultrabay or the Dell Module Bay. @@ -224,32 +218,6 @@ config ACPI_ASUS NOTE: This driver is deprecated and will probably be removed soon, use asus-laptop instead. -config ACPI_IBM - tristate "IBM ThinkPad Laptop Extras" - depends on X86 - select BACKLIGHT_CLASS_DEVICE - ---help--- - This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds - support for Fn-Fx key combinations, Bluetooth control, video - output switching, ThinkLight control, UltraBay eject and more. - For more information about this driver see <file:Documentation/ibm-acpi.txt> - and <http://ibm-acpi.sf.net/> . - - If you have an IBM ThinkPad laptop, say Y or M here. - -config ACPI_IBM_DOCK - bool "Legacy Docking Station Support" - depends on ACPI_IBM - depends on ACPI_DOCK=n - default n - ---help--- - Allows the ibm_acpi driver to handle docking station events. - This support is obsoleted by CONFIG_HOTPLUG_PCI_ACPI. It will - allow locking and removing the laptop from the docking station, - but will not properly connect PCI devices. - - If you are not sure, say N here. - config ACPI_TOSHIBA tristate "Toshiba Laptop Extras" depends on X86 @@ -383,11 +351,10 @@ config ACPI_HOTPLUG_MEMORY config ACPI_SBS tristate "Smart Battery System (EXPERIMENTAL)" - depends on X86 && I2C + depends on X86 depends on EXPERIMENTAL help This driver adds support for the Smart Battery System. - Depends on I2C (Device Drivers ---> I2C support) A "Smart Battery" is quite old and quite rare compared to today's ACPI "Control Method" battery. diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 856c32b..d4336f1 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -1,6 +1,6 @@ # # Makefile for the Linux ACPI interpreter -# +# export ACPI_CFLAGS @@ -32,21 +32,21 @@ obj-y += osl.o utils.o \ processor-objs += processor_core.o processor_throttling.o \ processor_idle.o processor_thermal.o ifdef CONFIG_CPU_FREQ -processor-objs += processor_perflib.o +processor-objs += processor_perflib.o endif obj-y += sleep/ obj-y += bus.o glue.o obj-y += scan.o +# Keep EC driver first. Initialization of others depend on it. +obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o -obj-$(CONFIG_ACPI_EC) += ec.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_BAY) += bay.o obj-$(CONFIG_ACPI_VIDEO) += video.o -obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o obj-$(CONFIG_ACPI_POWER) += power.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o @@ -56,8 +56,7 @@ obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o -obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o -obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o +obj-$(CONFIG_ACPI_SBS) += sbs.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 6daeace..37c7dc4 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -35,7 +35,6 @@ #define ACPI_AC_COMPONENT 0x00020000 #define ACPI_AC_CLASS "ac_adapter" #define ACPI_AC_HID "ACPI0003" -#define ACPI_AC_DRIVER_NAME "ACPI AC Adapter Driver" #define ACPI_AC_DEVICE_NAME "AC Adapter" #define ACPI_AC_FILE_STATE "state" #define ACPI_AC_NOTIFY_STATUS 0x80 @@ -44,10 +43,10 @@ #define ACPI_AC_STATUS_UNKNOWN 0xFF #define _COMPONENT ACPI_AC_COMPONENT -ACPI_MODULE_NAME("acpi_ac") +ACPI_MODULE_NAME("ac"); - MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME); +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI AC Adapter Driver"); MODULE_LICENSE("GPL"); extern struct proc_dir_entry *acpi_lock_ac_dir(void); @@ -58,7 +57,7 @@ static int acpi_ac_remove(struct acpi_device *device, int type); static int acpi_ac_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_ac_driver = { - .name = ACPI_AC_DRIVER_NAME, + .name = "ac", .class = ACPI_AC_CLASS, .ids = ACPI_AC_HID, .ops = { diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index cd946ed..e65628a 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -35,21 +35,15 @@ #define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000UL #define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" -#define ACPI_MEMORY_DEVICE_DRIVER_NAME "Hotplug Mem Driver" #define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" #define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT -ACPI_MODULE_NAME("acpi_memory") - MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); -MODULE_DESCRIPTION(ACPI_MEMORY_DEVICE_DRIVER_NAME); +ACPI_MODULE_NAME("acpi_memhotplug"); +MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); +MODULE_DESCRIPTION("Hotplug Mem Driver"); MODULE_LICENSE("GPL"); -/* ACPI _STA method values */ -#define ACPI_MEMORY_STA_PRESENT (0x00000001UL) -#define ACPI_MEMORY_STA_ENABLED (0x00000002UL) -#define ACPI_MEMORY_STA_FUNCTIONAL (0x00000008UL) - /* Memory Device States */ #define MEMORY_INVALID_STATE 0 #define MEMORY_POWER_ON_STATE 1 @@ -60,7 +54,7 @@ static int acpi_memory_device_remove(struct acpi_device *device, int type); static int acpi_memory_device_start(struct acpi_device *device); static struct acpi_driver acpi_memory_device_driver = { - .name = ACPI_MEMORY_DEVICE_DRIVER_NAME, + .name = "acpi_memhotplug", .class = ACPI_MEMORY_DEVICE_CLASS, .ids = ACPI_MEMORY_DEVICE_HID, .ops = { @@ -205,9 +199,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device) * Check for device status. Device should be * present/enabled/functioning. */ - if (!((current_status & ACPI_MEMORY_STA_PRESENT) - && (current_status & ACPI_MEMORY_STA_ENABLED) - && (current_status & ACPI_MEMORY_STA_FUNCTIONAL))) + if (!((current_status & ACPI_STA_DEVICE_PRESENT) + && (current_status & ACPI_STA_DEVICE_ENABLED) + && (current_status & ACPI_STA_DEVICE_FUNCTIONING))) return -ENODEV; return 0; @@ -287,7 +281,7 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) return -ENODEV; /* Check for device status. Device should be disabled */ - if (current_status & ACPI_MEMORY_STA_ENABLED) + if (current_status & ACPI_STA_DEVICE_ENABLED) return -EINVAL; return 0; diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c index 31ad70a..b770dea 100644 --- a/drivers/acpi/asus_acpi.c +++ b/drivers/acpi/asus_acpi.c @@ -141,6 +141,7 @@ struct asus_hotk { W5A, //W5A W3V, //W3030V xxN, //M2400N, M3700N, M5200N, M6800N, S1300N, S5200N + A4S, //Z81sp //(Centrino) END_MODEL } model; //Models currently supported @@ -397,7 +398,16 @@ static struct model_data model_conf[END_MODEL] = { .brightness_set = "SPLV", .brightness_get = "GPLV", .display_set = "SDSP", - .display_get = "\\ADVG"} + .display_get = "\\ADVG"}, + + { + .name = "A4S", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .mt_bt_switch = "BLED", + .mt_wled = "WLED" + } + }; /* procdir we use */ @@ -421,7 +431,7 @@ static struct asus_hotk *hotk; static int asus_hotk_add(struct acpi_device *device); static int asus_hotk_remove(struct acpi_device *device, int type); static struct acpi_driver asus_hotk_driver = { - .name = ACPI_HOTK_NAME, + .name = "asus_acpi", .class = ACPI_HOTK_CLASS, .ids = ACPI_HOTK_HID, .ops = { @@ -838,7 +848,7 @@ out: static int set_brightness_status(struct backlight_device *bd) { - return set_brightness(bd->props->brightness); + return set_brightness(bd->props.brightness); } static int @@ -1117,6 +1127,8 @@ static int asus_model_match(char *model) return W3V; else if (strncmp(model, "W5A", 3) == 0) return W5A; + else if (strncmp(model, "A4S", 3) == 0) + return A4S; else return END_MODEL; } @@ -1340,11 +1352,9 @@ static int asus_hotk_remove(struct acpi_device *device, int type) return 0; } -static struct backlight_properties asus_backlight_data = { - .owner = THIS_MODULE, +static struct backlight_ops asus_backlight_data = { .get_brightness = read_brightness, .update_status = set_brightness_status, - .max_brightness = 15, }; static void __exit asus_acpi_exit(void) @@ -1365,10 +1375,6 @@ static int __init asus_acpi_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(KERN_ERR "Using generic hotkey driver\n"); - return -ENODEV; - } asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); if (!asus_proc_dir) { printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); @@ -1402,6 +1408,7 @@ static int __init asus_acpi_init(void) asus_backlight_device = NULL; asus_acpi_exit(); } + asus_backlight_device->props.max_brightness = 15; return 0; } diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 2f4521a..e64c76c 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -42,7 +42,6 @@ #define ACPI_BATTERY_COMPONENT 0x00040000 #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_HID "PNP0C0A" -#define ACPI_BATTERY_DRIVER_NAME "ACPI Battery Driver" #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_FILE_INFO "info" #define ACPI_BATTERY_FILE_STATUS "state" @@ -53,10 +52,10 @@ #define ACPI_BATTERY_UNITS_AMPS "mA" #define _COMPONENT ACPI_BATTERY_COMPONENT -ACPI_MODULE_NAME("acpi_battery") +ACPI_MODULE_NAME("battery"); - MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME); +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Battery Driver"); MODULE_LICENSE("GPL"); extern struct proc_dir_entry *acpi_lock_battery_dir(void); @@ -67,7 +66,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type); static int acpi_battery_resume(struct acpi_device *device); static struct acpi_driver acpi_battery_driver = { - .name = ACPI_BATTERY_DRIVER_NAME, + .name = "battery", .class = ACPI_BATTERY_CLASS, .ids = ACPI_BATTERY_HID, .ops = { @@ -324,6 +323,13 @@ static int acpi_battery_check(struct acpi_battery *battery) return result; } +static void acpi_battery_check_present(struct acpi_battery *battery) +{ + if (!battery->flags.present) { + acpi_battery_check(battery); + } +} + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ @@ -340,6 +346,8 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) if (!battery) goto end; + acpi_battery_check_present(battery); + if (battery->flags.present) seq_printf(seq, "present: yes\n"); else { @@ -424,6 +432,8 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset) if (!battery) goto end; + acpi_battery_check_present(battery); + if (battery->flags.present) seq_printf(seq, "present: yes\n"); else { @@ -499,6 +509,8 @@ static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) if (!battery) goto end; + acpi_battery_check_present(battery); + if (!battery->flags.present) { seq_printf(seq, "present: no\n"); goto end; @@ -536,6 +548,8 @@ acpi_battery_write_alarm(struct file *file, if (!battery || (count > sizeof(alarm_string) - 1)) return -EINVAL; + acpi_battery_check_present(battery); + if (!battery->flags.present) return -ENODEV; diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c index 91082ce..fb3f31b 100644 --- a/drivers/acpi/bay.c +++ b/drivers/acpi/bay.c @@ -32,11 +32,9 @@ #include <asm/uaccess.h> #include <linux/platform_device.h> -#define ACPI_BAY_DRIVER_NAME "ACPI Removable Drive Bay Driver" - -ACPI_MODULE_NAME("bay") +ACPI_MODULE_NAME("bay"); MODULE_AUTHOR("Kristen Carlson Accardi"); -MODULE_DESCRIPTION(ACPI_BAY_DRIVER_NAME); +MODULE_DESCRIPTION("ACPI Removable Drive Bay Driver"); MODULE_LICENSE("GPL"); #define ACPI_BAY_CLASS "bay" #define ACPI_BAY_COMPONENT 0x10000000 @@ -47,18 +45,6 @@ MODULE_LICENSE("GPL"); acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\ printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); } static void bay_notify(acpi_handle handle, u32 event, void *data); -static int acpi_bay_add(struct acpi_device *device); -static int acpi_bay_remove(struct acpi_device *device, int type); - -static struct acpi_driver acpi_bay_driver = { - .name = ACPI_BAY_DRIVER_NAME, - .class = ACPI_BAY_CLASS, - .ids = ACPI_BAY_HID, - .ops = { - .add = acpi_bay_add, - .remove = acpi_bay_remove, - }, -}; struct bay { acpi_handle handle; @@ -234,14 +220,6 @@ int eject_removable_drive(struct device *dev) } EXPORT_SYMBOL_GPL(eject_removable_drive); -static int acpi_bay_add(struct acpi_device *device) -{ - bay_dprintk(device->handle, "adding bay device"); - strcpy(acpi_device_name(device), "Dockable Bay"); - strcpy(acpi_device_class(device), "bay"); - return 0; -} - static int acpi_bay_add_fs(struct bay *bay) { int ret; @@ -303,7 +281,7 @@ static int bay_add(acpi_handle handle, int id) /* initialize platform device stuff */ pdev = platform_device_register_simple(ACPI_BAY_CLASS, id, NULL, 0); - if (pdev == NULL) { + if (IS_ERR(pdev)) { printk(KERN_ERR PREFIX "Error registering bay device\n"); goto bay_add_err; } @@ -339,52 +317,6 @@ bay_add_err: return -ENODEV; } -static int acpi_bay_remove(struct acpi_device *device, int type) -{ - /*** FIXME: do something here */ - return 0; -} - -/** - * bay_create_acpi_device - add new devices to acpi - * @handle - handle of the device to add - * - * This function will create a new acpi_device for the given - * handle if one does not exist already. This should cause - * acpi to scan for drivers for the given devices, and call - * matching driver's add routine. - * - * Returns a pointer to the acpi_device corresponding to the handle. - */ -static struct acpi_device * bay_create_acpi_device(acpi_handle handle) -{ - struct acpi_device *device = NULL; - struct acpi_device *parent_device; - acpi_handle parent; - int ret; - - bay_dprintk(handle, "Trying to get device"); - if (acpi_bus_get_device(handle, &device)) { - /* - * no device created for this object, - * so we should create one. - */ - bay_dprintk(handle, "No device for handle"); - acpi_get_parent(handle, &parent); - if (acpi_bus_get_device(parent, &parent_device)) - parent_device = NULL; - - ret = acpi_bus_add(&device, parent_device, handle, - ACPI_BUS_TYPE_DEVICE); - if (ret) { - pr_debug("error adding bus, %x\n", - -ret); - return NULL; - } - } - return device; -} - /** * bay_notify - act upon an acpi bay notification * @handle: the bay handle @@ -394,38 +326,19 @@ static struct acpi_device * bay_create_acpi_device(acpi_handle handle) */ static void bay_notify(acpi_handle handle, u32 event, void *data) { - struct acpi_device *dev; + struct bay *bay_dev = (struct bay *)data; + struct device *dev = &bay_dev->pdev->dev; bay_dprintk(handle, "Bay event"); switch(event) { case ACPI_NOTIFY_BUS_CHECK: - printk("Bus Check\n"); case ACPI_NOTIFY_DEVICE_CHECK: - printk("Device Check\n"); - dev = bay_create_acpi_device(handle); - if (dev) - acpi_bus_generate_event(dev, event, 0); - else - printk("No device for generating event\n"); - /* wouldn't it be a good idea to just rescan SATA - * right here? - */ - break; case ACPI_NOTIFY_EJECT_REQUEST: - printk("Eject request\n"); - dev = bay_create_acpi_device(handle); - if (dev) - acpi_bus_generate_event(dev, event, 0); - else - printk("No device for generating eventn"); - - /* wouldn't it be a good idea to just call the - * eject_device here if we were a SATA device? - */ + kobject_uevent(&dev->kobj, KOBJ_CHANGE); break; default: - printk("unknown event %d\n", event); + printk(KERN_ERR PREFIX "Bay: unknown event %d\n", event); } } @@ -457,10 +370,6 @@ static int __init bay_init(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_bay, &bays, NULL); - if (bays) - if ((acpi_bus_register_driver(&acpi_bay_driver) < 0)) - printk(KERN_ERR "Unable to register bay driver\n"); - if (!bays) return -ENODEV; @@ -481,8 +390,6 @@ static void __exit bay_exit(void) kfree(bay->name); kfree(bay); } - - acpi_bus_unregister_driver(&acpi_bay_driver); } postcore_initcall(bay_init); diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index f289fd4..3ec110c 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -79,11 +79,17 @@ static int __init blacklist_by_year(void) { int year = dmi_get_year(DMI_BIOS_DATE); /* Doesn't exist? Likely an old system */ - if (year == -1) + if (year == -1) { + printk(KERN_ERR PREFIX "no DMI BIOS year, " + "acpi=force is required to enable ACPI\n" ); return 1; + } /* 0? Likely a buggy new BIOS */ - if (year == 0) + if (year == 0) { + printk(KERN_ERR PREFIX "DMI BIOS year==0, " + "assuming ACPI-capable machine\n" ); return 0; + } if (year < CONFIG_ACPI_BLACKLIST_YEAR) { printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), " "acpi=force is required to enable ACPI\n", diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c26468d..e5084ec 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -39,7 +39,7 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_BUS_COMPONENT -ACPI_MODULE_NAME("acpi_bus") +ACPI_MODULE_NAME("bus"); #ifdef CONFIG_X86 extern void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger); #endif @@ -103,7 +103,9 @@ int acpi_bus_get_status(struct acpi_device *device) else if (device->parent) device->status = device->parent->status; else - STRUCT_TO_INT(device->status) = 0x0F; + STRUCT_TO_INT(device->status) = + ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; if (device->status.functional && !device->status.present) { printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: " @@ -147,7 +149,7 @@ int acpi_bus_get_power(acpi_handle handle, int *state) *state = ACPI_STATE_D0; } else { /* - * Get the device's power state either directly (via _PSC) or + * Get the device's power state either directly (via _PSC) or * indirectly (via power resources). */ if (device->power.flags.explicit_get) { @@ -199,15 +201,14 @@ int acpi_bus_set_power(acpi_handle handle, int state) * Get device's current power state if it's unknown * This means device power state isn't initialized or previous setting failed */ - if (!device->flags.force_power_state) { - if (device->power.state == ACPI_STATE_UNKNOWN) - acpi_bus_get_power(device->handle, &device->power.state); - if (state == device->power.state) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", - state)); - return 0; - } + if ((device->power.state == ACPI_STATE_UNKNOWN) || device->flags.force_power_state) + acpi_bus_get_power(device->handle, &device->power.state); + if ((state == device->power.state) && !device->flags.force_power_state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", + state)); + return 0; } + if (!device->power.states[state].flags.valid) { printk(KERN_WARNING PREFIX "Device does not support D%d\n", state); return -ENODEV; @@ -462,7 +463,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) "Received BUS CHECK notification for device [%s]\n", device->pnp.bus_id)); result = acpi_bus_check_scope(device); - /* + /* * TBD: We'll need to outsource certain events to non-ACPI * drivers via the device manager (device.c). */ @@ -473,7 +474,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) "Received DEVICE CHECK notification for device [%s]\n", device->pnp.bus_id)); result = acpi_bus_check_device(device, NULL); - /* + /* * TBD: We'll need to outsource certain events to non-ACPI * drivers via the device manager (device.c). */ @@ -543,7 +544,7 @@ static int __init acpi_bus_init_irq(void) char *message = NULL; - /* + /* * Let the system know what interrupt model we are using by * evaluating the \_PIC object, if exists. */ @@ -684,7 +685,7 @@ static int __init acpi_bus_init(void) * the EC device is found in the namespace (i.e. before acpi_initialize_objects() * is called). * - * This is accomplished by looking for the ECDT table, and getting + * This is accomplished by looking for the ECDT table, and getting * the EC parameters out of that. */ status = acpi_ec_ecdt_probe(); @@ -699,6 +700,9 @@ static int __init acpi_bus_init(void) printk(KERN_INFO PREFIX "Interpreter enabled\n"); + /* Initialize sleep structures */ + acpi_sleep_init(); + /* * Get the system interrupt model and evaluate \_PIC. */ diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index c726612..cb4110b 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -34,7 +34,6 @@ #include <acpi/acpi_drivers.h> #define ACPI_BUTTON_COMPONENT 0x00080000 -#define ACPI_BUTTON_DRIVER_NAME "ACPI Button Driver" #define ACPI_BUTTON_CLASS "button" #define ACPI_BUTTON_FILE_INFO "info" #define ACPI_BUTTON_FILE_STATE "state" @@ -61,10 +60,10 @@ #define ACPI_BUTTON_TYPE_LID 0x05 #define _COMPONENT ACPI_BUTTON_COMPONENT -ACPI_MODULE_NAME("acpi_button") +ACPI_MODULE_NAME("button"); MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); +MODULE_DESCRIPTION("ACPI Button Driver"); MODULE_LICENSE("GPL"); static int acpi_button_add(struct acpi_device *device); @@ -73,7 +72,7 @@ static int acpi_button_info_open_fs(struct inode *inode, struct file *file); static int acpi_button_state_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_button_driver = { - .name = ACPI_BUTTON_DRIVER_NAME, + .name = "button", .class = ACPI_BUTTON_CLASS, .ids = "button_power,button_sleep,PNP0C0D,PNP0C0C,PNP0C0E", .ops = { diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 4a9b7bf..f9db4f4 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -31,7 +31,7 @@ #include <acpi/actypes.h> #include <acpi/acutils.h> -ACPI_MODULE_NAME("cm_sbs") +ACPI_MODULE_NAME("cm_sbs"); #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" #define ACPI_SBS_COMPONENT 0x00080000 diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 69a68fd..0dd3bf7 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -35,7 +35,6 @@ #include <acpi/acpi_drivers.h> #include <acpi/container.h> -#define ACPI_CONTAINER_DRIVER_NAME "ACPI container driver" #define ACPI_CONTAINER_DEVICE_NAME "ACPI container device" #define ACPI_CONTAINER_CLASS "container" @@ -44,19 +43,17 @@ #define ACPI_CONTAINER_COMPONENT 0x01000000 #define _COMPONENT ACPI_CONTAINER_COMPONENT -ACPI_MODULE_NAME("acpi_container") +ACPI_MODULE_NAME("container"); - MODULE_AUTHOR("Anil S Keshavamurthy"); -MODULE_DESCRIPTION(ACPI_CONTAINER_DRIVER_NAME); +MODULE_AUTHOR("Anil S Keshavamurthy"); +MODULE_DESCRIPTION("ACPI container driver"); MODULE_LICENSE("GPL"); -#define ACPI_STA_PRESENT (0x00000001) - static int acpi_container_add(struct acpi_device *device); static int acpi_container_remove(struct acpi_device *device, int type); static struct acpi_driver acpi_container_driver = { - .name = ACPI_CONTAINER_DRIVER_NAME, + .name = "container", .class = ACPI_CONTAINER_CLASS, .ids = "ACPI0004,PNP0A05,PNP0A06", .ops = { @@ -76,13 +73,13 @@ static int is_device_present(acpi_handle handle) status = acpi_get_handle(handle, "_STA", &temp); if (ACPI_FAILURE(status)) - return 1; /* _STA not found, assmue device present */ + return 1; /* _STA not found, assume device present */ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); if (ACPI_FAILURE(status)) return 0; /* Firmware error */ - return ((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT); + return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT); } /*******************************************************************/ diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index d48f65a..bf513e0 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -12,7 +12,7 @@ #include <acpi/acglobal.h> #define _COMPONENT ACPI_SYSTEM_COMPONENT -ACPI_MODULE_NAME("debug") +ACPI_MODULE_NAME("debug"); #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c index 1cbe619..1683e5c 100644 --- a/drivers/acpi/dispatcher/dsmethod.c +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -231,10 +231,8 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, * Obtain the method mutex if necessary. Do not acquire mutex for a * recursive call. */ - if (!walk_state || - !obj_desc->method.mutex->mutex.owner_thread || - (walk_state->thread != - obj_desc->method.mutex->mutex.owner_thread)) { + if (acpi_os_get_thread_id() != + obj_desc->method.mutex->mutex.owner_thread_id) { /* * Acquire the method mutex. This releases the interpreter if we * block (and reacquires it before it returns) @@ -248,14 +246,14 @@ acpi_ds_begin_method_execution(struct acpi_namespace_node *method_node, } /* Update the mutex and walk info and save the original sync_level */ + obj_desc->method.mutex->mutex.owner_thread_id = + acpi_os_get_thread_id(); if (walk_state) { obj_desc->method.mutex->mutex. original_sync_level = walk_state->thread->current_sync_level; - obj_desc->method.mutex->mutex.owner_thread = - walk_state->thread; walk_state->thread->current_sync_level = obj_desc->method.sync_level; } else { @@ -569,7 +567,7 @@ acpi_ds_terminate_control_method(union acpi_operand_object *method_desc, acpi_os_release_mutex(method_desc->method.mutex->mutex. os_mutex); - method_desc->method.mutex->mutex.owner_thread = NULL; + method_desc->method.mutex->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; } } diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 688e83a..4546bf8 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -29,14 +29,15 @@ #include <linux/notifier.h> #include <linux/platform_device.h> #include <linux/jiffies.h> +#include <linux/stddef.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_DOCK_DRIVER_NAME "ACPI Dock Station Driver" +#define ACPI_DOCK_DRIVER_DESCRIPTION "ACPI Dock Station Driver" -ACPI_MODULE_NAME("dock") +ACPI_MODULE_NAME("dock"); MODULE_AUTHOR("Kristen Carlson Accardi"); -MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_NAME); +MODULE_DESCRIPTION(ACPI_DOCK_DRIVER_DESCRIPTION); MODULE_LICENSE("GPL"); static struct atomic_notifier_head dock_notifier_list; @@ -667,6 +668,23 @@ static ssize_t write_undock(struct device *dev, struct device_attribute *attr, } DEVICE_ATTR(undock, S_IWUSR, NULL, write_undock); +/* + * show_dock_uid - read method for "uid" file in sysfs + */ +static ssize_t show_dock_uid(struct device *dev, + struct device_attribute *attr, char *buf) +{ + unsigned long lbuf; + acpi_status status = acpi_evaluate_integer(dock_station->handle, "_UID", NULL, &lbuf); + if(ACPI_FAILURE(status)) { + return 0; + } + return snprintf(buf, PAGE_SIZE, "%lx\n", lbuf); +} +DEVICE_ATTR(uid, S_IRUGO, show_dock_uid, NULL); + + + /** * dock_add - add a new dock station * @handle: the dock station handle @@ -715,6 +733,13 @@ static int dock_add(acpi_handle handle) kfree(dock_station); return ret; } + ret = device_create_file(&dock_device.dev, &dev_attr_uid); + if (ret) { + printk("Error %d adding sysfs file\n", ret); + platform_device_unregister(&dock_device); + kfree(dock_station); + return ret; + } /* Find dependent devices */ acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, @@ -741,7 +766,7 @@ static int dock_add(acpi_handle handle) goto dock_add_err; } - printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_NAME); + printk(KERN_INFO PREFIX "%s \n", ACPI_DOCK_DRIVER_DESCRIPTION); return 0; diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 743ce27..e08cf98 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1,6 +1,8 @@ /* - * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) + * ec.c - ACPI Embedded Controller Driver (v2.0) * + * Copyright (C) 2006, 2007 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> + * Copyright (C) 2006 Denis Sadykov <denis.m.sadykov@intel.com> * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> @@ -38,11 +40,10 @@ #include <acpi/actypes.h> #define _COMPONENT ACPI_EC_COMPONENT -ACPI_MODULE_NAME("acpi_ec") +ACPI_MODULE_NAME("ec"); #define ACPI_EC_COMPONENT 0x00100000 #define ACPI_EC_CLASS "embedded_controller" #define ACPI_EC_HID "PNP0C09" -#define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver" #define ACPI_EC_DEVICE_NAME "Embedded Controller" #define ACPI_EC_FILE_INFO "info" #undef PREFIX @@ -80,7 +81,7 @@ static int acpi_ec_stop(struct acpi_device *device, int type); static int acpi_ec_add(struct acpi_device *device); static struct acpi_driver acpi_ec_driver = { - .name = ACPI_EC_DRIVER_NAME, + .name = "ec", .class = ACPI_EC_CLASS, .ids = ACPI_EC_HID, .ops = { @@ -92,21 +93,18 @@ static struct acpi_driver acpi_ec_driver = { }; /* If we find an EC via the ECDT, we need to keep a ptr to its context */ +/* External interfaces use first EC only, so remember */ static struct acpi_ec { acpi_handle handle; - unsigned long uid; unsigned long gpe; unsigned long command_addr; unsigned long data_addr; unsigned long global_lock; struct mutex lock; atomic_t query_pending; - atomic_t leaving_burst; /* 0 : No, 1 : Yes, 2: abort */ + atomic_t event_count; wait_queue_head_t wait; -} *ec_ecdt; - -/* External interfaces use first EC only, so remember */ -static struct acpi_device *first_ec; +} *boot_ec, *first_ec; /* -------------------------------------------------------------------------- Transaction Management @@ -132,10 +130,12 @@ static inline void acpi_ec_write_data(struct acpi_ec *ec, u8 data) outb(data, ec->data_addr); } -static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) +static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event, + unsigned old_count) { u8 status = acpi_ec_read_status(ec); - + if (old_count == atomic_read(&ec->event_count)) + return 0; if (event == ACPI_EC_EVENT_OBF_1) { if (status & ACPI_EC_FLAG_OBF) return 1; @@ -147,19 +147,19 @@ static inline int acpi_ec_check_status(struct acpi_ec *ec, enum ec_event event) return 0; } -static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event) +static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, unsigned count) { if (acpi_ec_mode == EC_POLL) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); while (time_before(jiffies, delay)) { - if (acpi_ec_check_status(ec, event)) + if (acpi_ec_check_status(ec, event, 0)) return 0; } } else { if (wait_event_timeout(ec->wait, - acpi_ec_check_status(ec, event), + acpi_ec_check_status(ec, event, count), msecs_to_jiffies(ACPI_EC_DELAY)) || - acpi_ec_check_status(ec, event)) { + acpi_ec_check_status(ec, event, 0)) { return 0; } else { printk(KERN_ERR PREFIX "acpi_ec_wait timeout," @@ -171,76 +171,27 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event) return -ETIME; } -#ifdef ACPI_FUTURE_USAGE -/* - * Note: samsung nv5000 doesn't work with ec burst mode. - * http://bugzilla.kernel.org/show_bug.cgi?id=4980 - */ -int acpi_ec_enter_burst_mode(struct acpi_ec *ec) -{ - u8 tmp = 0; - u8 status = 0; - - status = acpi_ec_read_status(ec); - if (status != -EINVAL && !(status & ACPI_EC_FLAG_BURST)) { - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (status) - goto end; - acpi_ec_write_cmd(ec, ACPI_EC_BURST_ENABLE); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); - tmp = acpi_ec_read_data(ec); - if (tmp != 0x90) { /* Burst ACK byte */ - return -EINVAL; - } - } - - atomic_set(&ec->leaving_burst, 0); - return 0; - end: - ACPI_EXCEPTION((AE_INFO, status, "EC wait, burst mode")); - return -1; -} - -int acpi_ec_leave_burst_mode(struct acpi_ec *ec) -{ - u8 status = 0; - - status = acpi_ec_read_status(ec); - if (status != -EINVAL && (status & ACPI_EC_FLAG_BURST)) { - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - if (status) - goto end; - acpi_ec_write_cmd(ec, ACPI_EC_BURST_DISABLE); - acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); - } - atomic_set(&ec->leaving_burst, 1); - return 0; - end: - ACPI_EXCEPTION((AE_INFO, status, "EC leave burst mode")); - return -1; -} -#endif /* ACPI_FUTURE_USAGE */ - static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, const u8 * wdata, unsigned wdata_len, u8 * rdata, unsigned rdata_len) { int result = 0; - + unsigned count = atomic_read(&ec->event_count); acpi_ec_write_cmd(ec, command); for (; wdata_len > 0; --wdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); if (result) { printk(KERN_ERR PREFIX "write_cmd timeout, command = %d\n", command); goto end; } + count = atomic_read(&ec->event_count); acpi_ec_write_data(ec, *(wdata++)); } if (!rdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, count); if (result) { printk(KERN_ERR PREFIX "finish-write timeout, command = %d\n", command); @@ -251,13 +202,13 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, u8 command, } for (; rdata_len > 0; --rdata_len) { - result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF_1, count); if (result) { printk(KERN_ERR PREFIX "read timeout, command = %d\n", command); goto end; } - + count = atomic_read(&ec->event_count); *(rdata++) = acpi_ec_read_data(ec); } end: @@ -280,14 +231,16 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, mutex_lock(&ec->lock); if (ec->global_lock) { status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); - if (ACPI_FAILURE(status)) + if (ACPI_FAILURE(status)) { + mutex_unlock(&ec->lock); return -ENODEV; + } } /* Make sure GPE is enabled before doing transaction */ acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); - status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0); + status = acpi_ec_wait(ec, ACPI_EC_EVENT_IBF_0, 0); if (status) { printk(KERN_DEBUG PREFIX "input buffer is not empty, aborting transaction\n"); @@ -307,6 +260,21 @@ static int acpi_ec_transaction(struct acpi_ec *ec, u8 command, return status; } +/* + * Note: samsung nv5000 doesn't work with ec burst mode. + * http://bugzilla.kernel.org/show_bug.cgi?id=4980 + */ +int acpi_ec_burst_enable(struct acpi_ec *ec) +{ + u8 d; + return acpi_ec_transaction(ec, ACPI_EC_BURST_ENABLE, NULL, 0, &d, 1); +} + +int acpi_ec_burst_disable(struct acpi_ec *ec) +{ + return acpi_ec_transaction(ec, ACPI_EC_BURST_DISABLE, NULL, 0, NULL, 0); +} + static int acpi_ec_read(struct acpi_ec *ec, u8 address, u8 * data) { int result; @@ -328,18 +296,33 @@ static int acpi_ec_write(struct acpi_ec *ec, u8 address, u8 data) /* * Externally callable EC access functions. For now, assume 1 EC only */ +int ec_burst_enable(void) +{ + if (!first_ec) + return -ENODEV; + return acpi_ec_burst_enable(first_ec); +} + +EXPORT_SYMBOL(ec_burst_enable); + +int ec_burst_disable(void) +{ + if (!first_ec) + return -ENODEV; + return acpi_ec_burst_disable(first_ec); +} + +EXPORT_SYMBOL(ec_burst_disable); + int ec_read(u8 addr, u8 * val) { - struct acpi_ec *ec; int err; u8 temp_data; if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - err = acpi_ec_read(ec, addr, &temp_data); + err = acpi_ec_read(first_ec, addr, &temp_data); if (!err) { *val = temp_data; @@ -352,15 +335,12 @@ EXPORT_SYMBOL(ec_read); int ec_write(u8 addr, u8 val) { - struct acpi_ec *ec; int err; if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - err = acpi_ec_write(ec, addr, val); + err = acpi_ec_write(first_ec, addr, val); return err; } @@ -368,17 +348,13 @@ int ec_write(u8 addr, u8 val) EXPORT_SYMBOL(ec_write); int ec_transaction(u8 command, - const u8 * wdata, unsigned wdata_len, - u8 * rdata, unsigned rdata_len) + const u8 * wdata, unsigned wdata_len, + u8 * rdata, unsigned rdata_len) { - struct acpi_ec *ec; - if (!first_ec) return -ENODEV; - ec = acpi_driver_data(first_ec); - - return acpi_ec_transaction(ec, command, wdata, + return acpi_ec_transaction(first_ec, command, wdata, wdata_len, rdata, rdata_len); } @@ -415,7 +391,7 @@ static int acpi_ec_query(struct acpi_ec *ec, u8 * data) static void acpi_ec_gpe_query(void *ec_cxt) { - struct acpi_ec *ec = (struct acpi_ec *)ec_cxt; + struct acpi_ec *ec = ec_cxt; u8 value = 0; char object_name[8]; @@ -433,7 +409,8 @@ static u32 acpi_ec_gpe_handler(void *data) { acpi_status status = AE_OK; u8 value; - struct acpi_ec *ec = (struct acpi_ec *)data; + struct acpi_ec *ec = data; + atomic_inc(&ec->event_count); if (acpi_ec_mode == EC_INTR) { wake_up(&ec->wait); @@ -477,7 +454,7 @@ acpi_ec_space_handler(u32 function, void *handler_context, void *region_context) { int result = 0; - struct acpi_ec *ec = NULL; + struct acpi_ec *ec = handler_context; u64 temp = *value; acpi_integer f_v = 0; int i = 0; @@ -489,8 +466,6 @@ acpi_ec_space_handler(u32 function, return AE_BAD_PARAMETER; } - ec = (struct acpi_ec *)handler_context; - next_byte: switch (function) { case ACPI_READ: @@ -546,18 +521,16 @@ static struct proc_dir_entry *acpi_ec_dir; static int acpi_ec_read_info(struct seq_file *seq, void *offset) { - struct acpi_ec *ec = (struct acpi_ec *)seq->private; + struct acpi_ec *ec = seq->private; if (!ec) goto end; - seq_printf(seq, "gpe: 0x%02x\n", (u32) ec->gpe); - seq_printf(seq, "ports: 0x%02x, 0x%02x\n", - (u32) ec->command_addr, (u32) ec->data_addr); - seq_printf(seq, "use global lock: %s\n", + seq_printf(seq, "gpe:\t\t\t0x%02x\n", (u32) ec->gpe); + seq_printf(seq, "ports:\t\t\t0x%02x, 0x%02x\n", + (unsigned)ec->command_addr, (unsigned)ec->data_addr); + seq_printf(seq, "use global lock:\t%s\n", ec->global_lock ? "yes" : "no"); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); - end: return 0; } @@ -614,153 +587,122 @@ static int acpi_ec_remove_fs(struct acpi_device *device) /* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ +static acpi_status +ec_parse_io_ports(struct acpi_resource *resource, void *context); + +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval); + +static struct acpi_ec *make_acpi_ec(void) +{ + struct acpi_ec *ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec) + return NULL; + + atomic_set(&ec->query_pending, 1); + atomic_set(&ec->event_count, 1); + mutex_init(&ec->lock); + init_waitqueue_head(&ec->wait); + + return ec; +} static int acpi_ec_add(struct acpi_device *device) { - int result = 0; acpi_status status = AE_OK; struct acpi_ec *ec = NULL; if (!device) return -EINVAL; - ec = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec) - return -ENOMEM; - - ec->handle = device->handle; - ec->uid = -1; - mutex_init(&ec->lock); - atomic_set(&ec->query_pending, 0); - if (acpi_ec_mode == EC_INTR) { - atomic_set(&ec->leaving_burst, 1); - init_waitqueue_head(&ec->wait); - } strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_EC_CLASS); - acpi_driver_data(device) = ec; - /* Use the global lock for all EC transactions? */ - acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); - - /* XXX we don't test uids, because on some boxes ecdt uid = 0, see: - http://bugzilla.kernel.org/show_bug.cgi?id=6111 */ - if (ec_ecdt) { - acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler); - - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, - &acpi_ec_gpe_handler); + ec = make_acpi_ec(); + if (!ec) + return -ENOMEM; - kfree(ec_ecdt); + status = ec_parse_device(device->handle, 0, ec, NULL); + if (status != AE_CTRL_TERMINATE) { + kfree(ec); + return -EINVAL; } - /* Get GPE bit assignment (EC events). */ - /* TODO: Add support for _GPE returning a package */ - status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Obtaining GPE bit assignment")); - result = -ENODEV; - goto end; - } + /* Check if we found the boot EC */ + if (boot_ec) { + if (boot_ec->gpe == ec->gpe) { + /* We might have incorrect info for GL at boot time */ + mutex_lock(&boot_ec->lock); + boot_ec->global_lock = ec->global_lock; + mutex_unlock(&boot_ec->lock); + kfree(ec); + ec = boot_ec; + } + } else + first_ec = ec; + ec->handle = device->handle; + acpi_driver_data(device) = ec; - result = acpi_ec_add_fs(device); - if (result) - goto end; + acpi_ec_add_fs(device); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s [%s] (gpe %d) interrupt mode.", acpi_device_name(device), acpi_device_bid(device), (u32) ec->gpe)); - if (!first_ec) - first_ec = device; - - end: - if (result) - kfree(ec); - - return result; + return 0; } static int acpi_ec_remove(struct acpi_device *device, int type) { - struct acpi_ec *ec = NULL; + struct acpi_ec *ec; if (!device) return -EINVAL; ec = acpi_driver_data(device); - acpi_ec_remove_fs(device); + acpi_driver_data(device) = NULL; + if (ec == first_ec) + first_ec = NULL; - kfree(ec); - + /* Don't touch boot EC */ + if (boot_ec != ec) + kfree(ec); return 0; } static acpi_status -acpi_ec_io_ports(struct acpi_resource *resource, void *context) +ec_parse_io_ports(struct acpi_resource *resource, void *context) { - struct acpi_ec *ec = (struct acpi_ec *)context; + struct acpi_ec *ec = context; - if (resource->type != ACPI_RESOURCE_TYPE_IO) { + if (resource->type != ACPI_RESOURCE_TYPE_IO) return AE_OK; - } /* * The first address region returned is the data port, and * the second address region returned is the status/command * port. */ - if (ec->data_addr == 0) { + if (ec->data_addr == 0) ec->data_addr = resource->data.io.minimum; - } else if (ec->command_addr == 0) { + else if (ec->command_addr == 0) ec->command_addr = resource->data.io.minimum; - } else { + else return AE_CTRL_TERMINATE; - } return AE_OK; } -static int acpi_ec_start(struct acpi_device *device) +static int ec_install_handlers(struct acpi_ec *ec) { - acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; - - if (!device) - return -EINVAL; - - ec = acpi_driver_data(device); - - if (!ec) - return -EINVAL; - - /* - * Get I/O port addresses. Convert to GAS format. - */ - status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, - acpi_ec_io_ports, ec); - if (ACPI_FAILURE(status) || ec->command_addr == 0) { - ACPI_EXCEPTION((AE_INFO, status, - "Error getting I/O port addresses")); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", - ec->gpe, ec->command_addr, ec->data_addr)); - - /* - * Install GPE handler - */ + acpi_status status; status = acpi_install_gpe_handler(NULL, ec->gpe, ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status)) return -ENODEV; - } + acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); @@ -773,18 +715,49 @@ static int acpi_ec_start(struct acpi_device *device) return -ENODEV; } - return AE_OK; + /* EC is fully operational, allow queries */ + atomic_set(&ec->query_pending, 0); + + return 0; +} + +static int acpi_ec_start(struct acpi_device *device) +{ + struct acpi_ec *ec; + + if (!device) + return -EINVAL; + + ec = acpi_driver_data(device); + + if (!ec) + return -EINVAL; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02lx, ports=0x%2lx,0x%2lx", + ec->gpe, ec->command_addr, ec->data_addr)); + + /* Boot EC is already working */ + if (ec == boot_ec) + return 0; + + return ec_install_handlers(ec); } static int acpi_ec_stop(struct acpi_device *device, int type) { - acpi_status status = AE_OK; - struct acpi_ec *ec = NULL; + acpi_status status; + struct acpi_ec *ec; if (!device) return -EINVAL; ec = acpi_driver_data(device); + if (!ec) + return -EINVAL; + + /* Don't touch boot EC */ + if (ec == boot_ec) + return 0; status = acpi_remove_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, @@ -799,162 +772,67 @@ static int acpi_ec_stop(struct acpi_device *device, int type) return 0; } -static acpi_status __init -acpi_fake_ecdt_callback(acpi_handle handle, - u32 Level, void *context, void **retval) +static acpi_status +ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { acpi_status status; - mutex_init(&ec_ecdt->lock); - if (acpi_ec_mode == EC_INTR) { - init_waitqueue_head(&ec_ecdt->wait); - } + struct acpi_ec *ec = context; status = acpi_walk_resources(handle, METHOD_NAME__CRS, - acpi_ec_io_ports, ec_ecdt); + ec_parse_io_ports, ec); if (ACPI_FAILURE(status)) return status; - ec_ecdt->uid = -1; - acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); - - status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe); + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec->gpe); if (ACPI_FAILURE(status)) return status; - ec_ecdt->global_lock = TRUE; - ec_ecdt->handle = handle; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", - ec_ecdt->gpe, ec_ecdt->command_addr, - ec_ecdt->data_addr)); - - return AE_CTRL_TERMINATE; -} -/* - * Some BIOS (such as some from Gateway laptops) access EC region very early - * such as in BAT0._INI or EC._INI before an EC device is found and - * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily - * required, but if EC regison is accessed early, it is required. - * The routine tries to workaround the BIOS bug by pre-scan EC device - * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any - * op region (since _REG isn't invoked yet). The assumption is true for - * all systems found. - */ -static int __init acpi_ec_fake_ecdt(void) -{ - acpi_status status; - int ret = 0; + /* Use the global lock for all EC transactions? */ + acpi_evaluate_integer(handle, "_GLK", NULL, &ec->global_lock); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Try to make an fake ECDT")); + ec->handle = handle; - ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec_ecdt) { - ret = -ENOMEM; - goto error; - } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "GPE=0x%02lx, ports=0x%2lx, 0x%2lx", + ec->gpe, ec->command_addr, ec->data_addr)); - status = acpi_get_devices(ACPI_EC_HID, - acpi_fake_ecdt_callback, NULL, NULL); - if (ACPI_FAILURE(status)) { - kfree(ec_ecdt); - ec_ecdt = NULL; - ret = -ENODEV; - ACPI_EXCEPTION((AE_INFO, status, "Can't make an fake ECDT")); - goto error; - } - return 0; - error: - return ret; + return AE_CTRL_TERMINATE; } -static int __init acpi_ec_get_real_ecdt(void) +int __init acpi_ec_ecdt_probe(void) { + int ret; acpi_status status; struct acpi_table_ecdt *ecdt_ptr; - status = acpi_get_table(ACPI_SIG_ECDT, 1, - (struct acpi_table_header **)&ecdt_ptr); - if (ACPI_FAILURE(status)) - return -ENODEV; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); - + boot_ec = make_acpi_ec(); + if (!boot_ec) + return -ENOMEM; /* - * Generate a temporary ec context to use until the namespace is scanned + * Generate a boot ec context */ - ec_ecdt = kzalloc(sizeof(struct acpi_ec), GFP_KERNEL); - if (!ec_ecdt) - return -ENOMEM; - mutex_init(&ec_ecdt->lock); - if (acpi_ec_mode == EC_INTR) { - init_waitqueue_head(&ec_ecdt->wait); - } - ec_ecdt->command_addr = ecdt_ptr->control.address; - ec_ecdt->data_addr = ecdt_ptr->data.address; - ec_ecdt->gpe = ecdt_ptr->gpe; - /* use the GL just to be safe */ - ec_ecdt->global_lock = TRUE; - ec_ecdt->uid = ecdt_ptr->uid; - - status = acpi_get_handle(NULL, ecdt_ptr->id, &ec_ecdt->handle); - if (ACPI_FAILURE(status)) { + status = acpi_get_table(ACPI_SIG_ECDT, 1, + (struct acpi_table_header **)&ecdt_ptr); + if (ACPI_FAILURE(status)) goto error; - } - - return 0; - error: - ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); - kfree(ec_ecdt); - ec_ecdt = NULL; - return -ENODEV; -} - -static int __initdata acpi_fake_ecdt_enabled; -int __init acpi_ec_ecdt_probe(void) -{ - acpi_status status; - int ret; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found ECDT")); - ret = acpi_ec_get_real_ecdt(); - /* Try to make a fake ECDT */ - if (ret && acpi_fake_ecdt_enabled) { - ret = acpi_ec_fake_ecdt(); - } + boot_ec->command_addr = ecdt_ptr->control.address; + boot_ec->data_addr = ecdt_ptr->data.address; + boot_ec->gpe = ecdt_ptr->gpe; + boot_ec->handle = ACPI_ROOT_OBJECT; - if (ret) + ret = ec_install_handlers(boot_ec); + if (!ret) { + first_ec = boot_ec; return 0; - - /* - * Install GPE handler - */ - status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe, - ACPI_GPE_EDGE_TRIGGERED, - &acpi_ec_gpe_handler, ec_ecdt); - if (ACPI_FAILURE(status)) { - goto error; } - acpi_set_gpe_type(NULL, ec_ecdt->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec_ecdt->gpe, ACPI_NOT_ISR); - - status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT, - ACPI_ADR_SPACE_EC, - &acpi_ec_space_handler, - &acpi_ec_space_setup, - ec_ecdt); - if (ACPI_FAILURE(status)) { - acpi_remove_gpe_handler(NULL, ec_ecdt->gpe, - &acpi_ec_gpe_handler); - goto error; - } - - return 0; - error: - ACPI_EXCEPTION((AE_INFO, status, "Could not use ECDT")); - kfree(ec_ecdt); - ec_ecdt = NULL; + kfree(boot_ec); + boot_ec = NULL; return -ENODEV; } @@ -995,13 +873,6 @@ static void __exit acpi_ec_exit(void) } #endif /* 0 */ -static int __init acpi_fake_ecdt_setup(char *str) -{ - acpi_fake_ecdt_enabled = 1; - return 1; -} - -__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); static int __init acpi_ec_set_intr_mode(char *str) { int intr; @@ -1009,14 +880,9 @@ static int __init acpi_ec_set_intr_mode(char *str) if (!get_option(&str, &intr)) return 0; - if (intr) { - acpi_ec_mode = EC_INTR; - } else { - acpi_ec_mode = EC_POLL; - } - acpi_ec_driver.ops.add = acpi_ec_add; - printk(KERN_NOTICE PREFIX "%s mode.\n", - intr ? "interrupt" : "polling"); + acpi_ec_mode = (intr) ? EC_INTR : EC_POLL; + + printk(KERN_NOTICE PREFIX "%s mode.\n", intr ? "interrupt" : "polling"); return 1; } diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c index 959a893..3b23562 100644 --- a/drivers/acpi/event.c +++ b/drivers/acpi/event.c @@ -13,7 +13,7 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_SYSTEM_COMPONENT -ACPI_MODULE_NAME("event") +ACPI_MODULE_NAME("event"); /* Global vars for handling event proc entry */ static DEFINE_SPINLOCK(acpi_system_event_lock); diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index dfac3ec..635ba44 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -636,17 +636,6 @@ acpi_ev_gpe_dispatch(struct acpi_gpe_event_info *gpe_event_info, u32 gpe_number) } } - if (!acpi_gbl_system_awake_and_running) { - /* - * We just woke up because of a wake GPE. Disable any further GPEs - * until we are fully up and running (Only wake GPEs should be enabled - * at this time, but we just brute-force disable them all.) - * 1) We must disable this particular wake GPE so it won't fire again - * 2) We want to disable all wake GPEs, since we are now awake - */ - (void)acpi_hw_disable_all_gpes(); - } - /* * Dispatch the GPE to either an installed handler, or the control method * associated with this GPE (_Lxx or _Exx). If a handler exists, we invoke diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c index 1b784ffe..cae786c 100644 --- a/drivers/acpi/events/evmisc.c +++ b/drivers/acpi/events/evmisc.c @@ -196,12 +196,15 @@ acpi_ev_queue_notify_request(struct acpi_namespace_node * node, notify_info->notify.value = (u16) notify_value; notify_info->notify.handler_obj = handler_obj; - status = - acpi_os_execute(OSL_NOTIFY_HANDLER, acpi_ev_notify_dispatch, - notify_info); + acpi_ex_exit_interpreter(); + + acpi_ev_notify_dispatch(notify_info); + + status = acpi_ex_enter_interpreter(); if (ACPI_FAILURE(status)) { - acpi_ut_delete_generic_state(notify_info); + return_ACPI_STATUS(status); } + } if (!handler_obj) { @@ -424,6 +427,8 @@ static acpi_status acpi_ev_remove_global_lock_handler(void) * the global lock appear as a standard mutex on the OS side. * *****************************************************************************/ +static acpi_thread_id acpi_ev_global_lock_thread_id; +static int acpi_ev_global_lock_acquired; acpi_status acpi_ev_acquire_global_lock(u16 timeout) { @@ -436,11 +441,24 @@ acpi_status acpi_ev_acquire_global_lock(u16 timeout) * Only one thread can acquire the GL at a time, the global_lock_mutex * enforces this. This interface releases the interpreter if we must wait. */ - status = acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex, timeout); + status = acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex, 0); + if (status == AE_TIME) { + if (acpi_ev_global_lock_thread_id == acpi_os_get_thread_id()) { + acpi_ev_global_lock_acquired++; + return AE_OK; + } + } + + if (ACPI_FAILURE(status)) { + status = acpi_ex_system_wait_mutex(acpi_gbl_global_lock_mutex, timeout); + } if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } + acpi_ev_global_lock_thread_id = acpi_os_get_thread_id(); + acpi_ev_global_lock_acquired++; + /* * Make sure that a global lock actually exists. If not, just treat * the lock as a standard mutex. @@ -507,6 +525,11 @@ acpi_status acpi_ev_release_global_lock(void) return_ACPI_STATUS(AE_NOT_ACQUIRED); } + acpi_ev_global_lock_acquired--; + if (acpi_ev_global_lock_acquired > 0) { + return AE_OK; + } + if (acpi_gbl_global_lock_present) { /* Allow any thread to release the lock */ @@ -530,7 +553,8 @@ acpi_status acpi_ev_release_global_lock(void) acpi_gbl_global_lock_acquired = FALSE; /* Release the local GL mutex */ - + acpi_ev_global_lock_thread_id = NULL; + acpi_ev_global_lock_acquired = 0; acpi_os_release_mutex(acpi_gbl_global_lock_mutex); return_ACPI_STATUS(status); } diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c index e99f0c4..96b0e84 100644 --- a/drivers/acpi/events/evregion.c +++ b/drivers/acpi/events/evregion.c @@ -291,6 +291,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, u32 bit_width, acpi_integer * value) { acpi_status status; + acpi_status status2; acpi_adr_space_handler handler; acpi_adr_space_setup region_setup; union acpi_operand_object *handler_desc; @@ -344,7 +345,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * setup will potentially execute control methods * (e.g., _REG method for this region) */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); status = region_setup(region_obj, ACPI_REGION_ACTIVATE, handler_desc->address_space.context, @@ -352,7 +353,10 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, /* Re-enter the interpreter */ - acpi_ex_reacquire_interpreter(); + status2 = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } /* Check for failure of the Region Setup */ @@ -405,7 +409,7 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * exit the interpreter because the handler *might* block -- we don't * know what it will do, so we can't hold the lock on the intepreter. */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); } /* Call the handler */ @@ -426,7 +430,10 @@ acpi_ev_address_space_dispatch(union acpi_operand_object *region_obj, * We just returned from a non-default handler, we must re-enter the * interpreter */ - acpi_ex_reacquire_interpreter(); + status2 = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status2)) { + return_ACPI_STATUS(status2); + } } return_ACPI_STATUS(status); diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c index 685a103..a3379ba 100644 --- a/drivers/acpi/events/evxface.c +++ b/drivers/acpi/events/evxface.c @@ -768,9 +768,11 @@ acpi_status acpi_acquire_global_lock(u16 timeout, u32 * handle) return (AE_BAD_PARAMETER); } - /* Must lock interpreter to prevent race conditions */ + status = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + return (status); + } - acpi_ex_enter_interpreter(); status = acpi_ev_acquire_global_lock(timeout); acpi_ex_exit_interpreter(); diff --git a/drivers/acpi/executer/excreate.c b/drivers/acpi/executer/excreate.c index 7c38528..ae97812 100644 --- a/drivers/acpi/executer/excreate.c +++ b/drivers/acpi/executer/excreate.c @@ -583,7 +583,10 @@ acpi_ex_create_method(u8 * aml_start, * Get the sync_level. If method is serialized, a mutex will be * created for this method when it is parsed. */ - if (method_flags & AML_METHOD_SERIALIZED) { + if (acpi_gbl_all_methods_serialized) { + obj_desc->method.sync_level = 0; + obj_desc->method.method_flags |= AML_METHOD_SERIALIZED; + } else if (method_flags & AML_METHOD_SERIALIZED) { /* * ACPI 1.0: sync_level = 0 * ACPI 2.0: sync_level = sync_level in method declaration diff --git a/drivers/acpi/executer/exdump.c b/drivers/acpi/executer/exdump.c index 68d283f..1a73c14 100644 --- a/drivers/acpi/executer/exdump.c +++ b/drivers/acpi/executer/exdump.c @@ -134,7 +134,7 @@ static struct acpi_exdump_info acpi_ex_dump_method[8] = { static struct acpi_exdump_info acpi_ex_dump_mutex[5] = { {ACPI_EXD_INIT, ACPI_EXD_TABLE_SIZE(acpi_ex_dump_mutex), NULL}, {ACPI_EXD_UINT8, ACPI_EXD_OFFSET(mutex.sync_level), "Sync Level"}, - {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread), "Owner Thread"}, + {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.owner_thread_id), "Owner Thread"}, {ACPI_EXD_UINT16, ACPI_EXD_OFFSET(mutex.acquisition_depth), "Acquire Depth"}, {ACPI_EXD_POINTER, ACPI_EXD_OFFSET(mutex.os_mutex), "OsMutex"} diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c index 5101bad..4eb883b 100644 --- a/drivers/acpi/executer/exmutex.c +++ b/drivers/acpi/executer/exmutex.c @@ -66,10 +66,9 @@ acpi_ex_link_mutex(union acpi_operand_object *obj_desc, * ******************************************************************************/ -void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc) +void acpi_ex_unlink_mutex(union acpi_operand_object *obj_desc, + struct acpi_thread_state *thread) { - struct acpi_thread_state *thread = obj_desc->mutex.owner_thread; - if (!thread) { return; } @@ -174,16 +173,13 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, /* Support for multiple acquires by the owning thread */ - if (obj_desc->mutex.owner_thread) { - if (obj_desc->mutex.owner_thread->thread_id == - walk_state->thread->thread_id) { - /* - * The mutex is already owned by this thread, just increment the - * acquisition depth - */ - obj_desc->mutex.acquisition_depth++; - return_ACPI_STATUS(AE_OK); - } + if (obj_desc->mutex.owner_thread_id == acpi_os_get_thread_id()) { + /* + * The mutex is already owned by this thread, just increment the + * acquisition depth + */ + obj_desc->mutex.acquisition_depth++; + return_ACPI_STATUS(AE_OK); } /* Acquire the mutex, wait if necessary. Special case for Global Lock */ @@ -206,7 +202,7 @@ acpi_ex_acquire_mutex(union acpi_operand_object *time_desc, /* Have the mutex: update mutex and walk info and save the sync_level */ - obj_desc->mutex.owner_thread = walk_state->thread; + obj_desc->mutex.owner_thread_id = acpi_os_get_thread_id(); obj_desc->mutex.acquisition_depth = 1; obj_desc->mutex.original_sync_level = walk_state->thread->current_sync_level; @@ -246,7 +242,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* The mutex must have been previously acquired in order to release it */ - if (!obj_desc->mutex.owner_thread) { + if (!obj_desc->mutex.owner_thread_id) { ACPI_ERROR((AE_INFO, "Cannot release Mutex [%4.4s], not acquired", acpi_ut_get_node_name(obj_desc->mutex.node))); @@ -266,14 +262,14 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, * The Mutex is owned, but this thread must be the owner. * Special case for Global Lock, any thread can release */ - if ((obj_desc->mutex.owner_thread->thread_id != + if ((obj_desc->mutex.owner_thread_id != walk_state->thread->thread_id) && (obj_desc->mutex.os_mutex != acpi_gbl_global_lock_mutex)) { ACPI_ERROR((AE_INFO, "Thread %lX cannot release Mutex [%4.4s] acquired by thread %lX", (unsigned long)walk_state->thread->thread_id, acpi_ut_get_node_name(obj_desc->mutex.node), - (unsigned long)obj_desc->mutex.owner_thread->thread_id)); + (unsigned long)obj_desc->mutex.owner_thread_id)); return_ACPI_STATUS(AE_AML_NOT_OWNER); } @@ -300,7 +296,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* Unlink the mutex from the owner's list */ - acpi_ex_unlink_mutex(obj_desc); + acpi_ex_unlink_mutex(obj_desc, walk_state->thread); /* Release the mutex, special case for Global Lock */ @@ -312,7 +308,7 @@ acpi_ex_release_mutex(union acpi_operand_object *obj_desc, /* Update the mutex and restore sync_level */ - obj_desc->mutex.owner_thread = NULL; + obj_desc->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; walk_state->thread->current_sync_level = obj_desc->mutex.original_sync_level; @@ -367,7 +363,7 @@ void acpi_ex_release_all_mutexes(struct acpi_thread_state *thread) /* Mark mutex unowned */ - obj_desc->mutex.owner_thread = NULL; + obj_desc->mutex.owner_thread_id = ACPI_MUTEX_NOT_ACQUIRED; /* Update Thread sync_level (Last mutex is the important one) */ diff --git a/drivers/acpi/executer/exsystem.c b/drivers/acpi/executer/exsystem.c index 9460baf..b2edf62 100644 --- a/drivers/acpi/executer/exsystem.c +++ b/drivers/acpi/executer/exsystem.c @@ -66,6 +66,7 @@ ACPI_MODULE_NAME("exsystem") acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) { acpi_status status; + acpi_status status2; ACPI_FUNCTION_TRACE(ex_system_wait_semaphore); @@ -78,7 +79,7 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) /* We must wait, so unlock the interpreter */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); status = acpi_os_wait_semaphore(semaphore, 1, timeout); @@ -88,7 +89,13 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) /* Reacquire the interpreter */ - acpi_ex_reacquire_interpreter(); + status2 = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status2)) { + + /* Report fatal error, could not acquire interpreter */ + + return_ACPI_STATUS(status2); + } } return_ACPI_STATUS(status); @@ -112,6 +119,7 @@ acpi_status acpi_ex_system_wait_semaphore(acpi_semaphore semaphore, u16 timeout) acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) { acpi_status status; + acpi_status status2; ACPI_FUNCTION_TRACE(ex_system_wait_mutex); @@ -124,7 +132,7 @@ acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) /* We must wait, so unlock the interpreter */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); status = acpi_os_acquire_mutex(mutex, timeout); @@ -134,7 +142,13 @@ acpi_status acpi_ex_system_wait_mutex(acpi_mutex mutex, u16 timeout) /* Reacquire the interpreter */ - acpi_ex_reacquire_interpreter(); + status2 = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status2)) { + + /* Report fatal error, could not acquire interpreter */ + + return_ACPI_STATUS(status2); + } } return_ACPI_STATUS(status); @@ -195,18 +209,20 @@ acpi_status acpi_ex_system_do_stall(u32 how_long) acpi_status acpi_ex_system_do_suspend(acpi_integer how_long) { + acpi_status status; + ACPI_FUNCTION_ENTRY(); /* Since this thread will sleep, we must release the interpreter */ - acpi_ex_relinquish_interpreter(); + acpi_ex_exit_interpreter(); acpi_os_sleep(how_long); /* And now we must get the interpreter again */ - acpi_ex_reacquire_interpreter(); - return (AE_OK); + status = acpi_ex_enter_interpreter(); + return (status); } /******************************************************************************* diff --git a/drivers/acpi/executer/exutils.c b/drivers/acpi/executer/exutils.c index 6b0aecc..aea461f 100644 --- a/drivers/acpi/executer/exutils.c +++ b/drivers/acpi/executer/exutils.c @@ -76,15 +76,14 @@ static u32 acpi_ex_digits_needed(acpi_integer value, u32 base); * * PARAMETERS: None * - * RETURN: None + * RETURN: Status * - * DESCRIPTION: Enter the interpreter execution region. Failure to enter - * the interpreter region is a fatal system error. Used in - * conjunction with exit_interpreter. + * DESCRIPTION: Enter the interpreter execution region. Failure to enter + * the interpreter region is a fatal system error * ******************************************************************************/ -void acpi_ex_enter_interpreter(void) +acpi_status acpi_ex_enter_interpreter(void) { acpi_status status; @@ -92,42 +91,10 @@ void acpi_ex_enter_interpreter(void) status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, - "Could not acquire AML Interpreter mutex")); + ACPI_ERROR((AE_INFO, "Could not acquire interpreter mutex")); } - return_VOID; -} - -/******************************************************************************* - * - * FUNCTION: acpi_ex_reacquire_interpreter - * - * PARAMETERS: None - * - * RETURN: None - * - * DESCRIPTION: Reacquire the interpreter execution region from within the - * interpreter code. Failure to enter the interpreter region is a - * fatal system error. Used in conjuction with - * relinquish_interpreter - * - ******************************************************************************/ - -void acpi_ex_reacquire_interpreter(void) -{ - ACPI_FUNCTION_TRACE(ex_reacquire_interpreter); - - /* - * If the global serialized flag is set, do not release the interpreter, - * since it was not actually released by acpi_ex_relinquish_interpreter. - * This forces the interpreter to be single threaded. - */ - if (!acpi_gbl_all_methods_serialized) { - acpi_ex_enter_interpreter(); - } - - return_VOID; + return_ACPI_STATUS(status); } /******************************************************************************* @@ -138,9 +105,17 @@ void acpi_ex_reacquire_interpreter(void) * * RETURN: None * - * DESCRIPTION: Exit the interpreter execution region. This is the top level - * routine used to exit the interpreter when all processing has - * been completed. + * DESCRIPTION: Exit the interpreter execution region + * + * Cases where the interpreter is unlocked: + * 1) Completion of the execution of a control method + * 2) Method blocked on a Sleep() AML opcode + * 3) Method blocked on an Acquire() AML opcode + * 4) Method blocked on a Wait() AML opcode + * 5) Method blocked to acquire the global lock + * 6) Method blocked to execute a serialized control method that is + * already executing + * 7) About to invoke a user-installed opregion handler * ******************************************************************************/ @@ -152,46 +127,7 @@ void acpi_ex_exit_interpreter(void) status = acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); if (ACPI_FAILURE(status)) { - ACPI_ERROR((AE_INFO, - "Could not release AML Interpreter mutex")); - } - - return_VOID; -} - -/******************************************************************************* - * - * FUNCTION: acpi_ex_relinquish_interpreter - * - * PARAMETERS: None - * - * RETURN: None - * - * DESCRIPTION: Exit the interpreter execution region, from within the - * interpreter - before attempting an operation that will possibly - * block the running thread. - * - * Cases where the interpreter is unlocked internally - * 1) Method to be blocked on a Sleep() AML opcode - * 2) Method to be blocked on an Acquire() AML opcode - * 3) Method to be blocked on a Wait() AML opcode - * 4) Method to be blocked to acquire the global lock - * 5) Method to be blocked waiting to execute a serialized control method - * that is currently executing - * 6) About to invoke a user-installed opregion handler - * - ******************************************************************************/ - -void acpi_ex_relinquish_interpreter(void) -{ - ACPI_FUNCTION_TRACE(ex_relinquish_interpreter); - - /* - * If the global serialized flag is set, do not release the interpreter. - * This forces the interpreter to be single threaded. - */ - if (!acpi_gbl_all_methods_serialized) { - acpi_ex_exit_interpreter(); + ACPI_ERROR((AE_INFO, "Could not release interpreter mutex")); } return_VOID; @@ -205,8 +141,8 @@ void acpi_ex_relinquish_interpreter(void) * * RETURN: none * - * DESCRIPTION: Truncate an ACPI Integer to 32 bits if the execution mode is - * 32-bit, as determined by the revision of the DSDT. + * DESCRIPTION: Truncate a number to 32-bits if the currently executing method + * belongs to a 32-bit ACPI table. * ******************************************************************************/ diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index af22fdf..ec655c5 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -36,14 +36,13 @@ #define ACPI_FAN_COMPONENT 0x00200000 #define ACPI_FAN_CLASS "fan" -#define ACPI_FAN_DRIVER_NAME "ACPI Fan Driver" #define ACPI_FAN_FILE_STATE "state" #define _COMPONENT ACPI_FAN_COMPONENT -ACPI_MODULE_NAME("acpi_fan") +ACPI_MODULE_NAME("fan"); - MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_FAN_DRIVER_NAME); +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Fan Driver"); MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); @@ -52,7 +51,7 @@ static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); static int acpi_fan_resume(struct acpi_device *device); static struct acpi_driver acpi_fan_driver = { - .name = ACPI_FAN_DRIVER_NAME, + .name = "fan", .class = ACPI_FAN_CLASS, .ids = "PNP0C0B", .ops = { diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 7b6c9ff..4334c20 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -241,3 +241,65 @@ static int __init init_acpi_device_notify(void) } arch_initcall(init_acpi_device_notify); + + +#if defined(CONFIG_RTC_DRV_CMOS) || defined(CONFIG_RTC_DRV_CMOS_MODULE) + +/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find + * its device node and pass extra config data. This helps its driver use + * capabilities that the now-obsolete mc146818 didn't have, and informs it + * that this board's RTC is wakeup-capable (per ACPI spec). + */ +#include <linux/mc146818rtc.h> + +static struct cmos_rtc_board_info rtc_info; + + +/* PNP devices are registered in a subsys_initcall(); + * ACPI specifies the PNP IDs to use. + */ +#include <linux/pnp.h> + +static int __init pnp_match(struct device *dev, void *data) +{ + static const char *ids[] = { "PNP0b00", "PNP0b01", "PNP0b02", }; + struct pnp_dev *pnp = to_pnp_dev(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(ids); i++) { + if (compare_pnp_id(pnp->id, ids[i]) != 0) + return 1; + } + return 0; +} + +static struct device *__init get_rtc_dev(void) +{ + return bus_find_device(&pnp_bus_type, NULL, NULL, pnp_match); +} + +static int __init acpi_rtc_init(void) +{ + struct device *dev = get_rtc_dev(); + + if (dev) { + rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm; + rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm; + rtc_info.rtc_century = acpi_gbl_FADT.century; + + /* NOTE: acpi_gbl_FADT->rtcs4 is NOT currently useful */ + + dev->platform_data = &rtc_info; + + /* RTC always wakes from S1/S2/S3, and often S4/STD */ + device_init_wakeup(dev, 1); + + put_device(dev); + } else + pr_debug("ACPI: RTC unavailable?\n"); + return 0; +} +/* do this between RTC subsys_initcall() and rtc_cmos driver_initcall() */ +fs_initcall(acpi_rtc_init); + +#endif diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c index 57901ca..c84b1fa 100644 --- a/drivers/acpi/hardware/hwsleep.c +++ b/drivers/acpi/hardware/hwsleep.c @@ -235,6 +235,14 @@ acpi_status acpi_enter_sleep_state_prep(u8 sleep_state) "While executing method _SST")); } + /* + * 1) Disable/Clear all GPEs + */ + status = acpi_hw_disable_all_gpes(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + return_ACPI_STATUS(AE_OK); } @@ -290,13 +298,13 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) } /* - * 1) Disable/Clear all GPEs * 2) Enable all wakeup GPEs */ status = acpi_hw_disable_all_gpes(); if (ACPI_FAILURE(status)) { return_ACPI_STATUS(status); } + acpi_gbl_system_awake_and_running = FALSE; status = acpi_hw_enable_all_wakeup_gpes(); diff --git a/drivers/acpi/hotkey.c b/drivers/acpi/hotkey.c deleted file mode 100644 index 8edfb92..0000000 --- a/drivers/acpi/hotkey.c +++ /dev/null @@ -1,1042 +0,0 @@ -/* - * hotkey.c - ACPI Hotkey Driver ($Revision: 0.2 $) - * - * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or (at - * your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/proc_fs.h> -#include <linux/sched.h> -#include <linux/kmod.h> -#include <linux/seq_file.h> -#include <acpi/acpi_drivers.h> -#include <acpi/acpi_bus.h> -#include <asm/uaccess.h> - -#define HOTKEY_ACPI_VERSION "0.1" - -#define HOTKEY_PROC "hotkey" -#define HOTKEY_EV_CONFIG "event_config" -#define HOTKEY_PL_CONFIG "poll_config" -#define HOTKEY_ACTION "action" -#define HOTKEY_INFO "info" - -#define ACPI_HOTK_NAME "Generic Hotkey Driver" -#define ACPI_HOTK_CLASS "Hotkey" -#define ACPI_HOTK_DEVICE_NAME "Hotkey" -#define ACPI_HOTK_HID "Unknown?" -#define ACPI_HOTKEY_COMPONENT 0x20000000 - -#define ACPI_HOTKEY_EVENT 0x1 -#define ACPI_HOTKEY_POLLING 0x2 -#define ACPI_UNDEFINED_EVENT 0xf - -#define RESULT_STR_LEN 80 - -#define ACTION_METHOD 0 -#define POLL_METHOD 1 - -#define IS_EVENT(e) ((e) <= 10000 && (e) >0) -#define IS_POLL(e) ((e) > 10000) -#define IS_OTHERS(e) ((e)<=0 || (e)>=20000) -#define _COMPONENT ACPI_HOTKEY_COMPONENT -ACPI_MODULE_NAME("acpi_hotkey") - - MODULE_AUTHOR("luming.yu@intel.com"); -MODULE_DESCRIPTION(ACPI_HOTK_NAME); -MODULE_LICENSE("GPL"); - -/* standardized internal hotkey number/event */ -enum { - /* Video Extension event */ - HK_EVENT_CYCLE_OUTPUT_DEVICE = 0x80, - HK_EVENT_OUTPUT_DEVICE_STATUS_CHANGE, - HK_EVENT_CYCLE_DISPLAY_OUTPUT, - HK_EVENT_NEXT_DISPLAY_OUTPUT, - HK_EVENT_PREVIOUS_DISPLAY_OUTPUT, - HK_EVENT_CYCLE_BRIGHTNESS, - HK_EVENT_INCREASE_BRIGHTNESS, - HK_EVENT_DECREASE_BRIGHTNESS, - HK_EVENT_ZERO_BRIGHTNESS, - HK_EVENT_DISPLAY_DEVICE_OFF, - - /* Snd Card event */ - HK_EVENT_VOLUME_MUTE, - HK_EVENT_VOLUME_INCLREASE, - HK_EVENT_VOLUME_DECREASE, - - /* running state control */ - HK_EVENT_ENTERRING_S3, - HK_EVENT_ENTERRING_S4, - HK_EVENT_ENTERRING_S5, -}; - -enum conf_entry_enum { - bus_handle = 0, - bus_method = 1, - action_handle = 2, - method = 3, - LAST_CONF_ENTRY -}; - -/* procdir we use */ -static struct proc_dir_entry *hotkey_proc_dir; -static struct proc_dir_entry *hotkey_config; -static struct proc_dir_entry *hotkey_poll_config; -static struct proc_dir_entry *hotkey_action; -static struct proc_dir_entry *hotkey_info; - -/* linkage for all type of hotkey */ -struct acpi_hotkey_link { - struct list_head entries; - int hotkey_type; /* event or polling based hotkey */ - int hotkey_standard_num; /* standardized hotkey(event) number */ -}; - -/* event based hotkey */ -struct acpi_event_hotkey { - struct acpi_hotkey_link hotkey_link; - int flag; - acpi_handle bus_handle; /* bus to install notify handler */ - int external_hotkey_num; /* external hotkey/event number */ - acpi_handle action_handle; /* acpi handle attached aml action method */ - char *action_method; /* action method */ -}; - -/* - * There are two ways to poll status - * 1. directy call read_xxx method, without any arguments passed in - * 2. call write_xxx method, with arguments passed in, you need - * the result is saved in acpi_polling_hotkey.poll_result. - * anthoer read command through polling interface. - * - */ - -/* polling based hotkey */ -struct acpi_polling_hotkey { - struct acpi_hotkey_link hotkey_link; - int flag; - acpi_handle poll_handle; /* acpi handle attached polling method */ - char *poll_method; /* poll method */ - acpi_handle action_handle; /* acpi handle attached action method */ - char *action_method; /* action method */ - union acpi_object *poll_result; /* polling_result */ - struct proc_dir_entry *proc; -}; - -/* hotkey object union */ -union acpi_hotkey { - struct list_head entries; - struct acpi_hotkey_link link; - struct acpi_event_hotkey event_hotkey; - struct acpi_polling_hotkey poll_hotkey; -}; - -/* hotkey object list */ -struct acpi_hotkey_list { - struct list_head *entries; - int count; -}; - -static int auto_hotkey_add(struct acpi_device *device); -static int auto_hotkey_remove(struct acpi_device *device, int type); - -static struct acpi_driver hotkey_driver = { - .name = ACPI_HOTK_NAME, - .class = ACPI_HOTK_CLASS, - .ids = ACPI_HOTK_HID, - .ops = { - .add = auto_hotkey_add, - .remove = auto_hotkey_remove, - }, -}; - -static void free_hotkey_device(union acpi_hotkey *key); -static void free_hotkey_buffer(union acpi_hotkey *key); -static void free_poll_hotkey_buffer(union acpi_hotkey *key); -static int hotkey_open_config(struct inode *inode, struct file *file); -static int hotkey_poll_open_config(struct inode *inode, struct file *file); -static ssize_t hotkey_write_config(struct file *file, - const char __user * buffer, - size_t count, loff_t * data); -static int hotkey_info_open_fs(struct inode *inode, struct file *file); -static int hotkey_action_open_fs(struct inode *inode, struct file *file); -static ssize_t hotkey_execute_aml_method(struct file *file, - const char __user * buffer, - size_t count, loff_t * data); -static int hotkey_config_seq_show(struct seq_file *seq, void *offset); -static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset); -static int hotkey_polling_open_fs(struct inode *inode, struct file *file); -static union acpi_hotkey *get_hotkey_by_event(struct - acpi_hotkey_list - *hotkey_list, int event); - -/* event based config */ -static const struct file_operations hotkey_config_fops = { - .open = hotkey_open_config, - .read = seq_read, - .write = hotkey_write_config, - .llseek = seq_lseek, - .release = single_release, -}; - -/* polling based config */ -static const struct file_operations hotkey_poll_config_fops = { - .open = hotkey_poll_open_config, - .read = seq_read, - .write = hotkey_write_config, - .llseek = seq_lseek, - .release = single_release, -}; - -/* hotkey driver info */ -static const struct file_operations hotkey_info_fops = { - .open = hotkey_info_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/* action */ -static const struct file_operations hotkey_action_fops = { - .open = hotkey_action_open_fs, - .read = seq_read, - .write = hotkey_execute_aml_method, - .llseek = seq_lseek, - .release = single_release, -}; - -/* polling results */ -static const struct file_operations hotkey_polling_fops = { - .open = hotkey_polling_open_fs, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -struct acpi_hotkey_list global_hotkey_list; /* link all ev or pl hotkey */ -struct list_head hotkey_entries; /* head of the list of hotkey_list */ - -static int hotkey_info_seq_show(struct seq_file *seq, void *offset) -{ - - seq_printf(seq, "Hotkey generic driver ver: %s\n", HOTKEY_ACPI_VERSION); - - return 0; -} - -static int hotkey_info_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_info_seq_show, PDE(inode)->data); -} - -static char *format_result(union acpi_object *object) -{ - char *buf; - - buf = kzalloc(RESULT_STR_LEN, GFP_KERNEL); - if (!buf) - return NULL; - /* Now, just support integer type */ - if (object->type == ACPI_TYPE_INTEGER) - sprintf(buf, "%d\n", (u32) object->integer.value); - return buf; -} - -static int hotkey_polling_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_polling_hotkey *poll_hotkey = seq->private; - char *buf; - - - if (poll_hotkey->poll_result) { - buf = format_result(poll_hotkey->poll_result); - if (buf) - seq_printf(seq, "%s", buf); - kfree(buf); - } - return 0; -} - -static int hotkey_polling_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_polling_seq_show, PDE(inode)->data); -} - -static int hotkey_action_open_fs(struct inode *inode, struct file *file) -{ - return single_open(file, hotkey_info_seq_show, PDE(inode)->data); -} - -/* Mapping external hotkey number to standardized hotkey event num */ -static int hotkey_get_internal_event(int event, struct acpi_hotkey_list *list) -{ - struct list_head *entries; - int val = -1; - - - list_for_each(entries, list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT - && key->event_hotkey.external_hotkey_num == event) { - val = key->link.hotkey_standard_num; - break; - } - } - - return val; -} - -static void -acpi_hotkey_notify_handler(acpi_handle handle, u32 event, void *data) -{ - struct acpi_device *device = NULL; - u32 internal_event; - - - if (acpi_bus_get_device(handle, &device)) - return; - - internal_event = hotkey_get_internal_event(event, &global_hotkey_list); - acpi_bus_generate_event(device, internal_event, 0); - - return; -} - -/* Need to invent automatically hotkey add method */ -static int auto_hotkey_add(struct acpi_device *device) -{ - /* Implement me */ - return 0; -} - -/* Need to invent automatically hotkey remove method */ -static int auto_hotkey_remove(struct acpi_device *device, int type) -{ - /* Implement me */ - return 0; -} - -/* Create a proc file for each polling method */ -static int create_polling_proc(union acpi_hotkey *device) -{ - struct proc_dir_entry *proc; - char proc_name[80]; - mode_t mode; - - mode = S_IFREG | S_IRUGO | S_IWUGO; - - sprintf(proc_name, "%d", device->link.hotkey_standard_num); - /* - strcat(proc_name, device->poll_hotkey.poll_method); - */ - proc = create_proc_entry(proc_name, mode, hotkey_proc_dir); - - if (!proc) { - return -ENODEV; - } else { - proc->proc_fops = &hotkey_polling_fops; - proc->owner = THIS_MODULE; - proc->data = device; - proc->uid = 0; - proc->gid = 0; - device->poll_hotkey.proc = proc; - } - return 0; -} - -static int hotkey_add(union acpi_hotkey *device) -{ - int status = 0; - struct acpi_device *dev = NULL; - - - if (device->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_bus_get_device(device->event_hotkey.bus_handle, &dev); - status = acpi_install_notify_handler(dev->handle, - ACPI_DEVICE_NOTIFY, - acpi_hotkey_notify_handler, - dev); - } else /* Add polling hotkey */ - create_polling_proc(device); - - global_hotkey_list.count++; - - list_add_tail(&device->link.entries, global_hotkey_list.entries); - - return status; -} - -static int hotkey_remove(union acpi_hotkey *device) -{ - struct list_head *entries, *next; - - - list_for_each_safe(entries, next, global_hotkey_list.entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_standard_num == - device->link.hotkey_standard_num) { - list_del(&key->link.entries); - free_hotkey_device(key); - global_hotkey_list.count--; - break; - } - } - kfree(device); - return 0; -} - -static int hotkey_update(union acpi_hotkey *key) -{ - struct list_head *entries; - - - list_for_each(entries, global_hotkey_list.entries) { - union acpi_hotkey *tmp = - container_of(entries, union acpi_hotkey, entries); - if (tmp->link.hotkey_standard_num == - key->link.hotkey_standard_num) { - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - free_hotkey_buffer(tmp); - tmp->event_hotkey.bus_handle = - key->event_hotkey.bus_handle; - tmp->event_hotkey.external_hotkey_num = - key->event_hotkey.external_hotkey_num; - tmp->event_hotkey.action_handle = - key->event_hotkey.action_handle; - tmp->event_hotkey.action_method = - key->event_hotkey.action_method; - kfree(key); - } else { - /* - char proc_name[80]; - - sprintf(proc_name, "%d", tmp->link.hotkey_standard_num); - strcat(proc_name, tmp->poll_hotkey.poll_method); - remove_proc_entry(proc_name,hotkey_proc_dir); - */ - free_poll_hotkey_buffer(tmp); - tmp->poll_hotkey.poll_handle = - key->poll_hotkey.poll_handle; - tmp->poll_hotkey.poll_method = - key->poll_hotkey.poll_method; - tmp->poll_hotkey.action_handle = - key->poll_hotkey.action_handle; - tmp->poll_hotkey.action_method = - key->poll_hotkey.action_method; - tmp->poll_hotkey.poll_result = - key->poll_hotkey.poll_result; - /* - create_polling_proc(tmp); - */ - kfree(key); - } - return 0; - break; - } - } - - return -ENODEV; -} - -static void free_hotkey_device(union acpi_hotkey *key) -{ - struct acpi_device *dev; - - - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_bus_get_device(key->event_hotkey.bus_handle, &dev); - if (dev->handle) - acpi_remove_notify_handler(dev->handle, - ACPI_DEVICE_NOTIFY, - acpi_hotkey_notify_handler); - free_hotkey_buffer(key); - } else { - char proc_name[80]; - - sprintf(proc_name, "%d", key->link.hotkey_standard_num); - /* - strcat(proc_name, key->poll_hotkey.poll_method); - */ - remove_proc_entry(proc_name, hotkey_proc_dir); - free_poll_hotkey_buffer(key); - } - kfree(key); - return; -} - -static void free_hotkey_buffer(union acpi_hotkey *key) -{ - /* key would never be null, action method could be */ - kfree(key->event_hotkey.action_method); -} - -static void free_poll_hotkey_buffer(union acpi_hotkey *key) -{ - /* key would never be null, others could be*/ - kfree(key->poll_hotkey.action_method); - kfree(key->poll_hotkey.poll_method); - kfree(key->poll_hotkey.poll_result); -} -static int -init_hotkey_device(union acpi_hotkey *key, char **config_entry, - int std_num, int external_num) -{ - acpi_handle tmp_handle; - acpi_status status = AE_OK; - - if (std_num < 0 || IS_POLL(std_num) || !key) - goto do_fail; - - if (!config_entry[bus_handle] || !config_entry[action_handle] - || !config_entry[method]) - goto do_fail; - - key->link.hotkey_type = ACPI_HOTKEY_EVENT; - key->link.hotkey_standard_num = std_num; - key->event_hotkey.flag = 0; - key->event_hotkey.action_method = config_entry[method]; - - status = acpi_get_handle(NULL, config_entry[bus_handle], - &(key->event_hotkey.bus_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - key->event_hotkey.external_hotkey_num = external_num; - status = acpi_get_handle(NULL, config_entry[action_handle], - &(key->event_hotkey.action_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->event_hotkey.action_handle, - config_entry[method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - return AE_OK; -do_fail_zero: - key->event_hotkey.action_method = NULL; -do_fail: - return -ENODEV; -} - -static int -init_poll_hotkey_device(union acpi_hotkey *key, char **config_entry, - int std_num) -{ - acpi_status status = AE_OK; - acpi_handle tmp_handle; - - if (std_num < 0 || IS_EVENT(std_num) || !key) - goto do_fail; - if (!config_entry[bus_handle] ||!config_entry[bus_method] || - !config_entry[action_handle] || !config_entry[method]) - goto do_fail; - - key->link.hotkey_type = ACPI_HOTKEY_POLLING; - key->link.hotkey_standard_num = std_num; - key->poll_hotkey.flag = 0; - key->poll_hotkey.poll_method = config_entry[bus_method]; - key->poll_hotkey.action_method = config_entry[method]; - - status = acpi_get_handle(NULL, config_entry[bus_handle], - &(key->poll_hotkey.poll_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->poll_hotkey.poll_handle, - config_entry[bus_method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = - acpi_get_handle(NULL, config_entry[action_handle], - &(key->poll_hotkey.action_handle)); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - status = acpi_get_handle(key->poll_hotkey.action_handle, - config_entry[method], &tmp_handle); - if (ACPI_FAILURE(status)) - goto do_fail_zero; - key->poll_hotkey.poll_result = - kmalloc(sizeof(union acpi_object), GFP_KERNEL); - if (!key->poll_hotkey.poll_result) - goto do_fail_zero; - return AE_OK; - -do_fail_zero: - key->poll_hotkey.poll_method = NULL; - key->poll_hotkey.action_method = NULL; -do_fail: - return -ENODEV; -} - -static int hotkey_open_config(struct inode *inode, struct file *file) -{ - return (single_open - (file, hotkey_config_seq_show, PDE(inode)->data)); -} - -static int hotkey_poll_open_config(struct inode *inode, struct file *file) -{ - return (single_open - (file, hotkey_poll_config_seq_show, PDE(inode)->data)); -} - -static int hotkey_config_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - struct list_head *entries; - char bus_name[ACPI_PATHNAME_MAX] = { 0 }; - char action_name[ACPI_PATHNAME_MAX] = { 0 }; - struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; - struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; - - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_EVENT) { - acpi_get_name(key->event_hotkey.bus_handle, - ACPI_NAME_TYPE_MAX, &bus); - acpi_get_name(key->event_hotkey.action_handle, - ACPI_NAME_TYPE_MAX, &act); - seq_printf(seq, "%s:%s:%s:%d:%d\n", bus_name, - action_name, - key->event_hotkey.action_method, - key->link.hotkey_standard_num, - key->event_hotkey.external_hotkey_num); - } - } - seq_puts(seq, "\n"); - return 0; -} - -static int hotkey_poll_config_seq_show(struct seq_file *seq, void *offset) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - struct list_head *entries; - char bus_name[ACPI_PATHNAME_MAX] = { 0 }; - char action_name[ACPI_PATHNAME_MAX] = { 0 }; - struct acpi_buffer bus = { ACPI_PATHNAME_MAX, bus_name }; - struct acpi_buffer act = { ACPI_PATHNAME_MAX, action_name }; - - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_type == ACPI_HOTKEY_POLLING) { - acpi_get_name(key->poll_hotkey.poll_handle, - ACPI_NAME_TYPE_MAX, &bus); - acpi_get_name(key->poll_hotkey.action_handle, - ACPI_NAME_TYPE_MAX, &act); - seq_printf(seq, "%s:%s:%s:%s:%d\n", bus_name, - key->poll_hotkey.poll_method, - action_name, - key->poll_hotkey.action_method, - key->link.hotkey_standard_num); - } - } - seq_puts(seq, "\n"); - return 0; -} - -static int -get_parms(char *config_record, int *cmd, char **config_entry, - int *internal_event_num, int *external_event_num) -{ -/* the format of *config_record = - * "1:\d+:*" : "cmd:internal_event_num" - * "\d+:\w+:\w+:\w+:\w+:\d+:\d+" : - * "cmd:bus_handle:bus_method:action_handle:method:internal_event_num:external_event_num" - */ - char *tmp, *tmp1, count; - int i; - - sscanf(config_record, "%d", cmd); - if (*cmd == 1) { - if (sscanf(config_record, "%d:%d", cmd, internal_event_num) != - 2) - goto do_fail; - else - return (6); - } - tmp = strchr(config_record, ':'); - if (!tmp) - goto do_fail; - tmp++; - for (i = 0; i < LAST_CONF_ENTRY; i++) { - tmp1 = strchr(tmp, ':'); - if (!tmp1) { - goto do_fail; - } - count = tmp1 - tmp; - config_entry[i] = kzalloc(count + 1, GFP_KERNEL); - if (!config_entry[i]) - goto handle_failure; - strncpy(config_entry[i], tmp, count); - tmp = tmp1 + 1; - } - if (sscanf(tmp, "%d:%d", internal_event_num, external_event_num) <= 0) - goto handle_failure; - if (!IS_OTHERS(*internal_event_num)) { - return 6; - } -handle_failure: - while (i-- > 0) - kfree(config_entry[i]); -do_fail: - return -1; -} - -/* count is length for one input record */ -static ssize_t hotkey_write_config(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - char *config_record = NULL; - char *config_entry[LAST_CONF_ENTRY]; - int cmd, internal_event_num, external_event_num; - int ret = 0; - union acpi_hotkey *key = kzalloc(sizeof(union acpi_hotkey), GFP_KERNEL); - - if (!key) - return -ENOMEM; - - config_record = kzalloc(count + 1, GFP_KERNEL); - if (!config_record) { - kfree(key); - return -ENOMEM; - } - - if (copy_from_user(config_record, buffer, count)) { - kfree(config_record); - kfree(key); - printk(KERN_ERR PREFIX "Invalid data\n"); - return -EINVAL; - } - ret = get_parms(config_record, &cmd, config_entry, - &internal_event_num, &external_event_num); - kfree(config_record); - if (ret != 6) { - printk(KERN_ERR PREFIX "Invalid data format ret=%d\n", ret); - return -EINVAL; - } - - if (cmd == 1) { - union acpi_hotkey *tmp = NULL; - tmp = get_hotkey_by_event(&global_hotkey_list, - internal_event_num); - if (!tmp) - printk(KERN_ERR PREFIX "Invalid key\n"); - else - memcpy(key, tmp, sizeof(union acpi_hotkey)); - goto cont_cmd; - } - if (IS_EVENT(internal_event_num)) { - if (init_hotkey_device(key, config_entry, - internal_event_num, external_event_num)) - goto init_hotkey_fail; - } else { - if (init_poll_hotkey_device(key, config_entry, - internal_event_num)) - goto init_poll_hotkey_fail; - } -cont_cmd: - switch (cmd) { - case 0: - if (get_hotkey_by_event(&global_hotkey_list, - key->link.hotkey_standard_num)) - goto fail_out; - else - hotkey_add(key); - break; - case 1: - hotkey_remove(key); - break; - case 2: - /* key is kfree()ed if matched*/ - if (hotkey_update(key)) - goto fail_out; - break; - default: - goto fail_out; - break; - } - return count; - -init_poll_hotkey_fail: /* failed init_poll_hotkey_device */ - kfree(config_entry[bus_method]); - config_entry[bus_method] = NULL; -init_hotkey_fail: /* failed init_hotkey_device */ - kfree(config_entry[method]); -fail_out: - kfree(config_entry[bus_handle]); - kfree(config_entry[action_handle]); - /* No double free since elements =NULL for error cases */ - if (IS_EVENT(internal_event_num)) { - if (config_entry[bus_method]) - kfree(config_entry[bus_method]); - free_hotkey_buffer(key); /* frees [method] */ - } else - free_poll_hotkey_buffer(key); /* frees [bus_method]+[method] */ - kfree(key); - printk(KERN_ERR PREFIX "invalid key\n"); - return -EINVAL; -} - -/* - * This function evaluates an ACPI method, given an int as parameter, the - * method is searched within the scope of the handle, can be NULL. The output - * of the method is written is output, which can also be NULL - * - * returns 1 if write is successful, 0 else. - */ -static int write_acpi_int(acpi_handle handle, const char *method, int val, - struct acpi_buffer *output) -{ - struct acpi_object_list params; /* list of input parameters (an int here) */ - union acpi_object in_obj; /* the only param we use */ - acpi_status status; - - params.count = 1; - params.pointer = &in_obj; - in_obj.type = ACPI_TYPE_INTEGER; - in_obj.integer.value = val; - - status = acpi_evaluate_object(handle, (char *)method, ¶ms, output); - - return (status == AE_OK); -} - -static int read_acpi_int(acpi_handle handle, const char *method, - union acpi_object *val) -{ - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = acpi_evaluate_object(handle, (char *)method, NULL, &output); - if (val) { - val->integer.value = out_obj.integer.value; - val->type = out_obj.type; - } else - printk(KERN_ERR PREFIX "null val pointer\n"); - return ((status == AE_OK) - && (out_obj.type == ACPI_TYPE_INTEGER)); -} - -static union acpi_hotkey *get_hotkey_by_event(struct - acpi_hotkey_list - *hotkey_list, int event) -{ - struct list_head *entries; - - list_for_each(entries, hotkey_list->entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - if (key->link.hotkey_standard_num == event) { - return (key); - } - } - return (NULL); -} - -/* - * user call AML method interface: - * Call convention: - * echo "event_num: arg type : value" - * example: echo "1:1:30" > /proc/acpi/action - * Just support 1 integer arg passing to AML method - */ - -static ssize_t hotkey_execute_aml_method(struct file *file, - const char __user * buffer, - size_t count, loff_t * data) -{ - struct acpi_hotkey_list *hotkey_list = &global_hotkey_list; - char *arg; - int event, method_type, type, value; - union acpi_hotkey *key; - - - arg = kzalloc(count + 1, GFP_KERNEL); - if (!arg) - return -ENOMEM; - - if (copy_from_user(arg, buffer, count)) { - kfree(arg); - printk(KERN_ERR PREFIX "Invalid argument 2\n"); - return -EINVAL; - } - - if (sscanf(arg, "%d:%d:%d:%d", &event, &method_type, &type, &value) != - 4) { - kfree(arg); - printk(KERN_ERR PREFIX "Invalid argument 3\n"); - return -EINVAL; - } - kfree(arg); - if (type == ACPI_TYPE_INTEGER) { - key = get_hotkey_by_event(hotkey_list, event); - if (!key) - goto do_fail; - if (IS_EVENT(event)) - write_acpi_int(key->event_hotkey.action_handle, - key->event_hotkey.action_method, value, - NULL); - else if (IS_POLL(event)) { - if (method_type == POLL_METHOD) - read_acpi_int(key->poll_hotkey.poll_handle, - key->poll_hotkey.poll_method, - key->poll_hotkey.poll_result); - else if (method_type == ACTION_METHOD) - write_acpi_int(key->poll_hotkey.action_handle, - key->poll_hotkey.action_method, - value, NULL); - else - goto do_fail; - - } - } else { - printk(KERN_WARNING "Not supported\n"); - return -EINVAL; - } - return count; - do_fail: - return -EINVAL; - -} - -static int __init hotkey_init(void) -{ - int result; - mode_t mode = S_IFREG | S_IRUGO | S_IWUGO; - - - if (acpi_disabled) - return -ENODEV; - - if (acpi_specific_hotkey_enabled) { - printk("Using specific hotkey driver\n"); - return -ENODEV; - } - - hotkey_proc_dir = proc_mkdir(HOTKEY_PROC, acpi_root_dir); - if (!hotkey_proc_dir) { - return (-ENODEV); - } - hotkey_proc_dir->owner = THIS_MODULE; - - hotkey_config = - create_proc_entry(HOTKEY_EV_CONFIG, mode, hotkey_proc_dir); - if (!hotkey_config) { - goto do_fail1; - } else { - hotkey_config->proc_fops = &hotkey_config_fops; - hotkey_config->data = &global_hotkey_list; - hotkey_config->owner = THIS_MODULE; - hotkey_config->uid = 0; - hotkey_config->gid = 0; - } - - hotkey_poll_config = - create_proc_entry(HOTKEY_PL_CONFIG, mode, hotkey_proc_dir); - if (!hotkey_poll_config) { - goto do_fail2; - } else { - hotkey_poll_config->proc_fops = &hotkey_poll_config_fops; - hotkey_poll_config->data = &global_hotkey_list; - hotkey_poll_config->owner = THIS_MODULE; - hotkey_poll_config->uid = 0; - hotkey_poll_config->gid = 0; - } - - hotkey_action = create_proc_entry(HOTKEY_ACTION, mode, hotkey_proc_dir); - if (!hotkey_action) { - goto do_fail3; - } else { - hotkey_action->proc_fops = &hotkey_action_fops; - hotkey_action->owner = THIS_MODULE; - hotkey_action->uid = 0; - hotkey_action->gid = 0; - } - - hotkey_info = create_proc_entry(HOTKEY_INFO, mode, hotkey_proc_dir); - if (!hotkey_info) { - goto do_fail4; - } else { - hotkey_info->proc_fops = &hotkey_info_fops; - hotkey_info->owner = THIS_MODULE; - hotkey_info->uid = 0; - hotkey_info->gid = 0; - } - - result = acpi_bus_register_driver(&hotkey_driver); - if (result < 0) - goto do_fail5; - global_hotkey_list.count = 0; - global_hotkey_list.entries = &hotkey_entries; - - INIT_LIST_HEAD(&hotkey_entries); - - return (0); - - do_fail5: - remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); - do_fail4: - remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); - do_fail3: - remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); - do_fail2: - remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); - do_fail1: - remove_proc_entry(HOTKEY_PROC, acpi_root_dir); - return (-ENODEV); -} - -static void __exit hotkey_exit(void) -{ - struct list_head *entries, *next; - - - list_for_each_safe(entries, next, global_hotkey_list.entries) { - union acpi_hotkey *key = - container_of(entries, union acpi_hotkey, entries); - - acpi_os_wait_events_complete(NULL); - list_del(&key->link.entries); - global_hotkey_list.count--; - free_hotkey_device(key); - } - acpi_bus_unregister_driver(&hotkey_driver); - remove_proc_entry(HOTKEY_EV_CONFIG, hotkey_proc_dir); - remove_proc_entry(HOTKEY_PL_CONFIG, hotkey_proc_dir); - remove_proc_entry(HOTKEY_ACTION, hotkey_proc_dir); - remove_proc_entry(HOTKEY_INFO, hotkey_proc_dir); - remove_proc_entry(HOTKEY_PROC, acpi_root_dir); - return; -} - -module_init(hotkey_init); -module_exit(hotkey_exit); diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c deleted file mode 100644 index 76ec8b6..0000000 --- a/drivers/acpi/i2c_ec.c +++ /dev/null @@ -1,404 +0,0 @@ -/* - * SMBus driver for ACPI Embedded Controller ($Revision: 1.3 $) - * - * Copyright (c) 2002, 2005 Ducrot Bruno - * Copyright (c) 2005 Rich Townsend (tiny hacks & tweaks) - * - * This program is free software; 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. - */ - -#include <linux/version.h> -#include <linux/module.h> -#include <linux/slab.h> -#include <linux/kernel.h> -#include <linux/stddef.h> -#include <linux/init.h> -#include <linux/i2c.h> -#include <linux/acpi.h> -#include <linux/delay.h> - -#include "i2c_ec.h" - -#define xudelay(t) udelay(t) -#define xmsleep(t) msleep(t) - -#define ACPI_EC_HC_COMPONENT 0x00080000 -#define ACPI_EC_HC_CLASS "ec_hc_smbus" -#define ACPI_EC_HC_HID "ACPI0001" -#define ACPI_EC_HC_DRIVER_NAME "ACPI EC HC smbus driver" -#define ACPI_EC_HC_DEVICE_NAME "EC HC smbus" - -#define _COMPONENT ACPI_EC_HC_COMPONENT - -ACPI_MODULE_NAME("acpi_smbus") - -static int acpi_ec_hc_add(struct acpi_device *device); -static int acpi_ec_hc_remove(struct acpi_device *device, int type); - -static struct acpi_driver acpi_ec_hc_driver = { - .name = ACPI_EC_HC_DRIVER_NAME, - .class = ACPI_EC_HC_CLASS, - .ids = ACPI_EC_HC_HID, - .ops = { - .add = acpi_ec_hc_add, - .remove = acpi_ec_hc_remove, - }, -}; - -/* Various bit mask for EC_SC (R) */ -#define OBF 0x01 -#define IBF 0x02 -#define CMD 0x08 -#define BURST 0x10 -#define SCI_EVT 0x20 -#define SMI_EVT 0x40 - -/* Commands for EC_SC (W) */ -#define RD_EC 0x80 -#define WR_EC 0x81 -#define BE_EC 0x82 -#define BD_EC 0x83 -#define QR_EC 0x84 - -/* - * ACPI 2.0 chapter 13 SMBus 2.0 EC register model - */ - -#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ -#define ACPI_EC_SMB_STS 0x01 /* status */ -#define ACPI_EC_SMB_ADDR 0x02 /* address */ -#define ACPI_EC_SMB_CMD 0x03 /* command */ -#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ -#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ -#define ACPI_EC_SMB_ALRM_A 0x25 /* alarm address */ -#define ACPI_EC_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ - -#define ACPI_EC_SMB_STS_DONE 0x80 -#define ACPI_EC_SMB_STS_ALRM 0x40 -#define ACPI_EC_SMB_STS_RES 0x20 -#define ACPI_EC_SMB_STS_STATUS 0x1f - -#define ACPI_EC_SMB_STATUS_OK 0x00 -#define ACPI_EC_SMB_STATUS_FAIL 0x07 -#define ACPI_EC_SMB_STATUS_DNAK 0x10 -#define ACPI_EC_SMB_STATUS_DERR 0x11 -#define ACPI_EC_SMB_STATUS_CMD_DENY 0x12 -#define ACPI_EC_SMB_STATUS_UNKNOWN 0x13 -#define ACPI_EC_SMB_STATUS_ACC_DENY 0x17 -#define ACPI_EC_SMB_STATUS_TIMEOUT 0x18 -#define ACPI_EC_SMB_STATUS_NOTSUP 0x19 -#define ACPI_EC_SMB_STATUS_BUSY 0x1A -#define ACPI_EC_SMB_STATUS_PEC 0x1F - -#define ACPI_EC_SMB_PRTCL_WRITE 0x00 -#define ACPI_EC_SMB_PRTCL_READ 0x01 -#define ACPI_EC_SMB_PRTCL_QUICK 0x02 -#define ACPI_EC_SMB_PRTCL_BYTE 0x04 -#define ACPI_EC_SMB_PRTCL_BYTE_DATA 0x06 -#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 -#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a -#define ACPI_EC_SMB_PRTCL_PROC_CALL 0x0c -#define ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL 0x0d -#define ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA 0x4a -#define ACPI_EC_SMB_PRTCL_PEC 0x80 - -/* Length of pre/post transaction sleep (msec) */ -#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 -#define ACPI_EC_SMB_ACCESS_SLEEP1 1 -#define ACPI_EC_SMB_ACCESS_SLEEP2 10 - -static int acpi_ec_smb_read(struct acpi_ec_smbus *smbus, u8 address, u8 * data) -{ - u8 val; - int err; - - err = ec_read(smbus->base + address, &val); - if (!err) { - *data = val; - } - xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); - return (err); -} - -static int acpi_ec_smb_write(struct acpi_ec_smbus *smbus, u8 address, u8 data) -{ - int err; - - err = ec_write(smbus->base + address, data); - return (err); -} - -static int -acpi_ec_smb_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, - char read_write, u8 command, int size, - union i2c_smbus_data *data) -{ - struct acpi_ec_smbus *smbus = adap->algo_data; - unsigned char protocol, len = 0, pec, temp[2] = { 0, 0 }; - int i; - - if (read_write == I2C_SMBUS_READ) { - protocol = ACPI_EC_SMB_PRTCL_READ; - } else { - protocol = ACPI_EC_SMB_PRTCL_WRITE; - } - pec = (flags & I2C_CLIENT_PEC) ? ACPI_EC_SMB_PRTCL_PEC : 0; - - switch (size) { - - case I2C_SMBUS_QUICK: - protocol |= ACPI_EC_SMB_PRTCL_QUICK; - read_write = I2C_SMBUS_WRITE; - break; - - case I2C_SMBUS_BYTE: - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); - } - protocol |= ACPI_EC_SMB_PRTCL_BYTE; - break; - - case I2C_SMBUS_BYTE_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->byte); - } - protocol |= ACPI_EC_SMB_PRTCL_BYTE_DATA; - break; - - case I2C_SMBUS_WORD_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, - data->word >> 8); - } - protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA | pec; - break; - - case I2C_SMBUS_BLOCK_DATA: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - if (read_write == I2C_SMBUS_WRITE) { - len = min_t(u8, data->block[0], 32); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - for (i = 0; i < len; i++) - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - } - protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA | pec; - break; - - case I2C_SMBUS_I2C_BLOCK_DATA: - len = min_t(u8, data->block[0], 32); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - if (read_write == I2C_SMBUS_WRITE) { - for (i = 0; i < len; i++) { - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - } - } - protocol |= ACPI_EC_SMB_PRTCL_I2C_BLOCK_DATA; - break; - - case I2C_SMBUS_PROC_CALL: - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA, data->word); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + 1, data->word >> 8); - protocol = ACPI_EC_SMB_PRTCL_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; - - case I2C_SMBUS_BLOCK_PROC_CALL: - protocol |= pec; - len = min_t(u8, data->block[0], 31); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_CMD, command); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_BCNT, len); - for (i = 0; i < len; i++) - acpi_ec_smb_write(smbus, ACPI_EC_SMB_DATA + i, - data->block[i + 1]); - protocol = ACPI_EC_SMB_PRTCL_BLOCK_PROC_CALL | pec; - read_write = I2C_SMBUS_READ; - break; - - default: - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "EC SMBus adapter: " - "Unsupported transaction %d\n", size)); - return (-1); - } - - acpi_ec_smb_write(smbus, ACPI_EC_SMB_ADDR, addr << 1); - acpi_ec_smb_write(smbus, ACPI_EC_SMB_PRTCL, protocol); - - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xudelay(500); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - } - if (~temp[0] & ACPI_EC_SMB_STS_DONE) { - xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_STS, temp + 0); - } - if ((~temp[0] & ACPI_EC_SMB_STS_DONE) - || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { - return (-1); - } - - if (read_write == I2C_SMBUS_WRITE) { - return (0); - } - - switch (size) { - - case I2C_SMBUS_BYTE: - case I2C_SMBUS_BYTE_DATA: - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, &data->byte); - break; - - case I2C_SMBUS_WORD_DATA: - case I2C_SMBUS_PROC_CALL: - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA, temp + 0); - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + 1, temp + 1); - data->word = (temp[1] << 8) | temp[0]; - break; - - case I2C_SMBUS_BLOCK_DATA: - case I2C_SMBUS_BLOCK_PROC_CALL: - len = 0; - acpi_ec_smb_read(smbus, ACPI_EC_SMB_BCNT, &len); - len = min_t(u8, len, 32); - case I2C_SMBUS_I2C_BLOCK_DATA: - for (i = 0; i < len; i++) - acpi_ec_smb_read(smbus, ACPI_EC_SMB_DATA + i, - data->block + i + 1); - data->block[0] = len; - break; - } - - return (0); -} - -static u32 acpi_ec_smb_func(struct i2c_adapter *adapter) -{ - - return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | - I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | - I2C_FUNC_SMBUS_BLOCK_DATA | - I2C_FUNC_SMBUS_PROC_CALL | - I2C_FUNC_SMBUS_BLOCK_PROC_CALL | - I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC); -} - -static const struct i2c_algorithm acpi_ec_smbus_algorithm = { - .smbus_xfer = acpi_ec_smb_access, - .functionality = acpi_ec_smb_func, -}; - -static int acpi_ec_hc_add(struct acpi_device *device) -{ - int status; - unsigned long val; - struct acpi_ec_hc *ec_hc; - struct acpi_ec_smbus *smbus; - - if (!device) { - return -EINVAL; - } - - ec_hc = kzalloc(sizeof(struct acpi_ec_hc), GFP_KERNEL); - if (!ec_hc) { - return -ENOMEM; - } - - smbus = kzalloc(sizeof(struct acpi_ec_smbus), GFP_KERNEL); - if (!smbus) { - kfree(ec_hc); - return -ENOMEM; - } - - ec_hc->handle = device->handle; - strcpy(acpi_device_name(device), ACPI_EC_HC_DEVICE_NAME); - strcpy(acpi_device_class(device), ACPI_EC_HC_CLASS); - acpi_driver_data(device) = ec_hc; - - status = acpi_evaluate_integer(ec_hc->handle, "_EC", NULL, &val); - if (ACPI_FAILURE(status)) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error obtaining _EC\n")); - kfree(ec_hc); - kfree(smbus); - return -EIO; - } - - smbus->ec = acpi_driver_data(device->parent); - smbus->base = (val & 0xff00ull) >> 8; - smbus->alert = val & 0xffull; - - smbus->adapter.owner = THIS_MODULE; - smbus->adapter.algo = &acpi_ec_smbus_algorithm; - smbus->adapter.algo_data = smbus; - smbus->adapter.dev.parent = &device->dev; - - if (i2c_add_adapter(&smbus->adapter)) { - ACPI_DEBUG_PRINT((ACPI_DB_WARN, - "EC SMBus adapter: Failed to register adapter\n")); - kfree(smbus); - kfree(ec_hc); - return -EIO; - } - - ec_hc->smbus = smbus; - - printk(KERN_INFO PREFIX "%s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); - - return AE_OK; -} - -static int acpi_ec_hc_remove(struct acpi_device *device, int type) -{ - struct acpi_ec_hc *ec_hc; - - if (!device) { - return -EINVAL; - } - ec_hc = acpi_driver_data(device); - - i2c_del_adapter(&ec_hc->smbus->adapter); - kfree(ec_hc->smbus); - kfree(ec_hc); - - return AE_OK; -} - -static int __init acpi_ec_hc_init(void) -{ - int result; - - result = acpi_bus_register_driver(&acpi_ec_hc_driver); - if (result < 0) { - return -ENODEV; - } - return 0; -} - -static void __exit acpi_ec_hc_exit(void) -{ - acpi_bus_unregister_driver(&acpi_ec_hc_driver); -} - -struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device) -{ - return acpi_driver_data(device->parent); -} - -EXPORT_SYMBOL(acpi_get_ec_hc); - -module_init(acpi_ec_hc_init); -module_exit(acpi_ec_hc_exit); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Ducrot Bruno"); -MODULE_DESCRIPTION("ACPI EC SMBus driver"); diff --git a/drivers/acpi/i2c_ec.h b/drivers/acpi/i2c_ec.h deleted file mode 100644 index 7c53fb7..0000000 --- a/drivers/acpi/i2c_ec.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SMBus driver for ACPI Embedded Controller ($Revision: 1.2 $) - * - * Copyright (c) 2002, 2005 Ducrot Bruno - * - * This program is free software; 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. - */ - -struct acpi_ec_smbus { - struct i2c_adapter adapter; - union acpi_ec *ec; - int base; - int alert; -}; - -struct acpi_ec_hc { - acpi_handle handle; - struct acpi_ec_smbus *smbus; -}; - -struct acpi_ec_hc *acpi_get_ec_hc(struct acpi_device *device); diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c deleted file mode 100644 index c6144ca..0000000 --- a/drivers/acpi/ibm_acpi.c +++ /dev/null @@ -1,2763 +0,0 @@ -/* - * ibm_acpi.c - IBM ThinkPad ACPI Extras - * - * - * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> - * Copyright (C) 2006 Henrique de Moraes Holschuh <hmh@hmh.eng.br> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 IBM_VERSION "0.13" - -/* - * Changelog: - * - * 2006-11-22 0.13 new maintainer - * changelog now lives in git commit history, and will - * not be updated further in-file. - * - * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels - * 2005-03-17 0.11 support for 600e, 770x - * thanks to Jamie Lentin <lentinj@dial.pipex.com> - * support for 770e, G41 - * G40 and G41 don't have a thinklight - * temperatures no longer experimental - * experimental brightness control - * experimental volume control - * experimental fan enable/disable - * 2005-01-16 0.10 fix module loading on R30, R31 - * 2005-01-16 0.9 support for 570, R30, R31 - * ultrabay support on A22p, A3x - * limit arg for cmos, led, beep, drop experimental status - * more capable led control on A21e, A22p, T20-22, X20 - * experimental temperatures and fan speed - * experimental embedded controller register dump - * mark more functions as __init, drop incorrect __exit - * use MODULE_VERSION - * thanks to Henrik Brix Andersen <brix@gentoo.org> - * fix parameter passing on module loading - * thanks to Rusty Russell <rusty@rustcorp.com.au> - * thanks to Jim Radford <radford@blackbean.org> - * 2004-11-08 0.8 fix init error case, don't return from a macro - * thanks to Chris Wright <chrisw@osdl.org> - * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 - * fix led control on A21e - * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device - * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 - * proc file format changed - * video_switch command - * experimental cmos control - * experimental led control - * experimental acpi sounds - * 2004-09-16 0.4 support for module parameters - * hotkey mask can be prefixed by 0x - * video output switching - * video expansion control - * ultrabay eject support - * removed lcd brightness/on/off control, didn't work - * 2004-08-17 0.3 support for R40 - * lcd off, brightness control - * thinklight on/off - * 2004-08-14 0.2 support for T series, X20 - * bluetooth enable/disable - * hotkey events disabled by default - * removed fan control, currently useless - * 2004-08-09 0.1 initial release, support for X series - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/types.h> -#include <linux/string.h> - -#include <linux/proc_fs.h> -#include <linux/backlight.h> -#include <asm/uaccess.h> - -#include <linux/dmi.h> -#include <linux/jiffies.h> -#include <linux/workqueue.h> - -#include <acpi/acpi_drivers.h> -#include <acpi/acnamesp.h> - -#define IBM_NAME "ibm" -#define IBM_DESC "IBM ThinkPad ACPI Extras" -#define IBM_FILE "ibm_acpi" -#define IBM_URL "http://ibm-acpi.sf.net/" - -MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); -MODULE_DESCRIPTION(IBM_DESC); -MODULE_VERSION(IBM_VERSION); -MODULE_LICENSE("GPL"); - -#define IBM_DIR IBM_NAME - -#define IBM_LOG IBM_FILE ": " -#define IBM_ERR KERN_ERR IBM_LOG -#define IBM_NOTICE KERN_NOTICE IBM_LOG -#define IBM_INFO KERN_INFO IBM_LOG -#define IBM_DEBUG KERN_DEBUG IBM_LOG - -#define IBM_MAX_ACPI_ARGS 3 - -#define __unused __attribute__ ((unused)) - -static int experimental; -module_param(experimental, int, 0); - -static acpi_handle root_handle = NULL; - -#define IBM_HANDLE(object, parent, paths...) \ - static acpi_handle object##_handle; \ - static acpi_handle *object##_parent = &parent##_handle; \ - static char *object##_path; \ - static char *object##_paths[] = { paths } - -IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ - "\\_SB.PCI.ISA.EC", /* 570 */ - "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ - "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ - "\\_SB.PCI0.ICH3.EC0", /* R31 */ - "\\_SB.PCI0.LPC.EC", /* all others */ - ); - -IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ - "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ - "\\_SB.PCI0.VID0", /* 770e */ - "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ - "\\_SB.PCI0.AGP.VID", /* all others */ - ); /* R30, R31 */ - -IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ - -IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ - "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ - "\\CMS", /* R40, R40e */ - ); /* all others */ -#ifdef CONFIG_ACPI_IBM_DOCK -IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ - "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ - "\\_SB.PCI0.PCI1.DOCK", /* all others */ - "\\_SB.PCI.ISA.SLCE", /* 570 */ - ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ -#endif -IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ - "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ - "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ - "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ - ); /* A21e, R30, R31 */ - -IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ - "_EJ0", /* all others */ - ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ - -IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ - "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ - ); /* all others */ - -IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ - "_EJ0", /* 770x */ - ); /* all others */ - -/* don't list other alternatives as we install a notify handler on the 570 */ -IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ - -IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ - "^HKEY", /* R30, R31 */ - "HKEY", /* all others */ - ); /* 570 */ - -IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ -IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ - -IBM_HANDLE(led, ec, "SLED", /* 570 */ - "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - "LED", /* all others */ - ); /* R30, R31 */ - -IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ -IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ -IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ -IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ - -IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ - "\\FSPD", /* 600e/x, 770e, 770x */ - ); /* all others */ - -IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ - "JFNS", /* 770x-JL */ - ); /* all others */ - -#define IBM_HKEY_HID "IBM0068" -#define IBM_PCI_HID "PNP0A03" - -enum thermal_access_mode { - IBMACPI_THERMAL_NONE = 0, /* No thermal support */ - IBMACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ - IBMACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ - IBMACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ - IBMACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ -}; - -#define IBMACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ -struct ibm_thermal_sensors_struct { - s32 temp[IBMACPI_MAX_THERMAL_SENSORS]; -}; - -/* - * FAN ACCESS MODES - * - * IBMACPI_FAN_RD_ACPI_GFAN: - * ACPI GFAN method: returns fan level - * - * see IBMACPI_FAN_WR_ACPI_SFAN - * EC 0x2f not available if GFAN exists - * - * IBMACPI_FAN_WR_ACPI_SFAN: - * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) - * - * EC 0x2f might be available *for reading*, but never for writing. - * - * IBMACPI_FAN_WR_TPEC: - * ThinkPad EC register 0x2f (HFSP): fan control loop mode Supported - * on almost all ThinkPads - * - * Fan speed changes of any sort (including those caused by the - * disengaged mode) are usually done slowly by the firmware as the - * maximum ammount of fan duty cycle change per second seems to be - * limited. - * - * Reading is not available if GFAN exists. - * Writing is not available if SFAN exists. - * - * Bits - * 7 automatic mode engaged; - * (default operation mode of the ThinkPad) - * fan level is ignored in this mode. - * 6 disengage mode (takes precedence over bit 7); - * not available on all thinkpads. May disable - * the tachometer, and speeds up fan to 100% duty-cycle, - * which speeds it up far above the standard RPM - * levels. It is not impossible that it could cause - * hardware damage. - * 5-3 unused in some models. Extra bits for fan level - * in others, but still useless as all values above - * 7 map to the same speed as level 7 in these models. - * 2-0 fan level (0..7 usually) - * 0x00 = stop - * 0x07 = max (set when temperatures critical) - * Some ThinkPads may have other levels, see - * IBMACPI_FAN_WR_ACPI_FANS (X31/X40/X41) - * - * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at - * boot. Apparently the EC does not intialize it, so unless ACPI DSDT - * does so, its initial value is meaningless (0x07). - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * ---- - * - * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): - * Main fan tachometer reading (in RPM) - * - * This register is present on all ThinkPads with a new-style EC, and - * it is known not to be present on the A21m/e, and T22, as there is - * something else in offset 0x84 according to the ACPI DSDT. Other - * ThinkPads from this same time period (and earlier) probably lack the - * tachometer as well. - * - * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare - * was never fixed by IBM to report the EC firmware version string - * probably support the tachometer (like the early X models), so - * detecting it is quite hard. We need more data to know for sure. - * - * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings - * might result. - * - * FIRMWARE BUG: when EC 0x2f bit 6 is set (disengaged mode), this - * register is not invalidated in ThinkPads that disable tachometer - * readings. Thus, the tachometer readings go stale. - * - * For firmware bugs, refer to: - * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues - * - * IBMACPI_FAN_WR_ACPI_FANS: - * ThinkPad X31, X40, X41. Not available in the X60. - * - * FANS ACPI handle: takes three arguments: low speed, medium speed, - * high speed. ACPI DSDT seems to map these three speeds to levels - * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH - * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") - * - * The speeds are stored on handles - * (FANA:FAN9), (FANC:FANB), (FANE:FAND). - * - * There are three default speed sets, acessible as handles: - * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H - * - * ACPI DSDT switches which set is in use depending on various - * factors. - * - * IBMACPI_FAN_WR_TPEC is also available and should be used to - * command the fan. The X31/X40/X41 seems to have 8 fan levels, - * but the ACPI tables just mention level 7. - */ - -enum fan_status_access_mode { - IBMACPI_FAN_NONE = 0, /* No fan status or control */ - IBMACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ - IBMACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ -}; - -enum fan_control_access_mode { - IBMACPI_FAN_WR_NONE = 0, /* No fan control */ - IBMACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ - IBMACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ - IBMACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ -}; - -enum fan_control_commands { - IBMACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ - IBMACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ - IBMACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, - * and also watchdog cmd */ -}; - -enum { /* Fan control constants */ - fan_status_offset = 0x2f, /* EC register 0x2f */ - fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) - * 0x84 must be read before 0x85 */ - - IBMACPI_FAN_EC_DISENGAGED = 0x40, /* EC mode: tachometer - * disengaged */ - IBMACPI_FAN_EC_AUTO = 0x80, /* EC mode: auto fan - * control */ -}; - -static char *ibm_thinkpad_ec_found = NULL; - -struct ibm_struct { - char *name; - char param[32]; - - char *hid; - struct acpi_driver *driver; - - int (*init) (void); - int (*read) (char *); - int (*write) (char *); - void (*exit) (void); - - void (*notify) (struct ibm_struct *, u32); - acpi_handle *handle; - int type; - struct acpi_device *device; - - int driver_registered; - int proc_created; - int init_called; - int notify_installed; - - int experimental; -}; - -static struct proc_dir_entry *proc_dir = NULL; - -static struct backlight_device *ibm_backlight_device = NULL; - -#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") -#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") -#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) - -static int acpi_evalf(acpi_handle handle, - void *res, char *method, char *fmt, ...) -{ - char *fmt0 = fmt; - struct acpi_object_list params; - union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; - struct acpi_buffer result, *resultp; - union acpi_object out_obj; - acpi_status status; - va_list ap; - char res_type; - int success; - int quiet; - - if (!*fmt) { - printk(IBM_ERR "acpi_evalf() called with empty format\n"); - return 0; - } - - if (*fmt == 'q') { - quiet = 1; - fmt++; - } else - quiet = 0; - - res_type = *(fmt++); - - params.count = 0; - params.pointer = &in_objs[0]; - - va_start(ap, fmt); - while (*fmt) { - char c = *(fmt++); - switch (c) { - case 'd': /* int */ - in_objs[params.count].integer.value = va_arg(ap, int); - in_objs[params.count++].type = ACPI_TYPE_INTEGER; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", c); - return 0; - } - } - va_end(ap); - - if (res_type != 'v') { - result.length = sizeof(out_obj); - result.pointer = &out_obj; - resultp = &result; - } else - resultp = NULL; - - status = acpi_evaluate_object(handle, method, ¶ms, resultp); - - switch (res_type) { - case 'd': /* int */ - if (res) - *(int *)res = out_obj.integer.value; - success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; - break; - case 'v': /* void */ - success = status == AE_OK; - break; - /* add more types as needed */ - default: - printk(IBM_ERR "acpi_evalf() called " - "with invalid format character '%c'\n", res_type); - return 0; - } - - if (!success && !quiet) - printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", - method, fmt0, status); - - return success; -} - -static void __unused acpi_print_int(acpi_handle handle, char *method) -{ - int i; - - if (acpi_evalf(handle, &i, method, "d")) - printk(IBM_INFO "%s = 0x%x\n", method, i); - else - printk(IBM_ERR "error calling %s\n", method); -} - -static char *next_cmd(char **cmds) -{ - char *start = *cmds; - char *end; - - while ((end = strchr(start, ',')) && end == start) - start = end + 1; - - if (!end) - return NULL; - - *end = 0; - *cmds = end + 1; - return start; -} - -static int ibm_acpi_driver_init(void) -{ - printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); - printk(IBM_INFO "%s\n", IBM_URL); - - return 0; -} - -static int driver_read(char *p) -{ - int len = 0; - - len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); - len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); - - return len; -} - -static int hotkey_supported; -static int hotkey_mask_supported; -static int hotkey_orig_status; -static int hotkey_orig_mask; - -static int hotkey_get(int *status, int *mask) -{ - if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) - return 0; - - if (hotkey_mask_supported) - if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) - return 0; - - return 1; -} - -static int hotkey_set(int status, int mask) -{ - int i; - - if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) - return 0; - - if (hotkey_mask_supported) - for (i = 0; i < 32; i++) { - int bit = ((1 << i) & mask) != 0; - if (!acpi_evalf(hkey_handle, - NULL, "MHKM", "vdd", i + 1, bit)) - return 0; - } - - return 1; -} - -static int hotkey_init(void) -{ - /* hotkey not supported on 570 */ - hotkey_supported = hkey_handle != NULL; - - if (hotkey_supported) { - /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - A30, R30, R31, T20-22, X20-21, X22-24 */ - hotkey_mask_supported = - acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); - - if (!hotkey_get(&hotkey_orig_status, &hotkey_orig_mask)) - return -ENODEV; - } - - return 0; -} - -static int hotkey_read(char *p) -{ - int status, mask; - int len = 0; - - if (!hotkey_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - if (!hotkey_get(&status, &mask)) - return -EIO; - - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); - if (hotkey_mask_supported) { - len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); - len += sprintf(p + len, - "commands:\tenable, disable, reset, <mask>\n"); - } else { - len += sprintf(p + len, "mask:\t\tnot supported\n"); - len += sprintf(p + len, "commands:\tenable, disable, reset\n"); - } - - return len; -} - -static int hotkey_write(char *buf) -{ - int status, mask; - char *cmd; - int do_cmd = 0; - - if (!hotkey_supported) - return -ENODEV; - - if (!hotkey_get(&status, &mask)) - return -EIO; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status = 1; - } else if (strlencmp(cmd, "disable") == 0) { - status = 0; - } else if (strlencmp(cmd, "reset") == 0) { - status = hotkey_orig_status; - mask = hotkey_orig_mask; - } else if (sscanf(cmd, "0x%x", &mask) == 1) { - /* mask set */ - } else if (sscanf(cmd, "%x", &mask) == 1) { - /* mask set */ - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !hotkey_set(status, mask)) - return -EIO; - - return 0; -} - -static void hotkey_exit(void) -{ - if (hotkey_supported) - hotkey_set(hotkey_orig_status, hotkey_orig_mask); -} - -static void hotkey_notify(struct ibm_struct *ibm, u32 event) -{ - int hkey; - - if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) - acpi_bus_generate_event(ibm->device, event, hkey); - else { - printk(IBM_ERR "unknown hotkey event %d\n", event); - acpi_bus_generate_event(ibm->device, event, 0); - } -} - -static int bluetooth_supported; - -static int bluetooth_init(void) -{ - /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ - bluetooth_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); - - return 0; -} - -static int bluetooth_status(void) -{ - int status; - - if (!bluetooth_supported || - !acpi_evalf(hkey_handle, &status, "GBDC", "d")) - status = 0; - - return status; -} - -static int bluetooth_read(char *p) -{ - int len = 0; - int status = bluetooth_status(); - - if (!bluetooth_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int bluetooth_write(char *buf) -{ - int status = bluetooth_status(); - char *cmd; - int do_cmd = 0; - - if (!bluetooth_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) - return -EIO; - - return 0; -} - -static int wan_supported; - -static int wan_init(void) -{ - wan_supported = hkey_handle && - acpi_evalf(hkey_handle, NULL, "GWAN", "qv"); - - return 0; -} - -static int wan_status(void) -{ - int status; - - if (!wan_supported || !acpi_evalf(hkey_handle, &status, "GWAN", "d")) - status = 0; - - return status; -} - -static int wan_read(char *p) -{ - int len = 0; - int status = wan_status(); - - if (!wan_supported) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!(status & 1)) - len += sprintf(p + len, "status:\t\tnot installed\n"); - else { - len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); - len += sprintf(p + len, "commands:\tenable, disable\n"); - } - - return len; -} - -static int wan_write(char *buf) -{ - int status = wan_status(); - char *cmd; - int do_cmd = 0; - - if (!wan_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "enable") == 0) { - status |= 2; - } else if (strlencmp(cmd, "disable") == 0) { - status &= ~2; - } else - return -EINVAL; - do_cmd = 1; - } - - if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) - return -EIO; - - return 0; -} - -enum video_access_mode { - IBMACPI_VIDEO_NONE = 0, - IBMACPI_VIDEO_570, /* 570 */ - IBMACPI_VIDEO_770, /* 600e/x, 770e, 770x */ - IBMACPI_VIDEO_NEW, /* all others */ -}; - -static enum video_access_mode video_supported; -static int video_orig_autosw; - -static int video_init(void) -{ - int ivga; - - if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) - /* G41, assume IVGA doesn't change */ - vid_handle = vid2_handle; - - if (!vid_handle) - /* video switching not supported on R30, R31 */ - video_supported = IBMACPI_VIDEO_NONE; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) - /* 570 */ - video_supported = IBMACPI_VIDEO_570; - else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) - /* 600e/x, 770e, 770x */ - video_supported = IBMACPI_VIDEO_770; - else - /* all others */ - video_supported = IBMACPI_VIDEO_NEW; - - return 0; -} - -static int video_status(void) -{ - int status = 0; - int i; - - if (video_supported == IBMACPI_VIDEO_570) { - if (acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", 0x87)) - status = i & 3; - } else if (video_supported == IBMACPI_VIDEO_770) { - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - } else if (video_supported == IBMACPI_VIDEO_NEW) { - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); - if (acpi_evalf(NULL, &i, "\\VCDC", "d")) - status |= 0x02 * i; - - acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); - if (acpi_evalf(NULL, &i, "\\VCDL", "d")) - status |= 0x01 * i; - if (acpi_evalf(NULL, &i, "\\VCDD", "d")) - status |= 0x08 * i; - } - - return status; -} - -static int video_autosw(void) -{ - int autosw = 0; - - if (video_supported == IBMACPI_VIDEO_570) - acpi_evalf(vid_handle, &autosw, "SWIT", "d"); - else if (video_supported == IBMACPI_VIDEO_770 || - video_supported == IBMACPI_VIDEO_NEW) - acpi_evalf(vid_handle, &autosw, "^VDEE", "d"); - - return autosw & 1; -} - -static int video_read(char *p) -{ - int status = video_status(); - int autosw = video_autosw(); - int len = 0; - - if (!video_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); - len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); - len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); - len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); - len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); - if (video_supported == IBMACPI_VIDEO_NEW) - len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); - len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); - len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); - - return len; -} - -static int video_switch(void) -{ - int autosw = video_autosw(); - int ret; - - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - ret = video_supported == IBMACPI_VIDEO_570 ? - acpi_evalf(ec_handle, NULL, "_Q16", "v") : - acpi_evalf(vid_handle, NULL, "VSWT", "v"); - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - - return ret; -} - -static int video_expand(void) -{ - if (video_supported == IBMACPI_VIDEO_570) - return acpi_evalf(ec_handle, NULL, "_Q17", "v"); - else if (video_supported == IBMACPI_VIDEO_770) - return acpi_evalf(vid_handle, NULL, "VEXP", "v"); - else - return acpi_evalf(NULL, NULL, "\\VEXP", "v"); -} - -static int video_switch2(int status) -{ - int ret; - - if (video_supported == IBMACPI_VIDEO_570) { - ret = acpi_evalf(NULL, NULL, - "\\_SB.PHS2", "vdd", 0x8b, status | 0x80); - } else if (video_supported == IBMACPI_VIDEO_770) { - int autosw = video_autosw(); - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - - ret = acpi_evalf(vid_handle, NULL, - "ASWT", "vdd", status * 0x100, 0); - - acpi_evalf(vid_handle, NULL, "_DOS", "vd", autosw); - } else { - ret = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && - acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); - } - - return ret; -} - -static int video_write(char *buf) -{ - char *cmd; - int enable, disable, status; - - if (!video_supported) - return -ENODEV; - - enable = disable = 0; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "lcd_enable") == 0) { - enable |= 0x01; - } else if (strlencmp(cmd, "lcd_disable") == 0) { - disable |= 0x01; - } else if (strlencmp(cmd, "crt_enable") == 0) { - enable |= 0x02; - } else if (strlencmp(cmd, "crt_disable") == 0) { - disable |= 0x02; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_enable") == 0) { - enable |= 0x08; - } else if (video_supported == IBMACPI_VIDEO_NEW && - strlencmp(cmd, "dvi_disable") == 0) { - disable |= 0x08; - } else if (strlencmp(cmd, "auto_enable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "auto_disable") == 0) { - if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) - return -EIO; - } else if (strlencmp(cmd, "video_switch") == 0) { - if (!video_switch()) - return -EIO; - } else if (strlencmp(cmd, "expand_toggle") == 0) { - if (!video_expand()) - return -EIO; - } else - return -EINVAL; - } - - if (enable || disable) { - status = (video_status() & 0x0f & ~disable) | enable; - if (!video_switch2(status)) - return -EIO; - } - - return 0; -} - -static void video_exit(void) -{ - acpi_evalf(vid_handle, NULL, "_DOS", "vd", video_orig_autosw); -} - -static int light_supported; -static int light_status_supported; - -static int light_init(void) -{ - /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ - light_supported = (cmos_handle || lght_handle) && !ledb_handle; - - if (light_supported) - /* light status not supported on - 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ - light_status_supported = acpi_evalf(ec_handle, NULL, - "KBLT", "qv"); - - return 0; -} - -static int light_read(char *p) -{ - int len = 0; - int status = 0; - - if (!light_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - } else if (!light_status_supported) { - len += sprintf(p + len, "status:\t\tunknown\n"); - len += sprintf(p + len, "commands:\ton, off\n"); - } else { - if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) - return -EIO; - len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); - len += sprintf(p + len, "commands:\ton, off\n"); - } - - return len; -} - -static int light_write(char *buf) -{ - int cmos_cmd, lght_cmd; - char *cmd; - int success; - - if (!light_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "on") == 0) { - cmos_cmd = 0x0c; - lght_cmd = 1; - } else if (strlencmp(cmd, "off") == 0) { - cmos_cmd = 0x0d; - lght_cmd = 0; - } else - return -EINVAL; - - success = cmos_handle ? - acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : - acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); - if (!success) - return -EIO; - } - - return 0; -} - -static int _sta(acpi_handle handle) -{ - int status; - - if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) - status = 0; - - return status; -} - -#ifdef CONFIG_ACPI_IBM_DOCK -#define dock_docked() (_sta(dock_handle) & 1) - -static int dock_read(char *p) -{ - int len = 0; - int docked = dock_docked(); - - if (!dock_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else if (!docked) - len += sprintf(p + len, "status:\t\tundocked\n"); - else { - len += sprintf(p + len, "status:\t\tdocked\n"); - len += sprintf(p + len, "commands:\tdock, undock\n"); - } - - return len; -} - -static int dock_write(char *buf) -{ - char *cmd; - - if (!dock_docked()) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (strlencmp(cmd, "undock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || - !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) - return -EIO; - } else if (strlencmp(cmd, "dock") == 0) { - if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static void dock_notify(struct ibm_struct *ibm, u32 event) -{ - int docked = dock_docked(); - int pci = ibm->hid && strstr(ibm->hid, IBM_PCI_HID); - - if (event == 1 && !pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 1 && pci) /* 570 */ - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else if (event == 3 && docked) - acpi_bus_generate_event(ibm->device, event, 1); /* button */ - else if (event == 3 && !docked) - acpi_bus_generate_event(ibm->device, event, 2); /* undock */ - else if (event == 0 && docked) - acpi_bus_generate_event(ibm->device, event, 3); /* dock */ - else { - printk(IBM_ERR "unknown dock event %d, status %d\n", - event, _sta(dock_handle)); - acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ - } -} -#endif - -static int bay_status_supported; -static int bay_status2_supported; -static int bay_eject_supported; -static int bay_eject2_supported; - -static int bay_init(void) -{ - bay_status_supported = bay_handle && - acpi_evalf(bay_handle, NULL, "_STA", "qv"); - bay_status2_supported = bay2_handle && - acpi_evalf(bay2_handle, NULL, "_STA", "qv"); - - bay_eject_supported = bay_handle && bay_ej_handle && - (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); - bay_eject2_supported = bay2_handle && bay2_ej_handle && - (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); - - return 0; -} - -#define bay_occupied(b) (_sta(b##_handle) & 1) - -static int bay_read(char *p) -{ - int len = 0; - int occupied = bay_occupied(bay); - int occupied2 = bay_occupied(bay2); - int eject, eject2; - - len += sprintf(p + len, "status:\t\t%s\n", bay_status_supported ? - (occupied ? "occupied" : "unoccupied") : - "not supported"); - if (bay_status2_supported) - len += sprintf(p + len, "status2:\t%s\n", occupied2 ? - "occupied" : "unoccupied"); - - eject = bay_eject_supported && occupied; - eject2 = bay_eject2_supported && occupied2; - - if (eject && eject2) - len += sprintf(p + len, "commands:\teject, eject2\n"); - else if (eject) - len += sprintf(p + len, "commands:\teject\n"); - else if (eject2) - len += sprintf(p + len, "commands:\teject2\n"); - - return len; -} - -static int bay_write(char *buf) -{ - char *cmd; - - if (!bay_eject_supported && !bay_eject2_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (bay_eject_supported && strlencmp(cmd, "eject") == 0) { - if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else if (bay_eject2_supported && - strlencmp(cmd, "eject2") == 0) { - if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static void bay_notify(struct ibm_struct *ibm, u32 event) -{ - acpi_bus_generate_event(ibm->device, event, 0); -} - -static int cmos_read(char *p) -{ - int len = 0; - - /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, - R30, R31, T20-22, X20-21 */ - if (!cmos_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); - } - - return len; -} - -static int cmos_eval(int cmos_cmd) -{ - if (cmos_handle) - return acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd); - else - return 1; -} - -static int cmos_write(char *buf) -{ - char *cmd; - int cmos_cmd; - - if (!cmos_handle) - return -EINVAL; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &cmos_cmd) == 1 && - cmos_cmd >= 0 && cmos_cmd <= 21) { - /* cmos_cmd set */ - } else - return -EINVAL; - - if (!cmos_eval(cmos_cmd)) - return -EIO; - } - - return 0; -} - -enum led_access_mode { - IBMACPI_LED_NONE = 0, - IBMACPI_LED_570, /* 570 */ - IBMACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - IBMACPI_LED_NEW, /* all others */ -}; -static enum led_access_mode led_supported; - -static int led_init(void) -{ - if (!led_handle) - /* led not supported on R30, R31 */ - led_supported = IBMACPI_LED_NONE; - else if (strlencmp(led_path, "SLED") == 0) - /* 570 */ - led_supported = IBMACPI_LED_570; - else if (strlencmp(led_path, "SYSL") == 0) - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ - led_supported = IBMACPI_LED_OLD; - else - /* all others */ - led_supported = IBMACPI_LED_NEW; - - return 0; -} - -#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) - -static int led_read(char *p) -{ - int len = 0; - - if (!led_supported) { - len += sprintf(p + len, "status:\t\tnot supported\n"); - return len; - } - len += sprintf(p + len, "status:\t\tsupported\n"); - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - int i, status; - for (i = 0; i < 8; i++) { - if (!acpi_evalf(ec_handle, - &status, "GLED", "dd", 1 << i)) - return -EIO; - len += sprintf(p + len, "%d:\t\t%s\n", - i, led_status(status)); - } - } - - len += sprintf(p + len, "commands:\t" - "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); - - return len; -} - -/* off, on, blink */ -static const int led_sled_arg1[] = { 0, 1, 3 }; -static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ -static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ -static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; - -#define EC_HLCL 0x0c -#define EC_HLBL 0x0d -#define EC_HLMS 0x0e - -static int led_write(char *buf) -{ - char *cmd; - int led, ind, ret; - - if (!led_supported) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) - return -EINVAL; - - if (strstr(cmd, "off")) { - ind = 0; - } else if (strstr(cmd, "on")) { - ind = 1; - } else if (strstr(cmd, "blink")) { - ind = 2; - } else - return -EINVAL; - - if (led_supported == IBMACPI_LED_570) { - /* 570 */ - led = 1 << led; - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_sled_arg1[ind])) - return -EIO; - } else if (led_supported == IBMACPI_LED_OLD) { - /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ - led = 1 << led; - ret = ec_write(EC_HLMS, led); - if (ret >= 0) - ret = - ec_write(EC_HLBL, led * led_exp_hlbl[ind]); - if (ret >= 0) - ret = - ec_write(EC_HLCL, led * led_exp_hlcl[ind]); - if (ret < 0) - return ret; - } else { - /* all others */ - if (!acpi_evalf(led_handle, NULL, NULL, "vdd", - led, led_led_arg1[ind])) - return -EIO; - } - } - - return 0; -} - -static int beep_read(char *p) -{ - int len = 0; - - if (!beep_handle) - len += sprintf(p + len, "status:\t\tnot supported\n"); - else { - len += sprintf(p + len, "status:\t\tsupported\n"); - len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); - } - - return len; -} - -static int beep_write(char *buf) -{ - char *cmd; - int beep_cmd; - - if (!beep_handle) - return -ENODEV; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%u", &beep_cmd) == 1 && - beep_cmd >= 0 && beep_cmd <= 17) { - /* beep_cmd set */ - } else - return -EINVAL; - if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) - return -EIO; - } - - return 0; -} - -static int acpi_ec_read(int i, u8 * p) -{ - int v; - - if (ecrd_handle) { - if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) - return 0; - *p = v; - } else { - if (ec_read(i, p) < 0) - return 0; - } - - return 1; -} - -static int acpi_ec_write(int i, u8 v) -{ - if (ecwr_handle) { - if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) - return 0; - } else { - if (ec_write(i, v) < 0) - return 0; - } - - return 1; -} - -static enum thermal_access_mode thermal_read_mode; - -static int thermal_init(void) -{ - u8 t, ta1, ta2; - int i; - int acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); - - if (ibm_thinkpad_ec_found && experimental) { - /* - * Direct EC access mode: sensors at registers - * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for - * non-implemented, thermal sensors return 0x80 when - * not available - */ - - ta1 = ta2 = 0; - for (i = 0; i < 8; i++) { - if (likely(acpi_ec_read(0x78 + i, &t))) { - ta1 |= t; - } else { - ta1 = 0; - break; - } - if (likely(acpi_ec_read(0xC0 + i, &t))) { - ta2 |= t; - } else { - ta1 = 0; - break; - } - } - if (ta1 == 0) { - /* This is sheer paranoia, but we handle it anyway */ - if (acpi_tmp7) { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "falling back to ACPI TMPx access mode\n"); - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "disabling thermal sensors access\n"); - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - } else { - thermal_read_mode = - (ta2 != 0) ? - IBMACPI_THERMAL_TPEC_16 : IBMACPI_THERMAL_TPEC_8; - } - } else if (acpi_tmp7) { - if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { - /* 600e/x, 770e, 770x */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_UPDT; - } else { - /* Standard ACPI TMPx access, max 8 sensors */ - thermal_read_mode = IBMACPI_THERMAL_ACPI_TMP07; - } - } else { - /* temperatures not supported on 570, G4x, R30, R31, R32 */ - thermal_read_mode = IBMACPI_THERMAL_NONE; - } - - return 0; -} - -static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) -{ - int i, t; - s8 tmp; - char tmpi[] = "TMPi"; - - if (!s) - return -EINVAL; - - switch (thermal_read_mode) { -#if IBMACPI_MAX_THERMAL_SENSORS >= 16 - case IBMACPI_THERMAL_TPEC_16: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0xC0 + i, &tmp)) - return -EIO; - s->temp[i + 8] = tmp * 1000; - } - /* fallthrough */ -#endif - case IBMACPI_THERMAL_TPEC_8: - for (i = 0; i < 8; i++) { - if (!acpi_ec_read(0x78 + i, &tmp)) - return -EIO; - s->temp[i] = tmp * 1000; - } - return (thermal_read_mode == IBMACPI_THERMAL_TPEC_16) ? 16 : 8; - - case IBMACPI_THERMAL_ACPI_UPDT: - if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) - return -EIO; - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = (t - 2732) * 100; - } - return 8; - - case IBMACPI_THERMAL_ACPI_TMP07: - for (i = 0; i < 8; i++) { - tmpi[3] = '0' + i; - if (!acpi_evalf(ec_handle, &t, tmpi, "d")) - return -EIO; - s->temp[i] = t * 1000; - } - return 8; - - case IBMACPI_THERMAL_NONE: - default: - return 0; - } -} - -static int thermal_read(char *p) -{ - int len = 0; - int n, i; - struct ibm_thermal_sensors_struct t; - - n = thermal_get_sensors(&t); - if (unlikely(n < 0)) - return n; - - len += sprintf(p + len, "temperatures:\t"); - - if (n > 0) { - for (i = 0; i < (n - 1); i++) - len += sprintf(p + len, "%d ", t.temp[i] / 1000); - len += sprintf(p + len, "%d\n", t.temp[i] / 1000); - } else - len += sprintf(p + len, "not supported\n"); - - return len; -} - -static u8 ecdump_regs[256]; - -static int ecdump_read(char *p) -{ - int len = 0; - int i, j; - u8 v; - - len += sprintf(p + len, "EC " - " +00 +01 +02 +03 +04 +05 +06 +07" - " +08 +09 +0a +0b +0c +0d +0e +0f\n"); - for (i = 0; i < 256; i += 16) { - len += sprintf(p + len, "EC 0x%02x:", i); - for (j = 0; j < 16; j++) { - if (!acpi_ec_read(i + j, &v)) - break; - if (v != ecdump_regs[i + j]) - len += sprintf(p + len, " *%02x", v); - else - len += sprintf(p + len, " %02x", v); - ecdump_regs[i + j] = v; - } - len += sprintf(p + len, "\n"); - if (j != 16) - break; - } - - /* These are way too dangerous to advertise openly... */ -#if 0 - len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" - " (<offset> is 00-ff, <value> is 00-ff)\n"); - len += sprintf(p + len, "commands:\t0x<offset> <value> " - " (<offset> is 00-ff, <value> is 0-255)\n"); -#endif - return len; -} - -static int ecdump_write(char *buf) -{ - char *cmd; - int i, v; - - while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { - /* i and v set */ - } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { - /* i and v set */ - } else - return -EINVAL; - if (i >= 0 && i < 256 && v >= 0 && v < 256) { - if (!acpi_ec_write(i, v)) - return -EIO; - } else - return -EINVAL; - } - - return 0; -} - -static int brightness_offset = 0x31; - -static int brightness_get(struct backlight_device *bd) -{ - u8 level; - if (!acpi_ec_read(brightness_offset, &level)) - return -EIO; - - level &= 0x7; - - return level; -} - -static int brightness_read(char *p) -{ - int len = 0; - int level; - - if ((level = brightness_get(NULL)) < 0) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); - len += sprintf(p + len, "commands:\tup, down\n"); - len += sprintf(p + len, "commands:\tlevel <level>" - " (<level> is 0-7)\n"); - } - - return len; -} - -#define BRIGHTNESS_UP 4 -#define BRIGHTNESS_DOWN 5 - -static int brightness_set(int value) -{ - int cmos_cmd, inc, i; - int current_value = brightness_get(NULL); - - value &= 7; - - cmos_cmd = value > current_value ? BRIGHTNESS_UP : BRIGHTNESS_DOWN; - inc = value > current_value ? 1 : -1; - for (i = current_value; i != value; i += inc) { - if (!cmos_eval(cmos_cmd)) - return -EIO; - if (!acpi_ec_write(brightness_offset, i + inc)) - return -EIO; - } - - return 0; -} - -static int brightness_write(char *buf) -{ - int level; - int new_level; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if ((level = brightness_get(NULL)) < 0) - return level; - level &= 7; - - if (strlencmp(cmd, "up") == 0) { - new_level = level == 7 ? 7 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 7) { - /* new_level set */ - } else - return -EINVAL; - - brightness_set(new_level); - } - - return 0; -} - -static int brightness_update_status(struct backlight_device *bd) -{ - return brightness_set(bd->props->brightness); -} - -static struct backlight_properties ibm_backlight_data = { - .owner = THIS_MODULE, - .get_brightness = brightness_get, - .update_status = brightness_update_status, - .max_brightness = 7, -}; - -static int brightness_init(void) -{ - ibm_backlight_device = backlight_device_register("ibm", NULL, NULL, - &ibm_backlight_data); - if (IS_ERR(ibm_backlight_device)) { - printk(IBM_ERR "Could not register backlight device\n"); - return PTR_ERR(ibm_backlight_device); - } - - return 0; -} - -static void brightness_exit(void) -{ - if (ibm_backlight_device) { - backlight_device_unregister(ibm_backlight_device); - ibm_backlight_device = NULL; - } -} - -static int volume_offset = 0x30; - -static int volume_read(char *p) -{ - int len = 0; - u8 level; - - if (!acpi_ec_read(volume_offset, &level)) { - len += sprintf(p + len, "level:\t\tunreadable\n"); - } else { - len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); - len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); - len += sprintf(p + len, "commands:\tup, down, mute\n"); - len += sprintf(p + len, "commands:\tlevel <level>" - " (<level> is 0-15)\n"); - } - - return len; -} - -#define VOLUME_DOWN 0 -#define VOLUME_UP 1 -#define VOLUME_MUTE 2 - -static int volume_write(char *buf) -{ - int cmos_cmd, inc, i; - u8 level, mute; - int new_level, new_mute; - char *cmd; - - while ((cmd = next_cmd(&buf))) { - if (!acpi_ec_read(volume_offset, &level)) - return -EIO; - new_mute = mute = level & 0x40; - new_level = level = level & 0xf; - - if (strlencmp(cmd, "up") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 15 ? 15 : level + 1; - } else if (strlencmp(cmd, "down") == 0) { - if (mute) - new_mute = 0; - else - new_level = level == 0 ? 0 : level - 1; - } else if (sscanf(cmd, "level %d", &new_level) == 1 && - new_level >= 0 && new_level <= 15) { - /* new_level set */ - } else if (strlencmp(cmd, "mute") == 0) { - new_mute = 0x40; - } else - return -EINVAL; - - if (new_level != level) { /* mute doesn't change */ - cmos_cmd = new_level > level ? VOLUME_UP : VOLUME_DOWN; - inc = new_level > level ? 1 : -1; - - if (mute && (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level))) - return -EIO; - - for (i = level; i != new_level; i += inc) - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, i + inc)) - return -EIO; - - if (mute && (!cmos_eval(VOLUME_MUTE) || - !acpi_ec_write(volume_offset, - new_level + mute))) - return -EIO; - } - - if (new_mute != mute) { /* level doesn't change */ - cmos_cmd = new_mute ? VOLUME_MUTE : VOLUME_UP; - - if (!cmos_eval(cmos_cmd) || - !acpi_ec_write(volume_offset, level + new_mute)) - return -EIO; - } - } - - return 0; -} - -static enum fan_status_access_mode fan_status_access_mode; -static enum fan_control_access_mode fan_control_access_mode; -static enum fan_control_commands fan_control_commands; - -static int fan_control_status_known; -static u8 fan_control_initial_status; - -static void fan_watchdog_fire(struct work_struct *ignored); -static int fan_watchdog_maxinterval; -static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); - -static int fan_init(void) -{ - fan_status_access_mode = IBMACPI_FAN_NONE; - fan_control_access_mode = IBMACPI_FAN_WR_NONE; - fan_control_commands = 0; - fan_control_status_known = 1; - fan_watchdog_maxinterval = 0; - - if (gfan_handle) { - /* 570, 600e/x, 770e, 770x */ - fan_status_access_mode = IBMACPI_FAN_RD_ACPI_GFAN; - } else { - /* all other ThinkPads: note that even old-style - * ThinkPad ECs supports the fan control register */ - if (likely(acpi_ec_read(fan_status_offset, - &fan_control_initial_status))) { - fan_status_access_mode = IBMACPI_FAN_RD_TPEC; - - /* In some ThinkPads, neither the EC nor the ACPI - * DSDT initialize the fan status, and it ends up - * being set to 0x07 when it *could* be either - * 0x07 or 0x80. - * - * Enable for TP-1Y (T43), TP-78 (R51e), - * TP-76 (R52), TP-70 (T43, R52), which are known - * to be buggy. */ - if (fan_control_initial_status == 0x07 && - ibm_thinkpad_ec_found && - ((ibm_thinkpad_ec_found[0] == '1' && - ibm_thinkpad_ec_found[1] == 'Y') || - (ibm_thinkpad_ec_found[0] == '7' && - (ibm_thinkpad_ec_found[1] == '6' || - ibm_thinkpad_ec_found[1] == '8' || - ibm_thinkpad_ec_found[1] == '0')) - )) { - printk(IBM_NOTICE - "fan_init: initial fan status is " - "unknown, assuming it is in auto " - "mode\n"); - fan_control_status_known = 0; - } - } else { - printk(IBM_ERR - "ThinkPad ACPI EC access misbehaving, " - "fan status and control unavailable\n"); - return 0; - } - } - - if (sfan_handle) { - /* 570, 770x-JL */ - fan_control_access_mode = IBMACPI_FAN_WR_ACPI_SFAN; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | IBMACPI_FAN_CMD_ENABLE; - } else { - if (!gfan_handle) { - /* gfan without sfan means no fan control */ - /* all other models implement TP EC 0x2f control */ - - if (fans_handle) { - /* X31, X40, X41 */ - fan_control_access_mode = - IBMACPI_FAN_WR_ACPI_FANS; - fan_control_commands |= - IBMACPI_FAN_CMD_SPEED | - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } else { - fan_control_access_mode = IBMACPI_FAN_WR_TPEC; - fan_control_commands |= - IBMACPI_FAN_CMD_LEVEL | - IBMACPI_FAN_CMD_ENABLE; - } - } - } - - return 0; -} - -static int fan_get_status(u8 *status) -{ - u8 s; - - /* TODO: - * Add IBMACPI_FAN_RD_ACPI_FANS ? */ - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - - if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) - return -EIO; - - if (likely(status)) - *status = s & 0x07; - - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_status_offset, &s))) - return -EIO; - - if (likely(status)) - *status = s; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static int fan_get_speed(unsigned int *speed) -{ - u8 hi, lo; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || - !acpi_ec_read(fan_rpm_offset + 1, &hi))) - return -EIO; - - if (likely(speed)) - *speed = (hi << 8) | lo; - - break; - - default: - return -ENXIO; - } - - return 0; -} - -static void fan_exit(void) -{ - cancel_delayed_work(&fan_watchdog_task); - flush_scheduled_work(); -} - -static void fan_watchdog_reset(void) -{ - static int fan_watchdog_active = 0; - - if (fan_watchdog_active) - cancel_delayed_work(&fan_watchdog_task); - - if (fan_watchdog_maxinterval > 0) { - fan_watchdog_active = 1; - if (!schedule_delayed_work(&fan_watchdog_task, - msecs_to_jiffies(fan_watchdog_maxinterval - * 1000))) { - printk(IBM_ERR "failed to schedule the fan watchdog, " - "watchdog will not trigger\n"); - } - } else - fan_watchdog_active = 0; -} - -static int fan_read(char *p) -{ - int len = 0; - int rc; - u8 status; - unsigned int speed = 0; - - switch (fan_status_access_mode) { - case IBMACPI_FAN_RD_ACPI_GFAN: - /* 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - len += sprintf(p + len, "status:\t\t%s\n" - "level:\t\t%d\n", - (status != 0) ? "enabled" : "disabled", status); - break; - - case IBMACPI_FAN_RD_TPEC: - /* all except 570, 600e/x, 770e, 770x */ - if ((rc = fan_get_status(&status)) < 0) - return rc; - - if (unlikely(!fan_control_status_known)) { - if (status != fan_control_initial_status) - fan_control_status_known = 1; - else - /* Return most likely status. In fact, it - * might be the only possible status */ - status = IBMACPI_FAN_EC_AUTO; - } - - len += sprintf(p + len, "status:\t\t%s\n", - (status != 0) ? "enabled" : "disabled"); - - /* No ThinkPad boots on disengaged mode, we can safely - * assume the tachometer is online if fan control status - * was unknown */ - if ((rc = fan_get_speed(&speed)) < 0) - return rc; - - len += sprintf(p + len, "speed:\t\t%d\n", speed); - - if (status & IBMACPI_FAN_EC_DISENGAGED) - /* Disengaged mode takes precedence */ - len += sprintf(p + len, "level:\t\tdisengaged\n"); - else if (status & IBMACPI_FAN_EC_AUTO) - len += sprintf(p + len, "level:\t\tauto\n"); - else - len += sprintf(p + len, "level:\t\t%d\n", status); - break; - - case IBMACPI_FAN_NONE: - default: - len += sprintf(p + len, "status:\t\tnot supported\n"); - } - - if (fan_control_commands & IBMACPI_FAN_CMD_LEVEL) { - len += sprintf(p + len, "commands:\tlevel <level>"); - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - len += sprintf(p + len, " (<level> is 0-7)\n"); - break; - - default: - len += sprintf(p + len, " (<level> is 0-7, " - "auto, disengaged)\n"); - break; - } - } - - if (fan_control_commands & IBMACPI_FAN_CMD_ENABLE) - len += sprintf(p + len, "commands:\tenable, disable\n" - "commands:\twatchdog <timeout> (<timeout> is 0 (off), " - "1-120 (seconds))\n"); - - if (fan_control_commands & IBMACPI_FAN_CMD_SPEED) - len += sprintf(p + len, "commands:\tspeed <speed>" - " (<speed> is 0-65535)\n"); - - return len; -} - -static int fan_set_level(int level) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_SFAN: - if (level >= 0 && level <= 7) { - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) - return -EIO; - } else - return -EINVAL; - break; - - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((level != IBMACPI_FAN_EC_AUTO) && - (level != IBMACPI_FAN_EC_DISENGAGED) && - ((level < 0) || (level > 7))) - return -EINVAL; - - if (!acpi_ec_write(fan_status_offset, level)) - return -EIO; - else - fan_control_status_known = 1; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_enable(void) -{ - u8 s; - int rc; - - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - /* Don't go out of emergency fan mode */ - if (s != 7) - s = IBMACPI_FAN_EC_AUTO; - - if (!acpi_ec_write(fan_status_offset, s)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if ((rc = fan_get_status(&s)) < 0) - return rc; - - s &= 0x07; - - /* Set fan to at least level 4 */ - if (s < 4) - s = 4; - - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_disable(void) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - case IBMACPI_FAN_WR_TPEC: - if (!acpi_ec_write(fan_status_offset, 0x00)) - return -EIO; - else - fan_control_status_known = 1; - break; - - case IBMACPI_FAN_WR_ACPI_SFAN: - if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) - return -EIO; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_set_speed(int speed) -{ - switch (fan_control_access_mode) { - case IBMACPI_FAN_WR_ACPI_FANS: - if (speed >= 0 && speed <= 65535) { - if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", - speed, speed, speed)) - return -EIO; - } else - return -EINVAL; - break; - - default: - return -ENXIO; - } - return 0; -} - -static int fan_write_cmd_level(const char *cmd, int *rc) -{ - int level; - - if (strlencmp(cmd, "level auto") == 0) - level = IBMACPI_FAN_EC_AUTO; - else if (strlencmp(cmd, "level disengaged") == 0) - level = IBMACPI_FAN_EC_DISENGAGED; - else if (sscanf(cmd, "level %d", &level) != 1) - return 0; - - if ((*rc = fan_set_level(level)) == -ENXIO) - printk(IBM_ERR "level command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_enable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "enable") != 0) - return 0; - - if ((*rc = fan_set_enable()) == -ENXIO) - printk(IBM_ERR "enable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_disable(const char *cmd, int *rc) -{ - if (strlencmp(cmd, "disable") != 0) - return 0; - - if ((*rc = fan_set_disable()) == -ENXIO) - printk(IBM_ERR "disable command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_speed(const char *cmd, int *rc) -{ - int speed; - - /* TODO: - * Support speed <low> <medium> <high> ? */ - - if (sscanf(cmd, "speed %d", &speed) != 1) - return 0; - - if ((*rc = fan_set_speed(speed)) == -ENXIO) - printk(IBM_ERR "speed command accepted for unsupported " - "access mode %d", fan_control_access_mode); - - return 1; -} - -static int fan_write_cmd_watchdog(const char *cmd, int *rc) -{ - int interval; - - if (sscanf(cmd, "watchdog %d", &interval) != 1) - return 0; - - if (interval < 0 || interval > 120) - *rc = -EINVAL; - else - fan_watchdog_maxinterval = interval; - - return 1; -} - -static int fan_write(char *buf) -{ - char *cmd; - int rc = 0; - - while (!rc && (cmd = next_cmd(&buf))) { - if (!((fan_control_commands & IBMACPI_FAN_CMD_LEVEL) && - fan_write_cmd_level(cmd, &rc)) && - !((fan_control_commands & IBMACPI_FAN_CMD_ENABLE) && - (fan_write_cmd_enable(cmd, &rc) || - fan_write_cmd_disable(cmd, &rc) || - fan_write_cmd_watchdog(cmd, &rc))) && - !((fan_control_commands & IBMACPI_FAN_CMD_SPEED) && - fan_write_cmd_speed(cmd, &rc)) - ) - rc = -EINVAL; - else if (!rc) - fan_watchdog_reset(); - } - - return rc; -} - -static void fan_watchdog_fire(struct work_struct *ignored) -{ - printk(IBM_NOTICE "fan watchdog: enabling fan\n"); - if (fan_set_enable()) { - printk(IBM_ERR "fan watchdog: error while enabling fan\n"); - /* reschedule for later */ - fan_watchdog_reset(); - } -} - -static struct ibm_struct ibms[] = { - { - .name = "driver", - .init = ibm_acpi_driver_init, - .read = driver_read, - }, - { - .name = "hotkey", - .hid = IBM_HKEY_HID, - .init = hotkey_init, - .read = hotkey_read, - .write = hotkey_write, - .exit = hotkey_exit, - .notify = hotkey_notify, - .handle = &hkey_handle, - .type = ACPI_DEVICE_NOTIFY, - }, - { - .name = "bluetooth", - .init = bluetooth_init, - .read = bluetooth_read, - .write = bluetooth_write, - }, - { - .name = "wan", - .init = wan_init, - .read = wan_read, - .write = wan_write, - .experimental = 1, - }, - { - .name = "video", - .init = video_init, - .read = video_read, - .write = video_write, - .exit = video_exit, - }, - { - .name = "light", - .init = light_init, - .read = light_read, - .write = light_write, - }, -#ifdef CONFIG_ACPI_IBM_DOCK - { - .name = "dock", - .read = dock_read, - .write = dock_write, - .notify = dock_notify, - .handle = &dock_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, - { - .name = "dock", - .hid = IBM_PCI_HID, - .notify = dock_notify, - .handle = &pci_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, -#endif - { - .name = "bay", - .init = bay_init, - .read = bay_read, - .write = bay_write, - .notify = bay_notify, - .handle = &bay_handle, - .type = ACPI_SYSTEM_NOTIFY, - }, - { - .name = "cmos", - .read = cmos_read, - .write = cmos_write, - }, - { - .name = "led", - .init = led_init, - .read = led_read, - .write = led_write, - }, - { - .name = "beep", - .read = beep_read, - .write = beep_write, - }, - { - .name = "thermal", - .init = thermal_init, - .read = thermal_read, - }, - { - .name = "ecdump", - .read = ecdump_read, - .write = ecdump_write, - .experimental = 1, - }, - { - .name = "brightness", - .read = brightness_read, - .write = brightness_write, - .init = brightness_init, - .exit = brightness_exit, - }, - { - .name = "volume", - .read = volume_read, - .write = volume_write, - }, - { - .name = "fan", - .read = fan_read, - .write = fan_write, - .init = fan_init, - .exit = fan_exit, - .experimental = 1, - }, -}; - -static int dispatch_read(char *page, char **start, off_t off, int count, - int *eof, void *data) -{ - struct ibm_struct *ibm = data; - int len; - - if (!ibm || !ibm->read) - return -EINVAL; - - len = ibm->read(page); - if (len < 0) - return len; - - if (len <= off + count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - - return len; -} - -static int dispatch_write(struct file *file, const char __user * userbuf, - unsigned long count, void *data) -{ - struct ibm_struct *ibm = data; - char *kernbuf; - int ret; - - if (!ibm || !ibm->write) - return -EINVAL; - - kernbuf = kmalloc(count + 2, GFP_KERNEL); - if (!kernbuf) - return -ENOMEM; - - if (copy_from_user(kernbuf, userbuf, count)) { - kfree(kernbuf); - return -EFAULT; - } - - kernbuf[count] = 0; - strcat(kernbuf, ","); - ret = ibm->write(kernbuf); - if (ret == 0) - ret = count; - - kfree(kernbuf); - - return ret; -} - -static void dispatch_notify(acpi_handle handle, u32 event, void *data) -{ - struct ibm_struct *ibm = data; - - if (!ibm || !ibm->notify) - return; - - ibm->notify(ibm, event); -} - -static int __init setup_notify(struct ibm_struct *ibm) -{ - acpi_status status; - int ret; - - if (!*ibm->handle) - return 0; - - ret = acpi_bus_get_device(*ibm->handle, &ibm->device); - if (ret < 0) { - printk(IBM_ERR "%s device not present\n", ibm->name); - return 0; - } - - acpi_driver_data(ibm->device) = ibm; - sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); - - status = acpi_install_notify_handler(*ibm->handle, ibm->type, - dispatch_notify, ibm); - if (ACPI_FAILURE(status)) { - printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", - ibm->name, status); - return -ENODEV; - } - ibm->notify_installed = 1; - return 0; -} - -static int __init ibm_device_add(struct acpi_device *device) -{ - return 0; -} - -static int __init register_driver(struct ibm_struct *ibm) -{ - int ret; - - ibm->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); - if (!ibm->driver) { - printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); - return -1; - } - - sprintf(ibm->driver->name, "%s_%s", IBM_NAME, ibm->name); - ibm->driver->ids = ibm->hid; - ibm->driver->ops.add = &ibm_device_add; - - ret = acpi_bus_register_driver(ibm->driver); - if (ret < 0) { - printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", - ibm->hid, ret); - kfree(ibm->driver); - } - - return ret; -} - -static int __init ibm_init(struct ibm_struct *ibm) -{ - int ret; - struct proc_dir_entry *entry; - - if (ibm->experimental && !experimental) - return 0; - - if (ibm->hid) { - ret = register_driver(ibm); - if (ret < 0) - return ret; - ibm->driver_registered = 1; - } - - if (ibm->init) { - ret = ibm->init(); - if (ret != 0) - return ret; - ibm->init_called = 1; - } - - if (ibm->read) { - entry = create_proc_entry(ibm->name, - S_IFREG | S_IRUGO | S_IWUSR, - proc_dir); - if (!entry) { - printk(IBM_ERR "unable to create proc entry %s\n", - ibm->name); - return -ENODEV; - } - entry->owner = THIS_MODULE; - entry->data = ibm; - entry->read_proc = &dispatch_read; - if (ibm->write) - entry->write_proc = &dispatch_write; - ibm->proc_created = 1; - } - - if (ibm->notify) { - ret = setup_notify(ibm); - if (ret < 0) - return ret; - } - - return 0; -} - -static void ibm_exit(struct ibm_struct *ibm) -{ - if (ibm->notify_installed) - acpi_remove_notify_handler(*ibm->handle, ibm->type, - dispatch_notify); - - if (ibm->proc_created) - remove_proc_entry(ibm->name, proc_dir); - - if (ibm->init_called && ibm->exit) - ibm->exit(); - - if (ibm->driver_registered) { - acpi_bus_unregister_driver(ibm->driver); - kfree(ibm->driver); - } -} - -static void __init ibm_handle_init(char *name, - acpi_handle * handle, acpi_handle parent, - char **paths, int num_paths, char **path) -{ - int i; - acpi_status status; - - for (i = 0; i < num_paths; i++) { - status = acpi_get_handle(parent, paths[i], handle); - if (ACPI_SUCCESS(status)) { - *path = paths[i]; - return; - } - } - - *handle = NULL; -} - -#define IBM_HANDLE_INIT(object) \ - ibm_handle_init(#object, &object##_handle, *object##_parent, \ - object##_paths, ARRAY_SIZE(object##_paths), &object##_path) - -static int set_ibm_param(const char *val, struct kernel_param *kp) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) - if (strcmp(ibms[i].name, kp->name) == 0 && ibms[i].write) { - if (strlen(val) > sizeof(ibms[i].param) - 2) - return -ENOSPC; - strcpy(ibms[i].param, val); - strcat(ibms[i].param, ","); - return 0; - } - - return -EINVAL; -} - -#define IBM_PARAM(feature) \ - module_param_call(feature, set_ibm_param, NULL, NULL, 0) - -IBM_PARAM(hotkey); -IBM_PARAM(bluetooth); -IBM_PARAM(video); -IBM_PARAM(light); -#ifdef CONFIG_ACPI_IBM_DOCK -IBM_PARAM(dock); -#endif -IBM_PARAM(bay); -IBM_PARAM(cmos); -IBM_PARAM(led); -IBM_PARAM(beep); -IBM_PARAM(ecdump); -IBM_PARAM(brightness); -IBM_PARAM(volume); -IBM_PARAM(fan); - -static void acpi_ibm_exit(void) -{ - int i; - - for (i = ARRAY_SIZE(ibms) - 1; i >= 0; i--) - ibm_exit(&ibms[i]); - - remove_proc_entry(IBM_DIR, acpi_root_dir); - - if (ibm_thinkpad_ec_found) - kfree(ibm_thinkpad_ec_found); -} - -static char* __init check_dmi_for_ec(void) -{ - struct dmi_device *dev = NULL; - char ec_fw_string[18]; - - /* - * ThinkPad T23 or newer, A31 or newer, R50e or newer, - * X32 or newer, all Z series; Some models must have an - * up-to-date BIOS or they will not be detected. - * - * See http://thinkwiki.org/wiki/List_of_DMI_IDs - */ - while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { - if (sscanf(dev->name, - "IBM ThinkPad Embedded Controller -[%17c", - ec_fw_string) == 1) { - ec_fw_string[sizeof(ec_fw_string) - 1] = 0; - ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; - return kstrdup(ec_fw_string, GFP_KERNEL); - } - } - return NULL; -} - -static int __init acpi_ibm_init(void) -{ - int ret, i; - - if (acpi_disabled) - return -ENODEV; - - if (!acpi_specific_hotkey_enabled) { - printk(IBM_ERR "using generic hotkey driver\n"); - return -ENODEV; - } - - /* ec is required because many other handles are relative to it */ - IBM_HANDLE_INIT(ec); - if (!ec_handle) { - printk(IBM_ERR "ec object not found\n"); - return -ENODEV; - } - - /* Models with newer firmware report the EC in DMI */ - ibm_thinkpad_ec_found = check_dmi_for_ec(); - if (ibm_thinkpad_ec_found) - printk(IBM_INFO "ThinkPad EC firmware %s\n", - ibm_thinkpad_ec_found); - - /* these handles are not required */ - IBM_HANDLE_INIT(vid); - IBM_HANDLE_INIT(vid2); - IBM_HANDLE_INIT(ledb); - IBM_HANDLE_INIT(led); - IBM_HANDLE_INIT(hkey); - IBM_HANDLE_INIT(lght); - IBM_HANDLE_INIT(cmos); -#ifdef CONFIG_ACPI_IBM_DOCK - IBM_HANDLE_INIT(dock); -#endif - IBM_HANDLE_INIT(pci); - IBM_HANDLE_INIT(bay); - if (bay_handle) - IBM_HANDLE_INIT(bay_ej); - IBM_HANDLE_INIT(bay2); - if (bay2_handle) - IBM_HANDLE_INIT(bay2_ej); - IBM_HANDLE_INIT(beep); - IBM_HANDLE_INIT(ecrd); - IBM_HANDLE_INIT(ecwr); - IBM_HANDLE_INIT(fans); - IBM_HANDLE_INIT(gfan); - IBM_HANDLE_INIT(sfan); - - proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); - if (!proc_dir) { - printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); - return -ENODEV; - } - proc_dir->owner = THIS_MODULE; - - for (i = 0; i < ARRAY_SIZE(ibms); i++) { - ret = ibm_init(&ibms[i]); - if (ret >= 0 && *ibms[i].param) - ret = ibms[i].write(ibms[i].param); - if (ret < 0) { - acpi_ibm_exit(); - return ret; - } - } - - return 0; -} - -module_init(acpi_ibm_init); -module_exit(acpi_ibm_exit); diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c index aa6370c..26fd0dd 100644 --- a/drivers/acpi/namespace/nseval.c +++ b/drivers/acpi/namespace/nseval.c @@ -154,7 +154,11 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) * Execute the method via the interpreter. The interpreter is locked * here before calling into the AML parser */ - acpi_ex_enter_interpreter(); + status = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } + status = acpi_ps_execute_method(info); acpi_ex_exit_interpreter(); } else { @@ -178,7 +182,10 @@ acpi_status acpi_ns_evaluate(struct acpi_evaluate_info *info) * resolution, we must lock it because we could access an opregion. * The opregion access code assumes that the interpreter is locked. */ - acpi_ex_enter_interpreter(); + status = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + return_ACPI_STATUS(status); + } /* Function has a strange interface */ diff --git a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/namespace/nsinit.c index 33db224..c4ab615 100644 --- a/drivers/acpi/namespace/nsinit.c +++ b/drivers/acpi/namespace/nsinit.c @@ -214,7 +214,7 @@ acpi_ns_init_one_object(acpi_handle obj_handle, u32 level, void *context, void **return_value) { acpi_object_type type; - acpi_status status = AE_OK; + acpi_status status; struct acpi_init_walk_info *info = (struct acpi_init_walk_info *)context; struct acpi_namespace_node *node = @@ -268,7 +268,10 @@ acpi_ns_init_one_object(acpi_handle obj_handle, /* * Must lock the interpreter before executing AML code */ - acpi_ex_enter_interpreter(); + status = acpi_ex_enter_interpreter(); + if (ACPI_FAILURE(status)) { + return (status); + } /* * Each of these types can contain executable AML code within the diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c index 7ac6ace..8904d0f 100644 --- a/drivers/acpi/namespace/nsxfeval.c +++ b/drivers/acpi/namespace/nsxfeval.c @@ -170,6 +170,7 @@ acpi_evaluate_object(acpi_handle handle, struct acpi_buffer *return_buffer) { acpi_status status; + acpi_status status2; struct acpi_evaluate_info *info; acpi_size buffer_space_needed; u32 i; @@ -328,12 +329,14 @@ acpi_evaluate_object(acpi_handle handle, * Delete the internal return object. NOTE: Interpreter must be * locked to avoid race condition. */ - acpi_ex_enter_interpreter(); + status2 = acpi_ex_enter_interpreter(); + if (ACPI_SUCCESS(status2)) { - /* Remove one reference on the return object (should delete it) */ + /* Remove one reference on the return object (should delete it) */ - acpi_ut_remove_reference(info->return_object); - acpi_ex_exit_interpreter(); + acpi_ut_remove_reference(info->return_object); + acpi_ex_exit_interpreter(); + } } cleanup: diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c index 4a9faff..8fcd6a1 100644 --- a/drivers/acpi/numa.c +++ b/drivers/acpi/numa.c @@ -33,7 +33,7 @@ #define ACPI_NUMA 0x80000000 #define _COMPONENT ACPI_NUMA -ACPI_MODULE_NAME("numa") +ACPI_MODULE_NAME("numa"); static nodemask_t nodes_found_map = NODE_MASK_NONE; #define PXM_INVAL -1 @@ -45,12 +45,6 @@ int __cpuinitdata pxm_to_node_map[MAX_PXM_DOMAINS] int __cpuinitdata node_to_pxm_map[MAX_NUMNODES] = { [0 ... MAX_NUMNODES - 1] = PXM_INVAL }; -extern int __init acpi_table_parse_madt_family(char *id, - unsigned long madt_size, - int entry_id, - acpi_madt_entry_handler handler, - unsigned int max_entries); - int __cpuinit pxm_to_node(int pxm) { if (pxm < 0) @@ -208,9 +202,9 @@ static int __init acpi_parse_srat(struct acpi_table_header *table) int __init acpi_table_parse_srat(enum acpi_srat_type id, - acpi_madt_entry_handler handler, unsigned int max_entries) + acpi_table_entry_handler handler, unsigned int max_entries) { - return acpi_table_parse_madt_family(ACPI_SIG_SRAT, + return acpi_table_parse_entries(ACPI_SIG_SRAT, sizeof(struct acpi_table_srat), id, handler, max_entries); } @@ -220,9 +214,7 @@ int __init acpi_numa_init(void) int result; /* SRAT: Static Resource Affinity Table */ - result = acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat); - - if (result > 0) { + if (!acpi_table_parse(ACPI_SIG_SRAT, acpi_parse_srat)) { result = acpi_table_parse_srat(ACPI_SRAT_TYPE_CPU_AFFINITY, acpi_parse_processor_affinity, NR_CPUS); @@ -230,7 +222,7 @@ int __init acpi_numa_init(void) } /* SLIT: System Locality Information Table */ - result = acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); + acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit); acpi_numa_arch_fixup(); return 0; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 0f6f3bc..971eca4 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -46,7 +46,7 @@ #include <linux/efi.h> #define _COMPONENT ACPI_OS_SERVICES -ACPI_MODULE_NAME("osl") +ACPI_MODULE_NAME("osl"); #define PREFIX "ACPI: " struct acpi_os_dpc { acpi_osd_exec_callback function; @@ -68,9 +68,6 @@ EXPORT_SYMBOL(acpi_in_debugger); extern char line_buf[80]; #endif /*ENABLE_DEBUGGER */ -int acpi_specific_hotkey_enabled = TRUE; -EXPORT_SYMBOL(acpi_specific_hotkey_enabled); - static unsigned int acpi_irq_irq; static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; @@ -205,7 +202,7 @@ void __iomem *acpi_os_map_memory(acpi_physical_address phys, acpi_size size) { if (phys > ULONG_MAX) { printk(KERN_ERR PREFIX "Cannot map memory that high\n"); - return 0; + return NULL; } if (acpi_gbl_permanent_mmap) /* @@ -890,26 +887,6 @@ u32 acpi_os_get_line(char *buffer) } #endif /* ACPI_FUTURE_USAGE */ -/* Assumes no unreadable holes inbetween */ -u8 acpi_os_readable(void *ptr, acpi_size len) -{ -#if defined(__i386__) || defined(__x86_64__) - char tmp; - return !__get_user(tmp, (char __user *)ptr) - && !__get_user(tmp, (char __user *)ptr + len - 1); -#endif - return 1; -} - -#ifdef ACPI_FUTURE_USAGE -u8 acpi_os_writable(void *ptr, acpi_size len) -{ - /* could do dummy write (racy) or a kernel page table lookup. - The later may be difficult at early boot when kmap doesn't work yet. */ - return 1; -} -#endif - acpi_status acpi_os_signal(u32 function, void *info) { switch (function) { @@ -1012,14 +989,6 @@ static int __init acpi_wake_gpes_always_on_setup(char *str) __setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); -static int __init acpi_hotkey_setup(char *str) -{ - acpi_specific_hotkey_enabled = FALSE; - return 1; -} - -__setup("acpi_generic_hotkey", acpi_hotkey_setup); - /* * max_cstate is defined in the base kernel so modules can * change it w/o depending on the state of the processor module. diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index 55f57a6..0289693 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -36,7 +36,7 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_bind") +ACPI_MODULE_NAME("pci_bind"); struct acpi_pci_data { struct acpi_pci_id id; diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index fe7d007..dd3186a 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -38,7 +38,7 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_irq") +ACPI_MODULE_NAME("pci_irq"); static struct acpi_prt_list acpi_prt; static DEFINE_SPINLOCK(acpi_prt_lock); diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index 0f683c8..acc5947 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -44,10 +44,9 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_link") +ACPI_MODULE_NAME("pci_link"); #define ACPI_PCI_LINK_CLASS "pci_irq_routing" #define ACPI_PCI_LINK_HID "PNP0C0F" -#define ACPI_PCI_LINK_DRIVER_NAME "ACPI PCI Interrupt Link Driver" #define ACPI_PCI_LINK_DEVICE_NAME "PCI Interrupt Link" #define ACPI_PCI_LINK_FILE_INFO "info" #define ACPI_PCI_LINK_FILE_STATUS "state" @@ -56,7 +55,7 @@ static int acpi_pci_link_add(struct acpi_device *device); static int acpi_pci_link_remove(struct acpi_device *device, int type); static struct acpi_driver acpi_pci_link_driver = { - .name = ACPI_PCI_LINK_DRIVER_NAME, + .name = "pci_link", .class = ACPI_PCI_LINK_CLASS, .ids = ACPI_PCI_LINK_HID, .ops = { diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 4ecf701..ad4145a 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -36,17 +36,16 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_root") +ACPI_MODULE_NAME("pci_root"); #define ACPI_PCI_ROOT_CLASS "pci_bridge" #define ACPI_PCI_ROOT_HID "PNP0A03" -#define ACPI_PCI_ROOT_DRIVER_NAME "ACPI PCI Root Bridge Driver" #define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge" static int acpi_pci_root_add(struct acpi_device *device); static int acpi_pci_root_remove(struct acpi_device *device, int type); static int acpi_pci_root_start(struct acpi_device *device); static struct acpi_driver acpi_pci_root_driver = { - .name = ACPI_PCI_ROOT_DRIVER_NAME, + .name = "pci_root", .class = ACPI_PCI_ROOT_CLASS, .ids = ACPI_PCI_ROOT_HID, .ops = { diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0ba7dfb..4ffecd1 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -45,10 +45,9 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_POWER_COMPONENT -ACPI_MODULE_NAME("acpi_power") +ACPI_MODULE_NAME("power"); #define ACPI_POWER_COMPONENT 0x00800000 #define ACPI_POWER_CLASS "power_resource" -#define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver" #define ACPI_POWER_DEVICE_NAME "Power Resource" #define ACPI_POWER_FILE_INFO "info" #define ACPI_POWER_FILE_STATUS "state" @@ -57,25 +56,33 @@ ACPI_MODULE_NAME("acpi_power") #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); +static int acpi_power_resume(struct acpi_device *device); static int acpi_power_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_power_driver = { - .name = ACPI_POWER_DRIVER_NAME, + .name = "power", .class = ACPI_POWER_CLASS, .ids = ACPI_POWER_HID, .ops = { .add = acpi_power_add, .remove = acpi_power_remove, + .resume = acpi_power_resume, }, }; +struct acpi_power_reference { + struct list_head node; + struct acpi_device *device; +}; + struct acpi_power_resource { struct acpi_device * device; acpi_bus_id name; u32 system_level; u32 order; int state; - int references; + struct mutex resource_lock; + struct list_head reference; }; static struct list_head acpi_power_resource_list; @@ -171,22 +178,47 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return result; } -static int acpi_power_on(acpi_handle handle) +static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) { int result = 0; + int found = 0; acpi_status status = AE_OK; - struct acpi_device *device = NULL; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; result = acpi_power_get_context(handle, &resource); if (result) return result; - resource->references++; + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + if (dev->handle == ref->device->handle) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n", + dev->pnp.bus_id, resource->name)); + found = 1; + break; + } + } + + if (!found) { + ref = kmalloc(sizeof (struct acpi_power_reference), + irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); + if (!ref) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); + mutex_unlock(&resource->resource_lock); + return -ENOMEM; + } + list_add_tail(&ref->node, &resource->reference); + ref->device = dev; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n", + dev->pnp.bus_id, resource->name)); + } + mutex_unlock(&resource->resource_lock); - if ((resource->references > 1) - || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) { + if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", resource->name)); return 0; @@ -203,38 +235,49 @@ static int acpi_power_on(acpi_handle handle) return -ENOEXEC; /* Update the power resource's _device_ power state */ - device = resource->device; resource->device->power.state = ACPI_STATE_D0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", resource->name)); - return 0; } -static int acpi_power_off_device(acpi_handle handle) +static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) { int result = 0; acpi_status status = AE_OK; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; + result = acpi_power_get_context(handle, &resource); if (result) return result; - if (resource->references) - resource->references--; + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + if (dev->handle == ref->device->handle) { + list_del(&ref->node); + kfree(ref); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", + dev->pnp.bus_id, resource->name)); + break; + } + } - if (resource->references) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Resource [%s] is still in use, dereferencing\n", - resource->device->pnp.bus_id)); + if (!list_empty(&resource->reference)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", + resource->name)); + mutex_unlock(&resource->resource_lock); return 0; } + mutex_unlock(&resource->resource_lock); if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", - resource->device->pnp.bus_id)); + resource->name)); return 0; } @@ -276,7 +319,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev) arg.integer.value = 1; /* Open power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - ret = acpi_power_on(dev->wakeup.resources.handles[i]); + ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -323,7 +366,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { - ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); + ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev); if (ret) { printk(KERN_ERR PREFIX "Transition power state\n"); dev->wakeup.flags.valid = 0; @@ -393,8 +436,6 @@ int acpi_power_transition(struct acpi_device *device, int state) cl = &device->power.states[device->power.state].resources; tl = &device->power.states[state].resources; - device->power.state = ACPI_STATE_UNKNOWN; - if (!cl->count && !tl->count) { result = -ENODEV; goto end; @@ -407,26 +448,33 @@ int acpi_power_transition(struct acpi_device *device, int state) * (e.g. so the device doesn't lose power while transitioning). */ for (i = 0; i < tl->count; i++) { - result = acpi_power_on(tl->handles[i]); + result = acpi_power_on(tl->handles[i], device); if (result) goto end; } + if (device->power.state == state) { + goto end; + } + /* * Then we dereference all power resources used in the current list. */ for (i = 0; i < cl->count; i++) { - result = acpi_power_off_device(cl->handles[i]); + result = acpi_power_off_device(cl->handles[i], device); if (result) goto end; } - /* We shouldn't change the state till all above operations succeed */ - device->power.state = state; - end: - if (result) + end: + if (result) { + device->power.state = ACPI_STATE_UNKNOWN; printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n", device->pnp.bus_id, state); + } else { + /* We shouldn't change the state till all above operations succeed */ + device->power.state = state; + } return result; } @@ -439,7 +487,11 @@ static struct proc_dir_entry *acpi_power_dir; static int acpi_power_seq_show(struct seq_file *seq, void *offset) { + int count = 0; + int result = 0; struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; + struct acpi_power_reference *ref; resource = seq->private; @@ -447,6 +499,10 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) if (!resource) goto end; + result = acpi_power_get_state(resource); + if (result) + goto end; + seq_puts(seq, "state: "); switch (resource->state) { case ACPI_POWER_RESOURCE_STATE_ON: @@ -460,11 +516,18 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) break; } + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + ref = container_of(node, struct acpi_power_reference, node); + count++; + } + mutex_unlock(&resource->resource_lock); + seq_printf(seq, "system level: S%d\n" "order: %d\n" "reference count: %d\n", resource->system_level, - resource->order, resource->references); + resource->order, count); end: return 0; @@ -537,6 +600,8 @@ static int acpi_power_add(struct acpi_device *device) return -ENOMEM; resource->device = device; + mutex_init(&resource->resource_lock); + INIT_LIST_HEAD(&resource->reference); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); @@ -584,6 +649,7 @@ static int acpi_power_add(struct acpi_device *device) static int acpi_power_remove(struct acpi_device *device, int type) { struct acpi_power_resource *resource = NULL; + struct list_head *node, *next; if (!device || !acpi_driver_data(device)) @@ -593,11 +659,47 @@ static int acpi_power_remove(struct acpi_device *device, int type) acpi_power_remove_fs(device); + mutex_lock(&resource->resource_lock); + list_for_each_safe(node, next, &resource->reference) { + struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); + list_del(&ref->node); + kfree(ref); + } + mutex_unlock(&resource->resource_lock); + kfree(resource); return 0; } +static int acpi_power_resume(struct acpi_device *device) +{ + int result = 0; + struct acpi_power_resource *resource = NULL; + struct acpi_power_reference *ref; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + resource = (struct acpi_power_resource *)acpi_driver_data(device); + + result = acpi_power_get_state(resource); + if (result) + return result; + + mutex_lock(&resource->resource_lock); + if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) && + !list_empty(&resource->reference)) { + ref = container_of(resource->reference.next, struct acpi_power_reference, node); + mutex_unlock(&resource->resource_lock); + result = acpi_power_on(device->handle, ref->device); + return result; + } + + mutex_unlock(&resource->resource_lock); + return 0; +} + static int __init acpi_power_init(void) { int result = 0; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 0079bc5..f7de02a 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -60,7 +60,6 @@ #define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" #define ACPI_PROCESSOR_FILE_THROTTLING "throttling" @@ -71,13 +70,11 @@ #define ACPI_PROCESSOR_LIMIT_USER 0 #define ACPI_PROCESSOR_LIMIT_THERMAL 1 -#define ACPI_STA_PRESENT 0x00000001 - #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") +ACPI_MODULE_NAME("processor_core"); - MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME); +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION("ACPI Processor Driver"); MODULE_LICENSE("GPL"); static int acpi_processor_add(struct acpi_device *device); @@ -89,7 +86,7 @@ static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu); static int acpi_processor_handle_eject(struct acpi_processor *pr); static struct acpi_driver acpi_processor_driver = { - .name = ACPI_PROCESSOR_DRIVER_NAME, + .name = "processor", .class = ACPI_PROCESSOR_CLASS, .ids = ACPI_PROCESSOR_HID, .ops = { @@ -404,7 +401,7 @@ static int map_lsapic_id(struct acpi_subtable_header *entry, if (lsapic->lapic_flags & ACPI_MADT_ENABLED) { /* First check against id */ if (lsapic->processor_id == acpi_id) { - *apic_id = lsapic->id; + *apic_id = (lsapic->id << 8) | lsapic->eid; return 1; /* Check against optional uid */ } else if (entry->length >= 16 && @@ -780,7 +777,7 @@ static int is_processor_present(acpi_handle handle) status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); - if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) { + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT)) { ACPI_EXCEPTION((AE_INFO, status, "Processor Device is not present")); return 0; } @@ -1005,7 +1002,7 @@ static int __init acpi_processor_init(void) #ifdef CONFIG_SMP if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_MADT, 0, (struct acpi_table_header **)&madt))) - madt = 0; + madt = NULL; #endif acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 8206fc1..ae0654c 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -59,9 +59,8 @@ #define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") +ACPI_MODULE_NAME("processor_idle"); #define ACPI_PROCESSOR_FILE_POWER "power" #define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000) #define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */ @@ -261,6 +260,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cx) { struct acpi_processor_power *pwr = &pr->power; + u8 type = local_apic_timer_c2_ok ? ACPI_STATE_C3 : ACPI_STATE_C2; /* * Check, if one of the previous states already marked the lapic @@ -269,7 +269,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, if (pwr->timer_broadcast_on_state < state) return; - if (cx->type >= ACPI_STATE_C2) + if (cx->type >= type) pr->power.timer_broadcast_on_state = state; } diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index 058f13c..2f2e796 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -44,10 +44,9 @@ #define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") +ACPI_MODULE_NAME("processor_perflib"); static DEFINE_MUTEX(performance_mutex); diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index 40fecd6..06e6f3f 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -41,9 +41,8 @@ #define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") +ACPI_MODULE_NAME("processor_thermal"); /* -------------------------------------------------------------------------- Limit Interface diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 89dff36..b334860 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -41,9 +41,8 @@ #define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" -#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" #define _COMPONENT ACPI_PROCESSOR_COMPONENT -ACPI_MODULE_NAME("acpi_processor") +ACPI_MODULE_NAME("processor_throttling"); /* -------------------------------------------------------------------------- Throttling Control diff --git a/drivers/acpi/resources/rscreate.c b/drivers/acpi/resources/rscreate.c index 1358c06..cc48ab0 100644 --- a/drivers/acpi/resources/rscreate.c +++ b/drivers/acpi/resources/rscreate.c @@ -191,6 +191,9 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, user_prt = ACPI_CAST_PTR(struct acpi_pci_routing_table, buffer); for (index = 0; index < number_of_elements; index++) { + int source_name_index = 2; + int source_index_index = 3; + /* * Point user_prt past this current structure * @@ -261,10 +264,28 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, } /* + * If BIOS erroneously reversed the _PRT source_name and source_index, + * then reverse them back. + */ + if (ACPI_GET_OBJECT_TYPE (sub_object_list[3]) != ACPI_TYPE_INTEGER) { + if (acpi_gbl_enable_interpreter_slack) { + source_name_index = 3; + source_index_index = 2; + printk(KERN_WARNING "ACPI: Handling Garbled _PRT entry\n"); + } else { + ACPI_ERROR((AE_INFO, + "(PRT[%X].source_index) Need Integer, found %s", + index, + acpi_ut_get_object_type_name(sub_object_list[3]))); + return_ACPI_STATUS(AE_BAD_DATA); + } + } + + /* * 3) Third subobject: Dereference the PRT.source_name * The name may be unresolved (slack mode), so allow a null object */ - obj_desc = sub_object_list[2]; + obj_desc = sub_object_list[source_name_index]; if (obj_desc) { switch (ACPI_GET_OBJECT_TYPE(obj_desc)) { case ACPI_TYPE_LOCAL_REFERENCE: @@ -339,7 +360,7 @@ acpi_rs_create_pci_routing_table(union acpi_operand_object *package_object, /* 4) Fourth subobject: Dereference the PRT.source_index */ - obj_desc = sub_object_list[3]; + obj_desc = sub_object_list[source_index_index]; if (ACPI_GET_OBJECT_TYPE(obj_desc) == ACPI_TYPE_INTEGER) { user_prt->source_index = (u32) obj_desc->integer.value; } else { diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c index f58fc74..c1bae10 100644 --- a/drivers/acpi/sbs.c +++ b/drivers/acpi/sbs.c @@ -30,36 +30,15 @@ #include <linux/seq_file.h> #include <asm/uaccess.h> #include <linux/acpi.h> -#include <linux/i2c.h> +#include <linux/timer.h> +#include <linux/jiffies.h> #include <linux/delay.h> -#include "i2c_ec.h" - -#define DEF_CAPACITY_UNIT 3 -#define MAH_CAPACITY_UNIT 1 -#define MWH_CAPACITY_UNIT 2 -#define CAPACITY_UNIT DEF_CAPACITY_UNIT - -#define REQUEST_UPDATE_MODE 1 -#define QUEUE_UPDATE_MODE 2 - -#define DATA_TYPE_COMMON 0 -#define DATA_TYPE_INFO 1 -#define DATA_TYPE_STATE 2 -#define DATA_TYPE_ALARM 3 -#define DATA_TYPE_AC_STATE 4 - -extern struct proc_dir_entry *acpi_lock_ac_dir(void); -extern struct proc_dir_entry *acpi_lock_battery_dir(void); -extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); -extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); - #define ACPI_SBS_COMPONENT 0x00080000 #define ACPI_SBS_CLASS "sbs" #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" #define ACPI_SBS_HID "ACPI0002" -#define ACPI_SBS_DRIVER_NAME "ACPI Smart Battery System Driver" #define ACPI_SBS_DEVICE_NAME "Smart Battery System" #define ACPI_SBS_FILE_INFO "info" #define ACPI_SBS_FILE_STATE "state" @@ -75,50 +54,91 @@ extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); #define _COMPONENT ACPI_SBS_COMPONENT -#define MAX_SBS_BAT 4 -#define MAX_SMBUS_ERR 1 - -ACPI_MODULE_NAME("acpi_sbs"); +ACPI_MODULE_NAME("sbs"); MODULE_AUTHOR("Rich Townsend"); MODULE_DESCRIPTION("Smart Battery System ACPI interface driver"); MODULE_LICENSE("GPL"); -static struct semaphore sbs_sem; +#define xmsleep(t) msleep(t) + +#define ACPI_EC_SMB_PRTCL 0x00 /* protocol, PEC */ + +#define ACPI_EC_SMB_STS 0x01 /* status */ +#define ACPI_EC_SMB_ADDR 0x02 /* address */ +#define ACPI_EC_SMB_CMD 0x03 /* command */ +#define ACPI_EC_SMB_DATA 0x04 /* 32 data registers */ +#define ACPI_EC_SMB_BCNT 0x24 /* number of data bytes */ + +#define ACPI_EC_SMB_STS_DONE 0x80 +#define ACPI_EC_SMB_STS_STATUS 0x1f + +#define ACPI_EC_SMB_PRTCL_WRITE 0x00 +#define ACPI_EC_SMB_PRTCL_READ 0x01 +#define ACPI_EC_SMB_PRTCL_WORD_DATA 0x08 +#define ACPI_EC_SMB_PRTCL_BLOCK_DATA 0x0a + +#define ACPI_EC_SMB_TRANSACTION_SLEEP 1 +#define ACPI_EC_SMB_ACCESS_SLEEP1 1 +#define ACPI_EC_SMB_ACCESS_SLEEP2 10 + +#define DEF_CAPACITY_UNIT 3 +#define MAH_CAPACITY_UNIT 1 +#define MWH_CAPACITY_UNIT 2 +#define CAPACITY_UNIT DEF_CAPACITY_UNIT + +#define REQUEST_UPDATE_MODE 1 +#define QUEUE_UPDATE_MODE 2 + +#define DATA_TYPE_COMMON 0 +#define DATA_TYPE_INFO 1 +#define DATA_TYPE_STATE 2 +#define DATA_TYPE_ALARM 3 +#define DATA_TYPE_AC_STATE 4 + +extern struct proc_dir_entry *acpi_lock_ac_dir(void); +extern struct proc_dir_entry *acpi_lock_battery_dir(void); +extern void acpi_unlock_ac_dir(struct proc_dir_entry *acpi_ac_dir); +extern void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); + +#define MAX_SBS_BAT 4 +#define ACPI_SBS_BLOCK_MAX 32 -#define UPDATE_MODE QUEUE_UPDATE_MODE -/* REQUEST_UPDATE_MODE QUEUE_UPDATE_MODE */ -#define UPDATE_INFO_MODE 0 -#define UPDATE_TIME 60 -#define UPDATE_TIME2 0 +#define ACPI_SBS_SMBUS_READ 1 +#define ACPI_SBS_SMBUS_WRITE 2 -static int capacity_mode = CAPACITY_UNIT; -static int update_mode = UPDATE_MODE; -static int update_info_mode = UPDATE_INFO_MODE; -static int update_time = UPDATE_TIME; -static int update_time2 = UPDATE_TIME2; +#define ACPI_SBS_WORD_DATA 1 +#define ACPI_SBS_BLOCK_DATA 2 -module_param(capacity_mode, int, 0); -module_param(update_mode, int, 0); -module_param(update_info_mode, int, 0); -module_param(update_time, int, 0); -module_param(update_time2, int, 0); +#define UPDATE_DELAY 10 + +/* 0 - every time, > 0 - by update_time */ +static unsigned int update_time = 120; + +static unsigned int capacity_mode = CAPACITY_UNIT; + +module_param(update_time, uint, 0644); +module_param(capacity_mode, uint, 0444); static int acpi_sbs_add(struct acpi_device *device); static int acpi_sbs_remove(struct acpi_device *device, int type); -static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus); -static void acpi_sbs_update_queue(void *data); +static int acpi_sbs_resume(struct acpi_device *device); static struct acpi_driver acpi_sbs_driver = { - .name = ACPI_SBS_DRIVER_NAME, + .name = "sbs", .class = ACPI_SBS_CLASS, .ids = ACPI_SBS_HID, .ops = { .add = acpi_sbs_add, .remove = acpi_sbs_remove, + .resume = acpi_sbs_resume, }, }; +struct acpi_ac { + int ac_present; +}; + struct acpi_battery_info { int capacity_mode; s16 full_charge_capacity; @@ -127,18 +147,16 @@ struct acpi_battery_info { int vscale; int ipscale; s16 serial_number; - char manufacturer_name[I2C_SMBUS_BLOCK_MAX + 3]; - char device_name[I2C_SMBUS_BLOCK_MAX + 3]; - char device_chemistry[I2C_SMBUS_BLOCK_MAX + 3]; + char manufacturer_name[ACPI_SBS_BLOCK_MAX + 3]; + char device_name[ACPI_SBS_BLOCK_MAX + 3]; + char device_chemistry[ACPI_SBS_BLOCK_MAX + 3]; }; struct acpi_battery_state { s16 voltage; s16 amperage; s16 remaining_capacity; - s16 average_time_to_empty; - s16 average_time_to_full; - s16 battery_status; + s16 battery_state; }; struct acpi_battery_alarm { @@ -147,9 +165,9 @@ struct acpi_battery_alarm { struct acpi_battery { int alive; - int battery_present; int id; int init_state; + int battery_present; struct acpi_sbs *sbs; struct acpi_battery_info info; struct acpi_battery_state state; @@ -159,186 +177,251 @@ struct acpi_battery { struct acpi_sbs { acpi_handle handle; + int base; struct acpi_device *device; struct acpi_ec_smbus *smbus; + struct mutex mutex; int sbsm_present; int sbsm_batteries_supported; - int ac_present; struct proc_dir_entry *ac_entry; + struct acpi_ac ac; struct acpi_battery battery[MAX_SBS_BAT]; - int update_info_mode; int zombie; - int update_time; - int update_time2; struct timer_list update_timer; + int run_cnt; + int update_proc_flg; }; -static void acpi_update_delay(struct acpi_sbs *sbs); -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type); +static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type); +static void acpi_sbs_update_time(void *data); + +union sbs_rw_data { + u16 word; + u8 block[ACPI_SBS_BLOCK_MAX + 2]; +}; + +static int acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, + char read_write, u8 command, int size, + union sbs_rw_data *data); /* -------------------------------------------------------------------------- SMBus Communication -------------------------------------------------------------------------- */ -static void acpi_battery_smbus_err_handler(struct acpi_ec_smbus *smbus) +static int acpi_ec_sbs_read(struct acpi_sbs *sbs, u8 address, u8 * data) { - union i2c_smbus_data data; - int result = 0; - char *err_str; - int err_number; + u8 val; + int err; - data.word = 0; + err = ec_read(sbs->base + address, &val); + if (!err) { + *data = val; + } + xmsleep(ACPI_EC_SMB_TRANSACTION_SLEEP); + return (err); +} - result = smbus->adapter.algo-> - smbus_xfer(&smbus->adapter, - ACPI_SB_SMBUS_ADDR, - 0, I2C_SMBUS_READ, 0x16, I2C_SMBUS_BLOCK_DATA, &data); +static int acpi_ec_sbs_write(struct acpi_sbs *sbs, u8 address, u8 data) +{ + int err; - err_number = (data.word & 0x000f); + err = ec_write(sbs->base + address, data); + return (err); +} - switch (data.word & 0x000f) { - case 0x0000: - err_str = "unexpected bus error"; - break; - case 0x0001: - err_str = "busy"; - break; - case 0x0002: - err_str = "reserved command"; - break; - case 0x0003: - err_str = "unsupported command"; - break; - case 0x0004: - err_str = "access denied"; +static int +acpi_ec_sbs_access(struct acpi_sbs *sbs, u16 addr, + char read_write, u8 command, int size, + union sbs_rw_data *data) +{ + unsigned char protocol, len = 0, temp[2] = { 0, 0 }; + int i; + + if (read_write == ACPI_SBS_SMBUS_READ) { + protocol = ACPI_EC_SMB_PRTCL_READ; + } else { + protocol = ACPI_EC_SMB_PRTCL_WRITE; + } + + switch (size) { + + case ACPI_SBS_WORD_DATA: + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); + if (read_write == ACPI_SBS_SMBUS_WRITE) { + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA, data->word); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + 1, + data->word >> 8); + } + protocol |= ACPI_EC_SMB_PRTCL_WORD_DATA; break; - case 0x0005: - err_str = "overflow/underflow"; + case ACPI_SBS_BLOCK_DATA: + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_CMD, command); + if (read_write == ACPI_SBS_SMBUS_WRITE) { + len = min_t(u8, data->block[0], 32); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_BCNT, len); + for (i = 0; i < len; i++) + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_DATA + i, + data->block[i + 1]); + } + protocol |= ACPI_EC_SMB_PRTCL_BLOCK_DATA; break; - case 0x0006: - err_str = "bad size"; + default: + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "unsupported transaction %d", size)); + return (-1); + } + + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_ADDR, addr << 1); + acpi_ec_sbs_write(sbs, ACPI_EC_SMB_PRTCL, protocol); + + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xmsleep(ACPI_EC_SMB_ACCESS_SLEEP1); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + } + if (~temp[0] & ACPI_EC_SMB_STS_DONE) { + xmsleep(ACPI_EC_SMB_ACCESS_SLEEP2); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_STS, temp); + } + if ((~temp[0] & ACPI_EC_SMB_STS_DONE) + || (temp[0] & ACPI_EC_SMB_STS_STATUS)) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "transaction %d error", size)); + return (-1); + } + + if (read_write == ACPI_SBS_SMBUS_WRITE) { + return (0); + } + + switch (size) { + + case ACPI_SBS_WORD_DATA: + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA, temp); + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + 1, temp + 1); + data->word = (temp[1] << 8) | temp[0]; break; - case 0x0007: - err_str = "unknown error"; + + case ACPI_SBS_BLOCK_DATA: + len = 0; + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_BCNT, &len); + len = min_t(u8, len, 32); + for (i = 0; i < len; i++) + acpi_ec_sbs_read(sbs, ACPI_EC_SMB_DATA + i, + data->block + i + 1); + data->block[0] = len; break; default: - err_str = "unrecognized error"; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "unsupported transaction %d", size)); + return (-1); } - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "%s: ret %i, err %i\n", err_str, result, err_number)); + + return (0); } static int -acpi_sbs_smbus_read_word(struct acpi_ec_smbus *smbus, int addr, int func, - u16 * word, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_read_word(struct acpi_sbs *sbs, int addr, int func, u16 * word) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_READ, func, - I2C_SMBUS_WORD_DATA, &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo->smbus_xfer() failed\n", - i)); - if (err_handler) { - err_handler(smbus); - } - } else { - *word = data.word; - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_READ, func, + ACPI_SBS_WORD_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); + } else { + *word = data.word; } return result; } static int -acpi_sbs_smbus_read_str(struct acpi_ec_smbus *smbus, int addr, int func, - char *str, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_read_str(struct acpi_sbs *sbs, int addr, int func, char *str) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } - - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_READ, func, - I2C_SMBUS_BLOCK_DATA, - &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo->smbus_xfer() failed\n", - i)); - if (err_handler) { - err_handler(smbus); - } - } else { - strncpy(str, (const char *)data.block + 1, - data.block[0]); - str[data.block[0]] = 0; - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_READ, func, + ACPI_SBS_BLOCK_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); + } else { + strncpy(str, (const char *)data.block + 1, data.block[0]); + str[data.block[0]] = 0; } return result; } static int -acpi_sbs_smbus_write_word(struct acpi_ec_smbus *smbus, int addr, int func, - int word, - void (*err_handler) (struct acpi_ec_smbus * smbus)) +acpi_sbs_write_word(struct acpi_sbs *sbs, int addr, int func, int word) { - union i2c_smbus_data data; + union sbs_rw_data data; int result = 0; - int i; - - if (err_handler == NULL) { - err_handler = acpi_battery_smbus_err_handler; - } data.word = word; - for (i = 0; i < MAX_SMBUS_ERR; i++) { - result = - smbus->adapter.algo->smbus_xfer(&smbus->adapter, addr, 0, - I2C_SMBUS_WRITE, func, - I2C_SMBUS_WORD_DATA, &data); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "try %i: smbus->adapter.algo" - "->smbus_xfer() failed\n", i)); - if (err_handler) { - err_handler(smbus); - } - } else { - break; - } + result = acpi_ec_sbs_access(sbs, addr, + ACPI_SBS_SMBUS_WRITE, func, + ACPI_SBS_WORD_DATA, &data); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ec_sbs_access() failed")); } return result; } +static int sbs_zombie(struct acpi_sbs *sbs) +{ + return (sbs->zombie); +} + +static int sbs_mutex_lock(struct acpi_sbs *sbs) +{ + if (sbs_zombie(sbs)) { + return -ENODEV; + } + mutex_lock(&sbs->mutex); + return 0; +} + +static void sbs_mutex_unlock(struct acpi_sbs *sbs) +{ + mutex_unlock(&sbs->mutex); +} + /* -------------------------------------------------------------------------- Smart Battery System Management -------------------------------------------------------------------------- */ -/* Smart Battery */ +static int acpi_check_update_proc(struct acpi_sbs *sbs) +{ + acpi_status status = AE_OK; + + if (update_time == 0) { + sbs->update_proc_flg = 0; + return 0; + } + if (sbs->update_proc_flg == 0) { + status = acpi_os_execute(OSL_GPE_HANDLER, + acpi_sbs_update_time, sbs); + if (status != AE_OK) { + ACPI_EXCEPTION((AE_INFO, status, + "acpi_os_execute() failed")); + return 1; + } + sbs->update_proc_flg = 1; + } + return 0; +} static int acpi_sbs_generate_event(struct acpi_device *device, int event, int state, char *bid, char *class) @@ -367,12 +450,11 @@ static int acpi_battery_get_present(struct acpi_battery *battery) int result = 0; int is_present = 0; - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SBSM_SMBUS_ADDR, 0x01, - &state, NULL); + result = acpi_sbs_read_word(battery->sbs, + ACPI_SBSM_SMBUS_ADDR, 0x01, &state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); } if (!result) { is_present = (state & 0x000f) & (1 << battery->id); @@ -382,45 +464,33 @@ static int acpi_battery_get_present(struct acpi_battery *battery) return result; } -static int acpi_battery_is_present(struct acpi_battery *battery) -{ - return (battery->battery_present); -} - -static int acpi_ac_is_present(struct acpi_sbs *sbs) -{ - return (sbs->ac_present); -} - static int acpi_battery_select(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 state; int foo; - if (battery->sbs->sbsm_present) { + if (sbs->sbsm_present) { /* Take special care not to knobble other nibbles of * state (aka selector_state), since * it causes charging to halt on SBSELs */ result = - acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, - &state, NULL); + acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, &state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } foo = (state & 0x0fff) | (1 << (battery->id + 12)); result = - acpi_sbs_smbus_write_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x01, - foo, NULL); + acpi_sbs_write_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x01, foo); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } } @@ -431,15 +501,14 @@ static int acpi_battery_select(struct acpi_battery *battery) static int acpi_sbsm_get_info(struct acpi_sbs *sbs) { - struct acpi_ec_smbus *smbus = sbs->smbus; int result = 0; s16 battery_system_info; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SBSM_SMBUS_ADDR, 0x04, - &battery_system_info, NULL); + result = acpi_sbs_read_word(sbs, ACPI_SBSM_SMBUS_ADDR, 0x04, + &battery_system_info); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -452,53 +521,50 @@ static int acpi_sbsm_get_info(struct acpi_sbs *sbs) static int acpi_battery_get_info(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; s16 specification_info; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } battery->info.capacity_mode = (battery_mode & 0x8000) >> 15; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x10, - &battery->info.full_charge_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x10, + &battery->info.full_charge_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x18, - &battery->info.design_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x18, + &battery->info.design_capacity); if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x19, - &battery->info.design_voltage, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x19, + &battery->info.design_voltage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1a, - &specification_info, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1a, + &specification_info); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -530,37 +596,35 @@ static int acpi_battery_get_info(struct acpi_battery *battery) battery->info.ipscale = 1; } - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x1c, - &battery->info.serial_number, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x1c, + &battery->info.serial_number); if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x20, - battery->info.manufacturer_name, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x20, + battery->info.manufacturer_name); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x21, - battery->info.device_name, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x21, + battery->info.device_name); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } - result = acpi_sbs_smbus_read_str(smbus, ACPI_SB_SMBUS_ADDR, 0x22, - battery->info.device_chemistry, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_str(sbs, ACPI_SB_SMBUS_ADDR, 0x22, + battery->info.device_chemistry); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_str() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_str() failed")); goto end; } @@ -568,103 +632,60 @@ static int acpi_battery_get_info(struct acpi_battery *battery) return result; } -static void acpi_update_delay(struct acpi_sbs *sbs) -{ - if (sbs->zombie) { - return; - } - if (sbs->update_time2 > 0) { - msleep(sbs->update_time2 * 1000); - } -} - static int acpi_battery_get_state(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x09, - &battery->state.voltage, - &acpi_battery_smbus_err_handler); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); - goto end; - } - - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0a, - &battery->state.amperage, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x09, + &battery->state.voltage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x0f, - &battery->state.remaining_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0a, + &battery->state.amperage); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x12, - &battery->state.average_time_to_empty, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x0f, + &battery->state.remaining_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x13, - &battery->state.average_time_to_full, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x16, + &battery->state.battery_state); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x16, - &battery->state.battery_status, - &acpi_battery_smbus_err_handler); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); - goto end; - } - - acpi_update_delay(battery->sbs); - end: return result; } static int acpi_battery_get_alarm(struct acpi_battery *battery) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - &battery->alarm.remaining_capacity, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, + &battery->alarm.remaining_capacity); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - acpi_update_delay(battery->sbs); - end: return result; @@ -673,15 +694,15 @@ static int acpi_battery_get_alarm(struct acpi_battery *battery) static int acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm) { - struct acpi_ec_smbus *smbus = battery->sbs->smbus; + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; int foo; result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } @@ -689,33 +710,29 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery, if (alarm > 0) { result = - acpi_sbs_smbus_read_word(smbus, ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, - &acpi_battery_smbus_err_handler); + acpi_sbs_read_word(sbs, ACPI_SB_SMBUS_ADDR, 0x03, + &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } result = - acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - battery_mode & 0xbfff, - &acpi_battery_smbus_err_handler); + acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, + battery_mode & 0xbfff); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } } foo = alarm / (battery->info.capacity_mode ? 10 : 1); - result = acpi_sbs_smbus_write_word(smbus, ACPI_SB_SMBUS_ADDR, 0x01, - foo, - &acpi_battery_smbus_err_handler); + result = acpi_sbs_write_word(sbs, ACPI_SB_SMBUS_ADDR, 0x01, foo); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } @@ -726,6 +743,7 @@ static int acpi_battery_set_alarm(struct acpi_battery *battery, static int acpi_battery_set_mode(struct acpi_battery *battery) { + struct acpi_sbs *sbs = battery->sbs; int result = 0; s16 battery_mode; @@ -733,12 +751,11 @@ static int acpi_battery_set_mode(struct acpi_battery *battery) goto end; } - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, NULL); + result = acpi_sbs_read_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -747,21 +764,19 @@ static int acpi_battery_set_mode(struct acpi_battery *battery) } else { battery_mode |= 0x8000; } - result = acpi_sbs_smbus_write_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - battery_mode, NULL); + result = acpi_sbs_write_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_write_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_write_word() failed")); goto end; } - result = acpi_sbs_smbus_read_word(battery->sbs->smbus, - ACPI_SB_SMBUS_ADDR, 0x03, - &battery_mode, NULL); + result = acpi_sbs_read_word(sbs, + ACPI_SB_SMBUS_ADDR, 0x03, &battery_mode); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } @@ -775,36 +790,36 @@ static int acpi_battery_init(struct acpi_battery *battery) result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } result = acpi_battery_set_mode(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_set_mode() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_set_mode() failed")); goto end; } result = acpi_battery_get_info(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_info() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_info() failed")); goto end; } result = acpi_battery_get_state(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_state() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_state() failed")); goto end; } result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() failed")); goto end; } @@ -814,20 +829,19 @@ static int acpi_battery_init(struct acpi_battery *battery) static int acpi_ac_get_present(struct acpi_sbs *sbs) { - struct acpi_ec_smbus *smbus = sbs->smbus; int result = 0; s16 charger_status; - result = acpi_sbs_smbus_read_word(smbus, ACPI_SBC_SMBUS_ADDR, 0x13, - &charger_status, NULL); + result = acpi_sbs_read_word(sbs, ACPI_SBC_SMBUS_ADDR, 0x13, + &charger_status); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_smbus_read_word() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_read_word() failed")); goto end; } - sbs->ac_present = (charger_status & 0x8000) >> 15; + sbs->ac.ac_present = (charger_status & 0x8000) >> 15; end: @@ -853,8 +867,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, if (!*dir) { *dir = proc_mkdir(dir_name, parent_dir); if (!*dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "proc_mkdir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "proc_mkdir() failed")); return -ENODEV; } (*dir)->owner = THIS_MODULE; @@ -864,8 +878,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, if (info_fops) { entry = create_proc_entry(ACPI_SBS_FILE_INFO, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = info_fops; entry->data = data; @@ -877,8 +891,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, if (state_fops) { entry = create_proc_entry(ACPI_SBS_FILE_STATE, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = state_fops; entry->data = data; @@ -890,8 +904,8 @@ acpi_sbs_generic_add_fs(struct proc_dir_entry **dir, if (alarm_fops) { entry = create_proc_entry(ACPI_SBS_FILE_ALARM, S_IRUGO, *dir); if (!entry) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "create_proc_entry() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "create_proc_entry() failed")); } else { entry->proc_fops = alarm_fops; entry->data = data; @@ -924,24 +938,27 @@ static struct proc_dir_entry *acpi_battery_dir = NULL; static int acpi_battery_read_info(struct seq_file *seq, void *offset) { struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int cscale; int result = 0; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_INFO); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_INFO); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (acpi_battery_is_present(battery)) { + if (battery->battery_present) { seq_printf(seq, "present: yes\n"); } else { seq_printf(seq, "present: no\n"); @@ -953,13 +970,13 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) } else { cscale = battery->info.ipscale; } - seq_printf(seq, "design capacity: %i%s", + seq_printf(seq, "design capacity: %i%s\n", battery->info.design_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); - seq_printf(seq, "last full capacity: %i%s", + seq_printf(seq, "last full capacity: %i%s\n", battery->info.full_charge_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); seq_printf(seq, "battery technology: rechargeable\n"); @@ -985,7 +1002,7 @@ static int acpi_battery_read_info(struct seq_file *seq, void *offset) end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -997,26 +1014,29 @@ static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) static int acpi_battery_read_state(struct seq_file *seq, void *offset) { - struct acpi_battery *battery = (struct acpi_battery *)seq->private; + struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int result = 0; int cscale; int foo; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_STATE); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_STATE); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (acpi_battery_is_present(battery)) { + if (battery->battery_present) { seq_printf(seq, "present: yes\n"); } else { seq_printf(seq, "present: no\n"); @@ -1029,42 +1049,40 @@ static int acpi_battery_read_state(struct seq_file *seq, void *offset) cscale = battery->info.ipscale; } - if (battery->state.battery_status & 0x0010) { + if (battery->state.battery_state & 0x0010) { seq_printf(seq, "capacity state: critical\n"); } else { seq_printf(seq, "capacity state: ok\n"); } + + foo = (s16) battery->state.amperage * battery->info.ipscale; + if (battery->info.capacity_mode) { + foo = foo * battery->info.design_voltage / 1000; + } if (battery->state.amperage < 0) { seq_printf(seq, "charging state: discharging\n"); - foo = battery->state.remaining_capacity * cscale * 60 / - (battery->state.average_time_to_empty == 0 ? 1 : - battery->state.average_time_to_empty); - seq_printf(seq, "present rate: %i%s\n", - foo, battery->info.capacity_mode ? "0 mW" : " mA"); + seq_printf(seq, "present rate: %d %s\n", + -foo, battery->info.capacity_mode ? "mW" : "mA"); } else if (battery->state.amperage > 0) { seq_printf(seq, "charging state: charging\n"); - foo = (battery->info.full_charge_capacity - - battery->state.remaining_capacity) * cscale * 60 / - (battery->state.average_time_to_full == 0 ? 1 : - battery->state.average_time_to_full); - seq_printf(seq, "present rate: %i%s\n", - foo, battery->info.capacity_mode ? "0 mW" : " mA"); + seq_printf(seq, "present rate: %d %s\n", + foo, battery->info.capacity_mode ? "mW" : "mA"); } else { seq_printf(seq, "charging state: charged\n"); seq_printf(seq, "present rate: 0 %s\n", battery->info.capacity_mode ? "mW" : "mA"); } - seq_printf(seq, "remaining capacity: %i%s", + seq_printf(seq, "remaining capacity: %i%s\n", battery->state.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); seq_printf(seq, "present voltage: %i mV\n", battery->state.voltage * battery->info.vscale); end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -1077,24 +1095,27 @@ static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) { struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; int result = 0; int cscale; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(battery->sbs, DATA_TYPE_ALARM); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, battery->id, DATA_TYPE_ALARM); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } - if (!acpi_battery_is_present(battery)) { + if (!battery->battery_present) { seq_printf(seq, "present: no\n"); goto end; } @@ -1107,16 +1128,16 @@ static int acpi_battery_read_alarm(struct seq_file *seq, void *offset) seq_printf(seq, "alarm: "); if (battery->alarm.remaining_capacity) { - seq_printf(seq, "%i%s", + seq_printf(seq, "%i%s\n", battery->alarm.remaining_capacity * cscale, - battery->info.capacity_mode ? "0 mWh\n" : " mAh\n"); + battery->info.capacity_mode ? "0 mWh" : " mAh"); } else { seq_printf(seq, "disabled\n"); } end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); return result; } @@ -1127,16 +1148,19 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer, { struct seq_file *seq = file->private_data; struct acpi_battery *battery = seq->private; + struct acpi_sbs *sbs = battery->sbs; char alarm_string[12] = { '\0' }; int result, old_alarm, new_alarm; - if (battery->sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); + result = acpi_check_update_proc(sbs); + if (result) + goto end; - if (!acpi_battery_is_present(battery)) { + if (!battery->battery_present) { result = -ENODEV; goto end; } @@ -1158,21 +1182,21 @@ acpi_battery_write_alarm(struct file *file, const char __user * buffer, result = acpi_battery_set_alarm(battery, new_alarm); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_set_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_set_alarm() failed")); acpi_battery_set_alarm(battery, old_alarm); goto end; } result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() failed")); acpi_battery_set_alarm(battery, old_alarm); goto end; } end: - up(&sbs_sem); + sbs_mutex_unlock(sbs); if (result) { return result; @@ -1220,24 +1244,22 @@ static int acpi_ac_read_state(struct seq_file *seq, void *offset) struct acpi_sbs *sbs = seq->private; int result; - if (sbs->zombie) { + if (sbs_mutex_lock(sbs)) { return -ENODEV; } - down(&sbs_sem); - - if (update_mode == REQUEST_UPDATE_MODE) { - result = acpi_sbs_update_run(sbs, DATA_TYPE_AC_STATE); + if (update_time == 0) { + result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_AC_STATE); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } } seq_printf(seq, "state: %s\n", - sbs->ac_present ? "on-line" : "off-line"); + sbs->ac.ac_present ? "on-line" : "off-line"); - up(&sbs_sem); + sbs_mutex_unlock(sbs); return 0; } @@ -1278,25 +1300,25 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); goto end; } result = acpi_battery_get_present(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_present() failed")); goto end; } - is_present = acpi_battery_is_present(battery); + is_present = battery->battery_present; if (is_present) { result = acpi_battery_init(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_init() failed")); goto end; } battery->init_state = 1; @@ -1311,12 +1333,16 @@ static int acpi_battery_add(struct acpi_sbs *sbs, int id) &acpi_battery_state_fops, &acpi_battery_alarm_fops, battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generic_add_fs() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generic_add_fs() failed")); goto end; } battery->alive = 1; + printk(KERN_INFO PREFIX "%s [%s]: Battery Slot [%s] (battery %s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), dir_name, + sbs->battery->battery_present ? "present" : "absent"); + end: return result; } @@ -1336,8 +1362,8 @@ static int acpi_ac_add(struct acpi_sbs *sbs) result = acpi_ac_get_present(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_ac_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ac_get_present() failed")); goto end; } @@ -1346,11 +1372,15 @@ static int acpi_ac_add(struct acpi_sbs *sbs) ACPI_AC_DIR_NAME, NULL, &acpi_ac_state_fops, NULL, sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generic_add_fs() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generic_add_fs() failed")); goto end; } + printk(KERN_INFO PREFIX "%s [%s]: AC Adapter [%s] (%s)\n", + ACPI_SBS_DEVICE_NAME, acpi_device_bid(sbs->device), + ACPI_AC_DIR_NAME, sbs->ac.ac_present ? "on-line" : "off-line"); + end: return result; @@ -1364,45 +1394,85 @@ static void acpi_ac_remove(struct acpi_sbs *sbs) } } -static void acpi_sbs_update_queue_run(unsigned long data) +static void acpi_sbs_update_time_run(unsigned long data) { - acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_queue, (void *)data); + acpi_os_execute(OSL_GPE_HANDLER, acpi_sbs_update_time, (void *)data); } -static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) +static int acpi_sbs_update_run(struct acpi_sbs *sbs, int id, int data_type) { struct acpi_battery *battery; - int result = 0; - int old_ac_present; - int old_battery_present; - int new_ac_present; - int new_battery_present; - int id; + int result = 0, cnt; + int old_ac_present = -1; + int old_battery_present = -1; + int new_ac_present = -1; + int new_battery_present = -1; + int id_min = 0, id_max = MAX_SBS_BAT - 1; char dir_name[32]; - int do_battery_init, do_ac_init; - s16 old_remaining_capacity; + int do_battery_init = 0, do_ac_init = 0; + int old_remaining_capacity = 0; + int update_ac = 1, update_battery = 1; + int up_tm = update_time; + + if (sbs_zombie(sbs)) { + goto end; + } + + if (id >= 0) { + id_min = id_max = id; + } + + if (data_type == DATA_TYPE_COMMON && up_tm > 0) { + cnt = up_tm / (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); + if (sbs->run_cnt % cnt != 0) { + update_battery = 0; + } + } + + sbs->run_cnt++; - if (sbs->zombie) { + if (!update_ac && !update_battery) { goto end; } - old_ac_present = acpi_ac_is_present(sbs); + old_ac_present = sbs->ac.ac_present; result = acpi_ac_get_present(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_ac_get_present() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_ac_get_present() failed")); } - new_ac_present = acpi_ac_is_present(sbs); + new_ac_present = sbs->ac.ac_present; do_ac_init = (old_ac_present != new_ac_present); + if (sbs->run_cnt == 1 && data_type == DATA_TYPE_COMMON) { + do_ac_init = 1; + } + + if (do_ac_init) { + result = acpi_sbs_generate_event(sbs->device, + ACPI_SBS_AC_NOTIFY_STATUS, + new_ac_present, + ACPI_AC_DIR_NAME, + ACPI_AC_CLASS); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generate_event() failed")); + } + } - if (data_type == DATA_TYPE_AC_STATE) { + if (data_type == DATA_TYPE_COMMON) { + if (!do_ac_init && !update_battery) { + goto end; + } + } + + if (data_type == DATA_TYPE_AC_STATE && !do_ac_init) { goto end; } - for (id = 0; id < MAX_SBS_BAT; id++) { + for (id = id_min; id <= id_max; id++) { battery = &sbs->battery[id]; if (battery->alive == 0) { continue; @@ -1410,94 +1480,92 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) old_remaining_capacity = battery->state.remaining_capacity; - old_battery_present = acpi_battery_is_present(battery); + old_battery_present = battery->battery_present; result = acpi_battery_select(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_select() failed\n")); - } - if (sbs->zombie) { - goto end; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_select() failed")); } result = acpi_battery_get_present(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_present() failed\n")); - } - if (sbs->zombie) { - goto end; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_present() failed")); } - new_battery_present = acpi_battery_is_present(battery); + new_battery_present = battery->battery_present; do_battery_init = ((old_battery_present != new_battery_present) && new_battery_present); - - if (sbs->zombie) { + if (!new_battery_present) + goto event; + if (do_ac_init || do_battery_init) { + result = acpi_battery_init(battery); + if (result) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_init() " + "failed")); + } + } + if (sbs_zombie(sbs)) { goto end; } - if (do_ac_init || do_battery_init || - update_info_mode || sbs->update_info_mode) { - if (sbs->update_info_mode) { - sbs->update_info_mode = 0; - } else { - sbs->update_info_mode = 1; - } - result = acpi_battery_init(battery); + + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_INFO) + && new_battery_present) { + result = acpi_battery_get_info(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_init() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_info() failed")); } } if (data_type == DATA_TYPE_INFO) { continue; } - - if (sbs->zombie) { + if (sbs_zombie(sbs)) { goto end; } - if (new_battery_present) { - result = acpi_battery_get_alarm(battery); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_alarm() " - "failed\n")); - } - if (data_type == DATA_TYPE_ALARM) { - continue; - } + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_STATE) + && new_battery_present) { result = acpi_battery_get_state(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_get_state() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_state() failed")); } } - if (sbs->zombie) { - goto end; + if (data_type == DATA_TYPE_STATE) { + goto event; } - if (data_type != DATA_TYPE_COMMON) { - continue; + if (sbs_zombie(sbs)) { + goto end; } - if (old_battery_present != new_battery_present) { - sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_BATTERY_NOTIFY_STATUS, - new_battery_present, - dir_name, - ACPI_BATTERY_CLASS); + if ((data_type == DATA_TYPE_COMMON + || data_type == DATA_TYPE_ALARM) + && new_battery_present) { + result = acpi_battery_get_alarm(battery); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_get_alarm() " + "failed")); } } - if (old_remaining_capacity != battery->state.remaining_capacity) { + if (data_type == DATA_TYPE_ALARM) { + continue; + } + if (sbs_zombie(sbs)) { + goto end; + } + + event: + + if (old_battery_present != new_battery_present || do_ac_init || + old_remaining_capacity != + battery->state.remaining_capacity) { sprintf(dir_name, ACPI_BATTERY_DIR_NAME, id); result = acpi_sbs_generate_event(sbs->device, ACPI_SBS_BATTERY_NOTIFY_STATUS, @@ -1505,138 +1573,120 @@ static int acpi_sbs_update_run(struct acpi_sbs *sbs, int data_type) dir_name, ACPI_BATTERY_CLASS); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_generate_event() " + "failed")); } } - - } - if (sbs->zombie) { - goto end; - } - if (data_type != DATA_TYPE_COMMON) { - goto end; - } - - if (old_ac_present != new_ac_present) { - result = acpi_sbs_generate_event(sbs->device, - ACPI_SBS_AC_NOTIFY_STATUS, - new_ac_present, - ACPI_AC_DIR_NAME, - ACPI_AC_CLASS); - if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_generate_event() failed\n")); - } } end: + return result; } -static void acpi_sbs_update_queue(void *data) +static void acpi_sbs_update_time(void *data) { struct acpi_sbs *sbs = data; unsigned long delay = -1; int result; + unsigned int up_tm = update_time; - if (sbs->zombie) { - goto end; - } + if (sbs_mutex_lock(sbs)) + return; - result = acpi_sbs_update_run(sbs, DATA_TYPE_COMMON); + result = acpi_sbs_update_run(sbs, -1, DATA_TYPE_COMMON); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_update_run() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_update_run() failed")); } - if (sbs->zombie) { + if (sbs_zombie(sbs)) { goto end; } - if (update_mode == REQUEST_UPDATE_MODE) { - goto end; + if (!up_tm) { + if (timer_pending(&sbs->update_timer)) + del_timer(&sbs->update_timer); + } else { + delay = (up_tm > UPDATE_DELAY ? UPDATE_DELAY : up_tm); + delay = jiffies + HZ * delay; + if (timer_pending(&sbs->update_timer)) { + mod_timer(&sbs->update_timer, delay); + } else { + sbs->update_timer.data = (unsigned long)data; + sbs->update_timer.function = acpi_sbs_update_time_run; + sbs->update_timer.expires = delay; + add_timer(&sbs->update_timer); + } } - delay = jiffies + HZ * update_time; - sbs->update_timer.data = (unsigned long)data; - sbs->update_timer.function = acpi_sbs_update_queue_run; - sbs->update_timer.expires = delay; - add_timer(&sbs->update_timer); end: - ; + + sbs_mutex_unlock(sbs); } static int acpi_sbs_add(struct acpi_device *device) { struct acpi_sbs *sbs = NULL; - struct acpi_ec_hc *ec_hc = NULL; - int result, remove_result = 0; + int result = 0, remove_result = 0; unsigned long sbs_obj; - int id, cnt; + int id; acpi_status status = AE_OK; + unsigned long val; + + status = + acpi_evaluate_integer(device->parent->handle, "_EC", NULL, &val); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Error obtaining _EC")); + return -EIO; + } sbs = kzalloc(sizeof(struct acpi_sbs), GFP_KERNEL); if (!sbs) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "kmalloc() failed\n")); - return -ENOMEM; + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "kzalloc() failed")); + result = -ENOMEM; + goto end; } - cnt = 0; - while (cnt < 10) { - cnt++; - ec_hc = acpi_get_ec_hc(device); - if (ec_hc) { - break; - } - msleep(1000); - } + mutex_init(&sbs->mutex); - if (!ec_hc) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_get_ec_hc() failed: " - "NO driver found for EC HC SMBus\n")); - result = -ENODEV; - goto end; - } + sbs_mutex_lock(sbs); + sbs->base = (val & 0xff00ull) >> 8; sbs->device = device; - sbs->smbus = ec_hc->smbus; strcpy(acpi_device_name(device), ACPI_SBS_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_SBS_CLASS); acpi_driver_data(device) = sbs; - sbs->update_time = 0; - sbs->update_time2 = 0; - result = acpi_ac_add(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_ac_add() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, "acpi_ac_add() failed")); goto end; } - result = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); - if (ACPI_FAILURE(result)) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_evaluate_integer() failed\n")); + status = acpi_evaluate_integer(device->handle, "_SBS", NULL, &sbs_obj); + if (status) { + ACPI_EXCEPTION((AE_INFO, status, + "acpi_evaluate_integer() failed")); result = -EIO; goto end; } - if (sbs_obj > 0) { result = acpi_sbsm_get_info(sbs); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbsm_get_info() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbsm_get_info() failed")); goto end; } sbs->sbsm_present = 1; } + if (sbs->sbsm_present == 0) { result = acpi_battery_add(sbs, 0); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_add() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_add() failed")); goto end; } } else { @@ -1644,9 +1694,8 @@ static int acpi_sbs_add(struct acpi_device *device) if ((sbs->sbsm_batteries_supported & (1 << id))) { result = acpi_battery_add(sbs, id); if (result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_battery_add() " - "failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_battery_add() failed")); goto end; } } @@ -1656,33 +1705,26 @@ static int acpi_sbs_add(struct acpi_device *device) sbs->handle = device->handle; init_timer(&sbs->update_timer); - if (update_mode == QUEUE_UPDATE_MODE) { - status = acpi_os_execute(OSL_GPE_HANDLER, - acpi_sbs_update_queue, sbs); - if (status != AE_OK) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_os_execute() failed\n")); - } - } - sbs->update_time = update_time; - sbs->update_time2 = update_time2; - - printk(KERN_INFO PREFIX "%s [%s]\n", - acpi_device_name(device), acpi_device_bid(device)); + result = acpi_check_update_proc(sbs); + if (result) + goto end; end: + + sbs_mutex_unlock(sbs); + if (result) { remove_result = acpi_sbs_remove(device, 0); if (remove_result) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_sbs_remove() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_sbs_remove() failed")); } } return result; } -int acpi_sbs_remove(struct acpi_device *device, int type) +static int acpi_sbs_remove(struct acpi_device *device, int type) { struct acpi_sbs *sbs; int id; @@ -1691,15 +1733,14 @@ int acpi_sbs_remove(struct acpi_device *device, int type) return -EINVAL; } - sbs = (struct acpi_sbs *)acpi_driver_data(device); - + sbs = acpi_driver_data(device); if (!sbs) { return -EINVAL; } + sbs_mutex_lock(sbs); + sbs->zombie = 1; - sbs->update_time = 0; - sbs->update_time2 = 0; del_timer_sync(&sbs->update_timer); acpi_os_wait_events_complete(NULL); del_timer_sync(&sbs->update_timer); @@ -1710,11 +1751,41 @@ int acpi_sbs_remove(struct acpi_device *device, int type) acpi_ac_remove(sbs); + sbs_mutex_unlock(sbs); + + mutex_destroy(&sbs->mutex); + kfree(sbs); return 0; } +static void acpi_sbs_rmdirs(void) +{ + if (acpi_ac_dir) { + acpi_unlock_ac_dir(acpi_ac_dir); + acpi_ac_dir = NULL; + } + if (acpi_battery_dir) { + acpi_unlock_battery_dir(acpi_battery_dir); + acpi_battery_dir = NULL; + } +} + +static int acpi_sbs_resume(struct acpi_device *device) +{ + struct acpi_sbs *sbs; + + if (!device) + return -EINVAL; + + sbs = device->driver_data; + + sbs->run_cnt = 0; + + return 0; +} + static int __init acpi_sbs_init(void) { int result = 0; @@ -1722,35 +1793,34 @@ static int __init acpi_sbs_init(void) if (acpi_disabled) return -ENODEV; - init_MUTEX(&sbs_sem); - if (capacity_mode != DEF_CAPACITY_UNIT && capacity_mode != MAH_CAPACITY_UNIT && capacity_mode != MWH_CAPACITY_UNIT) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "acpi_sbs_init: " - "invalid capacity_mode = %d\n", - capacity_mode)); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "invalid capacity_mode = %d", capacity_mode)); return -EINVAL; } acpi_ac_dir = acpi_lock_ac_dir(); if (!acpi_ac_dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_lock_ac_dir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_lock_ac_dir() failed")); return -ENODEV; } acpi_battery_dir = acpi_lock_battery_dir(); if (!acpi_battery_dir) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_lock_battery_dir() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_lock_battery_dir() failed")); + acpi_sbs_rmdirs(); return -ENODEV; } result = acpi_bus_register_driver(&acpi_sbs_driver); if (result < 0) { - ACPI_DEBUG_PRINT((ACPI_DB_ERROR, - "acpi_bus_register_driver() failed\n")); + ACPI_EXCEPTION((AE_INFO, AE_ERROR, + "acpi_bus_register_driver() failed")); + acpi_sbs_rmdirs(); return -ENODEV; } @@ -1759,13 +1829,9 @@ static int __init acpi_sbs_init(void) static void __exit acpi_sbs_exit(void) { - acpi_bus_unregister_driver(&acpi_sbs_driver); - acpi_unlock_ac_dir(acpi_ac_dir); - acpi_ac_dir = NULL; - acpi_unlock_battery_dir(acpi_battery_dir); - acpi_battery_dir = NULL; + acpi_sbs_rmdirs(); return; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 64f26db..d80dd84 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -11,13 +11,12 @@ #include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ #define _COMPONENT ACPI_BUS_COMPONENT -ACPI_MODULE_NAME("scan") +ACPI_MODULE_NAME("scan"); #define STRUCT_TO_INT(s) (*((int*)&s)) extern struct acpi_device *acpi_root; #define ACPI_BUS_CLASS "system_bus" #define ACPI_BUS_HID "ACPI_BUS" -#define ACPI_BUS_DRIVER_NAME "ACPI Bus Driver" #define ACPI_BUS_DEVICE_NAME "System Bus" static LIST_HEAD(acpi_device_list); @@ -1069,7 +1068,9 @@ acpi_add_single_object(struct acpi_device **child, } break; default: - STRUCT_TO_INT(device->status) = 0x0F; + STRUCT_TO_INT(device->status) = + ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED | + ACPI_STA_DEVICE_UI | ACPI_STA_DEVICE_FUNCTIONING; break; } diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c index 62ce87d..f8c6341 100644 --- a/drivers/acpi/sleep/main.c +++ b/drivers/acpi/sleep/main.c @@ -168,9 +168,18 @@ int acpi_suspend(u32 acpi_state) static int acpi_pm_state_valid(suspend_state_t pm_state) { - u32 acpi_state = acpi_suspend_states[pm_state]; + u32 acpi_state; + + switch (pm_state) { + case PM_SUSPEND_ON: + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + acpi_state = acpi_suspend_states[pm_state]; - return sleep_states[acpi_state]; + return sleep_states[acpi_state]; + default: + return 0; + } } static struct pm_ops acpi_pm_ops = { @@ -200,7 +209,7 @@ static struct dmi_system_id __initdata acpisleep_dmi_table[] = { {}, }; -static int __init acpi_sleep_init(void) +int __init acpi_sleep_init(void) { int i = 0; @@ -229,4 +238,3 @@ static int __init acpi_sleep_init(void) return 0; } -late_initcall(acpi_sleep_init); diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index ccc11b3..2d912b7 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -350,21 +350,31 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) { struct list_head *node, *next; - seq_printf(seq, "Device Sleep state Status\n"); + seq_printf(seq, "Device\tS-state\t Status Sysfs node\n"); spin_lock(&acpi_device_lock); list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); + struct device *ldev; if (!dev->wakeup.flags.valid) continue; spin_unlock(&acpi_device_lock); - seq_printf(seq, "%4s %4d %s%8s\n", + + ldev = acpi_get_physical_device(dev->handle); + seq_printf(seq, "%s\t S%d\t%c%-8s ", dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, - dev->wakeup.flags.run_wake ? "*" : "", + dev->wakeup.flags.run_wake ? '*' : ' ', dev->wakeup.state.enabled ? "enabled" : "disabled"); + if (ldev) + seq_printf(seq, "%s:%s", + ldev->bus ? ldev->bus->name : "no-bus", + ldev->bus_id); + seq_printf(seq, "\n"); + put_device(ldev); + spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 7147b0b..83a8d30 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -31,14 +31,13 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_SYSTEM_COMPONENT -ACPI_MODULE_NAME("acpi_system") +ACPI_MODULE_NAME("system"); #ifdef MODULE_PARAM_PREFIX #undef MODULE_PARAM_PREFIX #endif #define MODULE_PARAM_PREFIX "acpi." #define ACPI_SYSTEM_CLASS "system" -#define ACPI_SYSTEM_DRIVER_NAME "ACPI System Driver" #define ACPI_SYSTEM_DEVICE_NAME "System" #define ACPI_SYSTEM_FILE_INFO "info" #define ACPI_SYSTEM_FILE_EVENT "event" diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 45bd173..c341918 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -42,7 +42,9 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata; -void acpi_table_print_madt_entry(struct acpi_subtable_header * header) +static int acpi_apic_instance __initdata; + +void acpi_table_print_madt_entry(struct acpi_subtable_header *header) { if (!header) return; @@ -169,40 +171,42 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header * header) int __init -acpi_table_parse_madt_family(char *id, - unsigned long madt_size, +acpi_table_parse_entries(char *id, + unsigned long table_size, int entry_id, - acpi_madt_entry_handler handler, + acpi_table_entry_handler handler, unsigned int max_entries) { - struct acpi_table_header *madt = NULL; + struct acpi_table_header *table_header = NULL; struct acpi_subtable_header *entry; unsigned int count = 0; - unsigned long madt_end; + unsigned long table_end; if (!handler) return -EINVAL; - /* Locate the MADT (if exists). There should only be one. */ - acpi_get_table(id, 0, &madt); + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table(id, acpi_apic_instance, &table_header); + else + acpi_get_table(id, 0, &table_header); - if (!madt) { + if (!table_header) { printk(KERN_WARNING PREFIX "%4.4s not present\n", id); return -ENODEV; } - madt_end = (unsigned long)madt + madt->length; + table_end = (unsigned long)table_header + table_header->length; /* Parse all entries looking for a match. */ entry = (struct acpi_subtable_header *) - ((unsigned long)madt + madt_size); + ((unsigned long)table_header + table_size); while (((unsigned long)entry) + sizeof(struct acpi_subtable_header) < - madt_end) { + table_end) { if (entry->type == entry_id && (!max_entries || count++ < max_entries)) - if (handler(entry, madt_end)) + if (handler(entry, table_end)) return -EINVAL; entry = (struct acpi_subtable_header *) @@ -218,25 +222,64 @@ acpi_table_parse_madt_family(char *id, int __init acpi_table_parse_madt(enum acpi_madt_type id, - acpi_madt_entry_handler handler, unsigned int max_entries) + acpi_table_entry_handler handler, unsigned int max_entries) { - return acpi_table_parse_madt_family(ACPI_SIG_MADT, + return acpi_table_parse_entries(ACPI_SIG_MADT, sizeof(struct acpi_table_madt), id, handler, max_entries); } +/** + * acpi_table_parse - find table with @id, run @handler on it + * + * @id: table id to find + * @handler: handler to run + * + * Scan the ACPI System Descriptor Table (STD) for a table matching @id, + * run @handler on it. Return 0 if table found, return on if not. + */ int __init acpi_table_parse(char *id, acpi_table_handler handler) { struct acpi_table_header *table = NULL; + if (!handler) return -EINVAL; - acpi_get_table(id, 0, &table); + if (strncmp(id, ACPI_SIG_MADT, 4) == 0) + acpi_get_table(id, acpi_apic_instance, &table); + else + acpi_get_table(id, 0, &table); + if (table) { handler(table); + return 0; + } else return 1; +} + +/* + * The BIOS is supposed to supply a single APIC/MADT, + * but some report two. Provide a knob to use either. + * (don't you wish instance 0 and 1 were not the same?) + */ +static void __init check_multiple_madt(void) +{ + struct acpi_table_header *table = NULL; + + acpi_get_table(ACPI_SIG_MADT, 2, &table); + if (table) { + printk(KERN_WARNING PREFIX + "BIOS bug: multiple APIC/MADT found," + " using %d\n", acpi_apic_instance); + printk(KERN_WARNING PREFIX + "If \"acpi_apic_instance=%d\" works better, " + "notify linux-acpi@vger.kernel.org\n", + acpi_apic_instance ? 0 : 2); + } else - return 0; + acpi_apic_instance = 0; + + return; } /* @@ -248,9 +291,22 @@ int __init acpi_table_parse(char *id, acpi_table_handler handler) * result: sdt_entry[] is initialized */ - int __init acpi_table_init(void) { acpi_initialize_tables(initial_tables, ACPI_MAX_TABLES, 0); + check_multiple_madt(); + return 0; +} + +static int __init acpi_parse_apic_instance(char *str) +{ + + acpi_apic_instance = simple_strtoul(str, NULL, 0); + + printk(KERN_NOTICE PREFIX "Shall use APIC/MADT table %d\n", + acpi_apic_instance); + return 0; } + +early_param("acpi_apic_instance", acpi_parse_apic_instance); diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 807c711..1db833e 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -347,6 +347,18 @@ static void acpi_tb_convert_fadt(void) acpi_gbl_xpm1b_enable.space_id = acpi_gbl_FADT.xpm1a_event_block.space_id; } + + /* + * For ACPI 1.0 FADTs, ensure that reserved fields (which should be zero) + * are indeed zero. This will workaround BIOSs that inadvertently placed + * values in these fields. + */ + if (acpi_gbl_FADT.header.revision < 3) { + acpi_gbl_FADT.preferred_profile = 0; + acpi_gbl_FADT.pstate_control = 0; + acpi_gbl_FADT.cst_control = 0; + acpi_gbl_FADT.boot_flags = 0; + } } /****************************************************************************** diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c index 807978d..417ef5f 100644 --- a/drivers/acpi/tables/tbxface.c +++ b/drivers/acpi/tables/tbxface.c @@ -338,9 +338,9 @@ acpi_status acpi_unload_table_id(acpi_owner_id id) int i; acpi_status status = AE_NOT_EXIST; - ACPI_FUNCTION_TRACE(acpi_unload_table); + ACPI_FUNCTION_TRACE(acpi_unload_table_id); - /* Find table from the requested type list */ + /* Find table in the global table list */ for (i = 0; i < acpi_gbl_root_table_list.count; ++i) { if (id != acpi_gbl_root_table_list.tables[i].owner_id) { continue; @@ -352,8 +352,9 @@ acpi_status acpi_unload_table_id(acpi_owner_id id) * simply a position within the hierarchy */ acpi_tb_delete_namespace_by_owner(i); - acpi_tb_release_owner_id(i); + status = acpi_tb_release_owner_id(i); acpi_tb_set_table_loaded_flag(i, FALSE); + break; } return_ACPI_STATUS(status); } @@ -408,7 +409,7 @@ acpi_get_table(char *signature, } if (!acpi_gbl_permanent_mmap) { - acpi_gbl_root_table_list.tables[i].pointer = 0; + acpi_gbl_root_table_list.tables[i].pointer = NULL; } return (status); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 986afd4..589b98b 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -47,7 +47,6 @@ #define ACPI_THERMAL_COMPONENT 0x04000000 #define ACPI_THERMAL_CLASS "thermal_zone" -#define ACPI_THERMAL_DRIVER_NAME "ACPI Thermal Zone Driver" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" #define ACPI_THERMAL_FILE_STATE "state" #define ACPI_THERMAL_FILE_TEMPERATURE "temperature" @@ -71,10 +70,10 @@ #define CELSIUS_TO_KELVIN(t) ((t+273)*10) #define _COMPONENT ACPI_THERMAL_COMPONENT -ACPI_MODULE_NAME("acpi_thermal") +ACPI_MODULE_NAME("thermal"); MODULE_AUTHOR("Paul Diefenbaugh"); -MODULE_DESCRIPTION(ACPI_THERMAL_DRIVER_NAME); +MODULE_DESCRIPTION("ACPI Thermal Zone Driver"); MODULE_LICENSE("GPL"); static int tzp; @@ -99,7 +98,7 @@ static ssize_t acpi_thermal_write_polling(struct file *, const char __user *, size_t, loff_t *); static struct acpi_driver acpi_thermal_driver = { - .name = ACPI_THERMAL_DRIVER_NAME, + .name = "thermal", .class = ACPI_THERMAL_CLASS, .ids = ACPI_THERMAL_HID, .ops = { @@ -270,7 +269,7 @@ static int acpi_thermal_set_polling(struct acpi_thermal *tz, int seconds) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency set to %lu seconds\n", - tz->polling_frequency)); + tz->polling_frequency/10)); return 0; } @@ -759,7 +758,8 @@ static void acpi_thermal_check(void *data) del_timer(&(tz->timer)); } else { if (timer_pending(&(tz->timer))) - mod_timer(&(tz->timer), (HZ * sleep_time) / 1000); + mod_timer(&(tz->timer), + jiffies + (HZ * sleep_time) / 1000); else { tz->timer.data = (unsigned long)tz; tz->timer.function = acpi_thermal_run; @@ -1357,28 +1357,32 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) static int acpi_thermal_resume(struct acpi_device *device) { struct acpi_thermal *tz = NULL; - int i; + int i, j, power_state, result; + if (!device || !acpi_driver_data(device)) return -EINVAL; tz = acpi_driver_data(device); - acpi_thermal_get_temperature(tz); - for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { - if (tz->trips.active[i].flags.valid) { - tz->temperature = tz->trips.active[i].temperature; - tz->trips.active[i].flags.enabled = 0; - - acpi_thermal_active(tz); - - tz->state.active |= tz->trips.active[i].flags.enabled; - tz->state.active_index = i; + if (!(&tz->trips.active[i])) + break; + if (!tz->trips.active[i].flags.valid) + break; + tz->trips.active[i].flags.enabled = 1; + for (j = 0; j < tz->trips.active[i].devices.count; j++) { + result = acpi_bus_get_power(tz->trips.active[i].devices. + handles[j], &power_state); + if (result || (power_state != ACPI_STATE_D0)) { + tz->trips.active[i].flags.enabled = 0; + break; + } } + tz->state.active |= tz->trips.active[i].flags.enabled; } - acpi_thermal_check(tz); + acpi_thermal_check(tz); return AE_OK; } diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c index d9b651f..3906d47 100644 --- a/drivers/acpi/toshiba_acpi.c +++ b/drivers/acpi/toshiba_acpi.c @@ -125,7 +125,7 @@ static int write_acpi_int(const char *methodName, int val) union acpi_object in_objs[1]; acpi_status status; - params.count = sizeof(in_objs) / sizeof(in_objs[0]); + params.count = ARRAY_SIZE(in_objs); params.pointer = in_objs; in_objs[0].type = ACPI_TYPE_INTEGER; in_objs[0].integer.value = val; @@ -315,7 +315,7 @@ static int set_lcd(int value) static int set_lcd_status(struct backlight_device *bd) { - return set_lcd(bd->props->brightness); + return set_lcd(bd->props.brightness); } static unsigned long write_lcd(const char *buffer, unsigned long count) @@ -533,11 +533,9 @@ static acpi_status __exit remove_device(void) return AE_OK; } -static struct backlight_properties toshiba_backlight_data = { - .owner = THIS_MODULE, +static struct backlight_ops toshiba_backlight_data = { .get_brightness = get_lcd, .update_status = set_lcd_status, - .max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1, }; static void __exit toshiba_acpi_exit(void) @@ -561,10 +559,6 @@ static int __init toshiba_acpi_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(MY_INFO "Using generic hotkey driver\n"); - return -ENODEV; - } /* simple device detection: look for HCI method */ if (is_valid_acpi_path(METHOD_HCI_1)) method_hci = METHOD_HCI_1; @@ -601,6 +595,7 @@ static int __init toshiba_acpi_init(void) toshiba_backlight_device = NULL; toshiba_acpi_exit(); } + toshiba_backlight_device->props.max_brightness = HCI_LCD_BRIGHTNESS_LEVELS - 1; return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; } diff --git a/drivers/acpi/utilities/utdelete.c b/drivers/acpi/utilities/utdelete.c index f777ceb..673a0ca 100644 --- a/drivers/acpi/utilities/utdelete.c +++ b/drivers/acpi/utilities/utdelete.c @@ -170,7 +170,6 @@ static void acpi_ut_delete_internal_obj(union acpi_operand_object *object) acpi_os_delete_mutex(object->mutex.os_mutex); acpi_gbl_global_lock_mutex = NULL; } else { - acpi_ex_unlink_mutex(object); acpi_os_delete_mutex(object->mutex.os_mutex); } break; diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 68a809f..34f1575 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -31,7 +31,7 @@ #include <acpi/acpi_drivers.h> #define _COMPONENT ACPI_BUS_COMPONENT -ACPI_MODULE_NAME("acpi_utils") +ACPI_MODULE_NAME("utils"); /* -------------------------------------------------------------------------- Object Evaluation Helpers diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index e0b97ad..00d25b3 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -40,7 +40,6 @@ #define ACPI_VIDEO_COMPONENT 0x08000000 #define ACPI_VIDEO_CLASS "video" -#define ACPI_VIDEO_DRIVER_NAME "ACPI Video Driver" #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" #define ACPI_VIDEO_NOTIFY_SWITCH 0x80 @@ -65,17 +64,17 @@ #define ACPI_VIDEO_DISPLAY_LCD 4 #define _COMPONENT ACPI_VIDEO_COMPONENT -ACPI_MODULE_NAME("acpi_video") +ACPI_MODULE_NAME("video"); - MODULE_AUTHOR("Bruno Ducrot"); -MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME); +MODULE_AUTHOR("Bruno Ducrot"); +MODULE_DESCRIPTION("ACPI Video Driver"); MODULE_LICENSE("GPL"); static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device, int type); static struct acpi_driver acpi_video_bus = { - .name = ACPI_VIDEO_DRIVER_NAME, + .name = "video", .class = ACPI_VIDEO_CLASS, .ids = ACPI_VIDEO_HID, .ops = { @@ -103,9 +102,9 @@ struct acpi_video_bus_cap { struct acpi_video_device_attrib { u32 display_index:4; /* A zero-based instance of the Display */ - u32 display_port_attachment:4; /*This field differenates displays type */ + u32 display_port_attachment:4; /*This field differentiates the display type */ u32 display_type:4; /*Describe the specific type in use */ - u32 vendor_specific:4; /*Chipset Vendor Specifi */ + u32 vendor_specific:4; /*Chipset Vendor Specific */ u32 bios_can_detect:1; /*BIOS can detect the device */ u32 depend_on_vga:1; /*Non-VGA output device whose power is related to the VGA device. */ @@ -170,7 +169,6 @@ struct acpi_video_device { struct acpi_device *dev; struct acpi_video_device_brightness *brightness; struct backlight_device *backlight; - struct backlight_properties *data; }; /* bus */ @@ -287,13 +285,18 @@ static int acpi_video_get_brightness(struct backlight_device *bd) static int acpi_video_set_brightness(struct backlight_device *bd) { - int request_level = bd->props->brightness; + int request_level = bd->props.brightness; struct acpi_video_device *vd = (struct acpi_video_device *)class_get_devdata(&bd->class_dev); acpi_video_device_lcd_set_level(vd, request_level); return 0; } +static struct backlight_ops acpi_backlight_ops = { + .get_brightness = acpi_video_get_brightness, + .update_status = acpi_video_set_brightness, +}; + /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -481,16 +484,16 @@ acpi_video_bus_POST_options(struct acpi_video_bus *video, * 0. The system BIOS should NOT automatically switch(toggle) * the active display output. * 1. The system BIOS should automatically switch (toggle) the - * active display output. No swich event. + * active display output. No switch event. * 2. The _DGS value should be locked. * 3. The system BIOS should not automatically switch (toggle) the * active display output, but instead generate the display switch * event notify code. * lcd_flag : * 0. The system BIOS should automatically control the brightness level - * of the LCD, when the power changes from AC to DC + * of the LCD when the power changes from AC to DC * 1. The system BIOS should NOT automatically control the brightness - * level of the LCD, when the power changes from AC to DC. + * level of the LCD when the power changes from AC to DC. * Return Value: * -1 wrong arg. */ @@ -522,7 +525,7 @@ acpi_video_bus_DOS(struct acpi_video_bus *video, int bios_flag, int lcd_flag) * Return Value: * None * - * Find out all required AML method defined under the output + * Find out all required AML methods defined under the output * device. */ @@ -609,31 +612,18 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) unsigned long tmp; static int count = 0; char *name; - struct backlight_properties *acpi_video_data; - name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); if (!name) return; - acpi_video_data = kzalloc( - sizeof(struct backlight_properties), - GFP_KERNEL); - if (!acpi_video_data){ - kfree(name); - return; - } - acpi_video_data->owner = THIS_MODULE; - acpi_video_data->get_brightness = - acpi_video_get_brightness; - acpi_video_data->update_status = - acpi_video_set_brightness; sprintf(name, "acpi_video%d", count++); - device->data = acpi_video_data; - acpi_video_data->max_brightness = max_level; acpi_video_device_lcd_get_level_current(device, &tmp); - acpi_video_data->brightness = (int)tmp; device->backlight = backlight_device_register(name, - NULL, device, acpi_video_data); + NULL, device, &acpi_backlight_ops); + device->backlight->props.max_brightness = max_level; + device->backlight->props.brightness = (int)tmp; + backlight_update_status(device->backlight); + kfree(name); } return; @@ -646,7 +636,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) * Return Value: * None * - * Find out all required AML method defined under the video bus device. + * Find out all required AML methods defined under the video bus device. */ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) @@ -691,19 +681,19 @@ static int acpi_video_bus_check(struct acpi_video_bus *video) * to check well known required nodes. */ - /* Does this device able to support video switching ? */ + /* Does this device support video switching? */ if (video->cap._DOS) { video->flags.multihead = 1; status = 0; } - /* Does this device able to retrieve a retrieve a video ROM ? */ + /* Does this device support retrieving a video ROM? */ if (video->cap._ROM) { video->flags.rom = 1; status = 0; } - /* Does this device able to configure which video device to POST ? */ + /* Does this device support configuring which video device to POST? */ if (video->cap._GPD && video->cap._SPD && video->cap._VPO) { video->flags.post = 1; status = 0; @@ -870,7 +860,7 @@ acpi_video_device_write_brightness(struct file *file, if (level > 100) return -EFAULT; - /* validate though the list of available levels */ + /* validate through the list of available levels */ for (i = 0; i < dev->brightness->count; i++) if (level == dev->brightness->levels[i]) { if (ACPI_SUCCESS @@ -1075,10 +1065,10 @@ static int acpi_video_bus_POST_info_seq_show(struct seq_file *seq, void *offset) printk(KERN_WARNING PREFIX "The motherboard VGA device is not listed as a possible POST device.\n"); printk(KERN_WARNING PREFIX - "This indicate a BIOS bug. Please contact the manufacturer.\n"); + "This indicates a BIOS bug. Please contact the manufacturer.\n"); } printk("%lx\n", options); - seq_printf(seq, "can POST: <intgrated video>"); + seq_printf(seq, "can POST: <integrated video>"); if (options & 2) seq_printf(seq, " <PCI video>"); if (options & 4) @@ -1112,7 +1102,7 @@ static int acpi_video_bus_POST_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "<not supported>\n"); goto end; } - seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]); + seq_printf(seq, "device POSTed is <%s>\n", device_decode[id & 3]); end: return 0; @@ -1166,7 +1156,7 @@ acpi_video_bus_write_POST(struct file *file, if (opt > 3) return -EFAULT; - /* just in case an OEM 'forget' the motherboard... */ + /* just in case an OEM 'forgot' the motherboard... */ options |= 1; if (options & (1ul << opt)) { @@ -1537,13 +1527,13 @@ static int acpi_video_device_enumerate(struct acpi_video_bus *video) /* * Arg: * video : video bus device - * event : Nontify Event + * event : notify event * * Return: * < 0 : error * * 1. Find out the current active output device. - * 2. Identify the next output device to switch + * 2. Identify the next output device to switch to. * 3. call _DSS to do actual switch. */ @@ -1678,10 +1668,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); - if (device->backlight){ - backlight_device_unregister(device->backlight); - kfree(device->data); - } + backlight_device_unregister(device->backlight); return 0; } @@ -1736,12 +1723,12 @@ static void acpi_video_bus_notify(acpi_handle handle, u32 event, void *data) device = video->device; switch (event) { - case ACPI_VIDEO_NOTIFY_SWITCH: /* User request that a switch occur, + case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch, * most likely via hotkey. */ acpi_bus_generate_event(device, event, 0); break; - case ACPI_VIDEO_NOTIFY_PROBE: /* User plug or remove a video + case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video * connector. */ acpi_video_device_enumerate(video); acpi_video_device_rebind(video); diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index fd54750..268e301 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -47,14 +47,13 @@ static int amba_match(struct device *dev, struct device_driver *drv) static int amba_uevent(struct device *dev, char **envp, int nr_env, char *buf, int bufsz) { struct amba_device *pcdev = to_amba_device(dev); + int retval = 0, i = 0, len = 0; - if (nr_env < 2) - return -ENOMEM; - - snprintf(buf, bufsz, "AMBA_ID=%08x", pcdev->periphid); - *envp++ = buf; - *envp++ = NULL; - return 0; + retval = add_uevent_var(envp, nr_env, &i, + buf, bufsz, &len, + "AMBA_ID=%08x", pcdev->periphid); + envp[i] = NULL; + return retval; } #else #define amba_uevent NULL diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 3747457fe..365c306 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -156,10 +156,18 @@ config SATA_INIC162X help This option enables support for Initio 162x Serial ATA. -config SATA_INTEL_COMBINED +config SATA_ACPI bool - depends on IDE=y && !BLK_DEV_IDE_SATA && (SATA_AHCI || ATA_PIIX) + depends on ACPI && PCI default y + help + This option adds support for SATA-related ACPI objects. + These ACPI objects add the ability to retrieve taskfiles + from the ACPI BIOS and write them to the disk controller. + These objects may be related to performance, security, + power management, or other areas. + You can disable this at kernel boot time by using the + option libata.noacpi=1 config PATA_ALI tristate "ALi PATA support (Experimental)" @@ -171,7 +179,7 @@ config PATA_ALI If unsure, say N. config PATA_AMD - tristate "AMD/NVidia PATA support (Experimental)" + tristate "AMD/NVidia PATA support" depends on PCI help This option enables support for the AMD and NVidia PATA @@ -196,6 +204,16 @@ config PATA_ATIIXP If unsure, say N. +config PATA_CMD640_PCI + tristate "CMD640 PCI PATA support (Very Experimental)" + depends on PCI && EXPERIMENTAL + help + This option enables support for the CMD640 PCI IDE + interface chip. Only the primary channel is currently + supported. + + If unsure, say N. + config PATA_CMD64X tristate "CMD64x PATA support (Very Experimental)" depends on PCI&& EXPERIMENTAL @@ -260,7 +278,7 @@ config ATA_GENERIC If unsure, say N. config PATA_HPT366 - tristate "HPT 366/368 PATA support (Very Experimental)" + tristate "HPT 366/368 PATA support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the HPT 366 and 368 @@ -269,7 +287,7 @@ config PATA_HPT366 If unsure, say N. config PATA_HPT37X - tristate "HPT 370/370A/371/372/374/302 PATA support (Very Experimental)" + tristate "HPT 370/370A/371/372/374/302 PATA support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the majority of the later HPT @@ -296,7 +314,7 @@ config PATA_HPT3X3 If unsure, say N. config PATA_ISAPNP - tristate "ISA Plug and Play PATA support (Very Experimental)" + tristate "ISA Plug and Play PATA support (Experimental)" depends on EXPERIMENTAL && ISAPNP help This option enables support for ISA plug & play ATA @@ -305,8 +323,8 @@ config PATA_ISAPNP If unsure, say N. config PATA_IT821X - tristate "IT8211/2 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "IT8211/2 PATA support" + depends on PCI help This option enables support for the ITE 8211 and 8212 PATA controllers via the new ATA layer, including RAID @@ -377,10 +395,10 @@ config PATA_MPIIX If unsure, say N. config PATA_OLDPIIX - tristate "Intel PATA old PIIX support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "Intel PATA old PIIX support" + depends on PCI help - This option enables support for old(?) PIIX PATA support. + This option enables support for early PIIX PATA support. If unsure, say N. @@ -431,7 +449,7 @@ config PATA_PCMCIA If unsure, say N. config PATA_PDC_OLD - tristate "Older Promise PATA controller support (Very Experimental)" + tristate "Older Promise PATA controller support (Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the Promise 20246, 20262, 20263, @@ -446,7 +464,7 @@ config PATA_QDI Support for QDI 6500 and 6580 PATA controllers on VESA local bus. config PATA_RADISYS - tristate "RADISYS 82600 PATA support (Very experimental)" + tristate "RADISYS 82600 PATA support (Very Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the RADISYS 82600 @@ -464,7 +482,7 @@ config PATA_RZ1000 If unsure, say N. config PATA_SC1200 - tristate "SC1200 PATA support (Raving Lunatic)" + tristate "SC1200 PATA support (Very Experimental)" depends on PCI && EXPERIMENTAL help This option enables support for the NatSemi/AMD SC1200 SoC @@ -473,8 +491,8 @@ config PATA_SC1200 If unsure, say N. config PATA_SERVERWORKS - tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support (Experimental)" - depends on PCI && EXPERIMENTAL + tristate "SERVERWORKS OSB4/CSB5/CSB6/HT1000 PATA support" + depends on PCI help This option enables support for the Serverworks OSB4/CSB5/CSB6 and HT1000 PATA controllers, via the new ATA layer. @@ -549,6 +567,15 @@ config PATA_IXP4XX_CF If unsure, say N. +config PATA_SCC + tristate "Toshiba's Cell Reference Set IDE support" + depends on PCI && PPC_CELLEB + help + This option enables support for the built-in IDE controller on + Toshiba Cell Reference Board. + + If unsure, say N. + endif endmenu diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index cd096f0..b7055e3 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_PATA_ALI) += pata_ali.o obj-$(CONFIG_PATA_AMD) += pata_amd.o obj-$(CONFIG_PATA_ARTOP) += pata_artop.o obj-$(CONFIG_PATA_ATIIXP) += pata_atiixp.o +obj-$(CONFIG_PATA_CMD640_PCI) += pata_cmd640.o obj-$(CONFIG_PATA_CMD64X) += pata_cmd64x.o obj-$(CONFIG_PATA_CS5520) += pata_cs5520.o obj-$(CONFIG_PATA_CS5530) += pata_cs5530.o @@ -59,6 +60,7 @@ obj-$(CONFIG_PATA_WINBOND_VLB) += pata_winbond.o obj-$(CONFIG_PATA_SIS) += pata_sis.o obj-$(CONFIG_PATA_TRIFLEX) += pata_triflex.o obj-$(CONFIG_PATA_IXP4XX_CF) += pata_ixp4xx_cf.o +obj-$(CONFIG_PATA_SCC) += pata_scc.o obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o # Should be last but one libata driver obj-$(CONFIG_ATA_GENERIC) += ata_generic.o @@ -66,4 +68,4 @@ obj-$(CONFIG_ATA_GENERIC) += ata_generic.o obj-$(CONFIG_PATA_LEGACY) += pata_legacy.o libata-objs := libata-core.o libata-scsi.o libata-sff.o libata-eh.o - +libata-$(CONFIG_SATA_ACPI) += libata-acpi.o diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index 6a3543e..34c5534 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -46,7 +46,7 @@ #include <linux/libata.h> #define DRV_NAME "ahci" -#define DRV_VERSION "2.0" +#define DRV_VERSION "2.1" enum { @@ -80,6 +80,7 @@ enum { board_ahci_pi = 1, board_ahci_vt8251 = 2, board_ahci_ign_iferr = 3, + board_ahci_sb600 = 4, /* global controller registers */ HOST_CAP = 0x00, /* host capabilities */ @@ -168,6 +169,11 @@ enum { AHCI_FLAG_NO_NCQ = (1 << 24), AHCI_FLAG_IGN_IRQ_IF_ERR = (1 << 25), /* ignore IRQ_IF_ERR */ AHCI_FLAG_HONOR_PI = (1 << 26), /* honor PORTS_IMPL */ + AHCI_FLAG_IGN_SERR_INTERNAL = (1 << 27), /* ignore SERR_INTERNAL */ + + AHCI_FLAG_COMMON = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | + ATA_FLAG_SKIP_D2H_BSY, }; struct ahci_cmd_hdr { @@ -186,8 +192,10 @@ struct ahci_sg { }; struct ahci_host_priv { - u32 cap; /* cache of HOST_CAP register */ - u32 port_map; /* cache of HOST_PORTS_IMPL reg */ + u32 cap; /* cap to use */ + u32 port_map; /* port map to use */ + u32 saved_cap; /* saved initial cap */ + u32 saved_port_map; /* saved initial port_map */ }; struct ahci_port_priv { @@ -198,16 +206,15 @@ struct ahci_port_priv { void *rx_fis; dma_addr_t rx_fis_dma; /* for NCQ spurious interrupt analysis */ - int ncq_saw_spurious_sdb_cnt; unsigned int ncq_saw_d2h:1; unsigned int ncq_saw_dmas:1; + unsigned int ncq_saw_sdb:1; }; static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg); static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t ahci_interrupt (int irq, void *dev_instance); static void ahci_irq_clear(struct ata_port *ap); static int ahci_port_start(struct ata_port *ap); static void ahci_port_stop(struct ata_port *ap); @@ -219,10 +226,12 @@ static void ahci_thaw(struct ata_port *ap); static void ahci_error_handler(struct ata_port *ap); static void ahci_vt8251_error_handler(struct ata_port *ap); static void ahci_post_internal_cmd(struct ata_queued_cmd *qc); +#ifdef CONFIG_PM static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg); static int ahci_port_resume(struct ata_port *ap); static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg); static int ahci_pci_device_resume(struct pci_dev *pdev); +#endif static struct scsi_host_template ahci_sht = { .module = THIS_MODULE, @@ -241,8 +250,10 @@ static struct scsi_host_template ahci_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static const struct ata_port_operations ahci_ops = { @@ -257,7 +268,6 @@ static const struct ata_port_operations ahci_ops = { .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, - .irq_handler = ahci_interrupt, .irq_clear = ahci_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -271,8 +281,10 @@ static const struct ata_port_operations ahci_ops = { .error_handler = ahci_error_handler, .post_internal_cmd = ahci_post_internal_cmd, +#ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume, +#endif .port_start = ahci_port_start, .port_stop = ahci_port_stop, @@ -290,7 +302,6 @@ static const struct ata_port_operations ahci_vt8251_ops = { .qc_prep = ahci_qc_prep, .qc_issue = ahci_qc_issue, - .irq_handler = ahci_interrupt, .irq_clear = ahci_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -304,8 +315,10 @@ static const struct ata_port_operations ahci_vt8251_ops = { .error_handler = ahci_vt8251_error_handler, .post_internal_cmd = ahci_post_internal_cmd, +#ifdef CONFIG_PM .port_suspend = ahci_port_suspend, .port_resume = ahci_port_resume, +#endif .port_start = ahci_port_start, .port_stop = ahci_port_stop, @@ -314,42 +327,37 @@ static const struct ata_port_operations ahci_vt8251_ops = { static const struct ata_port_info ahci_port_info[] = { /* board_ahci */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY, + .flags = AHCI_FLAG_COMMON, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, /* board_ahci_pi */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | AHCI_FLAG_HONOR_PI, + .flags = AHCI_FLAG_COMMON | AHCI_FLAG_HONOR_PI, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, }, /* board_ahci_vt8251 */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - ATA_FLAG_HRST_TO_RESUME | AHCI_FLAG_NO_NCQ, + .flags = AHCI_FLAG_COMMON | ATA_FLAG_HRST_TO_RESUME | + AHCI_FLAG_NO_NCQ, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_vt8251_ops, }, /* board_ahci_ign_iferr */ { - .sht = &ahci_sht, - .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | ATA_FLAG_PIO_DMA | - ATA_FLAG_SKIP_D2H_BSY | - AHCI_FLAG_IGN_IRQ_IF_ERR, + .flags = AHCI_FLAG_COMMON | AHCI_FLAG_IGN_IRQ_IF_ERR, + .pio_mask = 0x1f, /* pio0-4 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &ahci_ops, + }, + /* board_ahci_sb600 */ + { + .flags = AHCI_FLAG_COMMON | + AHCI_FLAG_IGN_SERR_INTERNAL, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &ahci_ops, @@ -381,23 +389,21 @@ static const struct pci_device_id ahci_pci_tbl[] = { { PCI_VDEVICE(INTEL, 0x2929), board_ahci_pi }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292a), board_ahci_pi }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292b), board_ahci_pi }, /* ICH9M */ + { PCI_VDEVICE(INTEL, 0x292c), board_ahci_pi }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x292f), board_ahci_pi }, /* ICH9M */ { PCI_VDEVICE(INTEL, 0x294d), board_ahci_pi }, /* ICH9 */ { PCI_VDEVICE(INTEL, 0x294e), board_ahci_pi }, /* ICH9M */ - /* JMicron */ - { PCI_VDEVICE(JMICRON, 0x2360), board_ahci_ign_iferr }, /* JMB360 */ - { PCI_VDEVICE(JMICRON, 0x2361), board_ahci_ign_iferr }, /* JMB361 */ - { PCI_VDEVICE(JMICRON, 0x2363), board_ahci_ign_iferr }, /* JMB363 */ - { PCI_VDEVICE(JMICRON, 0x2365), board_ahci_ign_iferr }, /* JMB365 */ - { PCI_VDEVICE(JMICRON, 0x2366), board_ahci_ign_iferr }, /* JMB366 */ + /* JMicron 360/1/3/5/6, match class to avoid IDE function */ + { PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, + PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff, board_ahci_ign_iferr }, /* ATI */ - { PCI_VDEVICE(ATI, 0x4380), board_ahci }, /* ATI SB600 non-raid */ - { PCI_VDEVICE(ATI, 0x4381), board_ahci }, /* ATI SB600 raid */ + { PCI_VDEVICE(ATI, 0x4380), board_ahci_sb600 }, /* ATI SB600 */ /* VIA */ { PCI_VDEVICE(VIA, 0x3349), board_ahci_vt8251 }, /* VIA VT8251 */ + { PCI_VDEVICE(VIA, 0x6287), board_ahci_vt8251 }, /* VIA VT8251 */ /* NVIDIA */ { PCI_VDEVICE(NVIDIA, 0x044c), board_ahci }, /* MCP65 */ @@ -439,8 +445,10 @@ static struct pci_driver ahci_pci_driver = { .id_table = ahci_pci_tbl, .probe = ahci_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ahci_pci_device_suspend, .resume = ahci_pci_device_resume, +#endif }; @@ -449,10 +457,100 @@ static inline int ahci_nr_ports(u32 cap) return (cap & 0x1f) + 1; } -static inline void __iomem *ahci_port_base(void __iomem *base, - unsigned int port) +static inline void __iomem *ahci_port_base(struct ata_port *ap) { - return base + 0x100 + (port * 0x80); + void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; + + return mmio + 0x100 + (ap->port_no * 0x80); +} + +/** + * ahci_save_initial_config - Save and fixup initial config values + * @pdev: target PCI device + * @pi: associated ATA port info + * @hpriv: host private area to store config values + * + * Some registers containing configuration info might be setup by + * BIOS and might be cleared on reset. This function saves the + * initial values of those registers into @hpriv such that they + * can be restored after controller reset. + * + * If inconsistent, config values are fixed up by this function. + * + * LOCKING: + * None. + */ +static void ahci_save_initial_config(struct pci_dev *pdev, + const struct ata_port_info *pi, + struct ahci_host_priv *hpriv) +{ + void __iomem *mmio = pcim_iomap_table(pdev)[AHCI_PCI_BAR]; + u32 cap, port_map; + int i; + + /* Values prefixed with saved_ are written back to host after + * reset. Values without are used for driver operation. + */ + hpriv->saved_cap = cap = readl(mmio + HOST_CAP); + hpriv->saved_port_map = port_map = readl(mmio + HOST_PORTS_IMPL); + + /* fixup zero port_map */ + if (!port_map) { + port_map = (1 << ahci_nr_ports(hpriv->cap)) - 1; + dev_printk(KERN_WARNING, &pdev->dev, + "PORTS_IMPL is zero, forcing 0x%x\n", port_map); + + /* write the fixed up value to the PI register */ + hpriv->saved_port_map = port_map; + } + + /* cross check port_map and cap.n_ports */ + if (pi->flags & AHCI_FLAG_HONOR_PI) { + u32 tmp_port_map = port_map; + int n_ports = ahci_nr_ports(cap); + + for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { + if (tmp_port_map & (1 << i)) { + n_ports--; + tmp_port_map &= ~(1 << i); + } + } + + /* Whine if inconsistent. No need to update cap. + * port_map is used to determine number of ports. + */ + if (n_ports || tmp_port_map) + dev_printk(KERN_WARNING, &pdev->dev, + "nr_ports (%u) and implemented port map " + "(0x%x) don't match\n", + ahci_nr_ports(cap), port_map); + } else { + /* fabricate port_map from cap.nr_ports */ + port_map = (1 << ahci_nr_ports(cap)) - 1; + } + + /* record values to use during operation */ + hpriv->cap = cap; + hpriv->port_map = port_map; +} + +/** + * ahci_restore_initial_config - Restore initial config + * @host: target ATA host + * + * Restore initial config stored by ahci_save_initial_config(). + * + * LOCKING: + * None. + */ +static void ahci_restore_initial_config(struct ata_host *host) +{ + struct ahci_host_priv *hpriv = host->private_data; + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + + writel(hpriv->saved_cap, mmio + HOST_CAP); + writel(hpriv->saved_port_map, mmio + HOST_PORTS_IMPL); + (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ } static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in) @@ -489,8 +587,9 @@ static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in, writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } -static void ahci_start_engine(void __iomem *port_mmio) +static void ahci_start_engine(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* start DMA */ @@ -500,8 +599,9 @@ static void ahci_start_engine(void __iomem *port_mmio) readl(port_mmio + PORT_CMD); /* flush */ } -static int ahci_stop_engine(void __iomem *port_mmio) +static int ahci_stop_engine(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; tmp = readl(port_mmio + PORT_CMD); @@ -523,19 +623,23 @@ static int ahci_stop_engine(void __iomem *port_mmio) return 0; } -static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +static void ahci_start_fis_rx(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_port_priv *pp = ap->private_data; u32 tmp; /* set FIS registers */ - if (cap & HOST_CAP_64) - writel((cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI); - writel(cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); + if (hpriv->cap & HOST_CAP_64) + writel((pp->cmd_slot_dma >> 16) >> 16, + port_mmio + PORT_LST_ADDR_HI); + writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR); - if (cap & HOST_CAP_64) - writel((rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI); - writel(rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); + if (hpriv->cap & HOST_CAP_64) + writel((pp->rx_fis_dma >> 16) >> 16, + port_mmio + PORT_FIS_ADDR_HI); + writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR); /* enable FIS reception */ tmp = readl(port_mmio + PORT_CMD); @@ -546,8 +650,9 @@ static void ahci_start_fis_rx(void __iomem *port_mmio, u32 cap, readl(port_mmio + PORT_CMD); } -static int ahci_stop_fis_rx(void __iomem *port_mmio) +static int ahci_stop_fis_rx(struct ata_port *ap) { + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* disable FIS reception */ @@ -564,14 +669,16 @@ static int ahci_stop_fis_rx(void __iomem *port_mmio) return 0; } -static void ahci_power_up(void __iomem *port_mmio, u32 cap) +static void ahci_power_up(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); u32 cmd; cmd = readl(port_mmio + PORT_CMD) & ~PORT_CMD_ICC_MASK; /* spin up device */ - if (cap & HOST_CAP_SSS) { + if (hpriv->cap & HOST_CAP_SSS) { cmd |= PORT_CMD_SPIN_UP; writel(cmd, port_mmio + PORT_CMD); } @@ -580,11 +687,14 @@ static void ahci_power_up(void __iomem *port_mmio, u32 cap) writel(cmd | PORT_CMD_ICC_ACTIVE, port_mmio + PORT_CMD); } -static void ahci_power_down(void __iomem *port_mmio, u32 cap) +#ifdef CONFIG_PM +static void ahci_power_down(struct ata_port *ap) { + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); u32 cmd, scontrol; - if (!(cap & HOST_CAP_SSS)) + if (!(hpriv->cap & HOST_CAP_SSS)) return; /* put device into listen mode, first set PxSCTL.DET to 0 */ @@ -597,30 +707,30 @@ static void ahci_power_down(void __iomem *port_mmio, u32 cap) cmd &= ~PORT_CMD_SPIN_UP; writel(cmd, port_mmio + PORT_CMD); } +#endif -static void ahci_init_port(void __iomem *port_mmio, u32 cap, - dma_addr_t cmd_slot_dma, dma_addr_t rx_fis_dma) +static void ahci_init_port(struct ata_port *ap) { /* enable FIS reception */ - ahci_start_fis_rx(port_mmio, cap, cmd_slot_dma, rx_fis_dma); + ahci_start_fis_rx(ap); /* enable DMA */ - ahci_start_engine(port_mmio); + ahci_start_engine(ap); } -static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) +static int ahci_deinit_port(struct ata_port *ap, const char **emsg) { int rc; /* disable DMA */ - rc = ahci_stop_engine(port_mmio); + rc = ahci_stop_engine(ap); if (rc) { *emsg = "failed to stop engine"; return rc; } /* disable FIS reception */ - rc = ahci_stop_fis_rx(port_mmio); + rc = ahci_stop_fis_rx(ap); if (rc) { *emsg = "failed stop FIS RX"; return rc; @@ -629,12 +739,11 @@ static int ahci_deinit_port(void __iomem *port_mmio, u32 cap, const char **emsg) return 0; } -static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) +static int ahci_reset_controller(struct ata_host *host) { - u32 cap_save, impl_save, tmp; - - cap_save = readl(mmio + HOST_CAP); - impl_save = readl(mmio + HOST_PORTS_IMPL); + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; + u32 tmp; /* global controller reset */ tmp = readl(mmio + HOST_CTL); @@ -650,7 +759,7 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) tmp = readl(mmio + HOST_CTL); if (tmp & HOST_RESET) { - dev_printk(KERN_ERR, &pdev->dev, + dev_printk(KERN_ERR, host->dev, "controller reset failed (0x%x)\n", tmp); return -EIO; } @@ -659,18 +768,8 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) writel(HOST_AHCI_EN, mmio + HOST_CTL); (void) readl(mmio + HOST_CTL); /* flush */ - /* These write-once registers are normally cleared on reset. - * Restore BIOS values... which we HOPE were present before - * reset. - */ - if (!impl_save) { - impl_save = (1 << ahci_nr_ports(cap_save)) - 1; - dev_printk(KERN_WARNING, &pdev->dev, - "PORTS_IMPL is zero, forcing 0x%x\n", impl_save); - } - writel(cap_save, mmio + HOST_CAP); - writel(impl_save, mmio + HOST_PORTS_IMPL); - (void) readl(mmio + HOST_PORTS_IMPL); /* flush */ + /* some registers might be cleared on reset. restore initial values */ + ahci_restore_initial_config(host); if (pdev->vendor == PCI_VENDOR_ID_INTEL) { u16 tmp16; @@ -684,23 +783,23 @@ static int ahci_reset_controller(void __iomem *mmio, struct pci_dev *pdev) return 0; } -static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, - int n_ports, unsigned int port_flags, - struct ahci_host_priv *hpriv) +static void ahci_init_controller(struct ata_host *host) { + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; int i, rc; u32 tmp; - for (i = 0; i < n_ports; i++) { - void __iomem *port_mmio = ahci_port_base(mmio, i); + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *port_mmio = ahci_port_base(ap); const char *emsg = NULL; - if ((port_flags & AHCI_FLAG_HONOR_PI) && - !(hpriv->port_map & (1 << i))) + if (ata_port_is_dummy(ap)) continue; /* make sure port is not active */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc) dev_printk(KERN_WARNING, &pdev->dev, "%s (%d)\n", emsg, rc); @@ -728,7 +827,7 @@ static void ahci_init_controller(void __iomem *mmio, struct pci_dev *pdev, static unsigned int ahci_dev_classify(struct ata_port *ap) { - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); struct ata_taskfile tf; u32 tmp; @@ -778,8 +877,7 @@ static int ahci_clo(struct ata_port *ap) static int ahci_softreset(struct ata_port *ap, unsigned int *class) { struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); const u32 cmd_fis_len = 5; /* five dwords */ const char *reason = NULL; struct ata_taskfile tf; @@ -796,7 +894,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) } /* prepare for SRST (AHCI-1.1 10.4.1) */ - rc = ahci_stop_engine(port_mmio); + rc = ahci_stop_engine(ap); if (rc) { reason = "failed to stop engine"; goto fail_restart; @@ -816,7 +914,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) } /* restart engine */ - ahci_start_engine(port_mmio); + ahci_start_engine(ap); ata_tf_init(ap->device, &tf); fis = pp->cmd_tbl; @@ -875,7 +973,7 @@ static int ahci_softreset(struct ata_port *ap, unsigned int *class) return 0; fail_restart: - ahci_start_engine(port_mmio); + ahci_start_engine(ap); fail: ata_port_printk(ap, KERN_ERR, "softreset failed (%s)\n", reason); return rc; @@ -886,13 +984,11 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) struct ahci_port_priv *pp = ap->private_data; u8 *d2h_fis = pp->rx_fis + RX_FIS_D2H_REG; struct ata_taskfile tf; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(port_mmio); + ahci_stop_engine(ap); /* clear D2H reception area to properly wait for D2H FIS */ ata_tf_init(ap->device, &tf); @@ -901,7 +997,7 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) rc = sata_std_hardreset(ap, class); - ahci_start_engine(port_mmio); + ahci_start_engine(ap); if (rc == 0 && ata_port_online(ap)) *class = ahci_dev_classify(ap); @@ -914,20 +1010,18 @@ static int ahci_hardreset(struct ata_port *ap, unsigned int *class) static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); int rc; DPRINTK("ENTER\n"); - ahci_stop_engine(port_mmio); + ahci_stop_engine(ap); rc = sata_port_hardreset(ap, sata_ehc_deb_timing(&ap->eh_context)); /* vt8251 needs SError cleared for the port to operate */ ahci_scr_write(ap, SCR_ERROR, ahci_scr_read(ap, SCR_ERROR)); - ahci_start_engine(port_mmio); + ahci_start_engine(ap); DPRINTK("EXIT, rc=%d, class=%u\n", rc, *class); @@ -939,7 +1033,7 @@ static int ahci_vt8251_hardreset(struct ata_port *ap, unsigned int *class) static void ahci_postreset(struct ata_port *ap, unsigned int *class) { - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); u32 new_tmp, tmp; ata_std_postreset(ap, class); @@ -1057,8 +1151,11 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) if (ap->flags & AHCI_FLAG_IGN_IRQ_IF_ERR) irq_stat &= ~PORT_IRQ_IF_ERR; - if (irq_stat & PORT_IRQ_TF_ERR) + if (irq_stat & PORT_IRQ_TF_ERR) { err_mask |= AC_ERR_DEV; + if (ap->flags & AHCI_FLAG_IGN_SERR_INTERNAL) + serror &= ~SERR_INTERNAL; + } if (irq_stat & (PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR)) { err_mask |= AC_ERR_HOST_BUS; @@ -1104,8 +1201,7 @@ static void ahci_error_intr(struct ata_port *ap, u32 irq_stat) static void ahci_host_intr(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ap->ioaddr.cmd_addr; struct ata_eh_info *ehi = &ap->eh_info; struct ahci_port_priv *pp = ap->private_data; u32 status, qc_active; @@ -1160,23 +1256,32 @@ static void ahci_host_intr(struct ata_port *ap) known_irq = 1; } - if (status & PORT_IRQ_SDB_FIS && - pp->ncq_saw_spurious_sdb_cnt < 10) { - /* SDB FIS containing spurious completions might be - * dangerous, we need to know more about them. Print - * more of it. - */ + if (status & PORT_IRQ_SDB_FIS) { const __le32 *f = pp->rx_fis + RX_FIS_SDB; - ata_port_printk(ap, KERN_INFO, "Spurious SDB FIS during NCQ " - "issue=0x%x SAct=0x%x FIS=%08x:%08x%s\n", + if (le32_to_cpu(f[1])) { + /* SDB FIS containing spurious completions + * might be dangerous, whine and fail commands + * with HSM violation. EH will turn off NCQ + * after several such failures. + */ + ata_ehi_push_desc(ehi, + "spurious completions during NCQ " + "issue=0x%x SAct=0x%x FIS=%08x:%08x", readl(port_mmio + PORT_CMD_ISSUE), readl(port_mmio + PORT_SCR_ACT), - le32_to_cpu(f[0]), le32_to_cpu(f[1]), - pp->ncq_saw_spurious_sdb_cnt < 10 ? - "" : ", shutting up"); - - pp->ncq_saw_spurious_sdb_cnt++; + le32_to_cpu(f[0]), le32_to_cpu(f[1])); + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_SOFTRESET; + ata_port_freeze(ap); + } else { + if (!pp->ncq_saw_sdb) + ata_port_printk(ap, KERN_INFO, + "spurious SDB FIS %08x:%08x during NCQ, " + "this message won't be printed again\n", + le32_to_cpu(f[0]), le32_to_cpu(f[1])); + pp->ncq_saw_sdb = 1; + } known_irq = 1; } @@ -1247,7 +1352,7 @@ static irqreturn_t ahci_interrupt(int irq, void *dev_instance) static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *port_mmio = ap->ioaddr.cmd_addr; + void __iomem *port_mmio = ahci_port_base(ap); if (qc->tf.protocol == ATA_PROT_NCQ) writel(1 << qc->tag, port_mmio + PORT_SCR_ACT); @@ -1259,8 +1364,7 @@ static unsigned int ahci_qc_issue(struct ata_queued_cmd *qc) static void ahci_freeze(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); /* turn IRQ off */ writel(0, port_mmio + PORT_IRQ_MASK); @@ -1269,7 +1373,7 @@ static void ahci_freeze(struct ata_port *ap) static void ahci_thaw(struct ata_port *ap) { void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); + void __iomem *port_mmio = ahci_port_base(ap); u32 tmp; /* clear IRQ */ @@ -1283,13 +1387,10 @@ static void ahci_thaw(struct ata_port *ap) static void ahci_error_handler(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } /* perform recovery */ @@ -1299,13 +1400,10 @@ static void ahci_error_handler(struct ata_port *ap) static void ahci_vt8251_error_handler(struct ata_port *ap) { - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (!(ap->pflags & ATA_PFLAG_FROZEN)) { /* restart engine */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } /* perform recovery */ @@ -1316,35 +1414,26 @@ static void ahci_vt8251_error_handler(struct ata_port *ap) static void ahci_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - - if (qc->err_mask) { + if (qc->flags & ATA_QCFLAG_FAILED) { /* make DMA engine forget about the failed command */ - ahci_stop_engine(port_mmio); - ahci_start_engine(port_mmio); + ahci_stop_engine(ap); + ahci_start_engine(ap); } } +#ifdef CONFIG_PM static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) { - struct ahci_host_priv *hpriv = ap->host->private_data; - struct ahci_port_priv *pp = ap->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); const char *emsg = NULL; int rc; - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc == 0) - ahci_power_down(port_mmio, hpriv->cap); + ahci_power_down(ap); else { ata_port_printk(ap, KERN_ERR, "%s (%d)\n", emsg, rc); - ahci_init_port(port_mmio, hpriv->cap, - pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_init_port(ap); } return rc; @@ -1352,13 +1441,8 @@ static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg) static int ahci_port_resume(struct ata_port *ap) { - struct ahci_port_priv *pp = ap->private_data; - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); - - ahci_power_up(port_mmio, hpriv->cap); - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_power_up(ap); + ahci_init_port(ap); return 0; } @@ -1386,8 +1470,6 @@ static int ahci_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) static int ahci_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); - struct ahci_host_priv *hpriv = host->private_data; - void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; int rc; rc = ata_pci_device_do_resume(pdev); @@ -1395,26 +1477,23 @@ static int ahci_pci_device_resume(struct pci_dev *pdev) return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - rc = ahci_reset_controller(mmio, pdev); + rc = ahci_reset_controller(host); if (rc) return rc; - ahci_init_controller(mmio, pdev, host->n_ports, - host->ports[0]->flags, hpriv); + ahci_init_controller(host); } ata_host_resume(host); return 0; } +#endif static int ahci_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; - struct ahci_host_priv *hpriv = ap->host->private_data; struct ahci_port_priv *pp; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); void *mem; dma_addr_t mem_dma; int rc; @@ -1462,85 +1541,29 @@ static int ahci_port_start(struct ata_port *ap) ap->private_data = pp; /* power up port */ - ahci_power_up(port_mmio, hpriv->cap); + ahci_power_up(ap); /* initialize port */ - ahci_init_port(port_mmio, hpriv->cap, pp->cmd_slot_dma, pp->rx_fis_dma); + ahci_init_port(ap); return 0; } static void ahci_port_stop(struct ata_port *ap) { - struct ahci_host_priv *hpriv = ap->host->private_data; - void __iomem *mmio = ap->host->iomap[AHCI_PCI_BAR]; - void __iomem *port_mmio = ahci_port_base(mmio, ap->port_no); const char *emsg = NULL; int rc; /* de-initialize port */ - rc = ahci_deinit_port(port_mmio, hpriv->cap, &emsg); + rc = ahci_deinit_port(ap, &emsg); if (rc) ata_port_printk(ap, KERN_WARNING, "%s (%d)\n", emsg, rc); } -static void ahci_setup_port(struct ata_ioports *port, void __iomem *base, - unsigned int port_idx) -{ - VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx); - base = ahci_port_base(base, port_idx); - VPRINTK("base now==0x%lx\n", base); - - port->cmd_addr = base; - port->scr_addr = base + PORT_SCR; - - VPRINTK("EXIT\n"); -} - -static int ahci_host_init(struct ata_probe_ent *probe_ent) +static int ahci_configure_dma_masks(struct pci_dev *pdev, int using_dac) { - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR]; - unsigned int i, cap_n_ports, using_dac; int rc; - rc = ahci_reset_controller(mmio, pdev); - if (rc) - return rc; - - hpriv->cap = readl(mmio + HOST_CAP); - hpriv->port_map = readl(mmio + HOST_PORTS_IMPL); - cap_n_ports = ahci_nr_ports(hpriv->cap); - - VPRINTK("cap 0x%x port_map 0x%x n_ports %d\n", - hpriv->cap, hpriv->port_map, cap_n_ports); - - if (probe_ent->port_flags & AHCI_FLAG_HONOR_PI) { - unsigned int n_ports = cap_n_ports; - u32 port_map = hpriv->port_map; - int max_port = 0; - - for (i = 0; i < AHCI_MAX_PORTS && n_ports; i++) { - if (port_map & (1 << i)) { - n_ports--; - port_map &= ~(1 << i); - max_port = i; - } else - probe_ent->dummy_port_mask |= 1 << i; - } - - if (n_ports || port_map) - dev_printk(KERN_WARNING, &pdev->dev, - "nr_ports (%u) and implemented port map " - "(0x%x) don't match\n", - cap_n_ports, hpriv->port_map); - - probe_ent->n_ports = max_port + 1; - } else - probe_ent->n_ports = cap_n_ports; - - using_dac = hpriv->cap & HOST_CAP_64; if (using_dac && !pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); @@ -1566,23 +1589,14 @@ static int ahci_host_init(struct ata_probe_ent *probe_ent) return rc; } } - - for (i = 0; i < probe_ent->n_ports; i++) - ahci_setup_port(&probe_ent->port[i], mmio, i); - - ahci_init_controller(mmio, pdev, probe_ent->n_ports, - probe_ent->port_flags, hpriv); - - pci_set_master(pdev); - return 0; } -static void ahci_print_info(struct ata_probe_ent *probe_ent) +static void ahci_print_info(struct ata_host *host) { - struct ahci_host_priv *hpriv = probe_ent->private_data; - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - void __iomem *mmio = probe_ent->iomap[AHCI_PCI_BAR]; + struct ahci_host_priv *hpriv = host->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[AHCI_PCI_BAR]; u32 vers, cap, impl, speed; const char *speed_s; u16 cc; @@ -1652,11 +1666,12 @@ static void ahci_print_info(struct ata_probe_ent *probe_ent) static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - unsigned int board_idx = (unsigned int) ent->driver_data; + struct ata_port_info pi = ahci_port_info[ent->driver_data]; + const struct ata_port_info *ppi[] = { &pi, NULL }; struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; struct ahci_host_priv *hpriv; - int rc; + struct ata_host *host; + int i, rc; VPRINTK("ENTER\n"); @@ -1665,13 +1680,7 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - if (pdev->vendor == PCI_VENDOR_ID_JMICRON) { - /* Function 1 is the PATA controller except on the 368, where - we are not AHCI anyway */ - if (PCI_FUNC(pdev->devfn)) - return -ENODEV; - } - + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1685,44 +1694,49 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (pci_enable_msi(pdev)) pci_intx(pdev, 1); - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; - probe_ent->sht = ahci_port_info[board_idx].sht; - probe_ent->port_flags = ahci_port_info[board_idx].flags; - probe_ent->pio_mask = ahci_port_info[board_idx].pio_mask; - probe_ent->udma_mask = ahci_port_info[board_idx].udma_mask; - probe_ent->port_ops = ahci_port_info[board_idx].port_ops; + /* save initial config */ + ahci_save_initial_config(pdev, &pi, hpriv); - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - probe_ent->private_data = hpriv; + /* prepare host */ + if (!(pi.flags & AHCI_FLAG_NO_NCQ) && (hpriv->cap & HOST_CAP_NCQ)) + pi.flags |= ATA_FLAG_NCQ; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, fls(hpriv->port_map)); + if (!host) + return -ENOMEM; + host->iomap = pcim_iomap_table(pdev); + host->private_data = hpriv; + + for (i = 0; i < host->n_ports; i++) { + if (hpriv->port_map & (1 << i)) { + struct ata_port *ap = host->ports[i]; + void __iomem *port_mmio = ahci_port_base(ap); + + ap->ioaddr.cmd_addr = port_mmio; + ap->ioaddr.scr_addr = port_mmio + PORT_SCR; + } else + host->ports[i]->ops = &ata_dummy_port_ops; + } /* initialize adapter */ - rc = ahci_host_init(probe_ent); + rc = ahci_configure_dma_masks(pdev, hpriv->cap & HOST_CAP_64); if (rc) return rc; - if (!(probe_ent->port_flags & AHCI_FLAG_NO_NCQ) && - (hpriv->cap & HOST_CAP_NCQ)) - probe_ent->port_flags |= ATA_FLAG_NCQ; - - ahci_print_info(probe_ent); + rc = ahci_reset_controller(host); + if (rc) + return rc; - if (!ata_device_add(probe_ent)) - return -ENODEV; + ahci_init_controller(host); + ahci_print_info(host); - devm_kfree(dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ahci_interrupt, IRQF_SHARED, + &ahci_sht); } static int __init ahci_init(void) diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c index be66ea0..92a491d 100644 --- a/drivers/ata/ata_generic.c +++ b/drivers/ata/ata_generic.c @@ -26,42 +26,13 @@ #include <linux/libata.h> #define DRV_NAME "ata_generic" -#define DRV_VERSION "0.2.10" +#define DRV_VERSION "0.2.11" /* * A generic parallel ATA driver using libata */ /** - * generic_pre_reset - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int generic_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - - -/** - * generic_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - - -static void generic_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, generic_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * generic_set_mode - mode setting * @ap: interface to set up * @unused: returned device on error @@ -90,10 +61,10 @@ static int generic_set_mode(struct ata_port *ap, struct ata_device **unused) /* We do need the right mode information for DMA or PIO and this comes from the current configuration flags */ if (dma_enabled & (1 << (5 + i))) { - dev->xfer_mode = XFER_MW_DMA_0; - dev->xfer_shift = ATA_SHIFT_MWDMA; + ata_id_to_dma_mode(dev, XFER_MW_DMA_0); dev->flags &= ~ATA_DFLAG_PIO; } else { + ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); dev->xfer_mode = XFER_PIO_0; dev->xfer_shift = ATA_SHIFT_PIO; dev->flags |= ATA_DFLAG_PIO; @@ -119,8 +90,10 @@ static struct scsi_host_template generic_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations generic_port_ops = { @@ -142,8 +115,9 @@ static struct ata_port_operations generic_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = generic_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -230,8 +204,10 @@ static struct pci_driver ata_generic_pci_driver = { .id_table = ata_generic, .probe = ata_generic_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init ata_generic_init(void) diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c index 4d716c7..55d306a 100644 --- a/drivers/ata/ata_piix.c +++ b/drivers/ata/ata_piix.c @@ -93,7 +93,7 @@ #include <linux/libata.h> #define DRV_NAME "ata_piix" -#define DRV_VERSION "2.00ac7" +#define DRV_VERSION "2.11" enum { PIIX_IOCFG = 0x54, /* IDE I/O configuration register */ @@ -155,11 +155,11 @@ struct piix_host_priv { static int piix_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static void piix_pata_error_handler(struct ata_port *ap); -static void ich_pata_error_handler(struct ata_port *ap); static void piix_sata_error_handler(struct ata_port *ap); static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev); static void piix_set_dmamode (struct ata_port *ap, struct ata_device *adev); static void ich_set_dmamode (struct ata_port *ap, struct ata_device *adev); +static int ich_pata_cable_detect(struct ata_port *ap); static unsigned int in_module_init = 1; @@ -169,8 +169,6 @@ static const struct pci_device_id piix_pci_tbl[] = { /* Intel PIIX4 for the 430TX/440BX/MX chipset: UDMA 33 */ /* Also PIIX4E (fn3 rev 2) and PIIX4M (fn3 rev 3) */ { 0x8086, 0x7111, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix_pata_33 }, - { 0x8086, 0x24db, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 }, - { 0x8086, 0x25a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ich_pata_100 }, /* Intel PIIX4 */ { 0x8086, 0x7199, PCI_ANY_ID, PCI_ANY_ID, 0, 0, piix_pata_33 }, /* Intel PIIX4 */ @@ -255,8 +253,10 @@ static struct pci_driver piix_pci_driver = { .id_table = piix_pci_tbl, .probe = piix_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static struct scsi_host_template piix_sht = { @@ -275,8 +275,10 @@ static struct scsi_host_template piix_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations piix_pata_ops = { @@ -303,6 +305,7 @@ static const struct ata_port_operations piix_pata_ops = { .thaw = ata_bmdma_thaw, .error_handler = piix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, @@ -334,8 +337,9 @@ static const struct ata_port_operations ich_pata_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ich_pata_error_handler, + .error_handler = piix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ich_pata_cable_detect, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, @@ -578,12 +582,13 @@ static const struct ich_laptop ich_laptop[] = { /* devid, subvendor, subdev */ { 0x27DF, 0x0005, 0x0280 }, /* ICH7 on Acer 5602WLMi */ { 0x27DF, 0x1025, 0x0110 }, /* ICH7 on Acer 3682WLMi */ + { 0x27DF, 0x1043, 0x1267 }, /* ICH7 on Asus W5F */ /* end marker */ { 0, } }; /** - * piix_pata_cbl_detect - Probe host controller cable detect info + * ich_pata_cable_detect - Probe host controller cable detect info * @ap: Port for which cable detect info is desired * * Read 80c cable indicator from ATA PCI device's PCI config @@ -593,23 +598,18 @@ static const struct ich_laptop ich_laptop[] = { * None (inherited from caller). */ -static void ich_pata_cbl_detect(struct ata_port *ap) +static int ich_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); const struct ich_laptop *lap = &ich_laptop[0]; u8 tmp, mask; - /* no 80c support in host controller? */ - if ((ap->udma_mask & ~ATA_UDMA_MASK_40C) == 0) - goto cbl40; - /* Check for specials - Acer Aspire 5602WLMi */ while (lap->device) { if (lap->device == pdev->device && lap->subvendor == pdev->subsystem_vendor && lap->subdevice == pdev->subsystem_device) { - ap->cbl = ATA_CBL_PATA40_SHORT; - return; + return ATA_CBL_PATA40_SHORT; } lap++; } @@ -618,20 +618,14 @@ static void ich_pata_cbl_detect(struct ata_port *ap) mask = ap->port_no == 0 ? PIIX_80C_PRI : PIIX_80C_SEC; pci_read_config_byte(pdev, PIIX_IOCFG, &tmp); if ((tmp & mask) == 0) - goto cbl40; - - ap->cbl = ATA_CBL_PATA80; - return; - -cbl40: - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } /** * piix_pata_prereset - prereset for PATA host controller * @ap: Target port * - * * LOCKING: * None (inherited from caller). */ @@ -641,8 +635,6 @@ static int piix_pata_prereset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -653,35 +645,6 @@ static void piix_pata_error_handler(struct ata_port *ap) } -/** - * ich_pata_prereset - prereset for PATA host controller - * @ap: Target port - * - * - * LOCKING: - * None (inherited from caller). - */ -static int ich_pata_prereset(struct ata_port *ap) -{ - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - - if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) { - ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n"); - ap->eh_context.i.action &= ~ATA_EH_RESET_MASK; - return 0; - } - - ich_pata_cbl_detect(ap); - - return ata_std_prereset(ap); -} - -static void ich_pata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, ich_pata_prereset, ata_std_softreset, NULL, - ata_std_postreset); -} - static void piix_sata_error_handler(struct ata_port *ap) { ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, NULL, diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c new file mode 100644 index 0000000..03a0acf --- /dev/null +++ b/drivers/ata/libata-acpi.c @@ -0,0 +1,709 @@ +/* + * libata-acpi.c + * Provides ACPI support for PATA/SATA. + * + * Copyright (C) 2006 Intel Corp. + * Copyright (C) 2006 Randy Dunlap + */ + +#include <linux/ata.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/acpi.h> +#include <linux/libata.h> +#include <linux/pci.h> +#include "libata.h" + +#include <acpi/acpi_bus.h> +#include <acpi/acnames.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> +#include <acpi/acexcep.h> +#include <acpi/acmacros.h> +#include <acpi/actypes.h> + +#define SATA_ROOT_PORT(x) (((x) >> 16) & 0xffff) +#define SATA_PORT_NUMBER(x) ((x) & 0xffff) /* or NO_PORT_MULT */ +#define NO_PORT_MULT 0xffff +#define SATA_ADR_RSVD 0xffffffff + +#define REGS_PER_GTF 7 +struct taskfile_array { + u8 tfa[REGS_PER_GTF]; /* regs. 0x1f1 - 0x1f7 */ +}; + +/* + * Helper - belongs in the PCI layer somewhere eventually + */ +static int is_pci_dev(struct device *dev) +{ + return (dev->bus == &pci_bus_type); +} + +/** + * sata_get_dev_handle - finds acpi_handle and PCI device.function + * @dev: device to locate + * @handle: returned acpi_handle for @dev + * @pcidevfn: return PCI device.func for @dev + * + * This function is somewhat SATA-specific. Or at least the + * PATA & SATA versions of this function are different, + * so it's not entirely generic code. + * + * Returns 0 on success, <0 on error. + */ +static int sata_get_dev_handle(struct device *dev, acpi_handle *handle, + acpi_integer *pcidevfn) +{ + struct pci_dev *pci_dev; + acpi_integer addr; + + if (!is_pci_dev(dev)) + return -ENODEV; + + pci_dev = to_pci_dev(dev); /* NOTE: PCI-specific */ + /* Please refer to the ACPI spec for the syntax of _ADR. */ + addr = (PCI_SLOT(pci_dev->devfn) << 16) | PCI_FUNC(pci_dev->devfn); + *pcidevfn = addr; + *handle = acpi_get_child(DEVICE_ACPI_HANDLE(dev->parent), addr); + if (!*handle) + return -ENODEV; + return 0; +} + +/** + * pata_get_dev_handle - finds acpi_handle and PCI device.function + * @dev: device to locate + * @handle: returned acpi_handle for @dev + * @pcidevfn: return PCI device.func for @dev + * + * The PATA and SATA versions of this function are different. + * + * Returns 0 on success, <0 on error. + */ +static int pata_get_dev_handle(struct device *dev, acpi_handle *handle, + acpi_integer *pcidevfn) +{ + unsigned int bus, devnum, func; + acpi_integer addr; + acpi_handle dev_handle, parent_handle; + struct acpi_buffer buffer = {.length = ACPI_ALLOCATE_BUFFER, + .pointer = NULL}; + acpi_status status; + struct acpi_device_info *dinfo = NULL; + int ret = -ENODEV; + struct pci_dev *pdev; + + if (!is_pci_dev(dev)) + return -ENODEV; + + pdev = to_pci_dev(dev); + + bus = pdev->bus->number; + devnum = PCI_SLOT(pdev->devfn); + func = PCI_FUNC(pdev->devfn); + + dev_handle = DEVICE_ACPI_HANDLE(dev); + parent_handle = DEVICE_ACPI_HANDLE(dev->parent); + + status = acpi_get_object_info(parent_handle, &buffer); + if (ACPI_FAILURE(status)) + goto err; + + dinfo = buffer.pointer; + if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && + dinfo->address == bus) { + /* ACPI spec for _ADR for PCI bus: */ + addr = (acpi_integer)(devnum << 16 | func); + *pcidevfn = addr; + *handle = dev_handle; + } else { + goto err; + } + + if (!*handle) + goto err; + ret = 0; +err: + kfree(dinfo); + return ret; +} + +struct walk_info { /* can be trimmed some */ + struct device *dev; + struct acpi_device *adev; + acpi_handle handle; + acpi_integer pcidevfn; + unsigned int drivenum; + acpi_handle obj_handle; + struct ata_port *ataport; + struct ata_device *atadev; + u32 sata_adr; + int status; + char basepath[ACPI_PATHNAME_MAX]; + int basepath_len; +}; + +static acpi_status get_devices(acpi_handle handle, + u32 level, void *context, void **return_value) +{ + acpi_status status; + struct walk_info *winfo = context; + struct acpi_buffer namebuf = {ACPI_ALLOCATE_BUFFER, NULL}; + char *pathname; + struct acpi_buffer buffer; + struct acpi_device_info *dinfo; + + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &namebuf); + if (status) + goto ret; + pathname = namebuf.pointer; + + buffer.length = ACPI_ALLOCATE_BUFFER; + buffer.pointer = NULL; + status = acpi_get_object_info(handle, &buffer); + if (ACPI_FAILURE(status)) + goto out2; + + dinfo = buffer.pointer; + + /* find full device path name for pcidevfn */ + if (dinfo && (dinfo->valid & ACPI_VALID_ADR) && + dinfo->address == winfo->pcidevfn) { + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + ":%s: matches pcidevfn (0x%llx)\n", + pathname, winfo->pcidevfn); + strlcpy(winfo->basepath, pathname, + sizeof(winfo->basepath)); + winfo->basepath_len = strlen(pathname); + goto out; + } + + /* if basepath is not yet known, ignore this object */ + if (!winfo->basepath_len) + goto out; + + /* if this object is in scope of basepath, maybe use it */ + if (strncmp(pathname, winfo->basepath, + winfo->basepath_len) == 0) { + if (!(dinfo->valid & ACPI_VALID_ADR)) + goto out; + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "GOT ONE: (%s) root_port = 0x%llx," + " port_num = 0x%llx\n", pathname, + SATA_ROOT_PORT(dinfo->address), + SATA_PORT_NUMBER(dinfo->address)); + /* heuristics: */ + if (SATA_PORT_NUMBER(dinfo->address) != NO_PORT_MULT) + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, + KERN_DEBUG, "warning: don't" + " know how to handle SATA port" + " multiplier\n"); + if (SATA_ROOT_PORT(dinfo->address) == + winfo->ataport->port_no && + SATA_PORT_NUMBER(dinfo->address) == NO_PORT_MULT) { + if (ata_msg_probe(winfo->ataport)) + ata_dev_printk(winfo->atadev, + KERN_DEBUG, + "THIS ^^^^^ is the requested" + " SATA drive (handle = 0x%p)\n", + handle); + winfo->sata_adr = dinfo->address; + winfo->obj_handle = handle; + } + } +out: + kfree(dinfo); +out2: + kfree(pathname); + +ret: + return status; +} + +/* Get the SATA drive _ADR object. */ +static int get_sata_adr(struct device *dev, acpi_handle handle, + acpi_integer pcidevfn, unsigned int drive, + struct ata_port *ap, + struct ata_device *atadev, u32 *dev_adr) +{ + acpi_status status; + struct walk_info *winfo; + int err = -ENOMEM; + + winfo = kzalloc(sizeof(struct walk_info), GFP_KERNEL); + if (!winfo) + goto out; + + winfo->dev = dev; + winfo->atadev = atadev; + winfo->ataport = ap; + if (acpi_bus_get_device(handle, &winfo->adev) < 0) + if (ata_msg_probe(ap)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "acpi_bus_get_device failed\n"); + winfo->handle = handle; + winfo->pcidevfn = pcidevfn; + winfo->drivenum = drive; + + status = acpi_get_devices(NULL, get_devices, winfo, NULL); + if (ACPI_FAILURE(status)) { + if (ata_msg_probe(ap)) + ata_dev_printk(winfo->atadev, KERN_DEBUG, + "%s: acpi_get_devices failed\n", + __FUNCTION__); + err = -ENODEV; + } else { + *dev_adr = winfo->sata_adr; + atadev->obj_handle = winfo->obj_handle; + err = 0; + } + kfree(winfo); +out: + return err; +} + +/** + * do_drive_get_GTF - get the drive bootup default taskfile settings + * @ap: the ata_port for the drive + * @ix: target ata_device (drive) index + * @gtf_length: number of bytes of _GTF data returned at @gtf_address + * @gtf_address: buffer containing _GTF taskfile arrays + * + * This applies to both PATA and SATA drives. + * + * The _GTF method has no input parameters. + * It returns a variable number of register set values (registers + * hex 1F1..1F7, taskfiles). + * The <variable number> is not known in advance, so have ACPI-CA + * allocate the buffer as needed and return it, then free it later. + * + * The returned @gtf_length and @gtf_address are only valid if the + * function return value is 0. + */ +static int do_drive_get_GTF(struct ata_port *ap, int ix, + unsigned int *gtf_length, unsigned long *gtf_address, + unsigned long *obj_loc) +{ + acpi_status status; + acpi_handle dev_handle = NULL; + acpi_handle chan_handle, drive_handle; + acpi_integer pcidevfn = 0; + u32 dev_adr; + struct acpi_buffer output; + union acpi_object *out_obj; + struct device *dev = ap->host->dev; + struct ata_device *atadev = &ap->device[ix]; + int err = -ENODEV; + + *gtf_length = 0; + *gtf_address = 0UL; + *obj_loc = 0UL; + + if (libata_noacpi) + return 0; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: ENTER: port#: %d\n", + __FUNCTION__, ap->port_no); + + if (!ata_dev_enabled(atadev) || (ap->flags & ATA_FLAG_DISABLED)) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: ERR: " + "ata_dev_present: %d, PORT_DISABLED: %lu\n", + __FUNCTION__, ata_dev_enabled(atadev), + ap->flags & ATA_FLAG_DISABLED); + goto out; + } + + /* Don't continue if device has no _ADR method. + * _GTF is intended for known motherboard devices. */ + if (!(ap->cbl == ATA_CBL_SATA)) { + err = pata_get_dev_handle(dev, &dev_handle, &pcidevfn); + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: pata_get_dev_handle failed (%d)\n", + __FUNCTION__, err); + goto out; + } + } else { + err = sata_get_dev_handle(dev, &dev_handle, &pcidevfn); + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: sata_get_dev_handle failed (%d\n", + __FUNCTION__, err); + goto out; + } + } + + /* Get this drive's _ADR info. if not already known. */ + if (!atadev->obj_handle) { + if (!(ap->cbl == ATA_CBL_SATA)) { + /* get child objects of dev_handle == channel objects, + * + _their_ children == drive objects */ + /* channel is ap->port_no */ + chan_handle = acpi_get_child(dev_handle, + ap->port_no); + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: chan adr=%d: chan_handle=0x%p\n", + __FUNCTION__, ap->port_no, + chan_handle); + if (!chan_handle) { + err = -ENODEV; + goto out; + } + /* TBD: could also check ACPI object VALID bits */ + drive_handle = acpi_get_child(chan_handle, ix); + if (!drive_handle) { + err = -ENODEV; + goto out; + } + dev_adr = ix; + atadev->obj_handle = drive_handle; + } else { /* for SATA mode */ + dev_adr = SATA_ADR_RSVD; + err = get_sata_adr(dev, dev_handle, pcidevfn, 0, + ap, atadev, &dev_adr); + } + if (err < 0 || dev_adr == SATA_ADR_RSVD || + !atadev->obj_handle) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: get_sata/pata_adr failed: " + "err=%d, dev_adr=%u, obj_handle=0x%p\n", + __FUNCTION__, err, dev_adr, + atadev->obj_handle); + goto out; + } + } + + /* Setting up output buffer */ + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; /* ACPI-CA sets this; save/free it later */ + + /* _GTF has no input parameters */ + err = -EIO; + status = acpi_evaluate_object(atadev->obj_handle, "_GTF", + NULL, &output); + if (ACPI_FAILURE(status)) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: Run _GTF error: status = 0x%x\n", + __FUNCTION__, status); + goto out; + } + + if (!output.length || !output.pointer) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: Run _GTF: " + "length or ptr is NULL (0x%llx, 0x%p)\n", + __FUNCTION__, + (unsigned long long)output.length, + output.pointer); + kfree(output.pointer); + goto out; + } + + out_obj = output.pointer; + if (out_obj->type != ACPI_TYPE_BUFFER) { + kfree(output.pointer); + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: Run _GTF: " + "error: expected object type of " + " ACPI_TYPE_BUFFER, got 0x%x\n", + __FUNCTION__, out_obj->type); + err = -ENOENT; + goto out; + } + + if (!out_obj->buffer.length || !out_obj->buffer.pointer || + out_obj->buffer.length % REGS_PER_GTF) { + if (ata_msg_drv(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: unexpected GTF length (%d) or addr (0x%p)\n", + __FUNCTION__, out_obj->buffer.length, + out_obj->buffer.pointer); + err = -ENOENT; + goto out; + } + + *gtf_length = out_obj->buffer.length; + *gtf_address = (unsigned long)out_obj->buffer.pointer; + *obj_loc = (unsigned long)out_obj; + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: returning " + "gtf_length=%d, gtf_address=0x%lx, obj_loc=0x%lx\n", + __FUNCTION__, *gtf_length, *gtf_address, *obj_loc); + err = 0; +out: + return err; +} + +/** + * taskfile_load_raw - send taskfile registers to host controller + * @ap: Port to which output is sent + * @gtf: raw ATA taskfile register set (0x1f1 - 0x1f7) + * + * Outputs ATA taskfile to standard ATA host controller using MMIO + * or PIO as indicated by the ATA_FLAG_MMIO flag. + * Writes the control, feature, nsect, lbal, lbam, and lbah registers. + * Optionally (ATA_TFLAG_LBA48) writes hob_feature, hob_nsect, + * hob_lbal, hob_lbam, and hob_lbah. + * + * This function waits for idle (!BUSY and !DRQ) after writing + * registers. If the control register has a new value, this + * function also waits for idle after writing control and before + * writing the remaining registers. + * + * LOCKING: TBD: + * Inherited from caller. + */ +static void taskfile_load_raw(struct ata_port *ap, + struct ata_device *atadev, + const struct taskfile_array *gtf) +{ + struct ata_taskfile tf; + unsigned int err; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: (0x1f1-1f7): hex: " + "%02x %02x %02x %02x %02x %02x %02x\n", + __FUNCTION__, + gtf->tfa[0], gtf->tfa[1], gtf->tfa[2], + gtf->tfa[3], gtf->tfa[4], gtf->tfa[5], gtf->tfa[6]); + + if ((gtf->tfa[0] == 0) && (gtf->tfa[1] == 0) && (gtf->tfa[2] == 0) + && (gtf->tfa[3] == 0) && (gtf->tfa[4] == 0) && (gtf->tfa[5] == 0) + && (gtf->tfa[6] == 0)) + return; + + ata_tf_init(atadev, &tf); + + /* convert gtf to tf */ + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; /* TBD */ + tf.protocol = atadev->class == ATA_DEV_ATAPI ? + ATA_PROT_ATAPI_NODATA : ATA_PROT_NODATA; + tf.feature = gtf->tfa[0]; /* 0x1f1 */ + tf.nsect = gtf->tfa[1]; /* 0x1f2 */ + tf.lbal = gtf->tfa[2]; /* 0x1f3 */ + tf.lbam = gtf->tfa[3]; /* 0x1f4 */ + tf.lbah = gtf->tfa[4]; /* 0x1f5 */ + tf.device = gtf->tfa[5]; /* 0x1f6 */ + tf.command = gtf->tfa[6]; /* 0x1f7 */ + + err = ata_exec_internal(atadev, &tf, NULL, DMA_NONE, NULL, 0); + if (err && ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: ata_exec_internal failed: %u\n", + __FUNCTION__, err); +} + +/** + * do_drive_set_taskfiles - write the drive taskfile settings from _GTF + * @ap: the ata_port for the drive + * @atadev: target ata_device + * @gtf_length: total number of bytes of _GTF taskfiles + * @gtf_address: location of _GTF taskfile arrays + * + * This applies to both PATA and SATA drives. + * + * Write {gtf_address, length gtf_length} in groups of + * REGS_PER_GTF bytes. + */ +static int do_drive_set_taskfiles(struct ata_port *ap, + struct ata_device *atadev, unsigned int gtf_length, + unsigned long gtf_address) +{ + int err = -ENODEV; + int gtf_count = gtf_length / REGS_PER_GTF; + int ix; + struct taskfile_array *gtf; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: ENTER: port#: %d\n", + __FUNCTION__, ap->port_no); + + if (libata_noacpi || !(ap->cbl == ATA_CBL_SATA)) + return 0; + + if (!ata_dev_enabled(atadev) || (ap->flags & ATA_FLAG_DISABLED)) + goto out; + if (!gtf_count) /* shouldn't be here */ + goto out; + + if (gtf_length % REGS_PER_GTF) { + if (ata_msg_drv(ap)) + ata_dev_printk(atadev, KERN_ERR, + "%s: unexpected GTF length (%d)\n", + __FUNCTION__, gtf_length); + goto out; + } + + for (ix = 0; ix < gtf_count; ix++) { + gtf = (struct taskfile_array *) + (gtf_address + ix * REGS_PER_GTF); + + /* send all TaskFile registers (0x1f1-0x1f7) *in*that*order* */ + taskfile_load_raw(ap, atadev, gtf); + } + + err = 0; +out: + return err; +} + +/** + * ata_acpi_exec_tfs - get then write drive taskfile settings + * @ap: the ata_port for the drive + * + * This applies to both PATA and SATA drives. + */ +int ata_acpi_exec_tfs(struct ata_port *ap) +{ + int ix; + int ret =0; + unsigned int gtf_length; + unsigned long gtf_address; + unsigned long obj_loc; + + if (libata_noacpi) + return 0; + /* + * TBD - implement PATA support. For now, + * we should not run GTF on PATA devices since some + * PATA require execution of GTM/STM before GTF. + */ + if (!(ap->cbl == ATA_CBL_SATA)) + return 0; + + for (ix = 0; ix < ATA_MAX_DEVICES; ix++) { + if (!ata_dev_enabled(&ap->device[ix])) + continue; + + ret = do_drive_get_GTF(ap, ix, + >f_length, >f_address, &obj_loc); + if (ret < 0) { + if (ata_msg_probe(ap)) + ata_port_printk(ap, KERN_DEBUG, + "%s: get_GTF error (%d)\n", + __FUNCTION__, ret); + break; + } + + ret = do_drive_set_taskfiles(ap, &ap->device[ix], + gtf_length, gtf_address); + kfree((void *)obj_loc); + if (ret < 0) { + if (ata_msg_probe(ap)) + ata_port_printk(ap, KERN_DEBUG, + "%s: set_taskfiles error (%d)\n", + __FUNCTION__, ret); + break; + } + } + + return ret; +} + +/** + * ata_acpi_push_id - send Identify data to drive + * @ap: the ata_port for the drive + * @ix: drive index + * + * _SDD ACPI object: for SATA mode only + * Must be after Identify (Packet) Device -- uses its data + * ATM this function never returns a failure. It is an optional + * method and if it fails for whatever reason, we should still + * just keep going. + */ +int ata_acpi_push_id(struct ata_port *ap, unsigned int ix) +{ + acpi_handle handle; + acpi_integer pcidevfn; + int err; + struct device *dev = ap->host->dev; + struct ata_device *atadev = &ap->device[ix]; + u32 dev_adr; + acpi_status status; + struct acpi_object_list input; + union acpi_object in_params[1]; + + if (libata_noacpi) + return 0; + + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, "%s: ix = %d, port#: %d\n", + __FUNCTION__, ix, ap->port_no); + + /* Don't continue if not a SATA device. */ + if (!(ap->cbl == ATA_CBL_SATA)) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: Not a SATA device\n", __FUNCTION__); + goto out; + } + + /* Don't continue if device has no _ADR method. + * _SDD is intended for known motherboard devices. */ + err = sata_get_dev_handle(dev, &handle, &pcidevfn); + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: sata_get_dev_handle failed (%d\n", + __FUNCTION__, err); + goto out; + } + + /* Get this drive's _ADR info, if not already known */ + if (!atadev->obj_handle) { + dev_adr = SATA_ADR_RSVD; + err = get_sata_adr(dev, handle, pcidevfn, ix, ap, atadev, + &dev_adr); + if (err < 0 || dev_adr == SATA_ADR_RSVD || + !atadev->obj_handle) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s: get_sata_adr failed: " + "err=%d, dev_adr=%u, obj_handle=0x%p\n", + __FUNCTION__, err, dev_adr, + atadev->obj_handle); + goto out; + } + } + + /* Give the drive Identify data to the drive via the _SDD method */ + /* _SDD: set up input parameters */ + input.count = 1; + input.pointer = in_params; + in_params[0].type = ACPI_TYPE_BUFFER; + in_params[0].buffer.length = sizeof(atadev->id[0]) * ATA_ID_WORDS; + in_params[0].buffer.pointer = (u8 *)atadev->id; + /* Output buffer: _SDD has no output */ + + /* It's OK for _SDD to be missing too. */ + swap_buf_le16(atadev->id, ATA_ID_WORDS); + status = acpi_evaluate_object(atadev->obj_handle, "_SDD", &input, NULL); + swap_buf_le16(atadev->id, ATA_ID_WORDS); + + err = ACPI_FAILURE(status) ? -EIO : 0; + if (err < 0) { + if (ata_msg_probe(ap)) + ata_dev_printk(atadev, KERN_DEBUG, + "%s _SDD error: status = 0x%x\n", + __FUNCTION__, status); + } + + /* always return success */ +out: + return 0; +} + + diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 2cf8251..ca67484 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -59,7 +59,7 @@ #include "libata.h" -#define DRV_VERSION "2.10" /* must be exactly four chars */ +#define DRV_VERSION "2.20" /* must be exactly four chars */ /* debounce timing parameters in msecs { interval, duration, timeout } */ @@ -72,7 +72,7 @@ static unsigned int ata_dev_init_params(struct ata_device *dev, static unsigned int ata_dev_set_xfermode(struct ata_device *dev); static void ata_dev_xfermask(struct ata_device *dev); -static unsigned int ata_unique_id = 1; +unsigned int ata_print_id = 1; static struct workqueue_struct *ata_wq; struct workqueue_struct *ata_aux_wq; @@ -89,10 +89,18 @@ int libata_fua = 0; module_param_named(fua, libata_fua, int, 0444); MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); +static int ata_ignore_hpa = 0; +module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644); +MODULE_PARM_DESC(ignore_hpa, "Ignore HPA limit (0=keep BIOS limits, 1=ignore limits, using full disk)"); + static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ; module_param(ata_probe_timeout, int, 0444); MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); +int libata_noacpi = 1; +module_param_named(noacpi, libata_noacpi, int, 0444); +MODULE_PARM_DESC(noacpi, "Disables the use of ACPI in suspend/resume when set"); + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Library module for ATA devices"); MODULE_LICENSE("GPL"); @@ -311,9 +319,7 @@ int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, tf->flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; tf->flags |= tf_flags; - if ((dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ_OFF | - ATA_DFLAG_NCQ)) == ATA_DFLAG_NCQ && - likely(tag != ATA_TAG_INTERNAL)) { + if (ata_ncq_enabled(dev) && likely(tag != ATA_TAG_INTERNAL)) { /* yay, NCQ */ if (!lba_48_ok(block, n_block)) return -ERANGE; @@ -596,6 +602,8 @@ void ata_dev_disable(struct ata_device *dev) { if (ata_dev_enabled(dev) && ata_msg_drv(dev->ap)) { ata_dev_printk(dev, KERN_WARNING, "disabled\n"); + ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO0 | + ATA_DNXFER_QUIET); dev->class++; } } @@ -704,7 +712,7 @@ unsigned int ata_dev_classify(const struct ata_taskfile *tf) * Device type - %ATA_DEV_ATA, %ATA_DEV_ATAPI or %ATA_DEV_NONE. */ -static unsigned int +unsigned int ata_dev_try_classify(struct ata_port *ap, unsigned int device, u8 *r_err) { struct ata_taskfile tf; @@ -804,6 +812,205 @@ void ata_id_c_string(const u16 *id, unsigned char *s, *p = '\0'; } +static u64 ata_tf_to_lba48(struct ata_taskfile *tf) +{ + u64 sectors = 0; + + sectors |= ((u64)(tf->hob_lbah & 0xff)) << 40; + sectors |= ((u64)(tf->hob_lbam & 0xff)) << 32; + sectors |= (tf->hob_lbal & 0xff) << 24; + sectors |= (tf->lbah & 0xff) << 16; + sectors |= (tf->lbam & 0xff) << 8; + sectors |= (tf->lbal & 0xff); + + return ++sectors; +} + +static u64 ata_tf_to_lba(struct ata_taskfile *tf) +{ + u64 sectors = 0; + + sectors |= (tf->device & 0x0f) << 24; + sectors |= (tf->lbah & 0xff) << 16; + sectors |= (tf->lbam & 0xff) << 8; + sectors |= (tf->lbal & 0xff); + + return ++sectors; +} + +/** + * ata_read_native_max_address_ext - LBA48 native max query + * @dev: Device to query + * + * Perform an LBA48 size query upon the device in question. Return the + * actual LBA48 size or zero if the command fails. + */ + +static u64 ata_read_native_max_address_ext(struct ata_device *dev) +{ + unsigned int err; + struct ata_taskfile tf; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_READ_NATIVE_MAX_EXT; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba48(&tf); +} + +/** + * ata_read_native_max_address - LBA28 native max query + * @dev: Device to query + * + * Performa an LBA28 size query upon the device in question. Return the + * actual LBA28 size or zero if the command fails. + */ + +static u64 ata_read_native_max_address(struct ata_device *dev) +{ + unsigned int err; + struct ata_taskfile tf; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_READ_NATIVE_MAX; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba(&tf); +} + +/** + * ata_set_native_max_address_ext - LBA48 native max set + * @dev: Device to query + * + * Perform an LBA48 size set max upon the device in question. Return the + * actual LBA48 size or zero if the command fails. + */ + +static u64 ata_set_native_max_address_ext(struct ata_device *dev, u64 new_sectors) +{ + unsigned int err; + struct ata_taskfile tf; + + new_sectors--; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_SET_MAX_EXT; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_LBA48 | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + tf.device |= 0x40; + + tf.lbal = (new_sectors >> 0) & 0xff; + tf.lbam = (new_sectors >> 8) & 0xff; + tf.lbah = (new_sectors >> 16) & 0xff; + + tf.hob_lbal = (new_sectors >> 24) & 0xff; + tf.hob_lbam = (new_sectors >> 32) & 0xff; + tf.hob_lbah = (new_sectors >> 40) & 0xff; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba48(&tf); +} + +/** + * ata_set_native_max_address - LBA28 native max set + * @dev: Device to query + * + * Perform an LBA28 size set max upon the device in question. Return the + * actual LBA28 size or zero if the command fails. + */ + +static u64 ata_set_native_max_address(struct ata_device *dev, u64 new_sectors) +{ + unsigned int err; + struct ata_taskfile tf; + + new_sectors--; + + ata_tf_init(dev, &tf); + + tf.command = ATA_CMD_SET_MAX; + tf.flags |= ATA_TFLAG_DEVICE | ATA_TFLAG_ISADDR; + tf.protocol |= ATA_PROT_NODATA; + + tf.lbal = (new_sectors >> 0) & 0xff; + tf.lbam = (new_sectors >> 8) & 0xff; + tf.lbah = (new_sectors >> 16) & 0xff; + tf.device |= ((new_sectors >> 24) & 0x0f) | 0x40; + + err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err) + return 0; + + return ata_tf_to_lba(&tf); +} + +/** + * ata_hpa_resize - Resize a device with an HPA set + * @dev: Device to resize + * + * Read the size of an LBA28 or LBA48 disk with HPA features and resize + * it if required to the full size of the media. The caller must check + * the drive has the HPA feature set enabled. + */ + +static u64 ata_hpa_resize(struct ata_device *dev) +{ + u64 sectors = dev->n_sectors; + u64 hpa_sectors; + + if (ata_id_has_lba48(dev->id)) + hpa_sectors = ata_read_native_max_address_ext(dev); + else + hpa_sectors = ata_read_native_max_address(dev); + + /* if no hpa, both should be equal */ + ata_dev_printk(dev, KERN_INFO, "%s 1: sectors = %lld, " + "hpa_sectors = %lld\n", + __FUNCTION__, (long long)sectors, (long long)hpa_sectors); + + if (hpa_sectors > sectors) { + ata_dev_printk(dev, KERN_INFO, + "Host Protected Area detected:\n" + "\tcurrent size: %lld sectors\n" + "\tnative size: %lld sectors\n", + (long long)sectors, (long long)hpa_sectors); + + if (ata_ignore_hpa) { + if (ata_id_has_lba48(dev->id)) + hpa_sectors = ata_set_native_max_address_ext(dev, hpa_sectors); + else + hpa_sectors = ata_set_native_max_address(dev, + hpa_sectors); + + if (hpa_sectors) { + ata_dev_printk(dev, KERN_INFO, "native size " + "increased to %lld sectors\n", + (long long)hpa_sectors); + return hpa_sectors; + } + } + } + return sectors; +} + static u64 ata_id_n_sectors(const u16 *id) { if (ata_id_has_lba(id)) { @@ -820,6 +1027,48 @@ static u64 ata_id_n_sectors(const u16 *id) } /** + * ata_id_to_dma_mode - Identify DMA mode from id block + * @dev: device to identify + * @unknown: mode to assume if we cannot tell + * + * Set up the timing values for the device based upon the identify + * reported values for the DMA mode. This function is used by drivers + * which rely upon firmware configured modes, but wish to report the + * mode correctly when possible. + * + * In addition we emit similarly formatted messages to the default + * ata_dev_set_mode handler, in order to provide consistency of + * presentation. + */ + +void ata_id_to_dma_mode(struct ata_device *dev, u8 unknown) +{ + unsigned int mask; + u8 mode; + + /* Pack the DMA modes */ + mask = ((dev->id[63] >> 8) << ATA_SHIFT_MWDMA) & ATA_MASK_MWDMA; + if (dev->id[53] & 0x04) + mask |= ((dev->id[88] >> 8) << ATA_SHIFT_UDMA) & ATA_MASK_UDMA; + + /* Select the mode in use */ + mode = ata_xfer_mask2mode(mask); + + if (mode != 0) { + ata_dev_printk(dev, KERN_INFO, "configured for %s\n", + ata_mode_string(mask)); + } else { + /* SWDMA perhaps ? */ + mode = unknown; + ata_dev_printk(dev, KERN_INFO, "configured for DMA\n"); + } + + /* Configure the device reporting */ + dev->xfer_mode = mode; + dev->xfer_shift = ata_xfer_mode2shift(mode); +} + +/** * ata_noop_dev_select - Select device 0/1 on ATA bus * @ap: ATA channel to manipulate * @device: ATA device (numbered from zero) to select @@ -887,8 +1136,8 @@ void ata_dev_select(struct ata_port *ap, unsigned int device, unsigned int wait, unsigned int can_sleep) { if (ata_msg_probe(ap)) - ata_port_printk(ap, KERN_INFO, "ata_dev_select: ENTER, ata%u: " - "device %u, wait %u\n", ap->id, device, wait); + ata_port_printk(ap, KERN_INFO, "ata_dev_select: ENTER, " + "device %u, wait %u\n", device, wait); if (wait) ata_wait_idle(ap); @@ -1224,12 +1473,16 @@ unsigned ata_exec_internal_sg(struct ata_device *dev, if (ap->ops->post_internal_cmd) ap->ops->post_internal_cmd(qc); - if ((qc->flags & ATA_QCFLAG_FAILED) && !qc->err_mask) { - if (ata_msg_warn(ap)) - ata_dev_printk(dev, KERN_WARNING, - "zero err_mask for failed " - "internal command, assuming AC_ERR_OTHER\n"); - qc->err_mask |= AC_ERR_OTHER; + /* perform minimal error analysis */ + if (qc->flags & ATA_QCFLAG_FAILED) { + if (qc->result_tf.command & (ATA_ERR | ATA_DF)) + qc->err_mask |= AC_ERR_DEV; + + if (!qc->err_mask) + qc->err_mask |= AC_ERR_OTHER; + + if (qc->err_mask & ~AC_ERR_OTHER) + qc->err_mask &= ~AC_ERR_OTHER; } /* finish up */ @@ -1333,30 +1586,44 @@ unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd) * Check if the current speed of the device requires IORDY. Used * by various controllers for chip configuration. */ - + unsigned int ata_pio_need_iordy(const struct ata_device *adev) { - int pio; - int speed = adev->pio_mode - XFER_PIO_0; - - if (speed < 2) + /* Controller doesn't support IORDY. Probably a pointless check + as the caller should know this */ + if (adev->ap->flags & ATA_FLAG_NO_IORDY) return 0; - if (speed > 2) + /* PIO3 and higher it is mandatory */ + if (adev->pio_mode > XFER_PIO_2) + return 1; + /* We turn it on when possible */ + if (ata_id_has_iordy(adev->id)) return 1; + return 0; +} +/** + * ata_pio_mask_no_iordy - Return the non IORDY mask + * @adev: ATA device + * + * Compute the highest mode possible if we are not using iordy. Return + * -1 if no iordy mode is available. + */ + +static u32 ata_pio_mask_no_iordy(const struct ata_device *adev) +{ /* If we have no drive specific rule, then PIO 2 is non IORDY */ - if (adev->id[ATA_ID_FIELD_VALID] & 2) { /* EIDE */ - pio = adev->id[ATA_ID_EIDE_PIO]; + u16 pio = adev->id[ATA_ID_EIDE_PIO]; /* Is the speed faster than the drive allows non IORDY ? */ if (pio) { /* This is cycle times not frequency - watch the logic! */ if (pio > 240) /* PIO2 is 240nS per cycle */ - return 1; - return 0; + return 3 << ATA_SHIFT_PIO; + return 7 << ATA_SHIFT_PIO; } } - return 0; + return 3 << ATA_SHIFT_PIO; } /** @@ -1385,14 +1652,13 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, struct ata_taskfile tf; unsigned int err_mask = 0; const char *reason; + int tried_spinup = 0; int rc; if (ata_msg_ctl(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n", - __FUNCTION__, ap->id, dev->devno); + ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__); ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */ - retry: ata_tf_init(dev, &tf); @@ -1426,7 +1692,7 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, if (err_mask) { if (err_mask & AC_ERR_NODEV_HINT) { DPRINTK("ata%u.%d: NODEV after polling detection\n", - ap->id, dev->devno); + ap->print_id, dev->devno); return -ENOENT; } @@ -1449,6 +1715,32 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class, goto err_out; } + if (!tried_spinup && (id[2] == 0x37c8 || id[2] == 0x738c)) { + tried_spinup = 1; + /* + * Drive powered-up in standby mode, and requires a specific + * SET_FEATURES spin-up subcommand before it will accept + * anything other than the original IDENTIFY command. + */ + ata_tf_init(dev, &tf); + tf.command = ATA_CMD_SET_FEATURES; + tf.feature = SETFEATURES_SPINUP; + tf.protocol = ATA_PROT_NODATA; + tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; + err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0); + if (err_mask) { + rc = -EIO; + reason = "SPINUP failed"; + goto err_out; + } + /* + * If the drive initially returned incomplete IDENTIFY info, + * we now must reissue the IDENTIFY command. + */ + if (id[2] == 0x37c8) + goto retry; + } + if ((flags & ATA_READID_POSTRESET) && class == ATA_DEV_ATA) { /* * The exact sequence expected by certain pre-ATA4 drives is: @@ -1515,20 +1807,6 @@ static void ata_dev_config_ncq(struct ata_device *dev, snprintf(desc, desc_sz, "NCQ (depth %d/%d)", hdepth, ddepth); } -static void ata_set_port_max_cmd_len(struct ata_port *ap) -{ - int i; - - if (ap->scsi_host) { - unsigned int len = 0; - - for (i = 0; i < ATA_MAX_DEVICES; i++) - len = max(len, ap->device[i].cdb_len); - - ap->scsi_host->max_cmd_len = len; - } -} - /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -1554,15 +1832,23 @@ int ata_dev_configure(struct ata_device *dev) int rc; if (!ata_dev_enabled(dev) && ata_msg_info(ap)) { - ata_dev_printk(dev, KERN_INFO, - "%s: ENTER/EXIT (host %u, dev %u) -- nodev\n", - __FUNCTION__, ap->id, dev->devno); + ata_dev_printk(dev, KERN_INFO, "%s: ENTER/EXIT -- nodev\n", + __FUNCTION__); return 0; } if (ata_msg_probe(ap)) - ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n", - __FUNCTION__, ap->id, dev->devno); + ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER\n", __FUNCTION__); + + /* set _SDD */ + rc = ata_acpi_push_id(ap, dev->devno); + if (rc) { + ata_dev_printk(dev, KERN_WARNING, "failed to set _SDD(%d)\n", + rc); + } + + /* retrieve and execute the ATA task file of _GTF */ + ata_acpi_exec_tfs(ap); /* print device capabilities */ if (ata_msg_probe(ap)) @@ -1596,14 +1882,16 @@ int ata_dev_configure(struct ata_device *dev) if (dev->class == ATA_DEV_ATA) { if (ata_id_is_cfa(id)) { if (id[162] & 1) /* CPRM may make this media unusable */ - ata_dev_printk(dev, KERN_WARNING, "ata%u: device %u supports DRM functions and may not be fully accessable.\n", - ap->id, dev->devno); + ata_dev_printk(dev, KERN_WARNING, + "supports DRM functions and may " + "not be fully accessable.\n"); snprintf(revbuf, 7, "CFA"); } else snprintf(revbuf, 7, "ATA-%d", ata_id_major_version(id)); dev->n_sectors = ata_id_n_sectors(id); + dev->n_sectors_boot = dev->n_sectors; /* SCSI only uses 4-char revisions, dump full 8 chars from ATA */ ata_id_c_string(dev->id, fwrevbuf, ATA_ID_FW_REV, @@ -1630,6 +1918,9 @@ int ata_dev_configure(struct ata_device *dev) dev->flags |= ATA_DFLAG_FLUSH_EXT; } + if (ata_id_hpa_enabled(dev->id)) + dev->n_sectors = ata_hpa_resize(dev); + /* config NCQ */ ata_dev_config_ncq(dev, ncq_desc, sizeof(ncq_desc)); @@ -1665,7 +1956,7 @@ int ata_dev_configure(struct ata_device *dev) "%s: %s, %s, max %s\n", revbuf, modelbuf, fwrevbuf, ata_mode_string(xfer_mask)); - ata_dev_printk(dev, KERN_INFO, + ata_dev_printk(dev, KERN_INFO, "%Lu sectors, multi %u, CHS %u/%u/%u\n", (unsigned long long)dev->n_sectors, dev->multi_count, dev->cylinders, @@ -1719,8 +2010,6 @@ int ata_dev_configure(struct ata_device *dev) } } - ata_set_port_max_cmd_len(ap); - /* limit bridge transfers to udma5, 200 sectors */ if (ata_dev_knobble(dev)) { if (ata_msg_drv(ap) && print_info) @@ -1730,8 +2019,16 @@ int ata_dev_configure(struct ata_device *dev) dev->max_sectors = ATA_MAX_SECTORS; } + if (ata_device_blacklisted(dev) & ATA_HORKAGE_MAX_SEC_128) + dev->max_sectors = min_t(unsigned int, ATA_MAX_SECTORS_128, + dev->max_sectors); + + /* limit ATAPI DMA to R/W commands only */ + if (ata_device_blacklisted(dev) & ATA_HORKAGE_DMA_RW_ONLY) + dev->horkage |= ATA_HORKAGE_DMA_RW_ONLY; + if (ap->ops->dev_config) - ap->ops->dev_config(ap, dev); + ap->ops->dev_config(dev); if (ata_msg_probe(ap)) ata_dev_printk(dev, KERN_DEBUG, "%s: EXIT, drv_stat = 0x%x\n", @@ -1746,6 +2043,56 @@ err_out_nosup: } /** + * ata_cable_40wire - return 40 wire cable type + * @ap: port + * + * Helper method for drivers which want to hardwire 40 wire cable + * detection. + */ + +int ata_cable_40wire(struct ata_port *ap) +{ + return ATA_CBL_PATA40; +} + +/** + * ata_cable_80wire - return 80 wire cable type + * @ap: port + * + * Helper method for drivers which want to hardwire 80 wire cable + * detection. + */ + +int ata_cable_80wire(struct ata_port *ap) +{ + return ATA_CBL_PATA80; +} + +/** + * ata_cable_unknown - return unknown PATA cable. + * @ap: port + * + * Helper method for drivers which have no PATA cable detection. + */ + +int ata_cable_unknown(struct ata_port *ap) +{ + return ATA_CBL_PATA_UNK; +} + +/** + * ata_cable_sata - return SATA cable type + * @ap: port + * + * Helper method for drivers which have SATA cables + */ + +int ata_cable_sata(struct ata_port *ap) +{ + return ATA_CBL_SATA; +} + +/** * ata_bus_probe - Reset and probe ATA bus * @ap: Bus to probe * @@ -1764,7 +2111,7 @@ int ata_bus_probe(struct ata_port *ap) { unsigned int classes[ATA_MAX_DEVICES]; int tries[ATA_MAX_DEVICES]; - int i, rc, down_xfermask; + int i, rc; struct ata_device *dev; ata_port_probe(ap); @@ -1773,8 +2120,6 @@ int ata_bus_probe(struct ata_port *ap) tries[i] = ATA_PROBE_MAX_TRIES; retry: - down_xfermask = 0; - /* reset and determine device classes */ ap->ops->phy_reset(ap); @@ -1798,8 +2143,11 @@ int ata_bus_probe(struct ata_port *ap) for (i = 0; i < ATA_MAX_DEVICES; i++) ap->device[i].pio_mode = XFER_PIO_0; - /* read IDENTIFY page and configure devices */ - for (i = 0; i < ATA_MAX_DEVICES; i++) { + /* read IDENTIFY page and configure devices. We have to do the identify + specific sequence bass-ackwards so that PDIAG- is released by + the slave device */ + + for (i = ATA_MAX_DEVICES - 1; i >= 0; i--) { dev = &ap->device[i]; if (tries[i]) @@ -1812,6 +2160,19 @@ int ata_bus_probe(struct ata_port *ap) dev->id); if (rc) goto fail; + } + + /* Now ask for the cable type as PDIAG- should have been released */ + if (ap->ops->cable_detect) + ap->cbl = ap->ops->cable_detect(ap); + + /* After the identify sequence we can now set up the devices. We do + this in the normal order so that the user doesn't get confused */ + + for(i = 0; i < ATA_MAX_DEVICES; i++) { + dev = &ap->device[i]; + if (!ata_dev_enabled(dev)) + continue; ap->eh_context.i.flags |= ATA_EHI_PRINTINFO; rc = ata_dev_configure(dev); @@ -1822,10 +2183,8 @@ int ata_bus_probe(struct ata_port *ap) /* configure transfer mode */ rc = ata_set_mode(ap, &dev); - if (rc) { - down_xfermask = 1; + if (rc) goto fail; - } for (i = 0; i < ATA_MAX_DEVICES; i++) if (ata_dev_enabled(&ap->device[i])) @@ -1837,25 +2196,29 @@ int ata_bus_probe(struct ata_port *ap) return -ENODEV; fail: + tries[dev->devno]--; + switch (rc) { case -EINVAL: - case -ENODEV: + /* eeek, something went very wrong, give up */ tries[dev->devno] = 0; break; + + case -ENODEV: + /* give it just one more chance */ + tries[dev->devno] = min(tries[dev->devno], 1); case -EIO: - sata_down_spd_limit(ap); - /* fall through */ - default: - tries[dev->devno]--; - if (down_xfermask && - ata_down_xfermask_limit(dev, tries[dev->devno] == 1)) - tries[dev->devno] = 0; + if (tries[dev->devno] == 1) { + /* This is the last chance, better to slow + * down than lose it. + */ + sata_down_spd_limit(ap); + ata_down_xfermask_limit(dev, ATA_DNXFER_PIO); + } } - if (!tries[dev->devno]) { - ata_down_xfermask_limit(dev, 1); + if (!tries[dev->devno]) ata_dev_disable(dev); - } goto retry; } @@ -1885,7 +2248,7 @@ void ata_port_probe(struct ata_port *ap) * LOCKING: * None. */ -static void sata_print_link_status(struct ata_port *ap) +void sata_print_link_status(struct ata_port *ap) { u32 sstatus, scontrol, tmp; @@ -2279,6 +2642,12 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed, t->active += (t->cycle - (t->active + t->recover)) / 2; t->recover = t->cycle - t->active; } + + /* In a few cases quantisation may produce enough errors to + leave t->cycle too low for the sum of active and recovery + if so we must correct this */ + if (t->active + t->recover > t->cycle) + t->cycle = t->active + t->recover; return 0; } @@ -2286,7 +2655,7 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed, /** * ata_down_xfermask_limit - adjust dev xfer masks downward * @dev: Device to adjust xfer masks - * @force_pio0: Force PIO0 + * @sel: ATA_DNXFER_* selector * * Adjust xfer masks of @dev downward. Note that this function * does not apply the change. Invoking ata_set_mode() afterwards @@ -2298,37 +2667,78 @@ int ata_timing_compute(struct ata_device *adev, unsigned short speed, * RETURNS: * 0 on success, negative errno on failure */ -int ata_down_xfermask_limit(struct ata_device *dev, int force_pio0) +int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel) { - unsigned long xfer_mask; - int highbit; + char buf[32]; + unsigned int orig_mask, xfer_mask; + unsigned int pio_mask, mwdma_mask, udma_mask; + int quiet, highbit; - xfer_mask = ata_pack_xfermask(dev->pio_mask, dev->mwdma_mask, - dev->udma_mask); + quiet = !!(sel & ATA_DNXFER_QUIET); + sel &= ~ATA_DNXFER_QUIET; - if (!xfer_mask) - goto fail; - /* don't gear down to MWDMA from UDMA, go directly to PIO */ - if (xfer_mask & ATA_MASK_UDMA) - xfer_mask &= ~ATA_MASK_MWDMA; + xfer_mask = orig_mask = ata_pack_xfermask(dev->pio_mask, + dev->mwdma_mask, + dev->udma_mask); + ata_unpack_xfermask(xfer_mask, &pio_mask, &mwdma_mask, &udma_mask); - highbit = fls(xfer_mask) - 1; - xfer_mask &= ~(1 << highbit); - if (force_pio0) - xfer_mask &= 1 << ATA_SHIFT_PIO; - if (!xfer_mask) - goto fail; + switch (sel) { + case ATA_DNXFER_PIO: + highbit = fls(pio_mask) - 1; + pio_mask &= ~(1 << highbit); + break; + + case ATA_DNXFER_DMA: + if (udma_mask) { + highbit = fls(udma_mask) - 1; + udma_mask &= ~(1 << highbit); + if (!udma_mask) + return -ENOENT; + } else if (mwdma_mask) { + highbit = fls(mwdma_mask) - 1; + mwdma_mask &= ~(1 << highbit); + if (!mwdma_mask) + return -ENOENT; + } + break; + + case ATA_DNXFER_40C: + udma_mask &= ATA_UDMA_MASK_40C; + break; + + case ATA_DNXFER_FORCE_PIO0: + pio_mask &= 1; + case ATA_DNXFER_FORCE_PIO: + mwdma_mask = 0; + udma_mask = 0; + break; + + default: + BUG(); + } + + xfer_mask &= ata_pack_xfermask(pio_mask, mwdma_mask, udma_mask); + + if (!(xfer_mask & ATA_MASK_PIO) || xfer_mask == orig_mask) + return -ENOENT; + + if (!quiet) { + if (xfer_mask & (ATA_MASK_MWDMA | ATA_MASK_UDMA)) + snprintf(buf, sizeof(buf), "%s:%s", + ata_mode_string(xfer_mask), + ata_mode_string(xfer_mask & ATA_MASK_PIO)); + else + snprintf(buf, sizeof(buf), "%s", + ata_mode_string(xfer_mask)); + + ata_dev_printk(dev, KERN_WARNING, + "limiting speed to %s\n", buf); + } ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, &dev->udma_mask); - ata_dev_printk(dev, KERN_WARNING, "limiting speed to %s\n", - ata_mode_string(xfer_mask)); - return 0; - - fail: - return -EINVAL; } static int ata_dev_set_mode(struct ata_device *dev) @@ -2367,12 +2777,13 @@ static int ata_dev_set_mode(struct ata_device *dev) } /** - * ata_set_mode - Program timings and issue SET FEATURES - XFER + * ata_do_set_mode - Program timings and issue SET FEATURES - XFER * @ap: port on which timings will be programmed * @r_failed_dev: out paramter for failed device * - * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If - * ata_set_mode() fails, pointer to the failing device is + * Standard implementation of the function used to tune and set + * ATA device disk transfer mode (PIO3, UDMA6, etc.). If + * ata_dev_set_mode() fails, pointer to the failing device is * returned in @r_failed_dev. * * LOCKING: @@ -2381,14 +2792,12 @@ static int ata_dev_set_mode(struct ata_device *dev) * RETURNS: * 0 on success, negative errno otherwise */ -int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) + +int ata_do_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) { struct ata_device *dev; int i, rc = 0, used_dma = 0, found = 0; - /* has private set_mode? */ - if (ap->ops->set_mode) - return ap->ops->set_mode(ap, r_failed_dev); /* step 1: calculate xfer_mask */ for (i = 0; i < ATA_MAX_DEVICES; i++) { @@ -2461,12 +2870,11 @@ int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) * host channels are not permitted to do so. */ if (used_dma && (ap->host->flags & ATA_HOST_SIMPLEX)) - ap->host->simplex_claimed = 1; + ap->host->simplex_claimed = ap; /* step5: chip specific finalisation */ if (ap->ops->post_set_mode) ap->ops->post_set_mode(ap); - out: if (rc) *r_failed_dev = dev; @@ -2474,6 +2882,29 @@ int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) } /** + * ata_set_mode - Program timings and issue SET FEATURES - XFER + * @ap: port on which timings will be programmed + * @r_failed_dev: out paramter for failed device + * + * Set ATA device disk transfer mode (PIO3, UDMA6, etc.). If + * ata_set_mode() fails, pointer to the failing device is + * returned in @r_failed_dev. + * + * LOCKING: + * PCI/etc. bus probe sem. + * + * RETURNS: + * 0 on success, negative errno otherwise + */ +int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + /* has private set_mode? */ + if (ap->ops->set_mode) + return ap->ops->set_mode(ap, r_failed_dev); + return ata_do_set_mode(ap, r_failed_dev); +} + +/** * ata_tf_to_host - issue ATA taskfile to host controller * @ap: port to which command is being issued * @tf: ATA taskfile register set @@ -2595,7 +3026,7 @@ static unsigned int ata_bus_softreset(struct ata_port *ap, { struct ata_ioports *ioaddr = &ap->ioaddr; - DPRINTK("ata%u: bus reset via SRST\n", ap->id); + DPRINTK("ata%u: bus reset via SRST\n", ap->print_id); /* software reset. causes dev0 to be selected */ iowrite8(ap->ctl, ioaddr->ctl_addr); @@ -2655,7 +3086,7 @@ void ata_bus_reset(struct ata_port *ap) u8 err; unsigned int dev0, dev1 = 0, devmask = 0; - DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no); + DPRINTK("ENTER, host %u, port %u\n", ap->print_id, ap->port_no); /* determine if device 0/1 are present */ if (ap->flags & ATA_FLAG_SATA_RESET) @@ -3154,6 +3585,11 @@ static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, "%llu != %llu\n", (unsigned long long)dev->n_sectors, (unsigned long long)new_n_sectors); + /* Are we the boot time size - if so we appear to be the + same disk at this point and our HPA got reapplied */ + if (ata_ignore_hpa && dev->n_sectors_boot == new_n_sectors + && ata_id_hpa_enabled(new_id)) + return 1; return 0; } @@ -3242,16 +3678,30 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { { "WPI CDD-820", NULL, ATA_HORKAGE_NODMA }, { "SAMSUNG CD-ROM SC-148C", NULL, ATA_HORKAGE_NODMA }, { "SAMSUNG CD-ROM SC", NULL, ATA_HORKAGE_NODMA }, - { "SanDisk SDP3B-64", NULL, ATA_HORKAGE_NODMA }, { "ATAPI CD-ROM DRIVE 40X MAXIMUM",NULL,ATA_HORKAGE_NODMA }, { "_NEC DV5800A", NULL, ATA_HORKAGE_NODMA }, { "SAMSUNG CD-ROM SN-124","N001", ATA_HORKAGE_NODMA }, + /* Weird ATAPI devices */ + { "TORiSAN DVD-ROM DRD-N216", NULL, ATA_HORKAGE_MAX_SEC_128 | + ATA_HORKAGE_DMA_RW_ONLY }, + /* Devices we expect to fail diagnostics */ /* Devices where NCQ should be avoided */ /* NCQ is slow */ { "WDC WD740ADFD-00", NULL, ATA_HORKAGE_NONCQ }, + /* http://thread.gmane.org/gmane.linux.ide/14907 */ + { "FUJITSU MHT2060BH", NULL, ATA_HORKAGE_NONCQ }, + /* NCQ is broken */ + { "Maxtor 6L250S0", "BANC1G10", ATA_HORKAGE_NONCQ }, + /* NCQ hard hangs device under heavier load, needs hard power cycle */ + { "Maxtor 6B250S0", "BANC1B70", ATA_HORKAGE_NONCQ }, + /* Blacklist entries taken from Silicon Image 3124/3132 + Windows driver .inf file - also several Linux problem reports */ + { "HTS541060G9SA00", "MB3OC60D", ATA_HORKAGE_NONCQ, }, + { "HTS541080G9SA00", "MB4OC60D", ATA_HORKAGE_NONCQ, }, + { "HTS541010G9SA00", "MBZOC60D", ATA_HORKAGE_NONCQ, }, /* Devices with NCQ limits */ @@ -3314,19 +3764,7 @@ static void ata_dev_xfermask(struct ata_device *dev) xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, ap->udma_mask); - /* Apply cable rule here. Don't apply it early because when - * we handle hot plug the cable type can itself change. - */ - if (ap->cbl == ATA_CBL_PATA40) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - /* Apply drive side cable rule. Unknown or 80 pin cables reported - * host side are checked drive side as well. Cases where we know a - * 40wire cable is used safely for 80 are not checked here. - */ - if (ata_drive_40wire(dev->id) && (ap->cbl == ATA_CBL_PATA_UNK || ap->cbl == ATA_CBL_PATA80)) - xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); - - + /* drive modes available */ xfer_mask &= ata_pack_xfermask(dev->pio_mask, dev->mwdma_mask, dev->udma_mask); xfer_mask &= ata_id_xfermask(dev->id); @@ -3348,14 +3786,37 @@ static void ata_dev_xfermask(struct ata_device *dev) "device is on DMA blacklist, disabling DMA\n"); } - if ((host->flags & ATA_HOST_SIMPLEX) && host->simplex_claimed) { + if ((host->flags & ATA_HOST_SIMPLEX) && + host->simplex_claimed && host->simplex_claimed != ap) { xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); ata_dev_printk(dev, KERN_WARNING, "simplex DMA is claimed by " "other device, disabling DMA\n"); } + if (ap->flags & ATA_FLAG_NO_IORDY) + xfer_mask &= ata_pio_mask_no_iordy(dev); + if (ap->ops->mode_filter) - xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask); + xfer_mask = ap->ops->mode_filter(dev, xfer_mask); + + /* Apply cable rule here. Don't apply it early because when + * we handle hot plug the cable type can itself change. + * Check this last so that we know if the transfer rate was + * solely limited by the cable. + * Unknown or 80 wire cables reported host side are checked + * drive side as well. Cases where we know a 40wire cable + * is used safely for 80 are not checked here. + */ + if (xfer_mask & (0xF8 << ATA_SHIFT_UDMA)) + /* UDMA/44 or higher would be available */ + if((ap->cbl == ATA_CBL_PATA40) || + (ata_drive_40wire(dev->id) && + (ap->cbl == ATA_CBL_PATA_UNK || + ap->cbl == ATA_CBL_PATA80))) { + ata_dev_printk(dev, KERN_WARNING, + "limited to UDMA/33 due to 40-wire cable\n"); + xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA); + } ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask, &dev->udma_mask); @@ -3562,6 +4023,26 @@ int ata_check_atapi_dma(struct ata_queued_cmd *qc) struct ata_port *ap = qc->ap; int rc = 0; /* Assume ATAPI DMA is OK by default */ + /* some drives can only do ATAPI DMA on read/write */ + if (unlikely(qc->dev->horkage & ATA_HORKAGE_DMA_RW_ONLY)) { + struct scsi_cmnd *cmd = qc->scsicmd; + u8 *scsicmd = cmd->cmnd; + + switch (scsicmd[0]) { + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + case READ_6: + case WRITE_6: + /* atapi dma maybe ok */ + break; + default: + /* turn off atapi dma */ + return 1; + } + } + if (ap->ops->check_atapi_dma) rc = ap->ops->check_atapi_dma(qc); @@ -3725,7 +4206,7 @@ static int ata_sg_setup(struct ata_queued_cmd *qc) struct scatterlist *lsg = &sg[qc->n_elem - 1]; int n_elem, pre_n_elem, dir, trim_sg = 0; - VPRINTK("ENTER, ata%u\n", ap->id); + VPRINTK("ENTER, ata%u\n", ap->print_id); WARN_ON(!(qc->flags & ATA_QCFLAG_SG)); /* we must lengthen transfers to end on a 32-bit boundary */ @@ -3874,10 +4355,10 @@ void ata_data_xfer_noirq(struct ata_device *adev, unsigned char *buf, /** - * ata_pio_sector - Transfer ATA_SECT_SIZE (512 bytes) of data. + * ata_pio_sector - Transfer a sector of data. * @qc: Command on going * - * Transfer ATA_SECT_SIZE of data from/to the ATA device. + * Transfer qc->sect_size bytes of data from/to the ATA device. * * LOCKING: * Inherited from caller. @@ -3892,7 +4373,7 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) unsigned int offset; unsigned char *buf; - if (qc->curbytes == qc->nbytes - ATA_SECT_SIZE) + if (qc->curbytes == qc->nbytes - qc->sect_size) ap->hsm_task_state = HSM_ST_LAST; page = sg[qc->cursg].page; @@ -3912,17 +4393,17 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) buf = kmap_atomic(page, KM_IRQ0); /* do the actual data transfer */ - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write); kunmap_atomic(buf, KM_IRQ0); local_irq_restore(flags); } else { buf = page_address(page); - ap->ops->data_xfer(qc->dev, buf + offset, ATA_SECT_SIZE, do_write); + ap->ops->data_xfer(qc->dev, buf + offset, qc->sect_size, do_write); } - qc->curbytes += ATA_SECT_SIZE; - qc->cursg_ofs += ATA_SECT_SIZE; + qc->curbytes += qc->sect_size; + qc->cursg_ofs += qc->sect_size; if (qc->cursg_ofs == (&sg[qc->cursg])->length) { qc->cursg++; @@ -3931,10 +4412,10 @@ static void ata_pio_sector(struct ata_queued_cmd *qc) } /** - * ata_pio_sectors - Transfer one or many 512-byte sectors. + * ata_pio_sectors - Transfer one or many sectors. * @qc: Command on going * - * Transfer one or many ATA_SECT_SIZE of data from/to the + * Transfer one or many sectors of data from/to the * ATA device for the DRQ request. * * LOCKING: @@ -3949,7 +4430,7 @@ static void ata_pio_sectors(struct ata_queued_cmd *qc) WARN_ON(qc->dev->multi_count == 0); - nsect = min((qc->nbytes - qc->curbytes) / ATA_SECT_SIZE, + nsect = min((qc->nbytes - qc->curbytes) / qc->sect_size, qc->dev->multi_count); while (nsect--) ata_pio_sector(qc); @@ -4126,7 +4607,7 @@ static void atapi_pio_bytes(struct ata_queued_cmd *qc) if (do_write != i_write) goto err_out; - VPRINTK("ata%u: xfering %d bytes\n", ap->id, bytes); + VPRINTK("ata%u: xfering %d bytes\n", ap->print_id, bytes); __atapi_pio_bytes(qc, bytes); @@ -4243,7 +4724,7 @@ int ata_hsm_move(struct ata_port *ap, struct ata_queued_cmd *qc, fsm_start: DPRINTK("ata%u: protocol %d task_state %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, ap->hsm_task_state, status); + ap->print_id, qc->tf.protocol, ap->hsm_task_state, status); switch (ap->hsm_task_state) { case HSM_ST_FIRST: @@ -4276,8 +4757,8 @@ fsm_start: * let the EH abort the command or reset the device. */ if (unlikely(status & (ATA_ERR | ATA_DF))) { - printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n", - ap->id, status); + ata_port_printk(ap, KERN_WARNING, "DRQ=1 with device " + "error, dev_stat 0x%X\n", status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -4334,8 +4815,9 @@ fsm_start: * let the EH abort the command or reset the device. */ if (unlikely(status & (ATA_ERR | ATA_DF))) { - printk(KERN_WARNING "ata%d: DRQ=1 with device error, dev_stat 0x%X\n", - ap->id, status); + ata_port_printk(ap, KERN_WARNING, "DRQ=1 with " + "device error, dev_stat 0x%X\n", + status); qc->err_mask |= AC_ERR_HSM; ap->hsm_task_state = HSM_ST_ERR; goto fsm_start; @@ -4421,7 +4903,7 @@ fsm_start: /* no more data to transfer */ DPRINTK("ata%u: dev %u command complete, drv_stat 0x%x\n", - ap->id, qc->dev->devno, status); + ap->print_id, qc->dev->devno, status); WARN_ON(qc->err_mask); @@ -4604,8 +5086,8 @@ static void fill_result_tf(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - ap->ops->tf_read(ap, &qc->result_tf); qc->result_tf.flags = qc->tf.flags; + ap->ops->tf_read(ap, &qc->result_tf); } /** @@ -4963,7 +5445,7 @@ inline unsigned int ata_host_intr (struct ata_port *ap, u8 status, host_stat = 0; VPRINTK("ata%u: protocol %d task_state %d\n", - ap->id, qc->tf.protocol, ap->hsm_task_state); + ap->print_id, qc->tf.protocol, ap->hsm_task_state); /* Check whether we are expecting interrupt in this state */ switch (ap->hsm_task_state) { @@ -4984,7 +5466,8 @@ inline unsigned int ata_host_intr (struct ata_port *ap, qc->tf.protocol == ATA_PROT_ATAPI_DMA) { /* check status of DMA engine */ host_stat = ap->ops->bmdma_status(ap); - VPRINTK("ata%u: host_stat 0x%X\n", ap->id, host_stat); + VPRINTK("ata%u: host_stat 0x%X\n", + ap->print_id, host_stat); /* if it's not our irq... */ if (!(host_stat & ATA_DMA_INTR)) @@ -5245,6 +5728,7 @@ int ata_flush_cache(struct ata_device *dev) return 0; } +#ifdef CONFIG_PM static int ata_host_request_pm(struct ata_host *host, pm_message_t mesg, unsigned int action, unsigned int ehi_flags, int wait) @@ -5360,6 +5844,7 @@ void ata_host_resume(struct ata_host *host) ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET, 0); host->dev->power.power_state = PMSG_ON; } +#endif /** * ata_port_start - Set port up for dma. @@ -5425,42 +5910,35 @@ void ata_dev_init(struct ata_device *dev) } /** - * ata_port_init - Initialize an ata_port structure - * @ap: Structure to initialize - * @host: Collection of hosts to which @ap belongs - * @ent: Probe information provided by low-level driver - * @port_no: Port number associated with this ata_port + * ata_port_alloc - allocate and initialize basic ATA port resources + * @host: ATA host this allocated port belongs to + * + * Allocate and initialize basic ATA port resources. * - * Initialize a new ata_port structure. + * RETURNS: + * Allocate ATA port on success, NULL on failure. * * LOCKING: - * Inherited from caller. + * Inherited from calling layer (may sleep). */ -void ata_port_init(struct ata_port *ap, struct ata_host *host, - const struct ata_probe_ent *ent, unsigned int port_no) +struct ata_port *ata_port_alloc(struct ata_host *host) { + struct ata_port *ap; unsigned int i; + DPRINTK("ENTER\n"); + + ap = kzalloc(sizeof(*ap), GFP_KERNEL); + if (!ap) + return NULL; + ap->lock = &host->lock; ap->flags = ATA_FLAG_DISABLED; - ap->id = ata_unique_id++; + ap->print_id = -1; ap->ctl = ATA_DEVCTL_OBS; ap->host = host; - ap->dev = ent->dev; - ap->port_no = port_no; - if (port_no == 1 && ent->pinfo2) { - ap->pio_mask = ent->pinfo2->pio_mask; - ap->mwdma_mask = ent->pinfo2->mwdma_mask; - ap->udma_mask = ent->pinfo2->udma_mask; - ap->flags |= ent->pinfo2->flags; - ap->ops = ent->pinfo2->port_ops; - } else { - ap->pio_mask = ent->pio_mask; - ap->mwdma_mask = ent->mwdma_mask; - ap->udma_mask = ent->udma_mask; - ap->flags |= ent->port_flags; - ap->ops = ent->port_ops; - } + ap->dev = host->dev; + ap->hw_sata_spd_limit = UINT_MAX; ap->active_tag = ATA_TAG_POISON; ap->last_ctl = 0xFF; @@ -5480,10 +5958,7 @@ void ata_port_init(struct ata_port *ap, struct ata_host *host, INIT_LIST_HEAD(&ap->eh_done_q); init_waitqueue_head(&ap->eh_wait_q); - /* set cable type */ ap->cbl = ATA_CBL_NONE; - if (ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *dev = &ap->device[i]; @@ -5496,94 +5971,209 @@ void ata_port_init(struct ata_port *ap, struct ata_host *host, ap->stats.unhandled_irq = 1; ap->stats.idle_irq = 1; #endif + return ap; +} + +static void ata_host_release(struct device *gendev, void *res) +{ + struct ata_host *host = dev_get_drvdata(gendev); + int i; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + if (!ap) + continue; + + if ((host->flags & ATA_HOST_STARTED) && ap->ops->port_stop) + ap->ops->port_stop(ap); + } + + if ((host->flags & ATA_HOST_STARTED) && host->ops->host_stop) + host->ops->host_stop(host); + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + + if (!ap) + continue; + + if (ap->scsi_host) + scsi_host_put(ap->scsi_host); - memcpy(&ap->ioaddr, &ent->port[port_no], sizeof(struct ata_ioports)); + kfree(ap); + host->ports[i] = NULL; + } + + dev_set_drvdata(gendev, NULL); } /** - * ata_port_init_shost - Initialize SCSI host associated with ATA port - * @ap: ATA port to initialize SCSI host for - * @shost: SCSI host associated with @ap + * ata_host_alloc - allocate and init basic ATA host resources + * @dev: generic device this host is associated with + * @max_ports: maximum number of ATA ports associated with this host + * + * Allocate and initialize basic ATA host resources. LLD calls + * this function to allocate a host, initializes it fully and + * attaches it using ata_host_register(). + * + * @max_ports ports are allocated and host->n_ports is + * initialized to @max_ports. The caller is allowed to decrease + * host->n_ports before calling ata_host_register(). The unused + * ports will be automatically freed on registration. * - * Initialize SCSI host @shost associated with ATA port @ap. + * RETURNS: + * Allocate ATA host on success, NULL on failure. * * LOCKING: - * Inherited from caller. + * Inherited from calling layer (may sleep). */ -static void ata_port_init_shost(struct ata_port *ap, struct Scsi_Host *shost) +struct ata_host *ata_host_alloc(struct device *dev, int max_ports) { - ap->scsi_host = shost; + struct ata_host *host; + size_t sz; + int i; + + DPRINTK("ENTER\n"); + + if (!devres_open_group(dev, NULL, GFP_KERNEL)) + return NULL; + + /* alloc a container for our list of ATA ports (buses) */ + sz = sizeof(struct ata_host) + (max_ports + 1) * sizeof(void *); + /* alloc a container for our list of ATA ports (buses) */ + host = devres_alloc(ata_host_release, sz, GFP_KERNEL); + if (!host) + goto err_out; + + devres_add(dev, host); + dev_set_drvdata(dev, host); + + spin_lock_init(&host->lock); + host->dev = dev; + host->n_ports = max_ports; + + /* allocate ports bound to this host */ + for (i = 0; i < max_ports; i++) { + struct ata_port *ap; + + ap = ata_port_alloc(host); + if (!ap) + goto err_out; + + ap->port_no = i; + host->ports[i] = ap; + } - shost->unique_id = ap->id; - shost->max_id = 16; - shost->max_lun = 1; - shost->max_channel = 1; - shost->max_cmd_len = 12; + devres_remove_group(dev, NULL); + return host; + + err_out: + devres_release_group(dev, NULL); + return NULL; } /** - * ata_port_add - Attach low-level ATA driver to system - * @ent: Information provided by low-level driver - * @host: Collections of ports to which we add - * @port_no: Port number associated with this host + * ata_host_alloc_pinfo - alloc host and init with port_info array + * @dev: generic device this host is associated with + * @ppi: array of ATA port_info to initialize host with + * @n_ports: number of ATA ports attached to this host * - * Attach low-level ATA driver to system. - * - * LOCKING: - * PCI/etc. bus probe sem. + * Allocate ATA host and initialize with info from @ppi. If NULL + * terminated, @ppi may contain fewer entries than @n_ports. The + * last entry will be used for the remaining ports. * * RETURNS: - * New ata_port on success, for NULL on error. + * Allocate ATA host on success, NULL on failure. + * + * LOCKING: + * Inherited from calling layer (may sleep). */ -static struct ata_port * ata_port_add(const struct ata_probe_ent *ent, - struct ata_host *host, - unsigned int port_no) +struct ata_host *ata_host_alloc_pinfo(struct device *dev, + const struct ata_port_info * const * ppi, + int n_ports) { - struct Scsi_Host *shost; - struct ata_port *ap; - - DPRINTK("ENTER\n"); + const struct ata_port_info *pi; + struct ata_host *host; + int i, j; - if (!ent->port_ops->error_handler && - !(ent->port_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) { - printk(KERN_ERR "ata%u: no reset mechanism available\n", - port_no); + host = ata_host_alloc(dev, n_ports); + if (!host) return NULL; - } - shost = scsi_host_alloc(ent->sht, sizeof(struct ata_port)); - if (!shost) - return NULL; + for (i = 0, j = 0, pi = NULL; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; - shost->transportt = &ata_scsi_transport_template; + if (ppi[j]) + pi = ppi[j++]; - ap = ata_shost_to_port(shost); + ap->pio_mask = pi->pio_mask; + ap->mwdma_mask = pi->mwdma_mask; + ap->udma_mask = pi->udma_mask; + ap->flags |= pi->flags; + ap->ops = pi->port_ops; - ata_port_init(ap, host, ent, port_no); - ata_port_init_shost(ap, shost); + if (!host->ops && (pi->port_ops != &ata_dummy_port_ops)) + host->ops = pi->port_ops; + if (!host->private_data && pi->private_data) + host->private_data = pi->private_data; + } - return ap; + return host; } -static void ata_host_release(struct device *gendev, void *res) +/** + * ata_host_start - start and freeze ports of an ATA host + * @host: ATA host to start ports for + * + * Start and then freeze ports of @host. Started status is + * recorded in host->flags, so this function can be called + * multiple times. Ports are guaranteed to get started only + * once. If host->ops isn't initialized yet, its set to the + * first non-dummy port ops. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 if all ports are started successfully, -errno otherwise. + */ +int ata_host_start(struct ata_host *host) { - struct ata_host *host = dev_get_drvdata(gendev); - int i; + int i, rc; + + if (host->flags & ATA_HOST_STARTED) + return 0; for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - if (!ap) - continue; + if (!host->ops && !ata_port_is_dummy(ap)) + host->ops = ap->ops; - if (ap->ops->port_stop) - ap->ops->port_stop(ap); + if (ap->ops->port_start) { + rc = ap->ops->port_start(ap); + if (rc) { + ata_port_printk(ap, KERN_ERR, "failed to " + "start port (errno=%d)\n", rc); + goto err_out; + } + } - scsi_host_put(ap->scsi_host); + ata_eh_freeze_port(ap); } - if (host->ops->host_stop) - host->ops->host_stop(host); + host->flags |= ATA_HOST_STARTED; + return 0; + + err_out: + while (--i >= 0) { + struct ata_port *ap = host->ports[i]; + + if (ap->ops->port_stop) + ap->ops->port_stop(ap); + } + return rc; } /** @@ -5597,7 +6187,7 @@ static void ata_host_release(struct device *gendev, void *res) * PCI/etc. bus probe sem. * */ - +/* KILLME - the only user left is ipr */ void ata_host_init(struct ata_host *host, struct device *dev, unsigned long flags, const struct ata_port_operations *ops) { @@ -5608,155 +6198,95 @@ void ata_host_init(struct ata_host *host, struct device *dev, } /** - * ata_device_add - Register hardware device with ATA and SCSI layers - * @ent: Probe information describing hardware device to be registered - * - * This function processes the information provided in the probe - * information struct @ent, allocates the necessary ATA and SCSI - * host information structures, initializes them, and registers - * everything with requisite kernel subsystems. + * ata_host_register - register initialized ATA host + * @host: ATA host to register + * @sht: template for SCSI host * - * This function requests irqs, probes the ATA bus, and probes - * the SCSI bus. + * Register initialized ATA host. @host is allocated using + * ata_host_alloc() and fully initialized by LLD. This function + * starts ports, registers @host with ATA and SCSI layers and + * probe registered devices. * * LOCKING: - * PCI/etc. bus probe sem. + * Inherited from calling layer (may sleep). * * RETURNS: - * Number of ports registered. Zero on error (no ports registered). + * 0 on success, -errno otherwise. */ -int ata_device_add(const struct ata_probe_ent *ent) +int ata_host_register(struct ata_host *host, struct scsi_host_template *sht) { - unsigned int i; - struct device *dev = ent->dev; - struct ata_host *host; - int rc; - - DPRINTK("ENTER\n"); + int i, rc; - if (ent->irq == 0) { - dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n"); - return 0; + /* host must have been started */ + if (!(host->flags & ATA_HOST_STARTED)) { + dev_printk(KERN_ERR, host->dev, + "BUG: trying to register unstarted host\n"); + WARN_ON(1); + return -EINVAL; } - if (!devres_open_group(dev, ata_device_add, GFP_KERNEL)) - return 0; + /* Blow away unused ports. This happens when LLD can't + * determine the exact number of ports to allocate at + * allocation time. + */ + for (i = host->n_ports; host->ports[i]; i++) + kfree(host->ports[i]); - /* alloc a container for our list of ATA ports (buses) */ - host = devres_alloc(ata_host_release, sizeof(struct ata_host) + - (ent->n_ports * sizeof(void *)), GFP_KERNEL); - if (!host) - goto err_out; - devres_add(dev, host); - dev_set_drvdata(dev, host); + /* give ports names and add SCSI hosts */ + for (i = 0; i < host->n_ports; i++) + host->ports[i]->print_id = ata_print_id++; - ata_host_init(host, dev, ent->_host_flags, ent->port_ops); - host->n_ports = ent->n_ports; - host->irq = ent->irq; - host->irq2 = ent->irq2; - host->iomap = ent->iomap; - host->private_data = ent->private_data; + rc = ata_scsi_add_hosts(host, sht); + if (rc) + return rc; - /* register each port bound to this device */ + /* set cable, sata_spd_limit and report */ for (i = 0; i < host->n_ports; i++) { - struct ata_port *ap; - unsigned long xfer_mode_mask; - int irq_line = ent->irq; - - ap = ata_port_add(ent, host, i); - host->ports[i] = ap; - if (!ap) - goto err_out; + struct ata_port *ap = host->ports[i]; + int irq_line; + u32 scontrol; + unsigned long xfer_mask; - /* dummy? */ - if (ent->dummy_port_mask & (1 << i)) { - ata_port_printk(ap, KERN_INFO, "DUMMY\n"); - ap->ops = &ata_dummy_port_ops; - continue; - } + /* set SATA cable type if still unset */ + if (ap->cbl == ATA_CBL_NONE && (ap->flags & ATA_FLAG_SATA)) + ap->cbl = ATA_CBL_SATA; - /* start port */ - rc = ap->ops->port_start(ap); - if (rc) { - host->ports[i] = NULL; - scsi_host_put(ap->scsi_host); - goto err_out; + /* init sata_spd_limit to the current value */ + if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { + int spd = (scontrol >> 4) & 0xf; + ap->hw_sata_spd_limit &= (1 << spd) - 1; } + ap->sata_spd_limit = ap->hw_sata_spd_limit; - /* Report the secondary IRQ for second channel legacy */ - if (i == 1 && ent->irq2) - irq_line = ent->irq2; + /* report the secondary IRQ for second channel legacy */ + irq_line = host->irq; + if (i == 1 && host->irq2) + irq_line = host->irq2; - xfer_mode_mask =(ap->udma_mask << ATA_SHIFT_UDMA) | - (ap->mwdma_mask << ATA_SHIFT_MWDMA) | - (ap->pio_mask << ATA_SHIFT_PIO); + xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask, + ap->udma_mask); /* print per-port info to dmesg */ - ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p " - "ctl 0x%p bmdma 0x%p irq %d\n", - ap->flags & ATA_FLAG_SATA ? 'S' : 'P', - ata_mode_string(xfer_mode_mask), - ap->ioaddr.cmd_addr, - ap->ioaddr.ctl_addr, - ap->ioaddr.bmdma_addr, - irq_line); - - /* freeze port before requesting IRQ */ - ata_eh_freeze_port(ap); - } - - /* obtain irq, that may be shared between channels */ - rc = devm_request_irq(dev, ent->irq, ent->port_ops->irq_handler, - ent->irq_flags, DRV_NAME, host); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq, rc); - goto err_out; - } - - /* do we have a second IRQ for the other channel, eg legacy mode */ - if (ent->irq2) { - /* We will get weird core code crashes later if this is true - so trap it now */ - BUG_ON(ent->irq == ent->irq2); - - rc = devm_request_irq(dev, ent->irq2, - ent->port_ops->irq_handler, ent->irq_flags, - DRV_NAME, host); - if (rc) { - dev_printk(KERN_ERR, dev, "irq %lu request failed: %d\n", - ent->irq2, rc); - goto err_out; - } + if (!ata_port_is_dummy(ap)) + ata_port_printk(ap, KERN_INFO, "%cATA max %s cmd 0x%p " + "ctl 0x%p bmdma 0x%p irq %d\n", + ap->cbl == ATA_CBL_SATA ? 'S' : 'P', + ata_mode_string(xfer_mask), + ap->ioaddr.cmd_addr, + ap->ioaddr.ctl_addr, + ap->ioaddr.bmdma_addr, + irq_line); + else + ata_port_printk(ap, KERN_INFO, "DUMMY\n"); } - /* resource acquisition complete */ - devres_remove_group(dev, ata_device_add); - /* perform each probe synchronously */ DPRINTK("probe begin\n"); for (i = 0; i < host->n_ports; i++) { struct ata_port *ap = host->ports[i]; - u32 scontrol; int rc; - /* init sata_spd_limit to the current value */ - if (sata_scr_read(ap, SCR_CONTROL, &scontrol) == 0) { - int spd = (scontrol >> 4) & 0xf; - ap->hw_sata_spd_limit &= (1 << spd) - 1; - } - ap->sata_spd_limit = ap->hw_sata_spd_limit; - - rc = scsi_add_host(ap->scsi_host, dev); - if (rc) { - ata_port_printk(ap, KERN_ERR, "scsi_add_host failed\n"); - /* FIXME: do something useful here */ - /* FIXME: handle unconditional calls to - * scsi_scan_host and ata_host_remove, below, - * at the very least - */ - } - + /* probe */ if (ap->ops->error_handler) { struct ata_eh_info *ehi = &ap->eh_info; unsigned long flags; @@ -5778,9 +6308,9 @@ int ata_device_add(const struct ata_probe_ent *ent) /* wait for EH to finish */ ata_port_wait_eh(ap); } else { - DPRINTK("ata%u: bus probe begin\n", ap->id); + DPRINTK("ata%u: bus probe begin\n", ap->print_id); rc = ata_bus_probe(ap); - DPRINTK("ata%u: bus probe end\n", ap->id); + DPRINTK("ata%u: bus probe end\n", ap->print_id); if (rc) { /* FIXME: do something useful here? @@ -5801,17 +6331,52 @@ int ata_device_add(const struct ata_probe_ent *ent) ata_scsi_scan_host(ap); } - VPRINTK("EXIT, returning %u\n", ent->n_ports); - return ent->n_ports; /* success */ - - err_out: - devres_release_group(dev, ata_device_add); - dev_set_drvdata(dev, NULL); - VPRINTK("EXIT, returning %d\n", rc); return 0; } /** + * ata_host_activate - start host, request IRQ and register it + * @host: target ATA host + * @irq: IRQ to request + * @irq_handler: irq_handler used when requesting IRQ + * @irq_flags: irq_flags used when requesting IRQ + * @sht: scsi_host_template to use when registering the host + * + * After allocating an ATA host and initializing it, most libata + * LLDs perform three steps to activate the host - start host, + * request IRQ and register it. This helper takes necessasry + * arguments and performs the three steps in one go. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_host_activate(struct ata_host *host, int irq, + irq_handler_t irq_handler, unsigned long irq_flags, + struct scsi_host_template *sht) +{ + int rc; + + rc = ata_host_start(host); + if (rc) + return rc; + + rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags, + dev_driver_string(host->dev), host); + if (rc) + return rc; + + rc = ata_host_register(host, sht); + /* if failed, just free the IRQ and leave ports alone */ + if (rc) + devm_free_irq(host->dev, irq, host); + + return rc; +} + +/** * ata_port_detach - Detach ATA port in prepration of device removal * @ap: ATA port to be detached * @@ -5886,36 +6451,6 @@ void ata_host_detach(struct ata_host *host) ata_port_detach(host->ports[i]); } -struct ata_probe_ent * -ata_probe_ent_alloc(struct device *dev, const struct ata_port_info *port) -{ - struct ata_probe_ent *probe_ent; - - /* XXX - the following if can go away once all LLDs are managed */ - if (!list_empty(&dev->devres_head)) - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - else - probe_ent = kzalloc(sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) { - printk(KERN_ERR DRV_NAME "(%s): out of memory\n", - kobject_name(&(dev->kobj))); - return NULL; - } - - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = dev; - - probe_ent->sht = port->sht; - probe_ent->port_flags = port->flags; - probe_ent->pio_mask = port->pio_mask; - probe_ent->mwdma_mask = port->mwdma_mask; - probe_ent->udma_mask = port->udma_mask; - probe_ent->port_ops = port->port_ops; - probe_ent->private_data = port->private_data; - - return probe_ent; -} - /** * ata_std_ports - initialize ioaddr with standard port offsets. * @ioaddr: IO address structure to be initialized @@ -5998,14 +6533,14 @@ int pci_test_config_bits(struct pci_dev *pdev, const struct pci_bits *bits) return (tmp == bits->val) ? 1 : 0; } +#ifdef CONFIG_PM void ata_pci_device_do_suspend(struct pci_dev *pdev, pm_message_t mesg) { pci_save_state(pdev); + pci_disable_device(pdev); - if (mesg.event == PM_EVENT_SUSPEND) { - pci_disable_device(pdev); + if (mesg.event == PM_EVENT_SUSPEND) pci_set_power_state(pdev, PCI_D3hot); - } } int ata_pci_device_do_resume(struct pci_dev *pdev) @@ -6050,6 +6585,8 @@ int ata_pci_device_resume(struct pci_dev *pdev) ata_host_resume(host); return rc; } +#endif /* CONFIG_PM */ + #endif /* CONFIG_PCI */ @@ -6179,6 +6716,10 @@ const struct ata_port_operations ata_dummy_port_ops = { .port_stop = ata_dummy_noret, }; +const struct ata_port_info ata_dummy_port_info = { + .port_ops = &ata_dummy_port_ops, +}; + /* * libata is essentially a library of internal helper functions for * low-level ATA host controller drivers. As such, the API/ABI is @@ -6190,10 +6731,15 @@ EXPORT_SYMBOL_GPL(sata_deb_timing_normal); EXPORT_SYMBOL_GPL(sata_deb_timing_hotplug); EXPORT_SYMBOL_GPL(sata_deb_timing_long); EXPORT_SYMBOL_GPL(ata_dummy_port_ops); +EXPORT_SYMBOL_GPL(ata_dummy_port_info); EXPORT_SYMBOL_GPL(ata_std_bios_param); EXPORT_SYMBOL_GPL(ata_std_ports); EXPORT_SYMBOL_GPL(ata_host_init); -EXPORT_SYMBOL_GPL(ata_device_add); +EXPORT_SYMBOL_GPL(ata_host_alloc); +EXPORT_SYMBOL_GPL(ata_host_alloc_pinfo); +EXPORT_SYMBOL_GPL(ata_host_start); +EXPORT_SYMBOL_GPL(ata_host_register); +EXPORT_SYMBOL_GPL(ata_host_activate); EXPORT_SYMBOL_GPL(ata_host_detach); EXPORT_SYMBOL_GPL(ata_sg_init); EXPORT_SYMBOL_GPL(ata_sg_init_one); @@ -6205,6 +6751,7 @@ EXPORT_SYMBOL_GPL(ata_tf_load); EXPORT_SYMBOL_GPL(ata_tf_read); EXPORT_SYMBOL_GPL(ata_noop_dev_select); EXPORT_SYMBOL_GPL(ata_std_dev_select); +EXPORT_SYMBOL_GPL(sata_print_link_status); EXPORT_SYMBOL_GPL(ata_tf_to_fis); EXPORT_SYMBOL_GPL(ata_tf_from_fis); EXPORT_SYMBOL_GPL(ata_check_status); @@ -6212,6 +6759,7 @@ EXPORT_SYMBOL_GPL(ata_altstatus); EXPORT_SYMBOL_GPL(ata_exec_command); EXPORT_SYMBOL_GPL(ata_port_start); EXPORT_SYMBOL_GPL(ata_interrupt); +EXPORT_SYMBOL_GPL(ata_do_set_mode); EXPORT_SYMBOL_GPL(ata_data_xfer); EXPORT_SYMBOL_GPL(ata_data_xfer_noirq); EXPORT_SYMBOL_GPL(ata_qc_prep); @@ -6227,6 +6775,7 @@ EXPORT_SYMBOL_GPL(ata_bmdma_drive_eh); EXPORT_SYMBOL_GPL(ata_bmdma_error_handler); EXPORT_SYMBOL_GPL(ata_bmdma_post_internal_cmd); EXPORT_SYMBOL_GPL(ata_port_probe); +EXPORT_SYMBOL_GPL(ata_dev_disable); EXPORT_SYMBOL_GPL(sata_set_spd); EXPORT_SYMBOL_GPL(sata_phy_debounce); EXPORT_SYMBOL_GPL(sata_phy_resume); @@ -6257,10 +6806,13 @@ EXPORT_SYMBOL_GPL(sata_scr_write); EXPORT_SYMBOL_GPL(sata_scr_write_flush); EXPORT_SYMBOL_GPL(ata_port_online); EXPORT_SYMBOL_GPL(ata_port_offline); +#ifdef CONFIG_PM EXPORT_SYMBOL_GPL(ata_host_suspend); EXPORT_SYMBOL_GPL(ata_host_resume); +#endif /* CONFIG_PM */ EXPORT_SYMBOL_GPL(ata_id_string); EXPORT_SYMBOL_GPL(ata_id_c_string); +EXPORT_SYMBOL_GPL(ata_id_to_dma_mode); EXPORT_SYMBOL_GPL(ata_device_blacklisted); EXPORT_SYMBOL_GPL(ata_scsi_simulate); @@ -6270,19 +6822,24 @@ EXPORT_SYMBOL_GPL(ata_timing_merge); #ifdef CONFIG_PCI EXPORT_SYMBOL_GPL(pci_test_config_bits); -EXPORT_SYMBOL_GPL(ata_pci_init_native_mode); +EXPORT_SYMBOL_GPL(ata_pci_init_native_host); +EXPORT_SYMBOL_GPL(ata_pci_prepare_native_host); EXPORT_SYMBOL_GPL(ata_pci_init_one); EXPORT_SYMBOL_GPL(ata_pci_remove_one); +#ifdef CONFIG_PM EXPORT_SYMBOL_GPL(ata_pci_device_do_suspend); EXPORT_SYMBOL_GPL(ata_pci_device_do_resume); EXPORT_SYMBOL_GPL(ata_pci_device_suspend); EXPORT_SYMBOL_GPL(ata_pci_device_resume); +#endif /* CONFIG_PM */ EXPORT_SYMBOL_GPL(ata_pci_default_filter); EXPORT_SYMBOL_GPL(ata_pci_clear_simplex); #endif /* CONFIG_PCI */ +#ifdef CONFIG_PM EXPORT_SYMBOL_GPL(ata_scsi_device_suspend); EXPORT_SYMBOL_GPL(ata_scsi_device_resume); +#endif /* CONFIG_PM */ EXPORT_SYMBOL_GPL(ata_eng_timeout); EXPORT_SYMBOL_GPL(ata_port_schedule_eh); @@ -6297,3 +6854,9 @@ EXPORT_SYMBOL_GPL(ata_irq_on); EXPORT_SYMBOL_GPL(ata_dummy_irq_on); EXPORT_SYMBOL_GPL(ata_irq_ack); EXPORT_SYMBOL_GPL(ata_dummy_irq_ack); +EXPORT_SYMBOL_GPL(ata_dev_try_classify); + +EXPORT_SYMBOL_GPL(ata_cable_40wire); +EXPORT_SYMBOL_GPL(ata_cable_80wire); +EXPORT_SYMBOL_GPL(ata_cable_unknown); +EXPORT_SYMBOL_GPL(ata_cable_sata); diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 52c85af..2bff9ad 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -44,10 +44,41 @@ #include "libata.h" +enum { + ATA_EH_SPDN_NCQ_OFF = (1 << 0), + ATA_EH_SPDN_SPEED_DOWN = (1 << 1), + ATA_EH_SPDN_FALLBACK_TO_PIO = (1 << 2), +}; + static void __ata_port_freeze(struct ata_port *ap); static void ata_eh_finish(struct ata_port *ap); +#ifdef CONFIG_PM static void ata_eh_handle_port_suspend(struct ata_port *ap); static void ata_eh_handle_port_resume(struct ata_port *ap); +static int ata_eh_suspend(struct ata_port *ap, + struct ata_device **r_failed_dev); +static void ata_eh_prep_resume(struct ata_port *ap); +static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev); +#else /* CONFIG_PM */ +static void ata_eh_handle_port_suspend(struct ata_port *ap) +{ } + +static void ata_eh_handle_port_resume(struct ata_port *ap) +{ } + +static int ata_eh_suspend(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + return 0; +} + +static void ata_eh_prep_resume(struct ata_port *ap) +{ } + +static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + return 0; +} +#endif /* CONFIG_PM */ static void ata_ering_record(struct ata_ering *ering, int is_io, unsigned int err_mask) @@ -65,12 +96,9 @@ static void ata_ering_record(struct ata_ering *ering, int is_io, ent->timestamp = get_jiffies_64(); } -static struct ata_ering_entry * ata_ering_top(struct ata_ering *ering) +static void ata_ering_clear(struct ata_ering *ering) { - struct ata_ering_entry *ent = &ering->ring[ering->cursor]; - if (!ent->err_mask) - return NULL; - return ent; + memset(ering, 0, sizeof(*ering)); } static int ata_ering_map(struct ata_ering *ering, @@ -585,7 +613,7 @@ static void __ata_port_freeze(struct ata_port *ap) ap->pflags |= ATA_PFLAG_FROZEN; - DPRINTK("ata%u port frozen\n", ap->id); + DPRINTK("ata%u port frozen\n", ap->print_id); } /** @@ -658,7 +686,7 @@ void ata_eh_thaw_port(struct ata_port *ap) spin_unlock_irqrestore(ap->lock, flags); - DPRINTK("ata%u port thawed\n", ap->id); + DPRINTK("ata%u port thawed\n", ap->print_id); } static void ata_eh_scsidone(struct scsi_cmnd *scmd) @@ -954,26 +982,27 @@ static int ata_eh_read_log_10h(struct ata_device *dev, * RETURNS: * 0 on success, AC_ERR_* mask on failure */ -static unsigned int atapi_eh_request_sense(struct ata_device *dev, - unsigned char *sense_buf) +static unsigned int atapi_eh_request_sense(struct ata_queued_cmd *qc) { + struct ata_device *dev = qc->dev; + unsigned char *sense_buf = qc->scsicmd->sense_buffer; struct ata_port *ap = dev->ap; struct ata_taskfile tf; u8 cdb[ATAPI_CDB_LEN]; DPRINTK("ATAPI request sense\n"); - ata_tf_init(dev, &tf); - /* FIXME: is this needed? */ memset(sense_buf, 0, SCSI_SENSE_BUFFERSIZE); - /* XXX: why tf_read here? */ - ap->ops->tf_read(ap, &tf); - - /* fill these in, for the case where they are -not- overwritten */ + /* initialize sense_buf with the error register, + * for the case where they are -not- overwritten + */ sense_buf[0] = 0x70; - sense_buf[2] = tf.feature >> 4; + sense_buf[2] = qc->result_tf.feature >> 4; + + /* some devices time out if garbage left in tf */ + ata_tf_init(dev, &tf); memset(cdb, 0, ATAPI_CDB_LEN); cdb[0] = REQUEST_SENSE; @@ -1027,7 +1056,7 @@ static void ata_eh_analyze_serror(struct ata_port *ap) } if (serror & SERR_INTERNAL) { err_mask |= AC_ERR_SYSTEM; - action |= ATA_EH_SOFTRESET; + action |= ATA_EH_HARDRESET; } if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG)) ata_ehi_hotplugged(&ehc->i); @@ -1122,7 +1151,9 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, return ATA_EH_SOFTRESET; } - if (!(qc->err_mask & AC_ERR_DEV)) + if (stat & (ATA_ERR | ATA_DF)) + qc->err_mask |= AC_ERR_DEV; + else return 0; switch (qc->dev->class) { @@ -1137,8 +1168,7 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, case ATA_DEV_ATAPI: if (!(qc->ap->pflags & ATA_PFLAG_FROZEN)) { - tmp = atapi_eh_request_sense(qc->dev, - qc->scsicmd->sense_buffer); + tmp = atapi_eh_request_sense(qc); if (!tmp) { /* ATA_QCFLAG_SENSE_VALID is used to * tell atapi_qc_complete() that sense @@ -1159,87 +1189,99 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, return action; } -static int ata_eh_categorize_ering_entry(struct ata_ering_entry *ent) +static int ata_eh_categorize_error(int is_io, unsigned int err_mask) { - if (ent->err_mask & (AC_ERR_ATA_BUS | AC_ERR_TIMEOUT)) + if (err_mask & AC_ERR_ATA_BUS) return 1; - if (ent->is_io) { - if (ent->err_mask & AC_ERR_HSM) - return 1; - if ((ent->err_mask & - (AC_ERR_DEV|AC_ERR_MEDIA|AC_ERR_INVALID)) == AC_ERR_DEV) + if (err_mask & AC_ERR_TIMEOUT) + return 2; + + if (is_io) { + if (err_mask & AC_ERR_HSM) return 2; + if ((err_mask & + (AC_ERR_DEV|AC_ERR_MEDIA|AC_ERR_INVALID)) == AC_ERR_DEV) + return 3; } return 0; } -struct speed_down_needed_arg { +struct speed_down_verdict_arg { u64 since; - int nr_errors[3]; + int nr_errors[4]; }; -static int speed_down_needed_cb(struct ata_ering_entry *ent, void *void_arg) +static int speed_down_verdict_cb(struct ata_ering_entry *ent, void *void_arg) { - struct speed_down_needed_arg *arg = void_arg; + struct speed_down_verdict_arg *arg = void_arg; + int cat = ata_eh_categorize_error(ent->is_io, ent->err_mask); if (ent->timestamp < arg->since) return -1; - arg->nr_errors[ata_eh_categorize_ering_entry(ent)]++; + arg->nr_errors[cat]++; return 0; } /** - * ata_eh_speed_down_needed - Determine wheter speed down is necessary + * ata_eh_speed_down_verdict - Determine speed down verdict * @dev: Device of interest * * This function examines error ring of @dev and determines - * whether speed down is necessary. Speed down is necessary if - * there have been more than 3 of Cat-1 errors or 10 of Cat-2 - * errors during last 15 minutes. + * whether NCQ needs to be turned off, transfer speed should be + * stepped down, or falling back to PIO is necessary. + * + * Cat-1 is ATA_BUS error for any command. * - * Cat-1 errors are ATA_BUS, TIMEOUT for any command and HSM - * violation for known supported commands. + * Cat-2 is TIMEOUT for any command or HSM violation for known + * supported commands. * - * Cat-2 errors are unclassified DEV error for known supported + * Cat-3 is is unclassified DEV error for known supported * command. * + * NCQ needs to be turned off if there have been more than 3 + * Cat-2 + Cat-3 errors during last 10 minutes. + * + * Speed down is necessary if there have been more than 3 Cat-1 + + * Cat-2 errors or 10 Cat-3 errors during last 10 minutes. + * + * Falling back to PIO mode is necessary if there have been more + * than 10 Cat-1 + Cat-2 + Cat-3 errors during last 5 minutes. + * * LOCKING: * Inherited from caller. * * RETURNS: - * 1 if speed down is necessary, 0 otherwise + * OR of ATA_EH_SPDN_* flags. */ -static int ata_eh_speed_down_needed(struct ata_device *dev) +static unsigned int ata_eh_speed_down_verdict(struct ata_device *dev) { - const u64 interval = 15LLU * 60 * HZ; - static const int err_limits[3] = { -1, 3, 10 }; - struct speed_down_needed_arg arg; - struct ata_ering_entry *ent; - int err_cat; - u64 j64; + const u64 j5mins = 5LLU * 60 * HZ, j10mins = 10LLU * 60 * HZ; + u64 j64 = get_jiffies_64(); + struct speed_down_verdict_arg arg; + unsigned int verdict = 0; - ent = ata_ering_top(&dev->ering); - if (!ent) - return 0; + /* scan past 10 mins of error history */ + memset(&arg, 0, sizeof(arg)); + arg.since = j64 - min(j64, j10mins); + ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg); - err_cat = ata_eh_categorize_ering_entry(ent); - if (err_cat == 0) - return 0; + if (arg.nr_errors[2] + arg.nr_errors[3] > 3) + verdict |= ATA_EH_SPDN_NCQ_OFF; + if (arg.nr_errors[1] + arg.nr_errors[2] > 3 || arg.nr_errors[3] > 10) + verdict |= ATA_EH_SPDN_SPEED_DOWN; + /* scan past 3 mins of error history */ memset(&arg, 0, sizeof(arg)); + arg.since = j64 - min(j64, j5mins); + ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg); - j64 = get_jiffies_64(); - if (j64 >= interval) - arg.since = j64 - interval; - else - arg.since = 0; - - ata_ering_map(&dev->ering, speed_down_needed_cb, &arg); + if (arg.nr_errors[1] + arg.nr_errors[2] + arg.nr_errors[3] > 10) + verdict |= ATA_EH_SPDN_FALLBACK_TO_PIO; - return arg.nr_errors[err_cat] > err_limits[err_cat]; + return verdict; } /** @@ -1257,31 +1299,80 @@ static int ata_eh_speed_down_needed(struct ata_device *dev) * Kernel thread context (may sleep). * * RETURNS: - * 0 on success, -errno otherwise + * Determined recovery action. */ -static int ata_eh_speed_down(struct ata_device *dev, int is_io, - unsigned int err_mask) +static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io, + unsigned int err_mask) { - if (!err_mask) + unsigned int verdict; + unsigned int action = 0; + + /* don't bother if Cat-0 error */ + if (ata_eh_categorize_error(is_io, err_mask) == 0) return 0; /* record error and determine whether speed down is necessary */ ata_ering_record(&dev->ering, is_io, err_mask); + verdict = ata_eh_speed_down_verdict(dev); - if (!ata_eh_speed_down_needed(dev)) - return 0; + /* turn off NCQ? */ + if ((verdict & ATA_EH_SPDN_NCQ_OFF) && + (dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ | + ATA_DFLAG_NCQ_OFF)) == ATA_DFLAG_NCQ) { + dev->flags |= ATA_DFLAG_NCQ_OFF; + ata_dev_printk(dev, KERN_WARNING, + "NCQ disabled due to excessive errors\n"); + goto done; + } + + /* speed down? */ + if (verdict & ATA_EH_SPDN_SPEED_DOWN) { + /* speed down SATA link speed if possible */ + if (sata_down_spd_limit(dev->ap) == 0) { + action |= ATA_EH_HARDRESET; + goto done; + } - /* speed down SATA link speed if possible */ - if (sata_down_spd_limit(dev->ap) == 0) - return ATA_EH_HARDRESET; + /* lower transfer mode */ + if (dev->spdn_cnt < 2) { + static const int dma_dnxfer_sel[] = + { ATA_DNXFER_DMA, ATA_DNXFER_40C }; + static const int pio_dnxfer_sel[] = + { ATA_DNXFER_PIO, ATA_DNXFER_FORCE_PIO0 }; + int sel; - /* lower transfer mode */ - if (ata_down_xfermask_limit(dev, 0) == 0) - return ATA_EH_SOFTRESET; + if (dev->xfer_shift != ATA_SHIFT_PIO) + sel = dma_dnxfer_sel[dev->spdn_cnt]; + else + sel = pio_dnxfer_sel[dev->spdn_cnt]; + + dev->spdn_cnt++; + + if (ata_down_xfermask_limit(dev, sel) == 0) { + action |= ATA_EH_SOFTRESET; + goto done; + } + } + } + + /* Fall back to PIO? Slowing down to PIO is meaningless for + * SATA. Consider it only for PATA. + */ + if ((verdict & ATA_EH_SPDN_FALLBACK_TO_PIO) && (dev->spdn_cnt >= 2) && + (dev->ap->cbl != ATA_CBL_SATA) && + (dev->xfer_shift != ATA_SHIFT_PIO)) { + if (ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO) == 0) { + dev->spdn_cnt = 0; + action |= ATA_EH_SOFTRESET; + goto done; + } + } - ata_dev_printk(dev, KERN_ERR, - "speed down requested but no transfer mode left\n"); return 0; + done: + /* device has been slowed down, blow error history */ + ata_ering_clear(&dev->ering); + return action; } /** @@ -1536,8 +1627,14 @@ static int ata_eh_reset(struct ata_port *ap, int classify, rc = prereset(ap); if (rc) { if (rc == -ENOENT) { - ata_port_printk(ap, KERN_DEBUG, "port disabled. ignoring.\n"); + ata_port_printk(ap, KERN_DEBUG, + "port disabled. ignoring.\n"); ap->eh_context.i.action &= ~ATA_EH_RESET_MASK; + + for (i = 0; i < ATA_MAX_DEVICES; i++) + classes[i] = ATA_DEV_NONE; + + rc = 0; } else ata_port_printk(ap, KERN_ERR, "prereset failed (errno=%d)\n", rc); @@ -1574,7 +1671,10 @@ static int ata_eh_reset(struct ata_port *ap, int classify, reset == softreset ? "soft" : "hard"); /* mark that this EH session started with reset */ - ehc->i.flags |= ATA_EHI_DID_RESET; + if (reset == hardreset) + ehc->i.flags |= ATA_EHI_DID_HARDRESET; + else + ehc->i.flags |= ATA_EHI_DID_SOFTRESET; rc = ata_do_reset(ap, reset, classes); @@ -1648,12 +1748,17 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, { struct ata_eh_context *ehc = &ap->eh_context; struct ata_device *dev; + unsigned int new_mask = 0; unsigned long flags; int i, rc = 0; DPRINTK("ENTER\n"); - for (i = 0; i < ATA_MAX_DEVICES; i++) { + /* For PATA drive side cable detection to work, IDENTIFY must + * be done backwards such that PDIAG- is released by the slave + * device before the master device is identified. + */ + for (i = ATA_MAX_DEVICES - 1; i >= 0; i--) { unsigned int action, readid_flags = 0; dev = &ap->device[i]; @@ -1665,13 +1770,13 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, if (action & ATA_EH_REVALIDATE && ata_dev_ready(dev)) { if (ata_port_offline(ap)) { rc = -EIO; - break; + goto err; } ata_eh_about_to_do(ap, dev, ATA_EH_REVALIDATE); rc = ata_dev_revalidate(dev, readid_flags); if (rc) - break; + goto err; ata_eh_done(ap, dev, ATA_EH_REVALIDATE); @@ -1689,43 +1794,61 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, rc = ata_dev_read_id(dev, &dev->class, readid_flags, dev->id); - if (rc == 0) { - ehc->i.flags |= ATA_EHI_PRINTINFO; - rc = ata_dev_configure(dev); - ehc->i.flags &= ~ATA_EHI_PRINTINFO; - } else if (rc == -ENOENT) { + switch (rc) { + case 0: + new_mask |= 1 << i; + break; + case -ENOENT: /* IDENTIFY was issued to non-existent * device. No need to reset. Just * thaw and kill the device. */ ata_eh_thaw_port(ap); dev->class = ATA_DEV_UNKNOWN; - rc = 0; - } - - if (rc) { - dev->class = ATA_DEV_UNKNOWN; break; + default: + dev->class = ATA_DEV_UNKNOWN; + goto err; } + } + } - if (ata_dev_enabled(dev)) { - spin_lock_irqsave(ap->lock, flags); - ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; - spin_unlock_irqrestore(ap->lock, flags); + /* PDIAG- should have been released, ask cable type if post-reset */ + if ((ehc->i.flags & ATA_EHI_DID_RESET) && ap->ops->cable_detect) + ap->cbl = ap->ops->cable_detect(ap); - /* new device discovered, configure xfermode */ - ehc->i.flags |= ATA_EHI_SETMODE; - } - } + /* Configure new devices forward such that user doesn't see + * device detection messages backwards. + */ + for (i = 0; i < ATA_MAX_DEVICES; i++) { + dev = &ap->device[i]; + + if (!(new_mask & (1 << i))) + continue; + + ehc->i.flags |= ATA_EHI_PRINTINFO; + rc = ata_dev_configure(dev); + ehc->i.flags &= ~ATA_EHI_PRINTINFO; + if (rc) + goto err; + + spin_lock_irqsave(ap->lock, flags); + ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; + spin_unlock_irqrestore(ap->lock, flags); + + /* new device discovered, configure xfermode */ + ehc->i.flags |= ATA_EHI_SETMODE; } - if (rc) - *r_failed_dev = dev; + return 0; - DPRINTK("EXIT\n"); + err: + *r_failed_dev = dev; + DPRINTK("EXIT rc=%d\n", rc); return rc; } +#ifdef CONFIG_PM /** * ata_eh_suspend - handle suspend EH action * @ap: target host port @@ -1883,6 +2006,7 @@ static int ata_eh_resume(struct ata_port *ap, struct ata_device **r_failed_dev) DPRINTK("EXIT\n"); return 0; } +#endif /* CONFIG_PM */ static int ata_port_nr_enabled(struct ata_port *ap) { @@ -1964,7 +2088,7 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, { struct ata_eh_context *ehc = &ap->eh_context; struct ata_device *dev; - int down_xfermask, i, rc; + int i, rc; DPRINTK("ENTER\n"); @@ -1993,7 +2117,6 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, } retry: - down_xfermask = 0; rc = 0; /* if UNLOADING, finish immediately */ @@ -2038,10 +2161,8 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, /* configure transfer mode if necessary */ if (ehc->i.flags & ATA_EHI_SETMODE) { rc = ata_set_mode(ap, &dev); - if (rc) { - down_xfermask = 1; + if (rc) goto dev_fail; - } ehc->i.flags &= ~ATA_EHI_SETMODE; } @@ -2053,20 +2174,27 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, goto out; dev_fail: + ehc->tries[dev->devno]--; + switch (rc) { - case -ENODEV: - /* device missing, schedule probing */ - ehc->i.probe_mask |= (1 << dev->devno); case -EINVAL: + /* eeek, something went very wrong, give up */ ehc->tries[dev->devno] = 0; break; + + case -ENODEV: + /* device missing or wrong IDENTIFY data, schedule probing */ + ehc->i.probe_mask |= (1 << dev->devno); + /* give it just one more chance */ + ehc->tries[dev->devno] = min(ehc->tries[dev->devno], 1); case -EIO: - sata_down_spd_limit(ap); - default: - ehc->tries[dev->devno]--; - if (down_xfermask && - ata_down_xfermask_limit(dev, ehc->tries[dev->devno] == 1)) - ehc->tries[dev->devno] = 0; + if (ehc->tries[dev->devno] == 1) { + /* This is the last chance, better to slow + * down than lose it. + */ + sata_down_spd_limit(ap); + ata_down_xfermask_limit(dev, ATA_DNXFER_PIO); + } } if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) { @@ -2181,6 +2309,7 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, ata_eh_finish(ap); } +#ifdef CONFIG_PM /** * ata_eh_handle_port_suspend - perform port suspend operation * @ap: port to suspend @@ -2296,3 +2425,4 @@ static void ata_eh_handle_port_resume(struct ata_port *ap) } spin_unlock_irqrestore(ap->lock, flags); } +#endif /* CONFIG_PM */ diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 0009818..9afba2b 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -104,7 +104,7 @@ static const u8 def_control_mpage[CONTROL_MPAGE_LEN] = { * libata transport template. libata doesn't do real transport stuff. * It just needs the eh_timed_out hook. */ -struct scsi_transport_template ata_scsi_transport_template = { +static struct scsi_transport_template ata_scsi_transport_template = { .eh_strategy_handler = ata_scsi_error, .eh_timed_out = ata_scsi_timed_out, .user_scan = ata_scsi_user_scan, @@ -333,6 +333,7 @@ int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg) scsi_cmd[8] = args[3]; scsi_cmd[10] = args[4]; scsi_cmd[12] = args[5]; + scsi_cmd[13] = args[6] & 0x4f; scsi_cmd[14] = args[0]; /* Good values for timeout and retries? Values below @@ -509,6 +510,7 @@ static void ata_dump_status(unsigned id, struct ata_taskfile *tf) } } +#ifdef CONFIG_PM /** * ata_scsi_device_suspend - suspend ATA device associated with sdev * @sdev: the SCSI device to suspend @@ -633,6 +635,7 @@ int ata_scsi_device_resume(struct scsi_device *sdev) sdev->sdev_gendev.power.power_state = PMSG_ON; return 0; } +#endif /* CONFIG_PM */ /** * ata_to_sense_error - convert ATA error to SCSI error @@ -781,7 +784,7 @@ static void ata_gen_passthru_sense(struct ata_queued_cmd *qc) */ if (qc->err_mask || tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { - ata_to_sense_error(qc->ap->id, tf->command, tf->feature, + ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sb[1], &sb[2], &sb[3], verbose); sb[1] &= 0x0f; } @@ -854,7 +857,7 @@ static void ata_gen_ata_sense(struct ata_queued_cmd *qc) */ if (qc->err_mask || tf->command & (ATA_BUSY | ATA_DF | ATA_ERR | ATA_DRQ)) { - ata_to_sense_error(qc->ap->id, tf->command, tf->feature, + ata_to_sense_error(qc->ap->print_id, tf->command, tf->feature, &sb[1], &sb[2], &sb[3], verbose); sb[1] &= 0x0f; } @@ -986,29 +989,32 @@ int ata_scsi_change_queue_depth(struct scsi_device *sdev, int queue_depth) struct ata_port *ap = ata_shost_to_port(sdev->host); struct ata_device *dev; unsigned long flags; - int max_depth; - if (queue_depth < 1) + if (queue_depth < 1 || queue_depth == sdev->queue_depth) return sdev->queue_depth; dev = ata_scsi_find_dev(ap, sdev); if (!dev || !ata_dev_enabled(dev)) return sdev->queue_depth; - max_depth = min(sdev->host->can_queue, ata_id_queue_depth(dev->id)); - max_depth = min(ATA_MAX_QUEUE - 1, max_depth); - if (queue_depth > max_depth) - queue_depth = max_depth; - - scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); - + /* NCQ enabled? */ spin_lock_irqsave(ap->lock, flags); - if (queue_depth > 1) - dev->flags &= ~ATA_DFLAG_NCQ_OFF; - else + dev->flags &= ~ATA_DFLAG_NCQ_OFF; + if (queue_depth == 1 || !ata_ncq_enabled(dev)) { dev->flags |= ATA_DFLAG_NCQ_OFF; + queue_depth = 1; + } spin_unlock_irqrestore(ap->lock, flags); + /* limit and apply queue depth */ + queue_depth = min(queue_depth, sdev->host->can_queue); + queue_depth = min(queue_depth, ata_id_queue_depth(dev->id)); + queue_depth = min(queue_depth, ATA_MAX_QUEUE - 1); + + if (sdev->queue_depth == queue_depth) + return -EINVAL; + + scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, queue_depth); return queue_depth; } @@ -1469,7 +1475,7 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) } if (need_sense && !ap->ops->error_handler) - ata_dump_status(ap->id, &qc->result_tf); + ata_dump_status(ap->print_id, &qc->result_tf); qc->scsidone(cmd); @@ -1495,11 +1501,9 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) static int ata_scmd_need_defer(struct ata_device *dev, int is_io) { struct ata_port *ap = dev->ap; + int is_ncq = is_io && ata_ncq_enabled(dev); - if (!(dev->flags & ATA_DFLAG_NCQ)) - return 0; - - if (is_io) { + if (is_ncq) { if (!ata_tag_valid(ap->active_tag)) return 0; } else { @@ -2674,6 +2678,18 @@ static unsigned int ata_scsi_pass_thru(struct ata_queued_cmd *qc) tf->device = qc->dev->devno ? tf->device | ATA_DEV1 : tf->device & ~ATA_DEV1; + /* READ/WRITE LONG use a non-standard sect_size */ + qc->sect_size = ATA_SECT_SIZE; + switch (tf->command) { + case ATA_CMD_READ_LONG: + case ATA_CMD_READ_LONG_ONCE: + case ATA_CMD_WRITE_LONG: + case ATA_CMD_WRITE_LONG_ONCE: + if (tf->protocol != ATA_PROT_PIO || tf->nsect != 1) + goto invalid_fld; + qc->sect_size = scmd->request_bufflen; + } + /* * Filter SET_FEATURES - XFER MODE command -- otherwise, * SET_FEATURES - XFER MODE must be preceded/succeeded @@ -2774,7 +2790,7 @@ static inline void ata_scsi_dump_cdb(struct ata_port *ap, u8 *scsicmd = cmd->cmnd; DPRINTK("CDB (%u:%d,%d,%d) %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", - ap->id, + ap->print_id, scsidev->channel, scsidev->id, scsidev->lun, scsicmd[0], scsicmd[1], scsicmd[2], scsicmd[3], scsicmd[4], scsicmd[5], scsicmd[6], scsicmd[7], @@ -2788,8 +2804,9 @@ static inline int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, { int rc = 0; - if (unlikely(!scmd->cmd_len)) { - ata_dev_printk(dev, KERN_WARNING, "WARNING: zero len CDB\n"); + if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) { + DPRINTK("bad CDB len=%u, max=%u\n", + scmd->cmd_len, dev->cdb_len); scmd->result = DID_ERROR << 16; done(scmd); return 0; @@ -2944,6 +2961,48 @@ void ata_scsi_simulate(struct ata_device *dev, struct scsi_cmnd *cmd, } } +int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht) +{ + int i, rc; + + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + struct Scsi_Host *shost; + + rc = -ENOMEM; + shost = scsi_host_alloc(sht, sizeof(struct ata_port *)); + if (!shost) + goto err_alloc; + + *(struct ata_port **)&shost->hostdata[0] = ap; + ap->scsi_host = shost; + + shost->transportt = &ata_scsi_transport_template; + shost->unique_id = ap->print_id; + shost->max_id = 16; + shost->max_lun = 1; + shost->max_channel = 1; + shost->max_cmd_len = 16; + + rc = scsi_add_host(ap->scsi_host, ap->host->dev); + if (rc) + goto err_add; + } + + return 0; + + err_add: + scsi_host_put(host->ports[i]->scsi_host); + err_alloc: + while (--i >= 0) { + struct Scsi_Host *shost = host->ports[i]->scsi_host; + + scsi_remove_host(shost); + scsi_host_put(shost); + } + return rc; +} + void ata_scsi_scan_host(struct ata_port *ap) { unsigned int i; @@ -3220,21 +3279,21 @@ struct ata_port *ata_sas_port_alloc(struct ata_host *host, struct ata_port_info *port_info, struct Scsi_Host *shost) { - struct ata_port *ap = kzalloc(sizeof(*ap), GFP_KERNEL); - struct ata_probe_ent *ent; + struct ata_port *ap; + ap = ata_port_alloc(host); if (!ap) return NULL; - ent = ata_probe_ent_alloc(host->dev, port_info); - if (!ent) { - kfree(ap); - return NULL; - } - - ata_port_init(ap, host, ent, 0); + ap->port_no = 0; ap->lock = shost->host_lock; - kfree(ent); + ap->pio_mask = port_info->pio_mask; + ap->mwdma_mask = port_info->mwdma_mask; + ap->udma_mask = port_info->udma_mask; + ap->flags |= port_info->flags; + ap->ops = port_info->port_ops; + ap->cbl = ATA_CBL_SATA; + return ap; } EXPORT_SYMBOL_GPL(ata_sas_port_alloc); @@ -3290,8 +3349,10 @@ int ata_sas_port_init(struct ata_port *ap) { int rc = ap->ops->port_start(ap); - if (!rc) + if (!rc) { + ap->print_id = ata_print_id++; rc = ata_bus_probe(ap); + } return rc; } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 16bc3e3..d211db6 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -175,7 +175,7 @@ void ata_tf_load(struct ata_port *ap, const struct ata_taskfile *tf) */ void ata_exec_command(struct ata_port *ap, const struct ata_taskfile *tf) { - DPRINTK("ata%u: cmd 0x%X\n", ap->id, tf->command); + DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command); iowrite8(tf->command, ap->ioaddr.command_addr); ata_pause(ap); @@ -521,173 +521,404 @@ void ata_bmdma_post_internal_cmd(struct ata_queued_cmd *qc) static int ata_resources_present(struct pci_dev *pdev, int port) { int i; - + /* Check the PCI resources for this channel are enabled */ port = port * 2; for (i = 0; i < 2; i ++) { if (pci_resource_start(pdev, port + i) == 0 || - pci_resource_len(pdev, port + i) == 0) - return 0; + pci_resource_len(pdev, port + i) == 0) + return 0; } return 1; } - + /** - * ata_pci_init_native_mode - Initialize native-mode driver - * @pdev: pci device to be initialized - * @port: array[2] of pointers to port info structures. - * @ports: bitmap of ports present - * - * Utility function which allocates and initializes an - * ata_probe_ent structure for a standard dual-port - * PIO-based IDE controller. The returned ata_probe_ent - * structure can be passed to ata_device_add(). The returned - * ata_probe_ent structure should then be freed with kfree(). - * - * The caller need only pass the address of the primary port, the - * secondary will be deduced automatically. If the device has non - * standard secondary port mappings this function can be called twice, - * once for each interface. + * ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host + * @host: target ATA host + * + * Acquire PCI BMDMA resources and initialize @host accordingly. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. */ +static int ata_pci_init_bmdma(struct ata_host *host) +{ + struct device *gdev = host->dev; + struct pci_dev *pdev = to_pci_dev(gdev); + int i, rc; + + /* TODO: If we get no DMA mask we should fall back to PIO */ + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + + /* request and iomap DMA region */ + rc = pcim_iomap_regions(pdev, 1 << 4, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, gdev, "failed to request/iomap BAR4\n"); + return -ENOMEM; + } + host->iomap = pcim_iomap_table(pdev); -struct ata_probe_ent * -ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int ports) + for (i = 0; i < host->n_ports; i++) { + struct ata_port *ap = host->ports[i]; + void __iomem *bmdma = host->iomap[4] + 8 * i; + + if (ata_port_is_dummy(ap)) + continue; + + ap->ioaddr.bmdma_addr = bmdma; + if ((!(ap->flags & ATA_FLAG_IGN_SIMPLEX)) && + (ioread8(bmdma + 2) & 0x80)) + host->flags |= ATA_HOST_SIMPLEX; + } + + return 0; +} + +/** + * ata_pci_init_native_host - acquire native ATA resources and init host + * @host: target ATA host + * @port_mask: ports to consider + * + * Acquire native PCI ATA resources for @host and initialize + * @host accordoingly. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_pci_init_native_host(struct ata_host *host, unsigned int port_mask) { - struct ata_probe_ent *probe_ent; - int i, p = 0; - void __iomem * const *iomap; - - /* iomap BARs */ - for (i = 0; i < 4; i++) { - if (pcim_iomap(pdev, i, 0) == NULL) { - dev_printk(KERN_ERR, &pdev->dev, - "failed to iomap PCI BAR %d\n", i); - return NULL; + struct device *gdev = host->dev; + struct pci_dev *pdev = to_pci_dev(gdev); + int i, rc; + + /* Discard disabled ports. Some controllers show their unused + * channels this way. Disabled ports are made dummy. + */ + for (i = 0; i < 2; i++) { + if ((port_mask & (1 << i)) && !ata_resources_present(pdev, i)) { + host->ports[i]->ops = &ata_dummy_port_ops; + port_mask &= ~(1 << i); } } - pcim_iomap(pdev, 4, 0); /* may fail */ - iomap = pcim_iomap_table(pdev); - - /* alloc and init probe_ent */ - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - if (!probe_ent) - return NULL; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - - /* Discard disabled ports. Some controllers show their - unused channels this way */ - if (ata_resources_present(pdev, 0) == 0) - ports &= ~ATA_PORT_PRIMARY; - if (ata_resources_present(pdev, 1) == 0) - ports &= ~ATA_PORT_SECONDARY; - - if (ports & ATA_PORT_PRIMARY) { - probe_ent->port[p].cmd_addr = iomap[0]; - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = (void __iomem *) - ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS); - if (iomap[4]) { - if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(iomap[4] + 2) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = iomap[4]; - } - ata_std_ports(&probe_ent->port[p]); - p++; + if (!port_mask) { + dev_printk(KERN_ERR, gdev, "no available port\n"); + return -ENODEV; } - if (ports & ATA_PORT_SECONDARY) { - probe_ent->port[p].cmd_addr = iomap[2]; - probe_ent->port[p].altstatus_addr = - probe_ent->port[p].ctl_addr = (void __iomem *) - ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS); - if (iomap[4]) { - if ((!(port[p]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(iomap[4] + 10) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - probe_ent->port[p].bmdma_addr = iomap[4] + 8; + /* request, iomap BARs and init port addresses accordingly */ + for (i = 0; i < 2; i++) { + struct ata_port *ap = host->ports[i]; + int base = i * 2; + void __iomem * const *iomap; + + if (!(port_mask & (1 << i))) + continue; + + rc = pcim_iomap_regions(pdev, 0x3 << base, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, gdev, "failed to request/iomap " + "BARs for port %d (errno=%d)\n", i, rc); + if (rc == -EBUSY) + pcim_pin_device(pdev); + return rc; } - ata_std_ports(&probe_ent->port[p]); - probe_ent->pinfo2 = port[1]; - p++; + host->iomap = iomap = pcim_iomap_table(pdev); + + ap->ioaddr.cmd_addr = iomap[base]; + ap->ioaddr.altstatus_addr = + ap->ioaddr.ctl_addr = (void __iomem *) + ((unsigned long)iomap[base + 1] | ATA_PCI_CTL_OFS); + ata_std_ports(&ap->ioaddr); + } + + return 0; +} + +/** + * ata_pci_prepare_native_host - helper to prepare native PCI ATA host + * @pdev: target PCI device + * @ppi: array of port_info + * @n_ports: number of ports to allocate + * @r_host: out argument for the initialized ATA host + * + * Helper to allocate ATA host for @pdev, acquire all native PCI + * resources and initialize it accordingly in one go. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +int ata_pci_prepare_native_host(struct pci_dev *pdev, + const struct ata_port_info * const * ppi, + int n_ports, struct ata_host **r_host) +{ + struct ata_host *host; + unsigned int port_mask; + int rc; + + if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to allocate ATA host\n"); + rc = -ENOMEM; + goto err_out; } - probe_ent->n_ports = p; - return probe_ent; + port_mask = ATA_PORT_PRIMARY; + if (n_ports > 1) + port_mask |= ATA_PORT_SECONDARY; + + rc = ata_pci_init_native_host(host, port_mask); + if (rc) + goto err_out; + + /* init DMA related stuff */ + rc = ata_pci_init_bmdma(host); + if (rc) + goto err_bmdma; + + devres_remove_group(&pdev->dev, NULL); + *r_host = host; + return 0; + + err_bmdma: + /* This is necessary because PCI and iomap resources are + * merged and releasing the top group won't release the + * acquired resources if some of those have been acquired + * before entering this function. + */ + pcim_iounmap_regions(pdev, 0xf); + err_out: + devres_release_group(&pdev->dev, NULL); + return rc; } -static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev, - struct ata_port_info **port, int port_mask) +struct ata_legacy_devres { + unsigned int mask; + unsigned long cmd_port[2]; + void __iomem * cmd_addr[2]; + void __iomem * ctl_addr[2]; + unsigned int irq[2]; + void * irq_dev_id[2]; +}; + +static void ata_legacy_free_irqs(struct ata_legacy_devres *legacy_dr) { - struct ata_probe_ent *probe_ent; - void __iomem *iomap[5] = { }, *bmdma; - - if (port_mask & ATA_PORT_PRIMARY) { - iomap[0] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CMD, 8); - iomap[1] = devm_ioport_map(&pdev->dev, ATA_PRIMARY_CTL, 1); - if (!iomap[0] || !iomap[1]) - return NULL; + int i; + + for (i = 0; i < 2; i++) { + if (!legacy_dr->irq[i]) + continue; + + free_irq(legacy_dr->irq[i], legacy_dr->irq_dev_id[i]); + legacy_dr->irq[i] = 0; + legacy_dr->irq_dev_id[i] = NULL; } +} - if (port_mask & ATA_PORT_SECONDARY) { - iomap[2] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CMD, 8); - iomap[3] = devm_ioport_map(&pdev->dev, ATA_SECONDARY_CTL, 1); - if (!iomap[2] || !iomap[3]) - return NULL; +static void ata_legacy_release(struct device *gdev, void *res) +{ + struct ata_legacy_devres *this = res; + int i; + + ata_legacy_free_irqs(this); + + for (i = 0; i < 2; i++) { + if (this->cmd_addr[i]) + ioport_unmap(this->cmd_addr[i]); + if (this->ctl_addr[i]) + ioport_unmap(this->ctl_addr[i]); + if (this->cmd_port[i]) + release_region(this->cmd_port[i], 8); } +} - bmdma = pcim_iomap(pdev, 4, 16); /* may fail */ - - /* alloc and init probe_ent */ - probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]); - if (!probe_ent) - return NULL; - - probe_ent->n_ports = 2; - probe_ent->irq_flags = IRQF_SHARED; - - if (port_mask & ATA_PORT_PRIMARY) { - probe_ent->irq = ATA_PRIMARY_IRQ(pdev); - probe_ent->port[0].cmd_addr = iomap[0]; - probe_ent->port[0].altstatus_addr = - probe_ent->port[0].ctl_addr = iomap[1]; - if (bmdma) { - probe_ent->port[0].bmdma_addr = bmdma; - if ((!(port[0]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(bmdma + 2) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; - } - ata_std_ports(&probe_ent->port[0]); - } else - probe_ent->dummy_port_mask |= ATA_PORT_PRIMARY; +static int ata_init_legacy_port(struct ata_port *ap, + struct ata_legacy_devres *legacy_dr) +{ + struct ata_host *host = ap->host; + int port_no = ap->port_no; + unsigned long cmd_port, ctl_port; + + if (port_no == 0) { + cmd_port = ATA_PRIMARY_CMD; + ctl_port = ATA_PRIMARY_CTL; + } else { + cmd_port = ATA_SECONDARY_CMD; + ctl_port = ATA_SECONDARY_CTL; + } + + /* request cmd_port */ + if (request_region(cmd_port, 8, "libata")) + legacy_dr->cmd_port[port_no] = cmd_port; + else { + dev_printk(KERN_WARNING, host->dev, + "0x%0lX IDE port busy\n", cmd_port); + return -EBUSY; + } - if (port_mask & ATA_PORT_SECONDARY) { - if (probe_ent->irq) - probe_ent->irq2 = ATA_SECONDARY_IRQ(pdev); + /* iomap cmd and ctl ports */ + legacy_dr->cmd_addr[port_no] = ioport_map(cmd_port, 8); + legacy_dr->ctl_addr[port_no] = ioport_map(ctl_port, 1); + if (!legacy_dr->cmd_addr[port_no] || !legacy_dr->ctl_addr[port_no]) + return -ENOMEM; + + /* init IO addresses */ + ap->ioaddr.cmd_addr = legacy_dr->cmd_addr[port_no]; + ap->ioaddr.altstatus_addr = legacy_dr->ctl_addr[port_no]; + ap->ioaddr.ctl_addr = legacy_dr->ctl_addr[port_no]; + ata_std_ports(&ap->ioaddr); + + return 0; +} + +/** + * ata_init_legacy_host - acquire legacy ATA resources and init ATA host + * @host: target ATA host + * @legacy_mask: out parameter, mask indicating ports is in legacy mode + * @was_busy: out parameter, indicates whether any port was busy + * + * Acquire legacy ATA resources for ports. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int ata_init_legacy_host(struct ata_host *host, + unsigned int *legacy_mask, int *was_busy) +{ + struct device *gdev = host->dev; + struct ata_legacy_devres *legacy_dr; + int i, rc; + + if (!devres_open_group(gdev, NULL, GFP_KERNEL)) + return -ENOMEM; + + rc = -ENOMEM; + legacy_dr = devres_alloc(ata_legacy_release, sizeof(*legacy_dr), + GFP_KERNEL); + if (!legacy_dr) + goto err_out; + devres_add(gdev, legacy_dr); + + for (i = 0; i < 2; i++) { + *legacy_mask &= ~(1 << i); + rc = ata_init_legacy_port(host->ports[i], legacy_dr); + if (rc == 0) + legacy_dr->mask |= 1 << i; + else if (rc == -EBUSY) + (*was_busy)++; + } + + if (!legacy_dr->mask) + return -EBUSY; + + for (i = 0; i < 2; i++) + if (!(legacy_dr->mask & (1 << i))) + host->ports[i]->ops = &ata_dummy_port_ops; + + *legacy_mask |= legacy_dr->mask; + + devres_remove_group(gdev, NULL); + return 0; + + err_out: + devres_release_group(gdev, NULL); + return rc; +} + +/** + * ata_request_legacy_irqs - request legacy ATA IRQs + * @host: target ATA host + * @handler: array of IRQ handlers + * @irq_flags: array of IRQ flags + * @dev_id: array of IRQ dev_ids + * + * Request legacy IRQs for non-dummy legacy ports in @host. All + * IRQ parameters are passed as array to allow ports to have + * separate IRQ handlers. + * + * LOCKING: + * Inherited from calling layer (may sleep). + * + * RETURNS: + * 0 on success, -errno otherwise. + */ +static int ata_request_legacy_irqs(struct ata_host *host, + irq_handler_t const *handler, + const unsigned int *irq_flags, + void * const *dev_id) +{ + struct device *gdev = host->dev; + struct ata_legacy_devres *legacy_dr; + int i, rc; + + legacy_dr = devres_find(host->dev, ata_legacy_release, NULL, NULL); + BUG_ON(!legacy_dr); + + for (i = 0; i < host->n_ports; i++) { + unsigned int irq; + + /* FIXME: ATA_*_IRQ() should take generic device not pci_dev */ + if (i == 0) + irq = ATA_PRIMARY_IRQ(to_pci_dev(gdev)); else - probe_ent->irq = ATA_SECONDARY_IRQ(pdev); - probe_ent->port[1].cmd_addr = iomap[2]; - probe_ent->port[1].altstatus_addr = - probe_ent->port[1].ctl_addr = iomap[3]; - if (bmdma) { - probe_ent->port[1].bmdma_addr = bmdma + 8; - if ((!(port[1]->flags & ATA_FLAG_IGN_SIMPLEX)) && - (ioread8(bmdma + 10) & 0x80)) - probe_ent->_host_flags |= ATA_HOST_SIMPLEX; + irq = ATA_SECONDARY_IRQ(to_pci_dev(gdev)); + + if (!(legacy_dr->mask & (1 << i))) + continue; + + if (!handler[i]) { + dev_printk(KERN_ERR, gdev, + "NULL handler specified for port %d\n", i); + rc = -EINVAL; + goto err_out; + } + + rc = request_irq(irq, handler[i], irq_flags[i], DRV_NAME, + dev_id[i]); + if (rc) { + dev_printk(KERN_ERR, gdev, + "irq %u request failed (errno=%d)\n", irq, rc); + goto err_out; } - ata_std_ports(&probe_ent->port[1]); - /* FIXME: could be pointing to stack area; must copy */ - probe_ent->pinfo2 = port[1]; - } else - probe_ent->dummy_port_mask |= ATA_PORT_SECONDARY; + /* record irq allocation in legacy_dr */ + legacy_dr->irq[i] = irq; + legacy_dr->irq_dev_id[i] = dev_id[i]; - return probe_ent; -} + /* only used to print info */ + if (i == 0) + host->irq = irq; + else + host->irq2 = irq; + } + return 0; + + err_out: + ata_legacy_free_irqs(legacy_dr); + return rc; +} /** * ata_pci_init_one - Initialize/register PCI IDE host controller @@ -718,8 +949,8 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, unsigned int n_ports) { struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent = NULL; - struct ata_port_info *port[2]; + struct ata_host *host = NULL; + const struct ata_port_info *port[2]; u8 mask; unsigned int legacy_mode = 0; int rc; @@ -732,10 +963,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, BUG_ON(n_ports < 1 || n_ports > 2); port[0] = port_info[0]; - if (n_ports > 1) - port[1] = port_info[1]; - else - port[1] = port[0]; + port[1] = (n_ports > 1) ? port_info[1] : NULL; /* FIXME: Really for ATA it isn't safe because the device may be multi-purpose and we want to leave it alone if it was already @@ -743,7 +971,7 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, Checking dev->is_enabled is insufficient as this is not set at boot for the primary video which is BIOS enabled - */ + */ rc = pcim_enable_device(pdev); if (rc) @@ -769,96 +997,68 @@ int ata_pci_init_one (struct pci_dev *pdev, struct ata_port_info **port_info, #endif } + /* alloc and init host */ + host = ata_host_alloc_pinfo(dev, port, n_ports); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, + "failed to allocate ATA host\n"); + rc = -ENOMEM; + goto err_out; + } + if (!legacy_mode) { - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); + unsigned int port_mask; + + port_mask = ATA_PORT_PRIMARY; + if (n_ports > 1) + port_mask |= ATA_PORT_SECONDARY; + + rc = ata_pci_init_native_host(host, port_mask); + if (rc) goto err_out; - } } else { - /* Deal with combined mode hack. This side of the logic all - goes away once the combined mode hack is killed in 2.6.21 */ - if (!devm_request_region(dev, ATA_PRIMARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_PRIMARY_CMD; - res.end = ATA_PRIMARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - while (conflict->child) - conflict = ____request_resource(conflict, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_PRIMARY; - else { - pcim_pin_device(pdev); - printk(KERN_WARNING "ata: 0x%0X IDE port busy\n" \ - "ata: conflict with %s\n", - ATA_PRIMARY_CMD, - conflict->name); - } - } else - legacy_mode |= ATA_PORT_PRIMARY; - - if (!devm_request_region(dev, ATA_SECONDARY_CMD, 8, "libata")) { - struct resource *conflict, res; - res.start = ATA_SECONDARY_CMD; - res.end = ATA_SECONDARY_CMD + 8 - 1; - conflict = ____request_resource(&ioport_resource, &res); - while (conflict->child) - conflict = ____request_resource(conflict, &res); - if (!strcmp(conflict->name, "libata")) - legacy_mode |= ATA_PORT_SECONDARY; - else { - pcim_pin_device(pdev); - printk(KERN_WARNING "ata: 0x%X IDE port busy\n" \ - "ata: conflict with %s\n", - ATA_SECONDARY_CMD, - conflict->name); - } - } else - legacy_mode |= ATA_PORT_SECONDARY; - - if (legacy_mode & ATA_PORT_PRIMARY) - pci_request_region(pdev, 1, DRV_NAME); - if (legacy_mode & ATA_PORT_SECONDARY) - pci_request_region(pdev, 3, DRV_NAME); - /* If there is a DMA resource, allocate it */ - pci_request_region(pdev, 4, DRV_NAME); - } + int was_busy = 0; - /* we have legacy mode, but all ports are unavailable */ - if (legacy_mode == (1 << 3)) { - rc = -EBUSY; - goto err_out; + rc = ata_init_legacy_host(host, &legacy_mode, &was_busy); + if (was_busy) + pcim_pin_device(pdev); + if (rc) + goto err_out; + + /* request respective PCI regions, may fail */ + rc = pci_request_region(pdev, 1, DRV_NAME); + rc = pci_request_region(pdev, 3, DRV_NAME); } - /* TODO: If we get no DMA mask we should fall back to PIO */ - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - goto err_out; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + /* init BMDMA, may fail */ + ata_pci_init_bmdma(host); + pci_set_master(pdev); + + /* start host and request IRQ */ + rc = ata_host_start(host); if (rc) goto err_out; - if (legacy_mode) { - probe_ent = ata_pci_init_legacy_port(pdev, port, legacy_mode); - } else { - if (n_ports == 2) - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - else - probe_ent = ata_pci_init_native_mode(pdev, port, ATA_PORT_PRIMARY); + if (!legacy_mode) + rc = devm_request_irq(dev, pdev->irq, + port_info[0]->port_ops->irq_handler, + IRQF_SHARED, DRV_NAME, host); + else { + irq_handler_t handler[2] = { host->ops->irq_handler, + host->ops->irq_handler }; + unsigned int irq_flags[2] = { IRQF_SHARED, IRQF_SHARED }; + void *dev_id[2] = { host, host }; + + rc = ata_request_legacy_irqs(host, handler, irq_flags, dev_id); } - if (!probe_ent) { - rc = -ENOMEM; + if (rc) goto err_out; - } - - pci_set_master(pdev); - if (!ata_device_add(probe_ent)) { - rc = -ENODEV; + /* register */ + rc = ata_host_register(host, port_info[0]->sht); + if (rc) goto err_out; - } - devm_kfree(dev, probe_ent); devres_remove_group(dev, NULL); return 0; @@ -893,12 +1093,12 @@ int ata_pci_clear_simplex(struct pci_dev *pdev) return 0; } -unsigned long ata_pci_default_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long xfer_mask) +unsigned long ata_pci_default_filter(struct ata_device *adev, unsigned long xfer_mask) { /* Filter out DMA modes if the device has been configured by the BIOS as PIO only */ - if (ap->ioaddr.bmdma_addr == 0) + if (adev->ap->ioaddr.bmdma_addr == 0) xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA); return xfer_mask; } diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index 06ccf23..5f4d40c 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -41,12 +41,23 @@ struct ata_scsi_args { enum { /* flags for ata_dev_read_id() */ ATA_READID_POSTRESET = (1 << 0), /* reading ID after reset */ + + /* selector for ata_down_xfermask_limit() */ + ATA_DNXFER_PIO = 0, /* speed down PIO */ + ATA_DNXFER_DMA = 1, /* speed down DMA */ + ATA_DNXFER_40C = 2, /* apply 40c cable limit */ + ATA_DNXFER_FORCE_PIO = 3, /* force PIO */ + ATA_DNXFER_FORCE_PIO0 = 4, /* force PIO0 */ + + ATA_DNXFER_QUIET = (1 << 31), }; +extern unsigned int ata_print_id; extern struct workqueue_struct *ata_aux_wq; extern int atapi_enabled; extern int atapi_dmadir; extern int libata_fua; +extern int libata_noacpi; extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, @@ -68,7 +79,7 @@ extern int ata_dev_revalidate(struct ata_device *dev, unsigned int flags); extern int ata_dev_configure(struct ata_device *dev); extern int sata_down_spd_limit(struct ata_port *ap); extern int sata_set_spd_needed(struct ata_port *ap); -extern int ata_down_xfermask_limit(struct ata_device *dev, int force_pio0); +extern int ata_down_xfermask_limit(struct ata_device *dev, unsigned int sel); extern int ata_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev); extern void ata_sg_clean(struct ata_queued_cmd *qc); extern void ata_qc_free(struct ata_queued_cmd *qc); @@ -82,15 +93,26 @@ extern int ata_flush_cache(struct ata_device *dev); extern void ata_dev_init(struct ata_device *dev); extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg); extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg); -extern void ata_port_init(struct ata_port *ap, struct ata_host *host, - const struct ata_probe_ent *ent, unsigned int port_no); -extern struct ata_probe_ent *ata_probe_ent_alloc(struct device *dev, - const struct ata_port_info *port); +extern struct ata_port *ata_port_alloc(struct ata_host *host); +/* libata-acpi.c */ +#ifdef CONFIG_SATA_ACPI +extern int ata_acpi_exec_tfs(struct ata_port *ap); +extern int ata_acpi_push_id(struct ata_port *ap, unsigned int ix); +#else +static inline int ata_acpi_exec_tfs(struct ata_port *ap) +{ + return 0; +} +static inline int ata_acpi_push_id(struct ata_port *ap, unsigned int ix) +{ + return 0; +} +#endif /* libata-scsi.c */ -extern struct scsi_transport_template ata_scsi_transport_template; - +extern int ata_scsi_add_hosts(struct ata_host *host, + struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap); extern int ata_scsi_offline_dev(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); @@ -135,7 +157,5 @@ extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc); /* libata-sff.c */ extern u8 ata_irq_on(struct ata_port *ap); -/* pata_sis.c */ -extern struct ata_port_info sis_info133; #endif /* __LIBATA_H__ */ diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c index ab44d18..d40edeb 100644 --- a/drivers/ata/pata_ali.c +++ b/drivers/ata/pata_ali.c @@ -34,7 +34,7 @@ #include <linux/dmi.h> #define DRV_NAME "pata_ali" -#define DRV_VERSION "0.7.2" +#define DRV_VERSION "0.7.4" /* * Cable special cases @@ -90,59 +90,6 @@ static int ali_c2_cable_detect(struct ata_port *ap) } /** - * ali_early_error_handler - reset for eary chip - * @ap: ATA port - * - * Handle the reset callback for the later chips with cable detect - */ - -static int ali_c2_pre_reset(struct ata_port *ap) -{ - ap->cbl = ali_c2_cable_detect(ap); - return ata_std_prereset(ap); -} - -static void ali_c2_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, ali_c2_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); -} - -/** - * ali_early_cable_detect - cable detection - * @ap: ATA port - * - * Perform cable detection for older chipsets. This turns out to be - * rather easy to implement - */ - -static int ali_early_cable_detect(struct ata_port *ap) -{ - return ATA_CBL_PATA40; -} - -/** - * ali_early_probe_init - reset for early chip - * @ap: ATA port - * - * Handle the reset callback for the early (pre cable detect) chips. - */ - -static int ali_early_pre_reset(struct ata_port *ap) -{ - ap->cbl = ali_early_cable_detect(ap); - return ata_std_prereset(ap); -} - -static void ali_early_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, ali_early_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); -} - -/** * ali_20_filter - filter for earlier ALI DMA * @ap: ALi ATA port * @adev: attached device @@ -151,7 +98,7 @@ static void ali_early_error_handler(struct ata_port *ap) * fix that later on. Also ensure we do not do UDMA on WDC drives */ -static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long ali_20_filter(struct ata_device *adev, unsigned long mask) { char model_num[ATA_ID_PROD_LEN + 1]; /* No DMA on anything but a disk for now */ @@ -160,7 +107,7 @@ static unsigned long ali_20_filter(const struct ata_port *ap, struct ata_device ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); if (strstr(model_num, "WDC")) return mask &= ~ATA_MASK_UDMA; - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -314,7 +261,6 @@ static void ali_set_dmamode(struct ata_port *ap, struct ata_device *adev) /** * ali_lock_sectors - Keep older devices to 255 sector mode - * @ap: ATA port * @adev: Device * * Called during the bus probe for each device that is found. We use @@ -324,7 +270,7 @@ static void ali_set_dmamode(struct ata_port *ap, struct ata_device *adev) * slower PIO methods */ -static void ali_lock_sectors(struct ata_port *ap, struct ata_device *adev) +static void ali_lock_sectors(struct ata_device *adev) { adev->max_sectors = 255; } @@ -345,8 +291,10 @@ static struct scsi_host_template ali_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; /* @@ -364,8 +312,9 @@ static struct ata_port_operations ali_early_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_early_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -400,8 +349,9 @@ static struct ata_port_operations ali_20_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_early_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -438,8 +388,9 @@ static struct ata_port_operations ali_c2_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_c2_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ali_c2_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -475,8 +426,9 @@ static struct ata_port_operations ali_c5_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = ali_c2_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ali_c2_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -667,11 +619,13 @@ static int ali_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int ali_reinit_one(struct pci_dev *pdev) { ali_init_chipset(pdev); return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id ali[] = { { PCI_VDEVICE(AL, PCI_DEVICE_ID_AL_M5228), }, @@ -685,8 +639,10 @@ static struct pci_driver ali_pci_driver = { .id_table = ali, .probe = ali_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ali_reinit_one, +#endif }; static int __init ali_init(void) diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c index 619e44b..536ee89 100644 --- a/drivers/ata/pata_amd.c +++ b/drivers/ata/pata_amd.c @@ -25,7 +25,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_amd" -#define DRV_VERSION "0.2.7" +#define DRV_VERSION "0.3.8" /** * timing_setup - shared timing computation and load @@ -119,32 +119,25 @@ static void timing_setup(struct ata_port *ap, struct ata_device *adev, int offse } /** - * amd_probe_init - cable detection + * amd_probe_init - perform reset handling * @ap: ATA port * - * Perform cable detection. The BIOS stores this in PCI config - * space for us. + * Reset sequence checking enable bits to see which ports are + * active. */ static int amd_pre_reset(struct ata_port *ap) { - static const u32 bitmask[2] = {0x03, 0xC0}; static const struct pci_bits amd_enable_bits[] = { { 0x40, 1, 0x02, 0x02 }, { 0x40, 1, 0x01, 0x01 } }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 ata66; if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x42, &ata66); - if (ata66 & bitmask[ap->port_no]) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -156,28 +149,16 @@ static void amd_error_handler(struct ata_port *ap) ata_std_postreset); } -static int amd_early_pre_reset(struct ata_port *ap) +static int amd_cable_detect(struct ata_port *ap) { + static const u32 bitmask[2] = {0x03, 0x0C}; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - static struct pci_bits amd_enable_bits[] = { - { 0x40, 1, 0x02, 0x02 }, - { 0x40, 1, 0x01, 0x01 } - }; - - if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) - return -ENOENT; - - /* No host side cable detection */ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); - -} + u8 ata66; -static void amd_early_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, amd_early_pre_reset, - ata_std_softreset, NULL, - ata_std_postreset); + pci_read_config_byte(pdev, 0x42, &ata66); + if (ata66 & bitmask[ap->port_no]) + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -247,31 +228,16 @@ static void amd133_set_dmamode(struct ata_port *ap, struct ata_device *adev) */ static int nv_pre_reset(struct ata_port *ap) { - static const u8 bitmask[2] = {0x03, 0xC0}; static const struct pci_bits nv_enable_bits[] = { { 0x50, 1, 0x02, 0x02 }, { 0x50, 1, 0x01, 0x01 } }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 ata66; - u16 udma; if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x52, &ata66); - if (ata66 & bitmask[ap->port_no]) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - /* We now have to double check because the Nvidia boxes BIOS - doesn't always set the cable bits but does set mode bits */ - - pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma); - if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400) - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -281,6 +247,29 @@ static void nv_error_handler(struct ata_port *ap) ata_std_softreset, NULL, ata_std_postreset); } + +static int nv_cable_detect(struct ata_port *ap) +{ + static const u8 bitmask[2] = {0x03, 0x0C}; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 ata66; + u16 udma; + int cbl; + + pci_read_config_byte(pdev, 0x52, &ata66); + if (ata66 & bitmask[ap->port_no]) + cbl = ATA_CBL_PATA80; + else + cbl = ATA_CBL_PATA40; + + /* We now have to double check because the Nvidia boxes BIOS + doesn't always set the cable bits but does set mode bits */ + pci_read_config_word(pdev, 0x62 - 2 * ap->port_no, &udma); + if ((udma & 0xC4) == 0xC4 || (udma & 0xC400) == 0xC400) + cbl = ATA_CBL_PATA80; + return cbl; +} + /** * nv100_set_piomode - set initial PIO mode data * @ap: ATA interface @@ -334,8 +323,10 @@ static struct scsi_host_template amd_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations amd33_port_ops = { @@ -351,8 +342,9 @@ static struct ata_port_operations amd33_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = amd_early_error_handler, + .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -385,8 +377,9 @@ static struct ata_port_operations amd66_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = amd_early_error_handler, + .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -421,6 +414,7 @@ static struct ata_port_operations amd100_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -455,6 +449,7 @@ static struct ata_port_operations amd133_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = amd_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = amd_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -489,6 +484,7 @@ static struct ata_port_operations nv100_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = nv_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -523,6 +519,7 @@ static struct ata_port_operations nv133_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = nv_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -663,6 +660,7 @@ static int amd_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int amd_reinit_one(struct pci_dev *pdev) { if (pdev->vendor == PCI_VENDOR_ID_AMD) { @@ -679,6 +677,7 @@ static int amd_reinit_one(struct pci_dev *pdev) } return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id amd[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_COBRA_7401), 0 }, @@ -708,8 +707,10 @@ static struct pci_driver amd_pci_driver = { .id_table = amd, .probe = amd_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = amd_reinit_one, +#endif }; static int __init amd_init(void) diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c index 21c3028..00e9ec3 100644 --- a/drivers/ata/pata_artop.c +++ b/drivers/ata/pata_artop.c @@ -49,8 +49,6 @@ static int artop6210_pre_reset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -85,18 +83,28 @@ static int artop6260_pre_reset(struct ata_port *ap) }; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; /* Odd numbered device ids are the units with enable bits (the -R cards) */ if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) return -ENOENT; + return ata_std_prereset(ap); +} +/** + * artop6260_cable_detect - identify cable type + * @ap: Port + * + * Identify the cable type for the ARTOp interface in question + */ + +static int artop6260_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; pci_read_config_byte(pdev, 0x49, &tmp); if (tmp & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } /** @@ -225,7 +233,7 @@ static void artop6260_set_piomode(struct ata_port *ap, struct ata_device *adev) /** * artop6210_set_dmamode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set DMA mode for device, in host controller PCI config space. * @@ -333,6 +341,7 @@ static const struct ata_port_operations artop6210_ops = { .thaw = ata_bmdma_thaw, .error_handler = artop6210_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -366,6 +375,7 @@ static const struct ata_port_operations artop6260_ops = { .thaw = ata_bmdma_thaw, .error_handler = artop6260_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = artop6260_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c index c3eb40c..39c871a 100644 --- a/drivers/ata/pata_atiixp.c +++ b/drivers/ata/pata_atiixp.c @@ -22,7 +22,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_atiixp" -#define DRV_VERSION "0.4.4" +#define DRV_VERSION "0.4.5" enum { ATIIXP_IDE_PIO_TIMING = 0x40, @@ -35,23 +35,15 @@ enum { static int atiixp_pre_reset(struct ata_port *ap) { - struct pci_dev *pdev = to_pci_dev(ap->host->dev); static const struct pci_bits atiixp_enable_bits[] = { { 0x48, 1, 0x01, 0x00 }, { 0x48, 1, 0x08, 0x00 } }; - u8 udma; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no])) return -ENOENT; - /* Hack from drivers/ide/pci. Really we want to know how to do the - raw detection not play follow the bios mode guess */ - pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma); - if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -60,6 +52,19 @@ static void atiixp_error_handler(struct ata_port *ap) ata_bmdma_drive_eh(ap, atiixp_pre_reset, ata_std_softreset, NULL, ata_std_postreset); } +static int atiixp_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 udma; + + /* Hack from drivers/ide/pci. Really we want to know how to do the + raw detection not play follow the bios mode guess */ + pci_read_config_byte(pdev, ATIIXP_IDE_UDMA_MODE + ap->port_no, &udma); + if ((udma & 0x07) >= 0x04 || (udma & 0x70) >= 0x40) + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; +} + /** * atiixp_set_pio_timing - set initial PIO mode data * @ap: ATA interface @@ -224,8 +229,10 @@ static struct scsi_host_template atiixp_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations atiixp_port_ops = { @@ -243,6 +250,7 @@ static struct ata_port_operations atiixp_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = atiixp_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = atiixp_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = atiixp_bmdma_start, @@ -290,8 +298,10 @@ static struct pci_driver atiixp_pci_driver = { .id_table = atiixp, .probe = atiixp_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .resume = ata_pci_device_resume, .suspend = ata_pci_device_suspend, +#endif }; static int __init atiixp_init(void) diff --git a/drivers/ata/pata_cmd640.c b/drivers/ata/pata_cmd640.c new file mode 100644 index 0000000..2105985 --- /dev/null +++ b/drivers/ata/pata_cmd640.c @@ -0,0 +1,312 @@ +/* + * pata_cmd640.c - CMD640 PCI PATA for new ATA layer + * (C) 2007 Red Hat Inc + * Alan Cox <alan@redhat.com> + * + * Based upon + * linux/drivers/ide/pci/cmd640.c Version 1.02 Sep 01, 1996 + * + * Copyright (C) 1995-1996 Linus Torvalds & authors (see driver) + * + * This drives only the PCI version of the controller. If you have a + * VLB one then we have enough docs to support it but you can write + * your own code. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <scsi/scsi_host.h> +#include <linux/libata.h> + +#define DRV_NAME "pata_cmd640" +#define DRV_VERSION "0.0.5" + +struct cmd640_reg { + int last; + u8 reg58[ATA_MAX_DEVICES]; +}; + +enum { + CFR = 0x50, + CNTRL = 0x51, + CMDTIM = 0x52, + ARTIM0 = 0x53, + DRWTIM0 = 0x54, + ARTIM23 = 0x57, + DRWTIM23 = 0x58, + BRST = 0x59 +}; + +/** + * cmd640_set_piomode - set initial PIO mode data + * @ap: ATA port + * @adev: ATA device + * + * Called to do the PIO mode setup. + */ + +static void cmd640_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct cmd640_reg *timing = ap->private_data; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct ata_timing t; + const unsigned long T = 1000000 / 33; + const u8 setup_data[] = { 0x40, 0x40, 0x40, 0x80, 0x00 }; + u8 reg; + int arttim = ARTIM0 + 2 * adev->devno; + struct ata_device *pair = ata_dev_pair(adev); + + if (ata_timing_compute(adev, adev->pio_mode, &t, T, 0) < 0) { + printk(KERN_ERR DRV_NAME ": mode computation failed.\n"); + return; + } + + /* The second channel has shared timings and the setup timing is + messy to switch to merge it for worst case */ + if (ap->port_no && pair) { + struct ata_timing p; + ata_timing_compute(pair, pair->pio_mode, &p, T, 1); + ata_timing_merge(&p, &t, &t, ATA_TIMING_SETUP); + } + + /* Make the timings fit */ + if (t.recover > 16) { + t.active += t.recover - 16; + t.recover = 16; + } + if (t.active > 16) + t.active = 16; + + /* Now convert the clocks into values we can actually stuff into + the chip */ + + if (t.recover > 1) + t.recover--; /* 640B only */ + else + t.recover = 15; + + if (t.setup > 4) + t.setup = 0xC0; + else + t.setup = setup_data[t.setup]; + + if (ap->port_no == 0) { + t.active &= 0x0F; /* 0 = 16 */ + + /* Load setup timing */ + pci_read_config_byte(pdev, arttim, ®); + reg &= 0x3F; + reg |= t.setup; + pci_write_config_byte(pdev, arttim, reg); + + /* Load active/recovery */ + pci_write_config_byte(pdev, arttim + 1, (t.active << 4) | t.recover); + } else { + /* Save the shared timings for channel, they will be loaded + by qc_issue_prot. Reloading the setup time is expensive + so we keep a merged one loaded */ + pci_read_config_byte(pdev, ARTIM23, ®); + reg &= 0x3F; + reg |= t.setup; + pci_write_config_byte(pdev, ARTIM23, reg); + timing->reg58[adev->devno] = (t.active << 4) | t.recover; + } +} + + +/** + * cmd640_qc_issue_prot - command preparation hook + * @qc: Command to be issued + * + * Channel 1 has shared timings. We must reprogram the + * clock each drive 2/3 switch we do. + */ + +static unsigned int cmd640_qc_issue_prot(struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + struct ata_device *adev = qc->dev; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct cmd640_reg *timing = ap->private_data; + + if (ap->port_no != 0 && adev->devno != timing->last) { + pci_write_config_byte(pdev, DRWTIM23, timing->reg58[adev->devno]); + timing->last = adev->devno; + } + return ata_qc_issue_prot(qc); +} + +/** + * cmd640_port_start - port setup + * @ap: ATA port being set up + * + * The CMD640 needs to maintain private data structures so we + * allocate space here. + */ + +static int cmd640_port_start(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + struct cmd640_reg *timing; + + int ret = ata_port_start(ap); + if (ret < 0) + return ret; + + timing = devm_kzalloc(&pdev->dev, sizeof(struct cmd640_reg), GFP_KERNEL); + if (timing == NULL) + return -ENOMEM; + timing->last = -1; /* Force a load */ + ap->private_data = timing; + return ret; +} + +static struct scsi_host_template cmd640_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .slave_destroy = ata_scsi_slave_destroy, + .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .resume = ata_scsi_device_resume, + .suspend = ata_scsi_device_suspend, +#endif +}; + +static struct ata_port_operations cmd640_port_ops = { + .port_disable = ata_port_disable, + .set_piomode = cmd640_set_piomode, + .mode_filter = ata_pci_default_filter, + .tf_load = ata_tf_load, + .tf_read = ata_tf_read, + .check_status = ata_check_status, + .exec_command = ata_exec_command, + .dev_select = ata_std_dev_select, + + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, + .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, + + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, + + .qc_prep = ata_qc_prep, + .qc_issue = cmd640_qc_issue_prot, + + /* In theory this is not needed once we kill the prefetcher */ + .data_xfer = ata_data_xfer_noirq, + + .irq_handler = ata_interrupt, + .irq_clear = ata_bmdma_irq_clear, + .irq_on = ata_irq_on, + .irq_ack = ata_irq_ack, + + .port_start = cmd640_port_start, +}; + +static void cmd640_hardware_init(struct pci_dev *pdev) +{ + u8 r; + u8 ctrl; + + /* CMD640 detected, commiserations */ + pci_write_config_byte(pdev, 0x5B, 0x00); + /* Get version info */ + pci_read_config_byte(pdev, CFR, &r); + /* PIO0 command cycles */ + pci_write_config_byte(pdev, CMDTIM, 0); + /* 512 byte bursts (sector) */ + pci_write_config_byte(pdev, BRST, 0x40); + /* + * A reporter a long time ago + * Had problems with the data fifo + * So don't run the risk + * Of putting crap on the disk + * For its better just to go slow + */ + /* Do channel 0 */ + pci_read_config_byte(pdev, CNTRL, &ctrl); + pci_write_config_byte(pdev, CNTRL, ctrl | 0xC0); + /* Ditto for channel 1 */ + pci_read_config_byte(pdev, ARTIM23, &ctrl); + ctrl |= 0x0C; + pci_write_config_byte(pdev, ARTIM23, ctrl); +} + +static int cmd640_init_one(struct pci_dev *pdev, const struct pci_device_id *id) +{ + static struct ata_port_info info = { + .sht = &cmd640_sht, + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, + .pio_mask = 0x1f, + .port_ops = &cmd640_port_ops + }; + + static struct ata_port_info *port_info[2] = { &info, &info }; + + cmd640_hardware_init(pdev); + return ata_pci_init_one(pdev, port_info, 2); +} + +static int cmd640_reinit_one(struct pci_dev *pdev) +{ + cmd640_hardware_init(pdev); +#ifdef CONFIG_PM + return ata_pci_device_resume(pdev); +#else + return 0; +#endif +} + +static const struct pci_device_id cmd640[] = { + { PCI_VDEVICE(CMD, 0x640), 0 }, + { }, +}; + +static struct pci_driver cmd640_pci_driver = { + .name = DRV_NAME, + .id_table = cmd640, + .probe = cmd640_init_one, + .remove = ata_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ata_pci_device_suspend, +#endif + .resume = cmd640_reinit_one, +}; + +static int __init cmd640_init(void) +{ + return pci_register_driver(&cmd640_pci_driver); +} + +static void __exit cmd640_exit(void) +{ + pci_unregister_driver(&cmd640_pci_driver); +} + +MODULE_AUTHOR("Alan Cox"); +MODULE_DESCRIPTION("low-level driver for CMD640 PATA controllers"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, cmd640); +MODULE_VERSION(DRV_VERSION); + +module_init(cmd640_init); +module_exit(cmd640_exit); diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c index da09828..3989cc5 100644 --- a/drivers/ata/pata_cmd64x.c +++ b/drivers/ata/pata_cmd64x.c @@ -1,5 +1,5 @@ /* - * pata_cmd64x.c - ATI PATA for new ATA layer + * pata_cmd64x.c - CMD64x PATA for new ATA layer * (C) 2005 Red Hat Inc * Alan Cox <alan@redhat.com> * @@ -75,13 +75,7 @@ enum { DTPR1 = 0x7C }; -static int cmd64x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static int cmd648_pre_reset(struct ata_port *ap) +static int cmd648_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 r; @@ -89,21 +83,8 @@ static int cmd648_pre_reset(struct ata_port *ap) /* Check cable detect bits */ pci_read_config_byte(pdev, BMIDECSR, &r); if (r & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - return ata_std_prereset(ap); -} - -static void cmd64x_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cmd64x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -static void cmd648_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cmd648_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -285,8 +266,10 @@ static struct scsi_host_template cmd64x_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations cmd64x_port_ops = { @@ -302,8 +285,9 @@ static struct ata_port_operations cmd64x_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd64x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -336,8 +320,9 @@ static struct ata_port_operations cmd646r1_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd64x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -370,8 +355,9 @@ static struct ata_port_operations cmd648_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cmd648_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = cmd648_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -479,6 +465,7 @@ static int cmd64x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int cmd64x_reinit_one(struct pci_dev *pdev) { u8 mrdmode; @@ -492,6 +479,7 @@ static int cmd64x_reinit_one(struct pci_dev *pdev) #endif return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id cmd64x[] = { { PCI_VDEVICE(CMD, PCI_DEVICE_ID_CMD_643), 0 }, @@ -507,8 +495,10 @@ static struct pci_driver cmd64x_pci_driver = { .id_table = cmd64x, .probe = cmd64x_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = cmd64x_reinit_one, +#endif }; static int __init cmd64x_init(void) diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c index 1ce8fcf..79bef0d 100644 --- a/drivers/ata/pata_cs5520.c +++ b/drivers/ata/pata_cs5520.c @@ -41,7 +41,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_cs5520" -#define DRV_VERSION "0.6.3" +#define DRV_VERSION "0.6.4" struct pio_clocks { @@ -139,18 +139,6 @@ static void cs5520_set_piomode(struct ata_port *ap, struct ata_device *adev) cs5520_set_timings(ap, adev, adev->pio_mode); } - -static int cs5520_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cs5520_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cs5520_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - static struct scsi_host_template cs5520_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -167,8 +155,10 @@ static struct scsi_host_template cs5520_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations cs5520_port_ops = { @@ -184,8 +174,9 @@ static struct ata_port_operations cs5520_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5520_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -195,7 +186,6 @@ static struct ata_port_operations cs5520_port_ops = { .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -203,91 +193,104 @@ static struct ata_port_operations cs5520_port_ops = { .port_start = ata_port_start, }; -static int __devinit cs5520_init_one(struct pci_dev *dev, const struct pci_device_id *id) +static int __devinit cs5520_init_one(struct pci_dev *pdev, const struct pci_device_id *id) { + struct ata_port_info pi = { + .flags = ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, + .port_ops = &cs5520_port_ops, + }; + const struct ata_port_info *ppi[2]; u8 pcicfg; void *iomap[5]; - static struct ata_probe_ent probe[2]; - int ports = 0; + struct ata_host *host; + struct ata_ioports *ioaddr; + int i, rc; /* IDE port enable bits */ - pci_read_config_byte(dev, 0x60, &pcicfg); + pci_read_config_byte(pdev, 0x60, &pcicfg); /* Check if the ATA ports are enabled */ if ((pcicfg & 3) == 0) return -ENODEV; + ppi[0] = ppi[1] = &ata_dummy_port_info; + if (pcicfg & 1) + ppi[0] = π + if (pcicfg & 2) + ppi[1] = π + if ((pcicfg & 0x40) == 0) { - printk(KERN_WARNING DRV_NAME ": DMA mode disabled. Enabling.\n"); - pci_write_config_byte(dev, 0x60, pcicfg | 0x40); + dev_printk(KERN_WARNING, &pdev->dev, + "DMA mode disabled. Enabling.\n"); + pci_write_config_byte(pdev, 0x60, pcicfg | 0x40); } + pi.mwdma_mask = id->driver_data; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) + return -ENOMEM; + /* Perform set up for DMA */ - if (pci_enable_device_bars(dev, 1<<2)) { + if (pci_enable_device_bars(pdev, 1<<2)) { printk(KERN_ERR DRV_NAME ": unable to configure BAR2.\n"); return -ENODEV; } - pci_set_master(dev); - if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + + if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_ERR DRV_NAME ": unable to configure DMA mask.\n"); return -ENODEV; } - if (pci_set_consistent_dma_mask(dev, DMA_32BIT_MASK)) { + if (pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK)) { printk(KERN_ERR DRV_NAME ": unable to configure consistent DMA mask.\n"); return -ENODEV; } - /* Map IO ports */ - iomap[0] = devm_ioport_map(&dev->dev, 0x1F0, 8); - iomap[1] = devm_ioport_map(&dev->dev, 0x3F6, 1); - iomap[2] = devm_ioport_map(&dev->dev, 0x170, 8); - iomap[3] = devm_ioport_map(&dev->dev, 0x376, 1); - iomap[4] = pcim_iomap(dev, 2, 0); + /* Map IO ports and initialize host accordingly */ + iomap[0] = devm_ioport_map(&pdev->dev, 0x1F0, 8); + iomap[1] = devm_ioport_map(&pdev->dev, 0x3F6, 1); + iomap[2] = devm_ioport_map(&pdev->dev, 0x170, 8); + iomap[3] = devm_ioport_map(&pdev->dev, 0x376, 1); + iomap[4] = pcim_iomap(pdev, 2, 0); if (!iomap[0] || !iomap[1] || !iomap[2] || !iomap[3] || !iomap[4]) return -ENOMEM; - /* We have to do our own plumbing as the PCI setup for this - chipset is non-standard so we can't punt to the libata code */ - - INIT_LIST_HEAD(&probe[0].node); - probe[0].dev = pci_dev_to_dev(dev); - probe[0].port_ops = &cs5520_port_ops; - probe[0].sht = &cs5520_sht; - probe[0].pio_mask = 0x1F; - probe[0].mwdma_mask = id->driver_data; - probe[0].irq = 14; - probe[0].irq_flags = 0; - probe[0].port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST; - probe[0].n_ports = 1; - probe[0].port[0].cmd_addr = iomap[0]; - probe[0].port[0].ctl_addr = iomap[1]; - probe[0].port[0].altstatus_addr = iomap[1]; - probe[0].port[0].bmdma_addr = iomap[4]; - - /* The secondary lurks at different addresses but is otherwise - the same beastie */ - - probe[1] = probe[0]; - INIT_LIST_HEAD(&probe[1].node); - probe[1].irq = 15; - probe[1].port[0].cmd_addr = iomap[2]; - probe[1].port[0].ctl_addr = iomap[3]; - probe[1].port[0].altstatus_addr = iomap[3]; - probe[1].port[0].bmdma_addr = iomap[4] + 8; - - /* Let libata fill in the port details */ - ata_std_ports(&probe[0].port[0]); - ata_std_ports(&probe[1].port[0]); - - /* Now add the ports that are active */ - if (pcicfg & 1) - ports += ata_device_add(&probe[0]); - if (pcicfg & 2) - ports += ata_device_add(&probe[1]); - if (ports) - return 0; - return -ENODEV; + ioaddr = &host->ports[0]->ioaddr; + ioaddr->cmd_addr = iomap[0]; + ioaddr->ctl_addr = iomap[1]; + ioaddr->altstatus_addr = iomap[1]; + ioaddr->bmdma_addr = iomap[4]; + ata_std_ports(ioaddr); + + ioaddr = &host->ports[1]->ioaddr; + ioaddr->cmd_addr = iomap[2]; + ioaddr->ctl_addr = iomap[3]; + ioaddr->altstatus_addr = iomap[3]; + ioaddr->bmdma_addr = iomap[4] + 8; + ata_std_ports(ioaddr); + + /* activate the host */ + pci_set_master(pdev); + rc = ata_host_start(host); + if (rc) + return rc; + + for (i = 0; i < 2; i++) { + static const int irq[] = { 14, 15 }; + struct ata_port *ap = host->ports[0]; + + if (ata_port_is_dummy(ap)) + continue; + + rc = devm_request_irq(&pdev->dev, irq[ap->port_no], + ata_interrupt, 0, DRV_NAME, host); + if (rc) + return rc; + } + + return ata_host_register(host, &cs5520_sht); } /** @@ -306,9 +309,9 @@ static void __devexit cs5520_remove_one(struct pci_dev *pdev) struct ata_host *host = dev_get_drvdata(dev); ata_host_detach(host); - dev_set_drvdata(dev, NULL); } +#ifdef CONFIG_PM /** * cs5520_reinit_one - device resume * @pdev: PCI device @@ -325,6 +328,31 @@ static int cs5520_reinit_one(struct pci_dev *pdev) pci_write_config_byte(pdev, 0x60, pcicfg | 0x40); return ata_pci_device_resume(pdev); } + +/** + * cs5520_pci_device_suspend - device suspend + * @pdev: PCI device + * + * We have to cut and waste bits from the standard method because + * the 5520 is a bit odd and not just a pure ATA device. As a result + * we must not disable it. The needed code is short and this avoids + * chip specific mess in the core code. + */ + +static int cs5520_pci_device_suspend(struct pci_dev *pdev, pm_message_t mesg) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + int rc = 0; + + rc = ata_host_suspend(host, mesg); + if (rc) + return rc; + + pci_save_state(pdev); + return 0; +} +#endif /* CONFIG_PM */ + /* For now keep DMA off. We can set it for all but A rev CS5510 once the core ATA code can handle it */ @@ -340,8 +368,10 @@ static struct pci_driver cs5520_pci_driver = { .id_table = pata_cs5520, .probe = cs5520_init_one, .remove = cs5520_remove_one, - .suspend = ata_pci_device_suspend, +#ifdef CONFIG_PM + .suspend = cs5520_pci_device_suspend, .resume = cs5520_reinit_one, +#endif }; static int __init cs5520_init(void) diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c index 3d7b7d8..29642d5 100644 --- a/drivers/ata/pata_cs5530.c +++ b/drivers/ata/pata_cs5530.c @@ -35,7 +35,7 @@ #include <linux/dmi.h> #define DRV_NAME "pata_cs5530" -#define DRV_VERSION "0.7.1" +#define DRV_VERSION "0.7.2" static void __iomem *cs5530_port_base(struct ata_port *ap) { @@ -160,18 +160,6 @@ static unsigned int cs5530_qc_issue_prot(struct ata_queued_cmd *qc) return ata_qc_issue_prot(qc); } -static int cs5530_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cs5530_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, cs5530_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - - static struct scsi_host_template cs5530_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -188,8 +176,10 @@ static struct scsi_host_template cs5530_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations cs5530_port_ops = { @@ -211,8 +201,9 @@ static struct ata_port_operations cs5530_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5530_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = cs5530_qc_issue_prot, @@ -376,6 +367,7 @@ static int cs5530_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int cs5530_reinit_one(struct pci_dev *pdev) { /* If we fail on resume we are doomed */ @@ -383,6 +375,7 @@ static int cs5530_reinit_one(struct pci_dev *pdev) BUG(); return ata_pci_device_resume(pdev); } +#endif /* CONFIG_PM */ static const struct pci_device_id cs5530[] = { { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE), }, @@ -395,8 +388,10 @@ static struct pci_driver cs5530_pci_driver = { .id_table = cs5530, .probe = cs5530_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = cs5530_reinit_one, +#endif }; static int __init cs5530_init(void) diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c index 17bc693..08cccc9 100644 --- a/drivers/ata/pata_cs5535.c +++ b/drivers/ata/pata_cs5535.c @@ -70,36 +70,23 @@ #define CS5535_BAD_PIO(timings) ( (timings&~0x80000000UL)==0x00009172 ) /** - * cs5535_pre_reset - detect cable type + * cs5535_cable_detect - detect cable type * @ap: Port to detect on * * Perform cable detection for ATA66 capable cable. Return a libata * cable type. */ -static int cs5535_pre_reset(struct ata_port *ap) +static int cs5535_cable_detect(struct ata_port *ap) { u8 cable; struct pci_dev *pdev = to_pci_dev(ap->host->dev); pci_read_config_byte(pdev, CS5535_CABLE_DETECT, &cable); if (cable & 1) - ap->cbl = ATA_CBL_PATA80; + return ATA_CBL_PATA80; else - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * cs5535_error_handler - reset/probe - * @ap: Port to reset - * - * Reset and configure a port - */ - -static void cs5535_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cs5535_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA40; } /** @@ -185,8 +172,10 @@ static struct scsi_host_template cs5535_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations cs5535_port_ops = { @@ -203,8 +192,9 @@ static struct ata_port_operations cs5535_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cs5535_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = cs5535_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -270,8 +260,10 @@ static struct pci_driver cs5535_pci_driver = { .id_table = cs5535, .probe = cs5535_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init cs5535_init(void) diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c index 63f48f0..6ec049c 100644 --- a/drivers/ata/pata_cypress.c +++ b/drivers/ata/pata_cypress.c @@ -41,17 +41,6 @@ enum { CY82_INDEX_TIMEOUT = 0x32 }; -static int cy82c693_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -static void cy82c693_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, cy82c693_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - /** * cy82c693_set_piomode - set initial PIO mode data * @ap: ATA interface @@ -136,8 +125,10 @@ static struct scsi_host_template cy82c693_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations cy82c693_port_ops = { @@ -154,8 +145,9 @@ static struct ata_port_operations cy82c693_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = cy82c693_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -206,8 +198,10 @@ static struct pci_driver cy82c693_pci_driver = { .id_table = cy82c693, .probe = cy82c693_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init cy82c693_init(void) diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c index c19b6a8..a321685 100644 --- a/drivers/ata/pata_efar.c +++ b/drivers/ata/pata_efar.c @@ -22,10 +22,10 @@ #include <linux/ata.h> #define DRV_NAME "pata_efar" -#define DRV_VERSION "0.4.3" +#define DRV_VERSION "0.4.4" /** - * efar_pre_reset - check for 40/80 pin + * efar_pre_reset - Enable bits * @ap: Port * * Perform cable detection for the EFAR ATA interface. This is @@ -38,18 +38,11 @@ static int efar_pre_reset(struct ata_port *ap) { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ { 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */ }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x47, &tmp); - if (tmp & (2 >> ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -67,6 +60,25 @@ static void efar_error_handler(struct ata_port *ap) } /** + * efar_cable_detect - check for 40/80 pin + * @ap: Port + * + * Perform cable detection for the EFAR ATA interface. This is + * different to the PIIX arrangement + */ + +static int efar_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; + + pci_read_config_byte(pdev, 0x47, &tmp); + if (tmp & (2 >> ap->port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** * efar_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring * @adev: um @@ -234,8 +246,10 @@ static struct scsi_host_template efar_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations efar_ops = { @@ -254,6 +268,7 @@ static const struct ata_port_operations efar_ops = { .thaw = ata_bmdma_thaw, .error_handler = efar_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = efar_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -317,8 +332,10 @@ static struct pci_driver efar_pci_driver = { .id_table = efar_pci_tbl, .probe = efar_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init efar_init(void) diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c index 27d724b..93cfa6d 100644 --- a/drivers/ata/pata_hpt366.c +++ b/drivers/ata/pata_hpt366.c @@ -27,7 +27,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt366" -#define DRV_VERSION "0.5.3" +#define DRV_VERSION "0.6.1" struct hpt_clock { u8 xfer_speed; @@ -169,13 +169,12 @@ static int hpt_dma_blacklisted(const struct ata_device *dev, char *modestr, cons /** * hpt366_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt366_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33)) @@ -185,7 +184,7 @@ static unsigned long hpt366_filter(const struct ata_port *ap, struct ata_device if (hpt_dma_blacklisted(adev, "UDMA4", bad_ata66_4)) mask &= ~(0x0F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -210,24 +209,28 @@ static u32 hpt36x_find_mode(struct ata_port *ap, int speed) return 0xffffffffU; /* silence compiler warning */ } +static int hpt36x_cable_detect(struct ata_port *ap) +{ + u8 ata66; + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + + pci_read_config_byte(pdev, 0x5A, &ata66); + if (ata66 & (1 << ap->port_no)) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + static int hpt36x_pre_reset(struct ata_port *ap) { static const struct pci_bits hpt36x_enable_bits[] = { { 0x50, 1, 0x04, 0x04 }, { 0x54, 1, 0x04, 0x04 } }; - - u8 ata66; struct pci_dev *pdev = to_pci_dev(ap->host->dev); if (!pci_test_config_bits(pdev, &hpt36x_enable_bits[ap->port_no])) return -ENOENT; - pci_read_config_byte(pdev, 0x5A, &ata66); - if (ata66 & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } @@ -328,8 +331,10 @@ static struct scsi_host_template hpt36x_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; /* @@ -352,6 +357,7 @@ static struct ata_port_operations hpt366_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = hpt36x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = hpt36x_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -457,12 +463,13 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } +#ifdef CONFIG_PM static int hpt36x_reinit_one(struct pci_dev *dev) { hpt36x_init_chipset(dev); return ata_pci_device_resume(dev); } - +#endif static const struct pci_device_id hpt36x[] = { { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, @@ -474,8 +481,10 @@ static struct pci_driver hpt36x_pci_driver = { .id_table = hpt36x, .probe = hpt36x_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = hpt36x_reinit_one, +#endif }; static int __init hpt36x_init(void) diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c index 4ffc392..41d83129 100644 --- a/drivers/ata/pata_hpt37x.c +++ b/drivers/ata/pata_hpt37x.c @@ -8,6 +8,7 @@ * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc + * Portions Copyright (C) 2005-2006 MontaVista Software, Inc. * * TODO * PLL mode @@ -25,7 +26,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt37x" -#define DRV_VERSION "0.5.2" +#define DRV_VERSION "0.6.5" struct hpt_clock { u8 xfer_speed; @@ -61,201 +62,75 @@ struct hpt_chip { * 31 FIFO enable. */ -/* from highpoint documentation. these are old values */ -static const struct hpt_clock hpt370_timings_33[] = { -/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */ - { XFER_UDMA_5, 0x16454e31 }, - { XFER_UDMA_4, 0x16454e31 }, - { XFER_UDMA_3, 0x166d4e31 }, - { XFER_UDMA_2, 0x16494e31 }, - { XFER_UDMA_1, 0x164d4e31 }, - { XFER_UDMA_0, 0x16514e31 }, - - { XFER_MW_DMA_2, 0x26514e21 }, - { XFER_MW_DMA_1, 0x26514e33 }, - { XFER_MW_DMA_0, 0x26514e97 }, - - { XFER_PIO_4, 0x06514e21 }, - { XFER_PIO_3, 0x06514e22 }, - { XFER_PIO_2, 0x06514e33 }, - { XFER_PIO_1, 0x06914e43 }, - { XFER_PIO_0, 0x06914e57 }, - { 0, 0x06514e57 } +static struct hpt_clock hpt37x_timings_33[] = { + { XFER_UDMA_6, 0x12446231 }, /* 0x12646231 ?? */ + { XFER_UDMA_5, 0x12446231 }, + { XFER_UDMA_4, 0x12446231 }, + { XFER_UDMA_3, 0x126c6231 }, + { XFER_UDMA_2, 0x12486231 }, + { XFER_UDMA_1, 0x124c6233 }, + { XFER_UDMA_0, 0x12506297 }, + + { XFER_MW_DMA_2, 0x22406c31 }, + { XFER_MW_DMA_1, 0x22406c33 }, + { XFER_MW_DMA_0, 0x22406c97 }, + + { XFER_PIO_4, 0x06414e31 }, + { XFER_PIO_3, 0x06414e42 }, + { XFER_PIO_2, 0x06414e53 }, + { XFER_PIO_1, 0x06814e93 }, + { XFER_PIO_0, 0x06814ea7 } }; -static const struct hpt_clock hpt370_timings_66[] = { - { XFER_UDMA_5, 0x14846231 }, - { XFER_UDMA_4, 0x14886231 }, - { XFER_UDMA_3, 0x148c6231 }, - { XFER_UDMA_2, 0x148c6231 }, - { XFER_UDMA_1, 0x14906231 }, - { XFER_UDMA_0, 0x14986231 }, - - { XFER_MW_DMA_2, 0x26514e21 }, - { XFER_MW_DMA_1, 0x26514e33 }, - { XFER_MW_DMA_0, 0x26514e97 }, - - { XFER_PIO_4, 0x06514e21 }, - { XFER_PIO_3, 0x06514e22 }, - { XFER_PIO_2, 0x06514e33 }, - { XFER_PIO_1, 0x06914e43 }, - { XFER_PIO_0, 0x06914e57 }, - { 0, 0x06514e57 } +static struct hpt_clock hpt37x_timings_50[] = { + { XFER_UDMA_6, 0x12848242 }, + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a } }; -/* these are the current (4 sep 2001) timings from highpoint */ -static const struct hpt_clock hpt370a_timings_33[] = { - { XFER_UDMA_5, 0x12446231 }, - { XFER_UDMA_4, 0x12446231 }, - { XFER_UDMA_3, 0x126c6231 }, - { XFER_UDMA_2, 0x12486231 }, - { XFER_UDMA_1, 0x124c6233 }, - { XFER_UDMA_0, 0x12506297 }, - - { XFER_MW_DMA_2, 0x22406c31 }, - { XFER_MW_DMA_1, 0x22406c33 }, - { XFER_MW_DMA_0, 0x22406c97 }, - - { XFER_PIO_4, 0x06414e31 }, - { XFER_PIO_3, 0x06414e42 }, - { XFER_PIO_2, 0x06414e53 }, - { XFER_PIO_1, 0x06814e93 }, - { XFER_PIO_0, 0x06814ea7 }, - { 0, 0x06814ea7 } +static struct hpt_clock hpt37x_timings_66[] = { + { XFER_UDMA_6, 0x1c869c62 }, + { XFER_UDMA_5, 0x1cae9c62 }, /* 0x1c8a9c62 */ + { XFER_UDMA_4, 0x1c8a9c62 }, + { XFER_UDMA_3, 0x1c8e9c62 }, + { XFER_UDMA_2, 0x1c929c62 }, + { XFER_UDMA_1, 0x1c9a9c62 }, + { XFER_UDMA_0, 0x1c829c62 }, + + { XFER_MW_DMA_2, 0x2c829c62 }, + { XFER_MW_DMA_1, 0x2c829c66 }, + { XFER_MW_DMA_0, 0x2c829d2e }, + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e } }; -/* 2x 33MHz timings */ -static const struct hpt_clock hpt370a_timings_66[] = { - { XFER_UDMA_5, 0x1488e673 }, - { XFER_UDMA_4, 0x1488e673 }, - { XFER_UDMA_3, 0x1498e673 }, - { XFER_UDMA_2, 0x1490e673 }, - { XFER_UDMA_1, 0x1498e677 }, - { XFER_UDMA_0, 0x14a0e73f }, - - { XFER_MW_DMA_2, 0x2480fa73 }, - { XFER_MW_DMA_1, 0x2480fa77 }, - { XFER_MW_DMA_0, 0x2480fb3f }, - - { XFER_PIO_4, 0x0c82be73 }, - { XFER_PIO_3, 0x0c82be95 }, - { XFER_PIO_2, 0x0c82beb7 }, - { XFER_PIO_1, 0x0d02bf37 }, - { XFER_PIO_0, 0x0d02bf5f }, - { 0, 0x0d02bf5f } -}; - -static const struct hpt_clock hpt370a_timings_50[] = { - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x0ac1f48a } -}; - -static const struct hpt_clock hpt372_timings_33[] = { - { XFER_UDMA_6, 0x1c81dc62 }, - { XFER_UDMA_5, 0x1c6ddc62 }, - { XFER_UDMA_4, 0x1c8ddc62 }, - { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */ - { XFER_UDMA_2, 0x1c91dc62 }, - { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */ - { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */ - - { XFER_MW_DMA_2, 0x2c829262 }, - { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */ - { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */ - - { XFER_PIO_4, 0x0c829c62 }, - { XFER_PIO_3, 0x0c829c84 }, - { XFER_PIO_2, 0x0c829ca6 }, - { XFER_PIO_1, 0x0d029d26 }, - { XFER_PIO_0, 0x0d029d5e }, - { 0, 0x0d029d5e } -}; - -static const struct hpt_clock hpt372_timings_50[] = { - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x0a81f443 } -}; - -static const struct hpt_clock hpt372_timings_66[] = { - { XFER_UDMA_6, 0x1c869c62 }, - { XFER_UDMA_5, 0x1cae9c62 }, - { XFER_UDMA_4, 0x1c8a9c62 }, - { XFER_UDMA_3, 0x1c8e9c62 }, - { XFER_UDMA_2, 0x1c929c62 }, - { XFER_UDMA_1, 0x1c9a9c62 }, - { XFER_UDMA_0, 0x1c829c62 }, - - { XFER_MW_DMA_2, 0x2c829c62 }, - { XFER_MW_DMA_1, 0x2c829c66 }, - { XFER_MW_DMA_0, 0x2c829d2e }, - - { XFER_PIO_4, 0x0c829c62 }, - { XFER_PIO_3, 0x0c829c84 }, - { XFER_PIO_2, 0x0c829ca6 }, - { XFER_PIO_1, 0x0d029d26 }, - { XFER_PIO_0, 0x0d029d5e }, - { 0, 0x0d029d26 } -}; - -static const struct hpt_clock hpt374_timings_33[] = { - { XFER_UDMA_6, 0x12808242 }, - { XFER_UDMA_5, 0x12848242 }, - { XFER_UDMA_4, 0x12ac8242 }, - { XFER_UDMA_3, 0x128c8242 }, - { XFER_UDMA_2, 0x120c8242 }, - { XFER_UDMA_1, 0x12148254 }, - { XFER_UDMA_0, 0x121882ea }, - - { XFER_MW_DMA_2, 0x22808242 }, - { XFER_MW_DMA_1, 0x22808254 }, - { XFER_MW_DMA_0, 0x228082ea }, - - { XFER_PIO_4, 0x0a81f442 }, - { XFER_PIO_3, 0x0a81f443 }, - { XFER_PIO_2, 0x0a81f454 }, - { XFER_PIO_1, 0x0ac1f465 }, - { XFER_PIO_0, 0x0ac1f48a }, - { 0, 0x06814e93 } -}; static const struct hpt_chip hpt370 = { "HPT370", 48, { - hpt370_timings_33, + hpt37x_timings_33, NULL, NULL, - hpt370_timings_66 + NULL } }; @@ -263,10 +138,10 @@ static const struct hpt_chip hpt370a = { "HPT370A", 48, { - hpt370a_timings_33, + hpt37x_timings_33, NULL, - hpt370a_timings_50, - hpt370a_timings_66 + hpt37x_timings_50, + NULL } }; @@ -274,10 +149,10 @@ static const struct hpt_chip hpt372 = { "HPT372", 55, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -285,10 +160,10 @@ static const struct hpt_chip hpt302 = { "HPT302", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -296,10 +171,10 @@ static const struct hpt_chip hpt371 = { "HPT371", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -307,10 +182,10 @@ static const struct hpt_chip hpt372a = { "HPT372A", 66, { - hpt372_timings_33, + hpt37x_timings_33, NULL, - hpt372_timings_50, - hpt372_timings_66 + hpt37x_timings_50, + hpt37x_timings_66 } }; @@ -318,7 +193,7 @@ static const struct hpt_chip hpt374 = { "HPT374", 48, { - hpt374_timings_33, + hpt37x_timings_33, NULL, NULL, NULL @@ -397,13 +272,12 @@ static const char *bad_ata100_5[] = { /** * hpt370_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt370_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA", bad_ata33)) @@ -411,24 +285,23 @@ static unsigned long hpt370_filter(const struct ata_port *ap, struct ata_device if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~(0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** * hpt370a_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device * * Block UDMA on devices that cause trouble with this controller. */ -static unsigned long hpt370a_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long hpt370a_filter(struct ata_device *adev, unsigned long mask) { if (adev->class != ATA_DEV_ATA) { if (hpt_dma_blacklisted(adev, "UDMA100", bad_ata100_5)) mask &= ~ (0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** @@ -462,8 +335,7 @@ static int hpt37x_pre_reset(struct ata_port *ap) ap->cbl = ATA_CBL_PATA80; /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); return ata_std_prereset(ap); @@ -513,8 +385,7 @@ static int hpt374_pre_reset(struct ata_port *ap) ap->cbl = ATA_CBL_PATA80; /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); return ata_std_prereset(ap); @@ -1032,6 +903,24 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) .udma_mask = 0x3f, .port_ops = &hpt370a_port_ops }; + /* HPT370 - UDMA100 */ + static struct ata_port_info info_hpt370_33 = { + .sht = &hpt37x_sht, + .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x0f, + .port_ops = &hpt370_port_ops + }; + /* HPT370A - UDMA100 */ + static struct ata_port_info info_hpt370a_33 = { + .sht = &hpt37x_sht, + .flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x0f, + .port_ops = &hpt370a_port_ops + }; /* HPT371, 372 and friends - UDMA133 */ static struct ata_port_info info_hpt372 = { .sht = &hpt37x_sht, @@ -1067,7 +956,11 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) u8 irqmask; u32 class_rev; + u8 mcr1; u32 freq; + int prefer_dpll = 1; + + unsigned long iobase = pci_resource_start(dev, 4); const struct hpt_chip *chip_table; int clock_slot; @@ -1088,10 +981,12 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) case 3: port = &info_hpt370; chip_table = &hpt370; + prefer_dpll = 0; break; case 4: port = &info_hpt370a; chip_table = &hpt370a; + prefer_dpll = 0; break; case 5: port = &info_hpt372; @@ -1119,8 +1014,16 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) chip_table = &hpt302; break; case PCI_DEVICE_ID_TTI_HPT371: + if (class_rev > 1) + return -ENODEV; port = &info_hpt372; chip_table = &hpt371; + /* Single channel device, master is not present + but the BIOS (or us for non x86) must mark it + absent */ + pci_read_config_byte(dev, 0x50, &mcr1); + mcr1 &= ~0x04; + pci_write_config_byte(dev, 0x50, mcr1); break; case PCI_DEVICE_ID_TTI_HPT374: chip_table = &hpt374; @@ -1150,8 +1053,18 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) */ pci_write_config_byte(dev, 0x5b, 0x23); + + /* + * HighPoint does this for HPT372A. + * NOTE: This register is only writeable via I/O space. + */ + if (chip_table == &hpt372a) + outb(0x0e, iobase + 0x9c); - pci_read_config_dword(dev, 0x70, &freq); + /* Some devices do not let this value be accessed via PCI space + according to the old driver */ + + freq = inl(iobase + 0x90); if ((freq >> 12) != 0xABCDE) { int i; u8 sr; @@ -1162,7 +1075,7 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) /* This is the process the HPT371 BIOS is reported to use */ for(i = 0; i < 128; i++) { pci_read_config_byte(dev, 0x78, &sr); - total += sr; + total += sr & 0x1FF; udelay(15); } freq = total / 128; @@ -1173,15 +1086,27 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) * Turn the frequency check into a band and then find a timing * table to match it. */ - + clock_slot = hpt37x_clock_slot(freq, chip_table->base); - if (chip_table->clocks[clock_slot] == NULL) { + if (chip_table->clocks[clock_slot] == NULL || prefer_dpll) { /* * We need to try PLL mode instead + * + * For non UDMA133 capable devices we should + * use a 50MHz DPLL by choice */ - unsigned int f_low = (MHz[clock_slot] * chip_table->base) / 192; - unsigned int f_high = f_low + 2; + unsigned int f_low, f_high; int adjust; + + clock_slot = 2; + if (port->udma_mask & 0xE0) + clock_slot = 3; + + f_low = (MHz[clock_slot] * chip_table->base) / 192; + f_high = f_low + 2; + + /* Select the DPLL clock. */ + pci_write_config_byte(dev, 0x5b, 0x21); for(adjust = 0; adjust < 8; adjust++) { if (hpt37x_calibrate_dpll(dev)) @@ -1197,25 +1122,27 @@ static int hpt37x_init_one(struct pci_dev *dev, const struct pci_device_id *id) printk(KERN_WARNING "hpt37x: DPLL did not stabilize.\n"); return -ENODEV; } - /* Check if this works for all cases */ - port->private_data = (void *)hpt370_timings_66; + if (clock_slot == 3) + port->private_data = (void *)hpt37x_timings_66; + else + port->private_data = (void *)hpt37x_timings_50; printk(KERN_INFO "hpt37x: Bus clock %dMHz, using DPLL.\n", MHz[clock_slot]); } else { port->private_data = (void *)chip_table->clocks[clock_slot]; /* - * Perform a final fixup. The 371 and 372 clock determines - * if UDMA133 is available. - */ - - if (clock_slot == 2 && chip_table == &hpt372) { /* 50Mhz */ - printk(KERN_WARNING "pata_hpt37x: No UDMA133 support available with 50MHz bus clock.\n"); - if (port == &info_hpt372) - port = &info_hpt372_50; - else BUG(); - } + * Perform a final fixup. Note that we will have used the + * DPLL on the HPT372 which means we don't have to worry + * about lack of UDMA133 support on lower clocks + */ + + if (clock_slot < 2 && port == &info_hpt370) + port = &info_hpt370_33; + if (clock_slot < 2 && port == &info_hpt370a) + port = &info_hpt370a_33; printk(KERN_INFO "hpt37x: %s: Bus clock %dMHz.\n", chip_table->name, MHz[clock_slot]); } + port_info[0] = port_info[1] = port; /* Now kick off ATA set up */ return ata_pci_init_one(dev, port_info, 2); diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c index 65f2e18..6a34521 100644 --- a/drivers/ata/pata_hpt3x2n.c +++ b/drivers/ata/pata_hpt3x2n.c @@ -8,10 +8,10 @@ * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc + * Portions Copyright (C) 2005-2006 MontaVista Software, Inc. * * * TODO - * 371N * Work out best PLL policy */ @@ -25,7 +25,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_hpt3x2n" -#define DRV_VERSION "0.3.2" +#define DRV_VERSION "0.3.3" enum { HPT_PCI_FAST = (1 << 31), @@ -115,14 +115,13 @@ static u32 hpt3x2n_find_mode(struct ata_port *ap, int speed) } /** - * hpt3x2n_pre_reset - reset the hpt3x2n bus - * @ap: ATA port to reset + * hpt3x2n_cable_detect - Detect the cable type + * @ap: ATA port to detect on * - * Perform the initial reset handling for the 3x2n series controllers. - * Reset the hardware and state machine, obtain the cable type. + * Return the cable type attached to this port */ -static int hpt3xn_pre_reset(struct ata_port *ap) +static int hpt3x2n_cable_detect(struct ata_port *ap) { u8 scr2, ata66; struct pci_dev *pdev = to_pci_dev(ap->host->dev); @@ -135,15 +134,26 @@ static int hpt3xn_pre_reset(struct ata_port *ap) pci_write_config_byte(pdev, 0x5B, scr2); if (ata66 & (1 << ap->port_no)) - ap->cbl = ATA_CBL_PATA40; + return ATA_CBL_PATA40; else - ap->cbl = ATA_CBL_PATA80; + return ATA_CBL_PATA80; +} + +/** + * hpt3x2n_pre_reset - reset the hpt3x2n bus + * @ap: ATA port to reset + * @deadline: deadline jiffies for the operation + * + * Perform the initial reset handling for the 3x2n series controllers. + * Reset the hardware and state machine, + */ +static int hpt3xn_pre_reset(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); /* Reset the state machine */ - pci_write_config_byte(pdev, 0x50, 0x37); - pci_write_config_byte(pdev, 0x54, 0x37); + pci_write_config_byte(pdev, 0x50 + 4 * ap->port_no, 0x37); udelay(100); - return ata_std_prereset(ap); } @@ -364,6 +374,7 @@ static struct ata_port_operations hpt3x2n_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = hpt3x2n_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = hpt3x2n_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -422,8 +433,9 @@ static int hpt3x2n_pci_clock(struct pci_dev *pdev) { unsigned long freq; u32 fcnt; + unsigned long iobase = pci_resource_start(pdev, 4); - pci_read_config_dword(pdev, 0x70/*CHECKME*/, &fcnt); + fcnt = inl(iobase + 0x90); /* Not PCI readable for some chips */ if ((fcnt >> 12) != 0xABCDE) { printk(KERN_WARNING "hpt3xn: BIOS clock data not set.\n"); return 33; /* Not BIOS set */ @@ -492,6 +504,7 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) unsigned int pci_mhz; unsigned int f_low, f_high; int adjust; + unsigned long iobase = pci_resource_start(dev, 4); pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xFF; @@ -501,6 +514,11 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (class_rev < 6) return -ENODEV; break; + case PCI_DEVICE_ID_TTI_HPT371: + if (class_rev < 2) + return -ENODEV; + /* 371N if rev > 1 */ + break; case PCI_DEVICE_ID_TTI_HPT372: /* 372N if rev >= 1*/ if (class_rev == 0) @@ -528,6 +546,19 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) irqmask &= ~0x10; pci_write_config_byte(dev, 0x5a, irqmask); + /* + * HPT371 chips physically have only one channel, the secondary one, + * but the primary channel registers do exist! Go figure... + * So, we manually disable the non-existing channel here + * (if the BIOS hasn't done this already). + */ + if (dev->device == PCI_DEVICE_ID_TTI_HPT371) { + u8 mcr1; + pci_read_config_byte(dev, 0x50, &mcr1); + mcr1 &= ~0x04; + pci_write_config_byte(dev, 0x50, mcr1); + } + /* Tune the PLL. HPT recommend using 75 for SATA, 66 for UDMA133 or 50 for UDMA100. Right now we always use 66 */ @@ -546,14 +577,24 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) break; pci_write_config_dword(dev, 0x5C, (f_high << 16) | f_low); } - if (adjust == 8) - printk(KERN_WARNING "hpt3xn: DPLL did not stabilize.\n"); + if (adjust == 8) { + printk(KERN_WARNING "hpt3x2n: DPLL did not stabilize.\n"); + return -ENODEV; + } /* Set our private data up. We only need a few flags so we use it directly */ port->private_data = NULL; - if (pci_mhz > 60) + if (pci_mhz > 60) { port->private_data = (void *)PCI66; + /* + * On HPT371N, if ATA clock is 66 MHz we must set bit 2 in + * the MISC. register to stretch the UltraDMA Tss timing. + * NOTE: This register is only writeable via I/O space. + */ + if (dev->device == PCI_DEVICE_ID_TTI_HPT371) + outb(inb(iobase + 0x9c) | 0x04, iobase + 0x9c); + } /* Now kick off ATA set up */ port_info[0] = port_info[1] = port; @@ -562,6 +603,7 @@ static int hpt3x2n_init_one(struct pci_dev *dev, const struct pci_device_id *id) static const struct pci_device_id hpt3x2n[] = { { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT366), }, + { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT371), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT302), }, { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT372N), }, diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c index 483ce7c..ac28ec8 100644 --- a/drivers/ata/pata_hpt3x3.c +++ b/drivers/ata/pata_hpt3x3.c @@ -25,25 +25,6 @@ #define DRV_NAME "pata_hpt3x3" #define DRV_VERSION "0.4.2" -static int hpt3x3_probe_init(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * hpt3x3_probe_reset - reset the hpt3x3 bus - * @ap: ATA port to reset - * - * Perform the housekeeping when doing an ATA bus reeset. We just - * need to force the cable type. - */ - -static void hpt3x3_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, hpt3x3_probe_init, ata_std_softreset, NULL, ata_std_postreset); -} - /** * hpt3x3_set_piomode - PIO setup * @ap: ATA interface @@ -119,8 +100,10 @@ static struct scsi_host_template hpt3x3_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations hpt3x3_port_ops = { @@ -137,8 +120,9 @@ static struct ata_port_operations hpt3x3_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = hpt3x3_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -206,11 +190,13 @@ static int hpt3x3_init_one(struct pci_dev *dev, const struct pci_device_id *id) return ata_pci_init_one(dev, port_info, 2); } +#ifdef CONFIG_PM static int hpt3x3_reinit_one(struct pci_dev *dev) { hpt3x3_init_chipset(dev); return ata_pci_device_resume(dev); } +#endif static const struct pci_device_id hpt3x3[] = { { PCI_VDEVICE(TTI, PCI_DEVICE_ID_TTI_HPT343), }, @@ -223,8 +209,10 @@ static struct pci_driver hpt3x3_pci_driver = { .id_table = hpt3x3, .probe = hpt3x3_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = hpt3x3_reinit_one, +#endif }; static int __init hpt3x3_init(void) diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c index 1bf5ec1..d042efd 100644 --- a/drivers/ata/pata_isapnp.c +++ b/drivers/ata/pata_isapnp.c @@ -17,7 +17,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_isapnp" -#define DRV_VERSION "0.1.5" +#define DRV_VERSION "0.2.0" static struct scsi_host_template isapnp_sht = { .module = THIS_MODULE, @@ -49,13 +49,13 @@ static struct ata_port_operations isapnp_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -74,8 +74,10 @@ static struct ata_port_operations isapnp_port_ops = { static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev_id) { - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; + int rc; if (pnp_port_valid(idev, 0) == 0) return -ENODEV; @@ -84,34 +86,36 @@ static int isapnp_init_one(struct pnp_dev *idev, const struct pnp_device_id *dev if (pnp_irq_valid(idev, 0) == 0) return -ENODEV; + /* allocate host */ + host = ata_host_alloc(&idev->dev, 1); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ cmd_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 0), 8); if (!cmd_addr) return -ENOMEM; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &idev->dev; - ae.port_ops = &isapnp_port_ops; - ae.sht = &isapnp_sht; - ae.n_ports = 1; - ae.pio_mask = 1; /* ISA so PIO 0 cycles */ - ae.irq = pnp_irq(idev, 0); - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS; - ae.port[0].cmd_addr = cmd_addr; + ap = host->ports[0]; + + ap->ops = &isapnp_port_ops; + ap->pio_mask = 1; + ap->flags |= ATA_FLAG_SLAVE_POSS; + + ap->ioaddr.cmd_addr = cmd_addr; if (pnp_port_valid(idev, 1) == 0) { ctl_addr = devm_ioport_map(&idev->dev, pnp_port_start(idev, 1), 1); - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ae.port_flags |= ATA_FLAG_SRST; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; } - ata_std_ports(&ae.port[0]); - if (ata_device_add(&ae) == 0) - return -ENODEV; - return 0; + ata_std_ports(&ap->ioaddr); + + /* activate */ + return ata_host_activate(host, pnp_irq(idev, 0), ata_interrupt, 0, + &isapnp_sht); } /** @@ -128,7 +132,6 @@ static void isapnp_remove_one(struct pnp_dev *idev) struct ata_host *host = dev_get_drvdata(dev); ata_host_detach(host); - dev_set_drvdata(dev, NULL); } static struct pnp_device_id isapnp_devices[] = { diff --git a/drivers/ata/pata_it8213.c b/drivers/ata/pata_it8213.c index 7eac869..011306e 100644 --- a/drivers/ata/pata_it8213.c +++ b/drivers/ata/pata_it8213.c @@ -25,8 +25,8 @@ * it8213_pre_reset - check for 40/80 pin * @ap: Port * - * Perform cable detection for the 8213 ATA interface. This is - * different to the PIIX arrangement + * Filter out ports by the enable bits before doing the normal reset + * and probe. */ static int it8213_pre_reset(struct ata_port *ap) @@ -34,23 +34,14 @@ static int it8213_pre_reset(struct ata_port *ap) static const struct pci_bits it8213_enable_bits[] = { { 0x41U, 1U, 0x80UL, 0x80UL }, /* port 0 */ }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - u8 tmp; - if (!pci_test_config_bits(pdev, &it8213_enable_bits[ap->port_no])) return -ENOENT; - - pci_read_config_byte(pdev, 0x42, &tmp); - if (tmp & 2) /* The initial docs are incorrect */ - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; return ata_std_prereset(ap); } /** - * it8213_probe_reset - Probe specified port on PATA host controller + * it8213_error_handler - Probe specified port on PATA host controller * @ap: Port to probe * * LOCKING: @@ -63,9 +54,27 @@ static void it8213_error_handler(struct ata_port *ap) } /** + * it8213_cable_detect - check for 40/80 pin + * @ap: Port + * + * Perform cable detection for the 8213 ATA interface. This is + * different to the PIIX arrangement + */ + +static int it8213_cable_detect(struct ata_port *ap) +{ + struct pci_dev *pdev = to_pci_dev(ap->host->dev); + u8 tmp; + pci_read_config_byte(pdev, 0x42, &tmp); + if (tmp & 2) /* The initial docs are incorrect */ + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} + +/** * it8213_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -246,8 +255,10 @@ static struct scsi_host_template it8213_sht = { .dma_boundary = ATA_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations it8213_ops = { @@ -266,6 +277,7 @@ static const struct ata_port_operations it8213_ops = { .thaw = ata_bmdma_thaw, .error_handler = it8213_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = it8213_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -330,8 +342,10 @@ static struct pci_driver it8213_pci_driver = { .id_table = it8213_pci_tbl, .probe = it8213_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init it8213_init(void) diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c index 73394c7..f1f8cec 100644 --- a/drivers/ata/pata_it821x.c +++ b/drivers/ata/pata_it821x.c @@ -80,7 +80,7 @@ #define DRV_NAME "pata_it821x" -#define DRV_VERSION "0.3.3" +#define DRV_VERSION "0.3.6" struct it821x_dev { @@ -113,31 +113,6 @@ struct it821x_dev static int it8212_noraid; /** - * it821x_pre_reset - probe - * @ap: ATA port - * - * Set the cable type - */ - -static int it821x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - -/** - * it821x_error_handler - probe/reset - * @ap: ATA port - * - * Set the cable type and trigger a probe - */ - -static void it821x_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, it821x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * it821x_program - program the PIO/MWDMA registers * @ap: ATA port * @adev: Device to program @@ -503,10 +478,12 @@ static int it821x_smart_set_mode(struct ata_port *ap, struct ata_device **unused /* We do need the right mode information for DMA or PIO and this comes from the current configuration flags */ if (dma_enabled & (1 << (5 + i))) { + ata_dev_printk(dev, KERN_INFO, "configured for DMA\n"); dev->xfer_mode = XFER_MW_DMA_0; dev->xfer_shift = ATA_SHIFT_MWDMA; dev->flags &= ~ATA_DFLAG_PIO; } else { + ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); dev->xfer_mode = XFER_PIO_0; dev->xfer_shift = ATA_SHIFT_PIO; dev->flags |= ATA_DFLAG_PIO; @@ -518,7 +495,6 @@ static int it821x_smart_set_mode(struct ata_port *ap, struct ata_device **unused /** * it821x_dev_config - Called each device identify - * @ap: ATA port * @adev: Device that has just been identified * * Perform the initial setup needed for each device that is chip @@ -529,7 +505,7 @@ static int it821x_smart_set_mode(struct ata_port *ap, struct ata_device **unused * basically we need to filter commands for this chip. */ -static void it821x_dev_config(struct ata_port *ap, struct ata_device *adev) +static void it821x_dev_config(struct ata_device *adev) { unsigned char model_num[ATA_ID_PROD_LEN + 1]; @@ -644,8 +620,10 @@ static struct scsi_host_template it821x_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations it821x_smart_port_ops = { @@ -663,8 +641,9 @@ static struct ata_port_operations it821x_smart_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = it821x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -699,8 +678,9 @@ static struct ata_port_operations it821x_passthru_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = it821x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = it821x_passthru_bmdma_start, @@ -778,6 +758,7 @@ static int it821x_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int it821x_reinit_one(struct pci_dev *pdev) { /* Resume - turn raid back off if need be */ @@ -785,6 +766,7 @@ static int it821x_reinit_one(struct pci_dev *pdev) it821x_disable_raid(pdev); return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id it821x[] = { { PCI_VDEVICE(ITE, PCI_DEVICE_ID_ITE_8211), }, @@ -798,8 +780,10 @@ static struct pci_driver it821x_pci_driver = { .id_table = it821x, .probe = it821x_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = it821x_reinit_one, +#endif }; static int __init it821x_init(void) diff --git a/drivers/ata/pata_ixp4xx_cf.c b/drivers/ata/pata_ixp4xx_cf.c index 3222ac7..420c343 100644 --- a/drivers/ata/pata_ixp4xx_cf.c +++ b/drivers/ata/pata_ixp4xx_cf.c @@ -23,15 +23,16 @@ #include <scsi/scsi_host.h> #define DRV_NAME "pata_ixp4xx_cf" -#define DRV_VERSION "0.1.1ac1" +#define DRV_VERSION "0.1.2" -static int ixp4xx_set_mode(struct ata_port *ap, struct ata_device *adev) +static int ixp4xx_set_mode(struct ata_port *ap, struct ata_device **error) { int i; for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *dev = &ap->device[i]; - if (ata_dev_enabled(dev)) { + if (ata_dev_ready(dev)) { + ata_dev_printk(dev, KERN_INFO, "configured for PIO0\n"); dev->pio_mode = XFER_PIO_0; dev->xfer_mode = XFER_PIO_0; dev->xfer_shift = ATA_SHIFT_PIO; @@ -128,8 +129,8 @@ static struct ata_port_operations ixp4xx_port_ops = { .qc_issue = ata_qc_issue_prot, .eng_timeout = ata_eng_timeout, .data_xfer = ixp4xx_mmio_data_xfer, + .cable_detect = ata_cable_40wire, - .irq_handler = ata_interrupt, .irq_clear = ixp4xx_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -172,12 +173,12 @@ static void ixp4xx_setup_port(struct ata_ioports *ioaddr, static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) { - int ret; unsigned int irq; struct resource *cs0, *cs1; - struct ata_probe_ent ae; - + struct ata_host *host; + struct ata_port *ap; struct ixp4xx_pata_data *data = pdev->dev.platform_data; + int rc; cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0); cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1); @@ -185,6 +186,12 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) if (!cs0 || !cs1) return -EINVAL; + /* allocate host */ + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ pdev->dev.coherent_dma_mask = DMA_32BIT_MASK; data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000); @@ -192,38 +199,28 @@ static __devinit int ixp4xx_pata_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq) - set_irq_type(irq, IRQT_HIGH); + set_irq_type(irq, IRQT_RISING); /* Setup expansion bus chip selects */ *data->cs0_cfg = data->cs0_bits; *data->cs1_cfg = data->cs1_bits; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); + ap = host->ports[0]; - ae.dev = &pdev->dev; - ae.port_ops = &ixp4xx_port_ops; - ae.sht = &ixp4xx_sht; - ae.n_ports = 1; - ae.pio_mask = 0x1f; /* PIO4 */ - ae.irq = irq; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY - | ATA_FLAG_NO_ATAPI | ATA_FLAG_SRST; + ap->ops = &ixp4xx_port_ops; + ap->pio_mask = 0x1f; /* PIO4 */ + ap->flags |= ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY | ATA_FLAG_NO_ATAPI; /* run in polling mode if no irq has been assigned */ if (!irq) - ae.port_flags |= ATA_FLAG_PIO_POLLING; + ap->flags |= ATA_FLAG_PIO_POLLING; - ixp4xx_setup_port(&ae.port[0], data); + ixp4xx_setup_port(&ap->ioaddr, data); dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); - ret = ata_device_add(&ae); - if (ret == 0) - return -ENODEV; - - return 0; + /* activate host */ + return ata_host_activate(host, irq, ata_interrupt, 0, &ixp4xx_sht); } static __devexit int ixp4xx_pata_remove(struct platform_device *dev) @@ -231,7 +228,6 @@ static __devexit int ixp4xx_pata_remove(struct platform_device *dev) struct ata_host *host = platform_get_drvdata(dev); ata_host_detach(host); - platform_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c index 7a635dd..43763c99 100644 --- a/drivers/ata/pata_jmicron.c +++ b/drivers/ata/pata_jmicron.c @@ -137,6 +137,10 @@ static struct scsi_host_template jmicron_sht = { .slave_destroy = ata_scsi_slave_destroy, /* Use standard CHS mapping rules */ .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, +#endif }; static const struct ata_port_operations jmicron_ops = { @@ -202,49 +206,20 @@ static int jmicron_init_one (struct pci_dev *pdev, const struct pci_device_id *i }; struct ata_port_info *port_info[2] = { &info, &info }; - u32 reg; - - /* PATA controller is fn 1, AHCI is fn 0 */ - if (id->driver_data != 368 && PCI_FUNC(pdev->devfn) != 1) - return -ENODEV; - - /* The 365/66 have two PATA channels, redirect the second */ - if (id->driver_data == 365 || id->driver_data == 366) { - pci_read_config_dword(pdev, 0x80, ®); - reg |= (1 << 24); /* IDE1 to PATA IDE secondary */ - pci_write_config_dword(pdev, 0x80, reg); - } - return ata_pci_init_one(pdev, port_info, 2); } -static int jmicron_reinit_one(struct pci_dev *pdev) -{ - u32 reg; - - switch(pdev->device) { - case PCI_DEVICE_ID_JMICRON_JMB368: - break; - case PCI_DEVICE_ID_JMICRON_JMB365: - case PCI_DEVICE_ID_JMICRON_JMB366: - /* Restore mapping or disks swap and boy does it get ugly */ - pci_read_config_dword(pdev, 0x80, ®); - reg |= (1 << 24); /* IDE1 to PATA IDE secondary */ - pci_write_config_dword(pdev, 0x80, reg); - /* Fall through */ - default: - /* Make sure AHCI is turned back on */ - pci_write_config_byte(pdev, 0x41, 0xa1); - } - return ata_pci_device_resume(pdev); -} - static const struct pci_device_id jmicron_pci_tbl[] = { - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 361}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 363}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 365}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 366}, - { PCI_VDEVICE(JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 368}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 361 }, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 363 }, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 365 }, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 366 }, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, + PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_STORAGE_IDE << 8, 0xffff00, 368 }, { } /* terminate list */ }; @@ -254,8 +229,10 @@ static struct pci_driver jmicron_pci_driver = { .id_table = jmicron_pci_tbl, .probe = jmicron_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, - .resume = jmicron_reinit_one, + .resume = ata_pci_device_resume, +#endif }; static int __init jmicron_init(void) diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c index 98c1fee..7070992 100644 --- a/drivers/ata/pata_legacy.c +++ b/drivers/ata/pata_legacy.c @@ -64,12 +64,12 @@ #include <linux/platform_device.h> #define DRV_NAME "pata_legacy" -#define DRV_VERSION "0.5.3" +#define DRV_VERSION "0.5.4" #define NR_HOST 6 static int legacy_port[NR_HOST] = { 0x1f0, 0x170, 0x1e8, 0x168, 0x1e0, 0x160 }; -static int legacy_irq[NR_HOST] = { 15, 14, 11, 10, 8, 12 }; +static int legacy_irq[NR_HOST] = { 14, 15, 11, 10, 8, 12 }; struct legacy_data { unsigned long timing; @@ -162,6 +162,7 @@ static struct ata_port_operations simple_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -185,8 +186,12 @@ static struct ata_port_operations legacy_port_ops = { .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .cable_detect = ata_cable_40wire, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -298,7 +303,11 @@ static struct ata_port_operations pdc20230_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -350,7 +359,11 @@ static struct ata_port_operations ht6560a_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -413,7 +426,11 @@ static struct ata_port_operations ht6560b_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -531,7 +548,11 @@ static struct ata_port_operations opti82c611a_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, @@ -661,7 +682,11 @@ static struct ata_port_operations opti82c46x_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = opti82c46x_qc_issue_prot, @@ -691,7 +716,8 @@ static struct ata_port_operations opti82c46x_port_ops = { static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl, int irq) { struct legacy_data *ld = &legacy_data[nr_legacy_host]; - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; struct platform_device *pdev; struct ata_port_operations *ops = &legacy_port_ops; void __iomem *io_addr, *ctrl_addr; @@ -773,24 +799,23 @@ static __init int legacy_init_one(int port, unsigned long io, unsigned long ctrl if (ops == &legacy_port_ops && (autospeed & mask)) ops = &simple_port_ops; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = ops; - ae.sht = &legacy_sht; - ae.n_ports = 1; - ae.pio_mask = pio_modes; - ae.irq = irq; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS|ATA_FLAG_SRST|iordy; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctrl_addr; - ae.port[0].ctl_addr = ctrl_addr; - ata_std_ports(&ae.port[0]); - ae.private_data = ld; - - ret = -ENODEV; - if (!ata_device_add(&ae)) + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto fail; + ap = host->ports[0]; + + ap->ops = ops; + ap->pio_mask = pio_modes; + ap->flags |= ATA_FLAG_SLAVE_POSS | iordy; + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctrl_addr; + ap->ioaddr.ctl_addr = ctrl_addr; + ata_std_ports(&ap->ioaddr); + ap->private_data = ld; + + ret = ata_host_activate(host, irq, ata_interrupt, 0, &legacy_sht); + if (ret) goto fail; legacy_host[nr_legacy_host++] = dev_get_drvdata(&pdev->dev); diff --git a/drivers/ata/pata_marvell.c b/drivers/ata/pata_marvell.c index 13a70ac..d9b94a1 100644 --- a/drivers/ata/pata_marvell.c +++ b/drivers/ata/pata_marvell.c @@ -20,7 +20,7 @@ #include <linux/ata.h> #define DRV_NAME "pata_marvell" -#define DRV_VERSION "0.1.1" +#define DRV_VERSION "0.1.4" /** * marvell_pre_reset - check for 40/80 pin @@ -52,22 +52,23 @@ static int marvell_pre_reset(struct ata_port *ap) if ((pdev->device == 0x6145) && (ap->port_no == 0) && (!(devices & 0x10))) /* PATA enable ? */ return -ENOENT; + return ata_std_prereset(ap); +} +static int marvell_cable_detect(struct ata_port *ap) +{ /* Cable type */ switch(ap->port_no) { case 0: if (ioread8(ap->ioaddr.bmdma_addr + 1) & 1) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - break; - + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; case 1: /* Legacy SATA port */ - ap->cbl = ATA_CBL_SATA; - break; + return ATA_CBL_SATA; } - return ata_std_prereset(ap); + BUG(); + return 0; /* Our BUG macro needs the right markup */ } /** @@ -103,8 +104,10 @@ static struct scsi_host_template marvell_sht = { .slave_destroy = ata_scsi_slave_destroy, /* Use standard CHS mapping rules */ .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations marvell_ops = { @@ -121,6 +124,7 @@ static const struct ata_port_operations marvell_ops = { .thaw = ata_bmdma_thaw, .error_handler = marvell_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = marvell_cable_detect, /* BMDMA handling is PCI ATA format, use helpers */ .bmdma_setup = ata_bmdma_setup, @@ -199,8 +203,10 @@ static struct pci_driver marvell_pci_driver = { .id_table = marvell_pci_tbl, .probe = marvell_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init marvell_init(void) diff --git a/drivers/ata/pata_mpc52xx.c b/drivers/ata/pata_mpc52xx.c index 29e1809..9587a89 100644 --- a/drivers/ata/pata_mpc52xx.c +++ b/drivers/ata/pata_mpc52xx.c @@ -24,7 +24,7 @@ #define DRV_NAME "mpc52xx_ata" -#define DRV_VERSION "0.1.0" +#define DRV_VERSION "0.1.0ac2" /* Private structures used by the driver */ @@ -280,6 +280,10 @@ static struct scsi_host_template mpc52xx_ata_sht = { .dma_boundary = ATA_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, +#endif }; static struct ata_port_operations mpc52xx_ata_port_ops = { @@ -293,39 +297,38 @@ static struct ata_port_operations mpc52xx_ata_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, .error_handler = mpc52xx_ata_error_handler, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .port_start = ata_port_start, }; -static struct ata_probe_ent mpc52xx_ata_probe_ent = { - .port_ops = &mpc52xx_ata_port_ops, - .sht = &mpc52xx_ata_sht, - .n_ports = 1, - .pio_mask = 0x1f, /* Up to PIO4 */ - .mwdma_mask = 0x00, /* No MWDMA */ - .udma_mask = 0x00, /* No UDMA */ - .port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST, - .irq_flags = 0, -}; - static int __devinit mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv) { - struct ata_probe_ent *ae = &mpc52xx_ata_probe_ent; - struct ata_ioports *aio = &ae->port[0]; - int rv; - - INIT_LIST_HEAD(&ae->node); - ae->dev = dev; - ae->irq = priv->ata_irq; - - aio->cmd_addr = 0; /* Don't have a classic reg block */ + struct ata_host *host; + struct ata_port *ap; + struct ata_ioports *aio; + int rc; + + host = ata_host_alloc(dev, 1); + if (!host) + return -ENOMEM; + + ap = host->ports[0]; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->pio_mask = 0x1f; /* Up to PIO4 */ + ap->mwdma_mask = 0x00; /* No MWDMA */ + ap->udma_mask = 0x00; /* No UDMA */ + ap->ops = &mpc52xx_ata_port_ops; + host->private_data = priv; + + aio = &ap->ioaddr; + aio->cmd_addr = NULL; /* Don't have a classic reg block */ aio->altstatus_addr = &priv->ata_regs->tf_control; aio->ctl_addr = &priv->ata_regs->tf_control; aio->data_addr = &priv->ata_regs->tf_data; @@ -339,11 +342,9 @@ mpc52xx_ata_init_one(struct device *dev, struct mpc52xx_ata_priv *priv) aio->status_addr = &priv->ata_regs->tf_command; aio->command_addr = &priv->ata_regs->tf_command; - ae->private_data = priv; - - rv = ata_device_add(ae); - - return rv ? 0 : -EINVAL; + /* activate host */ + return ata_host_activate(host, priv->ata_irq, ata_interrupt, 0, + &mpc52xx_ata_sht); } static struct mpc52xx_ata_priv * diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c index f2e7115..987c5fa 100644 --- a/drivers/ata/pata_mpiix.c +++ b/drivers/ata/pata_mpiix.c @@ -35,7 +35,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_mpiix" -#define DRV_VERSION "0.7.5" +#define DRV_VERSION "0.7.6" enum { IDETIM = 0x6C, /* IDE control register */ @@ -53,7 +53,6 @@ static int mpiix_pre_reset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &mpiix_enable_bits)) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -165,8 +164,10 @@ static struct scsi_host_template mpiix_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations mpiix_port_ops = { @@ -183,12 +184,12 @@ static struct ata_port_operations mpiix_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = mpiix_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = mpiix_qc_issue_prot, .data_xfer = ata_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -199,8 +200,9 @@ static struct ata_port_operations mpiix_port_ops = { static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id) { /* Single threaded by the PCI probe logic */ - static struct ata_probe_ent probe; static int printed_version; + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; u16 idetim; int irq; @@ -208,6 +210,10 @@ static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id) if (!printed_version++) dev_printk(KERN_DEBUG, &dev->dev, "version " DRV_VERSION "\n"); + host = ata_host_alloc(&dev->dev, 1); + if (!host) + return -ENOMEM; + /* MPIIX has many functions which can be turned on or off according to other devices present. Make sure IDE is enabled before we try and use it */ @@ -236,27 +242,21 @@ static int mpiix_init_one(struct pci_dev *dev, const struct pci_device_id *id) without BARs set fools the setup. #2 If you pci_disable_device the MPIIX your box goes castors up */ - INIT_LIST_HEAD(&probe.node); - probe.dev = pci_dev_to_dev(dev); - probe.port_ops = &mpiix_port_ops; - probe.sht = &mpiix_sht; - probe.pio_mask = 0x1F; - probe.irq_flags = IRQF_SHARED; - probe.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - probe.n_ports = 1; + ap = host->ports[0]; + ap->ops = &mpiix_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; - probe.irq = irq; - probe.port[0].cmd_addr = cmd_addr; - probe.port[0].ctl_addr = ctl_addr; - probe.port[0].altstatus_addr = ctl_addr; + ap->ioaddr.cmd_addr = cmd_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ap->ioaddr.altstatus_addr = ctl_addr; /* Let libata fill in the port details */ - ata_std_ports(&probe.port[0]); + ata_std_ports(&ap->ioaddr); - /* Now add the port that is active */ - if (ata_device_add(&probe)) - return 0; - return -ENODEV; + /* activate host */ + return ata_host_activate(host, irq, ata_interrupt, IRQF_SHARED, + &mpiix_sht); } static const struct pci_device_id mpiix[] = { @@ -270,8 +270,10 @@ static struct pci_driver mpiix_pci_driver = { .id_table = mpiix, .probe = mpiix_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init mpiix_init(void) diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c index e8393e19..dbba5b7 100644 --- a/drivers/ata/pata_netcell.c +++ b/drivers/ata/pata_netcell.c @@ -16,33 +16,7 @@ #include <linux/ata.h> #define DRV_NAME "pata_netcell" -#define DRV_VERSION "0.1.6" - -/** - * netcell_probe_init - check for 40/80 pin - * @ap: Port - * - * Cables are handled by the RAID controller. Report 80 pin. - */ - -static int netcell_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - -/** - * netcell_probe_reset - Probe specified port on PATA host controller - * @ap: Port to probe - * - * LOCKING: - * None (inherited from caller). - */ - -static void netcell_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, netcell_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} +#define DRV_VERSION "0.1.7" /* No PIO or DMA methods needed for this device */ @@ -63,8 +37,10 @@ static struct scsi_host_template netcell_sht = { .slave_destroy = ata_scsi_slave_destroy, /* Use standard CHS mapping rules */ .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations netcell_ops = { @@ -79,8 +55,9 @@ static const struct ata_port_operations netcell_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = netcell_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_80wire, /* BMDMA handling is PCI ATA format, use helpers */ .bmdma_setup = ata_bmdma_setup, @@ -153,8 +130,10 @@ static struct pci_driver netcell_pci_driver = { .id_table = netcell_pci_tbl, .probe = netcell_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init netcell_init(void) diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c index 3d1fa48..078aeda 100644 --- a/drivers/ata/pata_ns87410.c +++ b/drivers/ata/pata_ns87410.c @@ -28,13 +28,13 @@ #include <linux/libata.h> #define DRV_NAME "pata_ns87410" -#define DRV_VERSION "0.4.3" +#define DRV_VERSION "0.4.6" /** * ns87410_pre_reset - probe begin * @ap: ATA port * - * Set up cable type and use generic probe init + * Check enabled ports */ static int ns87410_pre_reset(struct ata_port *ap) @@ -47,7 +47,6 @@ static int ns87410_pre_reset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -157,8 +156,10 @@ static struct scsi_host_template ns87410_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations ns87410_port_ops = { @@ -175,6 +176,7 @@ static struct ata_port_operations ns87410_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ns87410_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ns87410_qc_issue_prot, @@ -212,8 +214,10 @@ static struct pci_driver ns87410_pci_driver = { .id_table = ns87410, .probe = ns87410_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init ns87410_init(void) diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c index 45215aa..dea4690 100644 --- a/drivers/ata/pata_oldpiix.c +++ b/drivers/ata/pata_oldpiix.c @@ -25,7 +25,7 @@ #include <linux/ata.h> #define DRV_NAME "pata_oldpiix" -#define DRV_VERSION "0.5.3" +#define DRV_VERSION "0.5.5" /** * oldpiix_pre_reset - probe begin @@ -44,7 +44,6 @@ static int oldpiix_pre_reset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -65,7 +64,7 @@ static void oldpiix_pata_error_handler(struct ata_port *ap) /** * oldpiix_set_piomode - Initialize host controller PATA PIO timings * @ap: Port whose timings we are configuring - * @adev: um + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -209,10 +208,9 @@ static unsigned int oldpiix_qc_issue_prot(struct ata_queued_cmd *qc) struct ata_device *adev = qc->dev; if (adev != ap->private_data) { + oldpiix_set_piomode(ap, adev); if (adev->dma_mode) oldpiix_set_dmamode(ap, adev); - else if (adev->pio_mode) - oldpiix_set_piomode(ap, adev); } return ata_qc_issue_prot(qc); } @@ -234,8 +232,10 @@ static struct scsi_host_template oldpiix_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations oldpiix_pata_ops = { @@ -254,6 +254,7 @@ static const struct ata_port_operations oldpiix_pata_ops = { .thaw = ata_bmdma_thaw, .error_handler = oldpiix_pata_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -317,8 +318,10 @@ static struct pci_driver oldpiix_pci_driver = { .id_table = oldpiix_pci_tbl, .probe = oldpiix_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init oldpiix_init(void) diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c index da1aa14..13b63e2 100644 --- a/drivers/ata/pata_opti.c +++ b/drivers/ata/pata_opti.c @@ -34,7 +34,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_opti" -#define DRV_VERSION "0.2.7" +#define DRV_VERSION "0.2.9" enum { READ_REG = 0, /* index of Read cycle timing register */ @@ -61,8 +61,6 @@ static int opti_pre_reset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no])) return -ENOENT; - - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -179,8 +177,10 @@ static struct scsi_host_template opti_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations opti_port_ops = { @@ -196,6 +196,7 @@ static struct ata_port_operations opti_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = opti_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -244,8 +245,10 @@ static struct pci_driver opti_pci_driver = { .id_table = opti, .probe = opti_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init opti_init(void) diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c index d80b36e..b70e04c 100644 --- a/drivers/ata/pata_optidma.c +++ b/drivers/ata/pata_optidma.c @@ -33,7 +33,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_optidma" -#define DRV_VERSION "0.2.3" +#define DRV_VERSION "0.3.2" enum { READ_REG = 0, /* index of Read cycle timing register */ @@ -62,7 +62,6 @@ static int optidma_pre_reset(struct ata_port *ap) if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits)) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -115,7 +114,7 @@ static void optidma_lock(struct ata_port *ap) } /** - * optidma_set_mode - set mode data + * optidma_mode_setup - set mode data * @ap: ATA interface * @adev: ATA device * @mode: Mode to set @@ -128,7 +127,7 @@ static void optidma_lock(struct ata_port *ap) * IRQ here we depend on the host set locking to avoid catastrophe. */ -static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode) +static void optidma_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode) { struct ata_device *pair = ata_dev_pair(adev); int pio = adev->pio_mode - XFER_PIO_0; @@ -202,7 +201,7 @@ static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mo } /** - * optiplus_set_mode - DMA setup for Firestar Plus + * optiplus_mode_setup - DMA setup for Firestar Plus * @ap: ATA port * @adev: device * @mode: desired mode @@ -213,7 +212,7 @@ static void optidma_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mo * one */ -static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 mode) +static void optiplus_mode_setup(struct ata_port *ap, struct ata_device *adev, u8 mode) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 udcfg; @@ -225,7 +224,7 @@ static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 m pci_read_config_byte(pdev, 0x44, &udcfg); if (mode <= XFER_UDMA_0) { udcfg &= ~(1 << unit); - optidma_set_mode(ap, adev, adev->dma_mode); + optidma_mode_setup(ap, adev, adev->dma_mode); } else { udcfg |= (1 << unit); if (ap->port_no) { @@ -253,7 +252,7 @@ static void optiplus_set_mode(struct ata_port *ap, struct ata_device *adev, u8 m static void optidma_set_pio_mode(struct ata_port *ap, struct ata_device *adev) { - optidma_set_mode(ap, adev, adev->pio_mode); + optidma_mode_setup(ap, adev, adev->pio_mode); } /** @@ -268,7 +267,7 @@ static void optidma_set_pio_mode(struct ata_port *ap, struct ata_device *adev) static void optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev) { - optidma_set_mode(ap, adev, adev->dma_mode); + optidma_mode_setup(ap, adev, adev->dma_mode); } /** @@ -283,7 +282,7 @@ static void optidma_set_dma_mode(struct ata_port *ap, struct ata_device *adev) static void optiplus_set_pio_mode(struct ata_port *ap, struct ata_device *adev) { - optiplus_set_mode(ap, adev, adev->pio_mode); + optiplus_mode_setup(ap, adev, adev->pio_mode); } /** @@ -298,7 +297,7 @@ static void optiplus_set_pio_mode(struct ata_port *ap, struct ata_device *adev) static void optiplus_set_dma_mode(struct ata_port *ap, struct ata_device *adev) { - optiplus_set_mode(ap, adev, adev->dma_mode); + optiplus_mode_setup(ap, adev, adev->dma_mode); } /** @@ -322,26 +321,29 @@ static u8 optidma_make_bits43(struct ata_device *adev) } /** - * optidma_post_set_mode - finalize PCI setup + * optidma_set_mode - mode setup * @ap: port to set up * - * Finalise the configuration by writing the nibble of extra bits - * of data into the chip. + * Use the standard setup to tune the chipset and then finalise the + * configuration by writing the nibble of extra bits of data into + * the chip. */ -static void optidma_post_set_mode(struct ata_port *ap) +static int optidma_set_mode(struct ata_port *ap, struct ata_device **r_failed) { u8 r; int nybble = 4 * ap->port_no; struct pci_dev *pdev = to_pci_dev(ap->host->dev); - - pci_read_config_byte(pdev, 0x43, &r); - - r &= (0x0F << nybble); - r |= (optidma_make_bits43(&ap->device[0]) + - (optidma_make_bits43(&ap->device[0]) << 2)) << nybble; - - pci_write_config_byte(pdev, 0x43, r); + int rc = ata_do_set_mode(ap, r_failed); + if (rc == 0) { + pci_read_config_byte(pdev, 0x43, &r); + + r &= (0x0F << nybble); + r |= (optidma_make_bits43(&ap->device[0]) + + (optidma_make_bits43(&ap->device[0]) << 2)) << nybble; + pci_write_config_byte(pdev, 0x43, r); + } + return rc; } static struct scsi_host_template optidma_sht = { @@ -360,8 +362,10 @@ static struct scsi_host_template optidma_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations optidma_port_ops = { @@ -379,7 +383,8 @@ static struct ata_port_operations optidma_port_ops = { .thaw = ata_bmdma_thaw, .post_internal_cmd = ata_bmdma_post_internal_cmd, .error_handler = optidma_error_handler, - .post_set_mode = optidma_post_set_mode, + .set_mode = optidma_set_mode, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -414,7 +419,8 @@ static struct ata_port_operations optiplus_port_ops = { .thaw = ata_bmdma_thaw, .post_internal_cmd = ata_bmdma_post_internal_cmd, .error_handler = optidma_error_handler, - .post_set_mode = optidma_post_set_mode, + .set_mode = optidma_set_mode, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -524,8 +530,10 @@ static struct pci_driver optidma_pci_driver = { .id_table = optidma, .probe = optidma_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init optidma_init(void) diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c index 36468ec..75dc847 100644 --- a/drivers/ata/pata_pcmcia.c +++ b/drivers/ata/pata_pcmcia.c @@ -42,7 +42,7 @@ #define DRV_NAME "pata_pcmcia" -#define DRV_VERSION "0.2.11" +#define DRV_VERSION "0.3.1" /* * Private data structure to glue stuff together @@ -54,6 +54,39 @@ struct ata_pcmcia_info { dev_node_t node; }; +/** + * pcmcia_set_mode - PCMCIA specific mode setup + * @ap: Port + * @r_failed_dev: Return pointer for failed device + * + * Perform the tuning and setup of the devices and timings, which + * for PCMCIA is the same as any other controller. We wrap it however + * as we need to spot hardware with incorrect or missing master/slave + * decode, which alas is embarrassingly common in the PC world + */ + +static int pcmcia_set_mode(struct ata_port *ap, struct ata_device **r_failed_dev) +{ + struct ata_device *master = &ap->device[0]; + struct ata_device *slave = &ap->device[1]; + + if (!ata_dev_enabled(master) || !ata_dev_enabled(slave)) + return ata_do_set_mode(ap, r_failed_dev); + + if (memcmp(master->id + ATA_ID_FW_REV, slave->id + ATA_ID_FW_REV, + ATA_ID_FW_REV_LEN + ATA_ID_PROD_LEN) == 0) + { + /* Suspicious match, but could be two cards from + the same vendor - check serial */ + if (memcmp(master->id + ATA_ID_SERNO, slave->id + ATA_ID_SERNO, + ATA_ID_SERNO_LEN) == 0 && master->id[ATA_ID_SERNO] >> 8) { + ata_dev_printk(slave, KERN_WARNING, "is a ghost device, ignoring.\n"); + ata_dev_disable(slave); + } + } + return ata_do_set_mode(ap, r_failed_dev); +} + static struct scsi_host_template pcmcia_sht = { .module = THIS_MODULE, .name = DRV_NAME, @@ -73,6 +106,7 @@ static struct scsi_host_template pcmcia_sht = { }; static struct ata_port_operations pcmcia_port_ops = { + .set_mode = pcmcia_set_mode, .port_disable = ata_port_disable, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -84,13 +118,13 @@ static struct ata_port_operations pcmcia_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer_noirq, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -111,7 +145,8 @@ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) static int pcmcia_init_one(struct pcmcia_device *pdev) { - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; struct ata_pcmcia_info *info; tuple_t tuple; struct { @@ -255,24 +290,24 @@ next_entry: * Having done the PCMCIA plumbing the ATA side is relatively * sane. */ - - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = &pcmcia_port_ops; - ae.sht = &pcmcia_sht; - ae.n_ports = 1; - ae.pio_mask = 1; /* ISA so PIO 0 cycles */ - ae.irq = pdev->irq.AssignedIRQ; - ae.irq_flags = IRQF_SHARED; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); - - ret = -ENODEV; - if (ata_device_add(&ae) == 0) + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto failed; + ap = host->ports[0]; + + ap->ops = &pcmcia_port_ops; + ap->pio_mask = 1; /* ISA so PIO 0 cycles */ + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); + + /* activate */ + ret = ata_host_activate(host, pdev->irq.AssignedIRQ, ata_interrupt, + IRQF_SHARED, &pcmcia_sht); + if (ret) goto failed; info->ndev = 1; @@ -308,7 +343,6 @@ static void pcmcia_remove_one(struct pcmcia_device *pdev) if (info->ndev) { struct ata_host *host = dev_get_drvdata(dev); ata_host_detach(host); - dev_set_drvdata(dev, NULL); } info->ndev = 0; pdev->priv = NULL; @@ -320,14 +354,17 @@ static void pcmcia_remove_one(struct pcmcia_device *pdev) static struct pcmcia_device_id pcmcia_devices[] = { PCMCIA_DEVICE_FUNC_ID(4), PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */ + PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000), /* I-O Data CFA */ + PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */ PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704), - PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), + PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */ PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */ PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d), PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */ PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000), /* Hitachi */ PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001), - PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar */ + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100), /* Viking CFA */ + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar, Viking CFA */ PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0), PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74), PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c index 6153787..a61cbc1 100644 --- a/drivers/ata/pata_pdc2027x.c +++ b/drivers/ata/pata_pdc2027x.c @@ -35,7 +35,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_pdc2027x" -#define DRV_VERSION "0.74-ac5" +#define DRV_VERSION "0.9" #undef PDC_DEBUG #ifdef PDC_DEBUG @@ -66,8 +66,10 @@ static int pdc2027x_init_one(struct pci_dev *pdev, const struct pci_device_id *e static void pdc2027x_error_handler(struct ata_port *ap); static void pdc2027x_set_piomode(struct ata_port *ap, struct ata_device *adev); static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev); -static void pdc2027x_post_set_mode(struct ata_port *ap); static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc); +static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask); +static int pdc2027x_cable_detect(struct ata_port *ap); +static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed); /* * ATA Timing Tables based on 133MHz controller clock. @@ -146,6 +148,7 @@ static struct scsi_host_template pdc2027x_sht = { static struct ata_port_operations pdc2027x_pata100_ops = { .port_disable = ata_port_disable, + .mode_filter = ata_pci_default_filter, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -166,8 +169,8 @@ static struct ata_port_operations pdc2027x_pata100_ops = { .thaw = ata_bmdma_thaw, .error_handler = pdc2027x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2027x_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -179,7 +182,8 @@ static struct ata_port_operations pdc2027x_pata133_ops = { .port_disable = ata_port_disable, .set_piomode = pdc2027x_set_piomode, .set_dmamode = pdc2027x_set_dmamode, - .post_set_mode = pdc2027x_post_set_mode, + .set_mode = pdc2027x_set_mode, + .mode_filter = pdc2027x_mode_filter, .tf_load = ata_tf_load, .tf_read = ata_tf_read, @@ -200,8 +204,8 @@ static struct ata_port_operations pdc2027x_pata133_ops = { .thaw = ata_bmdma_thaw, .error_handler = pdc2027x_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2027x_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -212,7 +216,6 @@ static struct ata_port_operations pdc2027x_pata133_ops = { static struct ata_port_info pdc2027x_port_info[] = { /* PDC_UDMA_100 */ { - .sht = &pdc2027x_sht, .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO, .pio_mask = 0x1f, /* pio0-4 */ @@ -222,7 +225,6 @@ static struct ata_port_info pdc2027x_port_info[] = { }, /* PDC_UDMA_133 */ { - .sht = &pdc2027x_sht, .flags = ATA_FLAG_NO_LEGACY | ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO, .pio_mask = 0x1f, /* pio0-4 */ @@ -261,7 +263,7 @@ static inline void __iomem *dev_mmio(struct ata_port *ap, struct ata_device *ade } /** - * pdc2027x_pata_cbl_detect - Probe host controller cable detect info + * pdc2027x_pata_cable_detect - Probe host controller cable detect info * @ap: Port for which cable detect info is desired * * Read 80c cable indicator from Promise extended register. @@ -270,7 +272,7 @@ static inline void __iomem *dev_mmio(struct ata_port *ap, struct ata_device *ade * LOCKING: * None (inherited from caller). */ -static void pdc2027x_cbl_detect(struct ata_port *ap) +static int pdc2027x_cable_detect(struct ata_port *ap) { u32 cgcr; @@ -281,13 +283,10 @@ static void pdc2027x_cbl_detect(struct ata_port *ap) PDPRINTK("No cable or 80-conductor cable on port %d\n", ap->port_no); - ap->cbl = ATA_CBL_PATA80; - return; - + return ATA_CBL_PATA80; cbl40: printk(KERN_INFO DRV_NAME ": 40-conductor cable detected on port %d\n", ap->port_no); - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; + return ATA_CBL_PATA40; } /** @@ -314,7 +313,6 @@ static int pdc2027x_prereset(struct ata_port *ap) /* Check whether port enabled */ if (!pdc2027x_port_enabled(ap)) return -ENOENT; - pdc2027x_cbl_detect(ap); return ata_std_prereset(ap); } @@ -334,6 +332,32 @@ static void pdc2027x_error_handler(struct ata_port *ap) } /** + * pdc2720x_mode_filter - mode selection filter + * @adev: ATA device + * @mask: list of modes proposed + * + * Block UDMA on devices that cause trouble with this controller. + */ + +static unsigned long pdc2027x_mode_filter(struct ata_device *adev, unsigned long mask) +{ + unsigned char model_num[ATA_ID_PROD_LEN + 1]; + struct ata_device *pair = ata_dev_pair(adev); + + if (adev->class != ATA_DEV_ATA || adev->devno == 0 || pair == NULL) + return ata_pci_default_filter(adev, mask); + + /* Check for slave of a Maxtor at UDMA6 */ + ata_id_c_string(pair->id, model_num, ATA_ID_PROD, + ATA_ID_PROD_LEN + 1); + /* If the master is a maxtor in UDMA6 then the slave should not use UDMA 6 */ + if(strstr(model_num, "Maxtor") == 0 && pair->dma_mode == XFER_UDMA_6) + mask &= ~ (1 << (6 + ATA_SHIFT_UDMA)); + + return ata_pci_default_filter(adev, mask); +} + +/** * pdc2027x_set_piomode - Initialize host controller PATA PIO timings * @ap: Port to configure * @adev: um @@ -444,17 +468,22 @@ static void pdc2027x_set_dmamode(struct ata_port *ap, struct ata_device *adev) } /** - * pdc2027x_post_set_mode - Set the timing registers back to correct values. + * pdc2027x_set_mode - Set the timing registers back to correct values. * @ap: Port to configure + * @r_failed: Returned device for failure * * The pdc2027x hardware will look at "SET FEATURES" and change the timing registers * automatically. The values set by the hardware might be incorrect, under 133Mhz PLL. * This function overwrites the possibly incorrect values set by the hardware to be correct. */ -static void pdc2027x_post_set_mode(struct ata_port *ap) +static int pdc2027x_set_mode(struct ata_port *ap, struct ata_device **r_failed) { int i; + i = ata_do_set_mode(ap, r_failed); + if (i < 0) + return i; + for (i = 0; i < ATA_MAX_DEVICES; i++) { struct ata_device *dev = &ap->device[i]; @@ -476,6 +505,7 @@ static void pdc2027x_post_set_mode(struct ata_port *ap) } } } + return 0; } /** @@ -521,12 +551,12 @@ static int pdc2027x_check_atapi_dma(struct ata_queued_cmd *qc) /** * pdc_read_counter - Read the ctr counter - * @probe_ent: for the port address + * @host: target ATA host */ -static long pdc_read_counter(struct ata_probe_ent *probe_ent) +static long pdc_read_counter(struct ata_host *host) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; long counter; int retry = 1; u32 bccrl, bccrh, bccrlv, bccrhv; @@ -564,12 +594,12 @@ retry: * adjust_pll - Adjust the PLL input clock in Hz. * * @pdc_controller: controller specific information - * @probe_ent: For the port address + * @host: target ATA host * @pll_clock: The input of PLL in HZ */ -static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsigned int board_idx) +static void pdc_adjust_pll(struct ata_host *host, long pll_clock, unsigned int board_idx) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; u16 pll_ctl; long pll_clock_khz = pll_clock / 1000; long pout_required = board_idx? PDC_133_MHZ:PDC_100_MHZ; @@ -649,19 +679,19 @@ static void pdc_adjust_pll(struct ata_probe_ent *probe_ent, long pll_clock, unsi /** * detect_pll_input_clock - Detect the PLL input clock in Hz. - * @probe_ent: for the port address + * @host: target ATA host * Ex. 16949000 on 33MHz PCI bus for pdc20275. * Half of the PCI clock. */ -static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent) +static long pdc_detect_pll_input_clock(struct ata_host *host) { - void __iomem *mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[PDC_MMIO_BAR]; u32 scr; long start_count, end_count; long pll_clock; /* Read current counter value */ - start_count = pdc_read_counter(probe_ent); + start_count = pdc_read_counter(host); /* Start the test mode */ scr = readl(mmio_base + PDC_SYS_CTL); @@ -673,7 +703,7 @@ static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent) mdelay(100); /* Read the counter values again */ - end_count = pdc_read_counter(probe_ent); + end_count = pdc_read_counter(host); /* Stop the test mode */ scr = readl(mmio_base + PDC_SYS_CTL); @@ -692,11 +722,10 @@ static long pdc_detect_pll_input_clock(struct ata_probe_ent *probe_ent) /** * pdc_hardware_init - Initialize the hardware. - * @pdev: instance of pci_dev found - * @pdc_controller: controller specific information - * @pe: for the port address + * @host: target ATA host + * @board_idx: board identifier */ -static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, unsigned int board_idx) +static int pdc_hardware_init(struct ata_host *host, unsigned int board_idx) { long pll_clock; @@ -706,15 +735,15 @@ static int pdc_hardware_init(struct pci_dev *pdev, struct ata_probe_ent *pe, uns * Ex. 25MHz or 40MHz, we have to adjust the cycle_time. * The pdc20275 controller employs PLL circuit to help correct timing registers setting. */ - pll_clock = pdc_detect_pll_input_clock(pe); + pll_clock = pdc_detect_pll_input_clock(host); if (pll_clock < 0) /* counter overflow? Try again. */ - pll_clock = pdc_detect_pll_input_clock(pe); + pll_clock = pdc_detect_pll_input_clock(host); - dev_printk(KERN_INFO, &pdev->dev, "PLL input clock %ld kHz\n", pll_clock/1000); + dev_printk(KERN_INFO, host->dev, "PLL input clock %ld kHz\n", pll_clock/1000); /* Adjust PLL control register */ - pdc_adjust_pll(pe, pll_clock, board_idx); + pdc_adjust_pll(host, pll_clock, board_idx); return 0; } @@ -746,8 +775,7 @@ static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base) * Called when an instance of PCI adapter is inserted. * This function checks whether the hardware is supported, * initialize hardware and register an instance of ata_host to - * libata by providing struct ata_probe_ent and ata_device_add(). - * (implements struct pci_driver.probe() ) + * libata. (implements struct pci_driver.probe() ) * * @pdev: instance of pci_dev found * @ent: matching entry in the id_tbl[] @@ -756,14 +784,21 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de { static int printed_version; unsigned int board_idx = (unsigned int) ent->driver_data; - - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &pdc2027x_port_info[board_idx], NULL }; + struct ata_host *host; void __iomem *mmio_base; int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 2); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -771,6 +806,7 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de rc = pcim_iomap_regions(pdev, 1 << PDC_MMIO_BAR, DRV_NAME); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -780,46 +816,22 @@ static int __devinit pdc2027x_init_one(struct pci_dev *pdev, const struct pci_de if (rc) return rc; - /* Prepare the probe entry */ - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = pdc2027x_port_info[board_idx].sht; - probe_ent->port_flags = pdc2027x_port_info[board_idx].flags; - probe_ent->pio_mask = pdc2027x_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc2027x_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc2027x_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc2027x_port_info[board_idx].port_ops; + mmio_base = host->iomap[PDC_MMIO_BAR]; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); + pdc_ata_setup_port(&host->ports[0]->ioaddr, mmio_base + 0x17c0); + host->ports[0]->ioaddr.bmdma_addr = mmio_base + 0x1000; + pdc_ata_setup_port(&host->ports[1]->ioaddr, mmio_base + 0x15c0); + host->ports[1]->ioaddr.bmdma_addr = mmio_base + 0x1008; - mmio_base = probe_ent->iomap[PDC_MMIO_BAR]; - - pdc_ata_setup_port(&probe_ent->port[0], mmio_base + 0x17c0); - probe_ent->port[0].bmdma_addr = mmio_base + 0x1000; - pdc_ata_setup_port(&probe_ent->port[1], mmio_base + 0x15c0); - probe_ent->port[1].bmdma_addr = mmio_base + 0x1008; - - probe_ent->n_ports = 2; - - pci_set_master(pdev); //pci_enable_intx(pdev); /* initialize adapter */ - if (pdc_hardware_init(pdev, probe_ent, board_idx) != 0) + if (pdc_hardware_init(host, board_idx) != 0) return -EIO; - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &pdc2027x_sht); } /** diff --git a/drivers/ata/pata_pdc202xx_old.c b/drivers/ata/pata_pdc202xx_old.c index 6dd6341..ee636be 100644 --- a/drivers/ata/pata_pdc202xx_old.c +++ b/drivers/ata/pata_pdc202xx_old.c @@ -2,13 +2,14 @@ * pata_pdc202xx_old.c - Promise PDC202xx PATA for new ATA layer * (C) 2005 Red Hat Inc * Alan Cox <alan@redhat.com> + * (C) 2007 Bartlomiej Zolnierkiewicz * * Based in part on linux/drivers/ide/pci/pdc202xx_old.c * * First cut with LBA48/ATAPI * * TODO: - * Channel interlock/reset on both required ? + * Channel interlock/reset on both required */ #include <linux/kernel.h> @@ -21,45 +22,17 @@ #include <linux/libata.h> #define DRV_NAME "pata_pdc202xx_old" -#define DRV_VERSION "0.2.3" +#define DRV_VERSION "0.4.2" -/** - * pdc2024x_pre_reset - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int pdc2024x_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - - -static void pdc2024x_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, pdc2024x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - - -static int pdc2026x_pre_reset(struct ata_port *ap) +static int pdc2026x_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u16 cis; pci_read_config_word(pdev, 0x50, &cis); if (cis & (1 << (10 + ap->port_no))) - ap->cbl = ATA_CBL_PATA80; - else - ap->cbl = ATA_CBL_PATA40; - - return ata_std_prereset(ap); -} - -static void pdc2026x_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, pdc2026x_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA80; + return ATA_CBL_PATA40; } /** @@ -76,7 +49,7 @@ static void pdc2026x_error_handler(struct ata_port *ap) static void pdc202xx_configure_piomode(struct ata_port *ap, struct ata_device *adev, int pio) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); - int port = 0x60 + 4 * ap->port_no + 2 * adev->devno; + int port = 0x60 + 8 * ap->port_no + 4 * adev->devno; static u16 pio_timing[5] = { 0x0913, 0x050C , 0x0308, 0x0206, 0x0104 }; @@ -85,7 +58,7 @@ static void pdc202xx_configure_piomode(struct ata_port *ap, struct ata_device *a pci_read_config_byte(pdev, port, &r_ap); pci_read_config_byte(pdev, port + 1, &r_bp); r_ap &= ~0x3F; /* Preserve ERRDY_EN, SYNC_IN */ - r_bp &= ~0x07; + r_bp &= ~0x1F; r_ap |= (pio_timing[pio] >> 8); r_bp |= (pio_timing[pio] & 0xFF); @@ -123,7 +96,7 @@ static void pdc202xx_set_piomode(struct ata_port *ap, struct ata_device *adev) static void pdc202xx_set_dmamode(struct ata_port *ap, struct ata_device *adev) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); - int port = 0x60 + 4 * ap->port_no + 2 * adev->devno; + int port = 0x60 + 8 * ap->port_no + 4 * adev->devno; static u8 udma_timing[6][2] = { { 0x60, 0x03 }, /* 33 Mhz Clock */ { 0x40, 0x02 }, @@ -132,12 +105,17 @@ static void pdc202xx_set_dmamode(struct ata_port *ap, struct ata_device *adev) { 0x20, 0x01 }, { 0x20, 0x01 } }; + static u8 mdma_timing[3][2] = { + { 0x60, 0x03 }, + { 0x60, 0x04 }, + { 0xe0, 0x0f }, + }; u8 r_bp, r_cp; pci_read_config_byte(pdev, port + 1, &r_bp); pci_read_config_byte(pdev, port + 2, &r_cp); - r_bp &= ~0xF0; + r_bp &= ~0xE0; r_cp &= ~0x0F; if (adev->dma_mode >= XFER_UDMA_0) { @@ -147,8 +125,8 @@ static void pdc202xx_set_dmamode(struct ata_port *ap, struct ata_device *adev) } else { int speed = adev->dma_mode - XFER_MW_DMA_0; - r_bp |= 0x60; - r_cp |= (5 - speed); + r_bp |= mdma_timing[speed][0]; + r_cp |= mdma_timing[speed][1]; } pci_write_config_byte(pdev, port + 1, r_bp); pci_write_config_byte(pdev, port + 2, r_cp); @@ -189,7 +167,7 @@ static void pdc2026x_bmdma_start(struct ata_queued_cmd *qc) /* Cases the state machine will not complete correctly without help */ if ((tf->flags & ATA_TFLAG_LBA48) || tf->protocol == ATA_PROT_ATAPI_DMA) { - len = qc->nbytes; + len = qc->nbytes / 2; if (tf->flags & ATA_TFLAG_WRITE) len |= 0x06000000; @@ -238,7 +216,6 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc) /** * pdc2026x_dev_config - device setup hook - * @ap: ATA port * @adev: newly found device * * Perform chip specific early setup. We need to lock the transfer @@ -246,7 +223,7 @@ static void pdc2026x_bmdma_stop(struct ata_queued_cmd *qc) * barf. */ -static void pdc2026x_dev_config(struct ata_port *ap, struct ata_device *adev) +static void pdc2026x_dev_config(struct ata_device *adev) { adev->max_sectors = 256; } @@ -267,8 +244,10 @@ static struct scsi_host_template pdc202xx_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations pdc2024x_port_ops = { @@ -284,8 +263,9 @@ static struct ata_port_operations pdc2024x_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = pdc2024x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -318,8 +298,9 @@ static struct ata_port_operations pdc2026x_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = pdc2026x_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = pdc2026x_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = pdc2026x_bmdma_start, @@ -399,8 +380,10 @@ static struct pci_driver pdc202xx_pci_driver = { .id_table = pdc202xx, .probe = pdc202xx_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init pdc202xx_init(void) diff --git a/drivers/ata/pata_platform.c b/drivers/ata/pata_platform.c index 479a326..a0a650c 100644 --- a/drivers/ata/pata_platform.c +++ b/drivers/ata/pata_platform.c @@ -42,6 +42,7 @@ static int pata_platform_set_mode(struct ata_port *ap, struct ata_device **unuse dev->pio_mode = dev->xfer_mode = XFER_PIO_0; dev->xfer_shift = ATA_SHIFT_PIO; dev->flags |= ATA_DFLAG_PIO; + ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); } } return 0; @@ -79,13 +80,13 @@ static struct ata_port_operations pata_platform_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer_noirq, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -134,7 +135,8 @@ static void pata_platform_setup_port(struct ata_ioports *ioaddr, static int __devinit pata_platform_probe(struct platform_device *pdev) { struct resource *io_res, *ctl_res; - struct ata_probe_ent ae; + struct ata_host *host; + struct ata_port *ap; unsigned int mmio; /* @@ -174,44 +176,41 @@ static int __devinit pata_platform_probe(struct platform_device *pdev) /* * Now that that's out of the way, wire up the port.. */ - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - ae.port_ops = &pata_platform_port_ops; - ae.sht = &pata_platform_sht; - ae.n_ports = 1; - ae.pio_mask = pio_mask; - ae.irq = platform_get_irq(pdev, 0); - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + return -ENOMEM; + ap = host->ports[0]; + + ap->ops = &pata_platform_port_ops; + ap->pio_mask = pio_mask; + ap->flags |= ATA_FLAG_SLAVE_POSS; /* * Handle the MMIO case */ if (mmio) { - ae.port[0].cmd_addr = devm_ioremap(&pdev->dev, io_res->start, + ap->ioaddr.cmd_addr = devm_ioremap(&pdev->dev, io_res->start, io_res->end - io_res->start + 1); - ae.port[0].ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, + ap->ioaddr.ctl_addr = devm_ioremap(&pdev->dev, ctl_res->start, ctl_res->end - ctl_res->start + 1); } else { - ae.port[0].cmd_addr = devm_ioport_map(&pdev->dev, io_res->start, + ap->ioaddr.cmd_addr = devm_ioport_map(&pdev->dev, io_res->start, io_res->end - io_res->start + 1); - ae.port[0].ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start, + ap->ioaddr.ctl_addr = devm_ioport_map(&pdev->dev, ctl_res->start, ctl_res->end - ctl_res->start + 1); } - if (!ae.port[0].cmd_addr || !ae.port[0].ctl_addr) { + if (!ap->ioaddr.cmd_addr || !ap->ioaddr.ctl_addr) { dev_err(&pdev->dev, "failed to map IO/CTL base\n"); return -ENOMEM; } - ae.port[0].altstatus_addr = ae.port[0].ctl_addr; + ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; - pata_platform_setup_port(&ae.port[0], pdev->dev.platform_data); + pata_platform_setup_port(&ap->ioaddr, pdev->dev.platform_data); - if (unlikely(ata_device_add(&ae) == 0)) - return -ENODEV; - - return 0; + /* activate */ + return ata_host_activate(host, platform_get_irq(pdev, 0), ata_interrupt, + 0, &pata_platform_sht); } /** @@ -227,7 +226,6 @@ static int __devexit pata_platform_remove(struct platform_device *pdev) struct ata_host *host = dev_get_drvdata(dev); ata_host_detach(host); - dev_set_drvdata(dev, NULL); return 0; } diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c index 4362141..27685ce 100644 --- a/drivers/ata/pata_qdi.c +++ b/drivers/ata/pata_qdi.c @@ -26,7 +26,7 @@ #include <linux/platform_device.h> #define DRV_NAME "pata_qdi" -#define DRV_VERSION "0.2.4" +#define DRV_VERSION "0.3.0" #define NR_HOST 4 /* Two 6580s */ @@ -183,13 +183,13 @@ static struct ata_port_operations qdi6500_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = qdi_qc_issue_prot, .data_xfer = qdi_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -211,13 +211,13 @@ static struct ata_port_operations qdi6580_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = qdi_qc_issue_prot, .data_xfer = qdi_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -238,8 +238,9 @@ static struct ata_port_operations qdi6580_port_ops = { static __init int qdi_init_one(unsigned long port, int type, unsigned long io, int irq, int fast) { - struct ata_probe_ent ae; struct platform_device *pdev; + struct ata_host *host; + struct ata_port *ap; void __iomem *io_addr, *ctl_addr; int ret; @@ -257,34 +258,31 @@ static __init int qdi_init_one(unsigned long port, int type, unsigned long io, i if (!io_addr || !ctl_addr) goto fail; - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; + ret = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto fail; + ap = host->ports[0]; if (type == 6580) { - ae.port_ops = &qdi6580_port_ops; - ae.pio_mask = 0x1F; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; + ap->ops = &qdi6580_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; } else { - ae.port_ops = &qdi6500_port_ops; - ae.pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */ - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | - ATA_FLAG_NO_IORDY; + ap->ops = &qdi6500_port_ops; + ap->pio_mask = 0x07; /* Actually PIO3 !IORDY is possible */ + ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY; } - ae.sht = &qdi_sht; - ae.n_ports = 1; - ae.irq = irq; - ae.irq_flags = 0; - ae.port[0].cmd_addr = io_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); + ap->ioaddr.cmd_addr = io_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); /* * Hook in a private data structure per channel */ - ae.private_data = &qdi_data[nr_qdi_host]; + ap->private_data = &qdi_data[nr_qdi_host]; qdi_data[nr_qdi_host].timing = port; qdi_data[nr_qdi_host].fast = fast; @@ -292,8 +290,9 @@ static __init int qdi_init_one(unsigned long port, int type, unsigned long io, i printk(KERN_INFO DRV_NAME": qd%d at 0x%lx.\n", type, io); - ret = -ENODEV; - if (!ata_device_add(&ae)) + /* activate */ + ret = ata_host_activate(host, irq, ata_interrupt, 0, &qdi_sht); + if (ret) goto fail; qdi_host[nr_qdi_host++] = dev_get_drvdata(&pdev->dev); @@ -363,7 +362,8 @@ static __init int qdi_init(void) release_region(port, 2); continue; } - ct += qdi_init_one(port, 6500, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04); + if (qdi_init_one(port, 6500, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04) == 0) + ct++; } if (((r & 0xF0) == 0xA0) || (r & 0xF0) == 0x50) { /* QD6580: dual channel */ @@ -375,11 +375,14 @@ static __init int qdi_init(void) res = inb(port + 3); if (res & 1) { /* Single channel mode */ - ct += qdi_init_one(port, 6580, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04); + if (qdi_init_one(port, 6580, ide_port[r & 0x01], ide_irq[r & 0x01], r & 0x04)) + ct++; } else { /* Dual channel mode */ - ct += qdi_init_one(port, 6580, 0x1F0, 14, r & 0x04); - ct += qdi_init_one(port + 2, 6580, 0x170, 15, r & 0x04); + if (qdi_init_one(port, 6580, 0x1F0, 14, r & 0x04) == 0) + ct++; + if (qdi_init_one(port + 2, 6580, 0x170, 15, r & 0x04) == 0) + ct++; } } } diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c index 0d1e571..1c54673 100644 --- a/drivers/ata/pata_radisys.c +++ b/drivers/ata/pata_radisys.c @@ -24,40 +24,12 @@ #include <linux/ata.h> #define DRV_NAME "pata_radisys" -#define DRV_VERSION "0.4.1" - -/** - * radisys_probe_init - probe begin - * @ap: ATA port - * - * Set up cable type and use generic probe init - */ - -static int radisys_pre_reset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA80; - return ata_std_prereset(ap); -} - - -/** - * radisys_pata_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - -static void radisys_pata_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, radisys_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} +#define DRV_VERSION "0.4.4" /** * radisys_set_piomode - Initialize host controller PATA PIO timings - * @ap: Port whose timings we are configuring - * @adev: um + * @ap: ATA port + * @adev: Device whose timings we are configuring * * Set PIO mode for device, in host controller PCI config space. * @@ -228,8 +200,10 @@ static struct scsi_host_template radisys_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations radisys_pata_ops = { @@ -246,8 +220,9 @@ static const struct ata_port_operations radisys_pata_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = radisys_pata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_unknown, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -312,8 +287,10 @@ static struct pci_driver radisys_pci_driver = { .id_table = radisys_pci_tbl, .probe = radisys_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init radisys_init(void) diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c index 71a2bac..85c4529 100644 --- a/drivers/ata/pata_rz1000.c +++ b/drivers/ata/pata_rz1000.c @@ -25,31 +25,6 @@ /** - * rz1000_prereset - probe begin - * @ap: ATA port - * - * Set up cable type and use generics - */ - -static int rz1000_prereset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_PATA40; - return ata_std_prereset(ap); -} - -/** - * rz1000_error_handler - probe reset - * @ap: ATA port - * - * Perform the ATA standard reset sequence - */ - -static void rz1000_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, rz1000_prereset, ata_std_softreset, NULL, ata_std_postreset); -} - -/** * rz1000_set_mode - mode setting function * @ap: ATA interface * @unused: returned device on set_mode failure @@ -71,6 +46,7 @@ static int rz1000_set_mode(struct ata_port *ap, struct ata_device **unused) dev->xfer_mode = XFER_PIO_0; dev->xfer_shift = ATA_SHIFT_PIO; dev->flags |= ATA_DFLAG_PIO; + ata_dev_printk(dev, KERN_INFO, "configured for PIO\n"); } } return 0; @@ -93,8 +69,10 @@ static struct scsi_host_template rz1000_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations rz1000_port_ops = { @@ -119,8 +97,9 @@ static struct ata_port_operations rz1000_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = rz1000_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, @@ -177,6 +156,7 @@ static int rz1000_init_one (struct pci_dev *pdev, const struct pci_device_id *en return -ENODEV; } +#ifdef CONFIG_PM static int rz1000_reinit_one(struct pci_dev *pdev) { /* If this fails on resume (which is a "cant happen" case), we @@ -185,6 +165,7 @@ static int rz1000_reinit_one(struct pci_dev *pdev) panic("rz1000 fifo"); return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id pata_rz1000[] = { { PCI_VDEVICE(PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000), }, @@ -198,8 +179,10 @@ static struct pci_driver rz1000_pci_driver = { .id_table = pata_rz1000, .probe = rz1000_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = rz1000_reinit_one, +#endif }; static int __init rz1000_init(void) diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c index 58e42fb..66e8ff4 100644 --- a/drivers/ata/pata_sc1200.c +++ b/drivers/ata/pata_sc1200.c @@ -194,8 +194,10 @@ static struct scsi_host_template sc1200_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations sc1200_port_ops = { @@ -210,7 +212,11 @@ static struct ata_port_operations sc1200_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -266,8 +272,10 @@ static struct pci_driver sc1200_pci_driver = { .id_table = sc1200, .probe = sc1200_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init sc1200_init(void) diff --git a/drivers/ata/pata_scc.c b/drivers/ata/pata_scc.c new file mode 100644 index 0000000..5df354d --- /dev/null +++ b/drivers/ata/pata_scc.c @@ -0,0 +1,1211 @@ +/* + * Support for IDE interfaces on Celleb platform + * + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * This code is based on drivers/ata/ata_piix.c: + * Copyright 2003-2005 Red Hat Inc + * Copyright 2003-2005 Jeff Garzik + * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer + * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat Inc <alan@redhat.com> + * + * and drivers/ata/ahci.c: + * Copyright 2004-2005 Red Hat, Inc. + * + * and drivers/ata/libata-core.c: + * Copyright 2003-2004 Red Hat, Inc. All rights reserved. + * Copyright 2003-2004 Jeff Garzik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <scsi/scsi_host.h> +#include <linux/libata.h> + +#define DRV_NAME "pata_scc" +#define DRV_VERSION "0.1" + +#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4 + +/* PCI BARs */ +#define SCC_CTRL_BAR 0 +#define SCC_BMID_BAR 1 + +/* offset of CTRL registers */ +#define SCC_CTL_PIOSHT 0x000 +#define SCC_CTL_PIOCT 0x004 +#define SCC_CTL_MDMACT 0x008 +#define SCC_CTL_MCRCST 0x00C +#define SCC_CTL_SDMACT 0x010 +#define SCC_CTL_SCRCST 0x014 +#define SCC_CTL_UDENVT 0x018 +#define SCC_CTL_TDVHSEL 0x020 +#define SCC_CTL_MODEREG 0x024 +#define SCC_CTL_ECMODE 0xF00 +#define SCC_CTL_MAEA0 0xF50 +#define SCC_CTL_MAEC0 0xF54 +#define SCC_CTL_CCKCTRL 0xFF0 + +/* offset of BMID registers */ +#define SCC_DMA_CMD 0x000 +#define SCC_DMA_STATUS 0x004 +#define SCC_DMA_TABLE_OFS 0x008 +#define SCC_DMA_INTMASK 0x010 +#define SCC_DMA_INTST 0x014 +#define SCC_DMA_PTERADD 0x018 +#define SCC_REG_CMD_ADDR 0x020 +#define SCC_REG_DATA 0x000 +#define SCC_REG_ERR 0x004 +#define SCC_REG_FEATURE 0x004 +#define SCC_REG_NSECT 0x008 +#define SCC_REG_LBAL 0x00C +#define SCC_REG_LBAM 0x010 +#define SCC_REG_LBAH 0x014 +#define SCC_REG_DEVICE 0x018 +#define SCC_REG_STATUS 0x01C +#define SCC_REG_CMD 0x01C +#define SCC_REG_ALTSTATUS 0x020 + +/* register value */ +#define TDVHSEL_MASTER 0x00000001 +#define TDVHSEL_SLAVE 0x00000004 + +#define MODE_JCUSFEN 0x00000080 + +#define ECMODE_VALUE 0x01 + +#define CCKCTRL_ATARESET 0x00040000 +#define CCKCTRL_BUFCNT 0x00020000 +#define CCKCTRL_CRST 0x00010000 +#define CCKCTRL_OCLKEN 0x00000100 +#define CCKCTRL_ATACLKOEN 0x00000002 +#define CCKCTRL_LCLKEN 0x00000001 + +#define QCHCD_IOS_SS 0x00000001 + +#define QCHSD_STPDIAG 0x00020000 + +#define INTMASK_MSK 0xD1000012 +#define INTSTS_SERROR 0x80000000 +#define INTSTS_PRERR 0x40000000 +#define INTSTS_RERR 0x10000000 +#define INTSTS_ICERR 0x01000000 +#define INTSTS_BMSINT 0x00000010 +#define INTSTS_BMHE 0x00000008 +#define INTSTS_IOIRQS 0x00000004 +#define INTSTS_INTRQ 0x00000002 +#define INTSTS_ACTEINT 0x00000001 + + +/* PIO transfer mode table */ +/* JCHST */ +static const unsigned long JCHSTtbl[2][7] = { + {0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */ + {0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */ +}; + +/* JCHHT */ +static const unsigned long JCHHTtbl[2][7] = { + {0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */ + {0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */ +}; + +/* JCHCT */ +static const unsigned long JCHCTtbl[2][7] = { + {0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */ + {0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */ +}; + +/* DMA transfer mode table */ +/* JCHDCTM/JCHDCTS */ +static const unsigned long JCHDCTxtbl[2][7] = { + {0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */ + {0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */ +}; + +/* JCSTWTM/JCSTWTS */ +static const unsigned long JCSTWTxtbl[2][7] = { + {0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */ + {0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ +}; + +/* JCTSS */ +static const unsigned long JCTSStbl[2][7] = { + {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */ + {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */ +}; + +/* JCENVT */ +static const unsigned long JCENVTtbl[2][7] = { + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */ + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ +}; + +/* JCACTSELS/JCACTSELM */ +static const unsigned long JCACTSELtbl[2][7] = { + {0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */ +}; + +static const struct pci_device_id scc_pci_tbl[] = { + {PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { } /* terminate list */ +}; + +/** + * scc_set_piomode - Initialize host controller PATA PIO timings + * @ap: Port whose timings we are configuring + * @adev: um + * + * Set PIO mode for device. + * + * LOCKING: + * None (inherited from caller). + */ + +static void scc_set_piomode (struct ata_port *ap, struct ata_device *adev) +{ + unsigned int pio = adev->pio_mode - XFER_PIO_0; + void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; + void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; + void __iomem *piosht_port = ctrl_base + SCC_CTL_PIOSHT; + void __iomem *pioct_port = ctrl_base + SCC_CTL_PIOCT; + unsigned long reg; + int offset; + + reg = in_be32(cckctrl_port); + if (reg & CCKCTRL_ATACLKOEN) + offset = 1; /* 133MHz */ + else + offset = 0; /* 100MHz */ + + reg = JCHSTtbl[offset][pio] << 16 | JCHHTtbl[offset][pio]; + out_be32(piosht_port, reg); + reg = JCHCTtbl[offset][pio]; + out_be32(pioct_port, reg); +} + +/** + * scc_set_dmamode - Initialize host controller PATA DMA timings + * @ap: Port whose timings we are configuring + * @adev: um + * @udma: udma mode, 0 - 6 + * + * Set UDMA mode for device. + * + * LOCKING: + * None (inherited from caller). + */ + +static void scc_set_dmamode (struct ata_port *ap, struct ata_device *adev) +{ + unsigned int udma = adev->dma_mode; + unsigned int is_slave = (adev->devno != 0); + u8 speed = udma; + void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; + void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; + void __iomem *mdmact_port = ctrl_base + SCC_CTL_MDMACT; + void __iomem *mcrcst_port = ctrl_base + SCC_CTL_MCRCST; + void __iomem *sdmact_port = ctrl_base + SCC_CTL_SDMACT; + void __iomem *scrcst_port = ctrl_base + SCC_CTL_SCRCST; + void __iomem *udenvt_port = ctrl_base + SCC_CTL_UDENVT; + void __iomem *tdvhsel_port = ctrl_base + SCC_CTL_TDVHSEL; + int offset, idx; + + if (in_be32(cckctrl_port) & CCKCTRL_ATACLKOEN) + offset = 1; /* 133MHz */ + else + offset = 0; /* 100MHz */ + + if (speed >= XFER_UDMA_0) + idx = speed - XFER_UDMA_0; + else + return; + + if (is_slave) { + out_be32(sdmact_port, JCHDCTxtbl[offset][idx]); + out_be32(scrcst_port, JCSTWTxtbl[offset][idx]); + out_be32(tdvhsel_port, + (in_be32(tdvhsel_port) & ~TDVHSEL_SLAVE) | (JCACTSELtbl[offset][idx] << 2)); + } else { + out_be32(mdmact_port, JCHDCTxtbl[offset][idx]); + out_be32(mcrcst_port, JCSTWTxtbl[offset][idx]); + out_be32(tdvhsel_port, + (in_be32(tdvhsel_port) & ~TDVHSEL_MASTER) | JCACTSELtbl[offset][idx]); + } + out_be32(udenvt_port, + JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx]); +} + +/** + * scc_tf_load - send taskfile registers to host controller + * @ap: Port to which output is sent + * @tf: ATA taskfile register set + * + * Note: Original code is ata_tf_load(). + */ + +static void scc_tf_load (struct ata_port *ap, const struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int is_addr = tf->flags & ATA_TFLAG_ISADDR; + + if (tf->ctl != ap->last_ctl) { + out_be32(ioaddr->ctl_addr, tf->ctl); + ap->last_ctl = tf->ctl; + ata_wait_idle(ap); + } + + if (is_addr && (tf->flags & ATA_TFLAG_LBA48)) { + out_be32(ioaddr->feature_addr, tf->hob_feature); + out_be32(ioaddr->nsect_addr, tf->hob_nsect); + out_be32(ioaddr->lbal_addr, tf->hob_lbal); + out_be32(ioaddr->lbam_addr, tf->hob_lbam); + out_be32(ioaddr->lbah_addr, tf->hob_lbah); + VPRINTK("hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X\n", + tf->hob_feature, + tf->hob_nsect, + tf->hob_lbal, + tf->hob_lbam, + tf->hob_lbah); + } + + if (is_addr) { + out_be32(ioaddr->feature_addr, tf->feature); + out_be32(ioaddr->nsect_addr, tf->nsect); + out_be32(ioaddr->lbal_addr, tf->lbal); + out_be32(ioaddr->lbam_addr, tf->lbam); + out_be32(ioaddr->lbah_addr, tf->lbah); + VPRINTK("feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X\n", + tf->feature, + tf->nsect, + tf->lbal, + tf->lbam, + tf->lbah); + } + + if (tf->flags & ATA_TFLAG_DEVICE) { + out_be32(ioaddr->device_addr, tf->device); + VPRINTK("device 0x%X\n", tf->device); + } + + ata_wait_idle(ap); +} + +/** + * scc_check_status - Read device status reg & clear interrupt + * @ap: port where the device is + * + * Note: Original code is ata_check_status(). + */ + +static u8 scc_check_status (struct ata_port *ap) +{ + return in_be32(ap->ioaddr.status_addr); +} + +/** + * scc_tf_read - input device's ATA taskfile shadow registers + * @ap: Port from which input is read + * @tf: ATA taskfile register set for storing input + * + * Note: Original code is ata_tf_read(). + */ + +static void scc_tf_read (struct ata_port *ap, struct ata_taskfile *tf) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + tf->command = scc_check_status(ap); + tf->feature = in_be32(ioaddr->error_addr); + tf->nsect = in_be32(ioaddr->nsect_addr); + tf->lbal = in_be32(ioaddr->lbal_addr); + tf->lbam = in_be32(ioaddr->lbam_addr); + tf->lbah = in_be32(ioaddr->lbah_addr); + tf->device = in_be32(ioaddr->device_addr); + + if (tf->flags & ATA_TFLAG_LBA48) { + out_be32(ioaddr->ctl_addr, tf->ctl | ATA_HOB); + tf->hob_feature = in_be32(ioaddr->error_addr); + tf->hob_nsect = in_be32(ioaddr->nsect_addr); + tf->hob_lbal = in_be32(ioaddr->lbal_addr); + tf->hob_lbam = in_be32(ioaddr->lbam_addr); + tf->hob_lbah = in_be32(ioaddr->lbah_addr); + } +} + +/** + * scc_exec_command - issue ATA command to host controller + * @ap: port to which command is being issued + * @tf: ATA taskfile register set + * + * Note: Original code is ata_exec_command(). + */ + +static void scc_exec_command (struct ata_port *ap, + const struct ata_taskfile *tf) +{ + DPRINTK("ata%u: cmd 0x%X\n", ap->print_id, tf->command); + + out_be32(ap->ioaddr.command_addr, tf->command); + ata_pause(ap); +} + +/** + * scc_check_altstatus - Read device alternate status reg + * @ap: port where the device is + */ + +static u8 scc_check_altstatus (struct ata_port *ap) +{ + return in_be32(ap->ioaddr.altstatus_addr); +} + +/** + * scc_std_dev_select - Select device 0/1 on ATA bus + * @ap: ATA channel to manipulate + * @device: ATA device (numbered from zero) to select + * + * Note: Original code is ata_std_dev_select(). + */ + +static void scc_std_dev_select (struct ata_port *ap, unsigned int device) +{ + u8 tmp; + + if (device == 0) + tmp = ATA_DEVICE_OBS; + else + tmp = ATA_DEVICE_OBS | ATA_DEV1; + + out_be32(ap->ioaddr.device_addr, tmp); + ata_pause(ap); +} + +/** + * scc_bmdma_setup - Set up PCI IDE BMDMA transaction + * @qc: Info associated with this ATA transaction. + * + * Note: Original code is ata_bmdma_setup(). + */ + +static void scc_bmdma_setup (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); + u8 dmactl; + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + /* load PRD table addr */ + out_be32(mmio + SCC_DMA_TABLE_OFS, ap->prd_dma); + + /* specify data direction, triple-check start bit is clear */ + dmactl = in_be32(mmio + SCC_DMA_CMD); + dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); + if (!rw) + dmactl |= ATA_DMA_WR; + out_be32(mmio + SCC_DMA_CMD, dmactl); + + /* issue r/w command */ + ap->ops->exec_command(ap, &qc->tf); +} + +/** + * scc_bmdma_start - Start a PCI IDE BMDMA transaction + * @qc: Info associated with this ATA transaction. + * + * Note: Original code is ata_bmdma_start(). + */ + +static void scc_bmdma_start (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + u8 dmactl; + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + /* start host DMA transaction */ + dmactl = in_be32(mmio + SCC_DMA_CMD); + out_be32(mmio + SCC_DMA_CMD, dmactl | ATA_DMA_START); +} + +/** + * scc_devchk - PATA device presence detection + * @ap: ATA channel to examine + * @device: Device to examine (starting at zero) + * + * Note: Original code is ata_devchk(). + */ + +static unsigned int scc_devchk (struct ata_port *ap, + unsigned int device) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 nsect, lbal; + + ap->ops->dev_select(ap, device); + + out_be32(ioaddr->nsect_addr, 0x55); + out_be32(ioaddr->lbal_addr, 0xaa); + + out_be32(ioaddr->nsect_addr, 0xaa); + out_be32(ioaddr->lbal_addr, 0x55); + + out_be32(ioaddr->nsect_addr, 0x55); + out_be32(ioaddr->lbal_addr, 0xaa); + + nsect = in_be32(ioaddr->nsect_addr); + lbal = in_be32(ioaddr->lbal_addr); + + if ((nsect == 0x55) && (lbal == 0xaa)) + return 1; /* we found a device */ + + return 0; /* nothing found */ +} + +/** + * scc_bus_post_reset - PATA device post reset + * + * Note: Original code is ata_bus_post_reset(). + */ + +static void scc_bus_post_reset (struct ata_port *ap, unsigned int devmask) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + unsigned int dev0 = devmask & (1 << 0); + unsigned int dev1 = devmask & (1 << 1); + unsigned long timeout; + + /* if device 0 was found in ata_devchk, wait for its + * BSY bit to clear + */ + if (dev0) + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* if device 1 was found in ata_devchk, wait for + * register access, then wait for BSY to clear + */ + timeout = jiffies + ATA_TMOUT_BOOT; + while (dev1) { + u8 nsect, lbal; + + ap->ops->dev_select(ap, 1); + nsect = in_be32(ioaddr->nsect_addr); + lbal = in_be32(ioaddr->lbal_addr); + if ((nsect == 1) && (lbal == 1)) + break; + if (time_after(jiffies, timeout)) { + dev1 = 0; + break; + } + msleep(50); /* give drive a breather */ + } + if (dev1) + ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT); + + /* is all this really necessary? */ + ap->ops->dev_select(ap, 0); + if (dev1) + ap->ops->dev_select(ap, 1); + if (dev0) + ap->ops->dev_select(ap, 0); +} + +/** + * scc_bus_softreset - PATA device software reset + * + * Note: Original code is ata_bus_softreset(). + */ + +static unsigned int scc_bus_softreset (struct ata_port *ap, + unsigned int devmask) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + DPRINTK("ata%u: bus reset via SRST\n", ap->print_id); + + /* software reset. causes dev0 to be selected */ + out_be32(ioaddr->ctl_addr, ap->ctl); + udelay(20); + out_be32(ioaddr->ctl_addr, ap->ctl | ATA_SRST); + udelay(20); + out_be32(ioaddr->ctl_addr, ap->ctl); + + /* spec mandates ">= 2ms" before checking status. + * We wait 150ms, because that was the magic delay used for + * ATAPI devices in Hale Landis's ATADRVR, for the period of time + * between when the ATA command register is written, and then + * status is checked. Because waiting for "a while" before + * checking status is fine, post SRST, we perform this magic + * delay here as well. + * + * Old drivers/ide uses the 2mS rule and then waits for ready + */ + msleep(150); + + /* Before we perform post reset processing we want to see if + * the bus shows 0xFF because the odd clown forgets the D7 + * pulldown resistor. + */ + if (scc_check_status(ap) == 0xFF) + return 0; + + scc_bus_post_reset(ap, devmask); + + return 0; +} + +/** + * scc_std_softreset - reset host port via ATA SRST + * @ap: port to reset + * @classes: resulting classes of attached devices + * + * Note: Original code is ata_std_softreset(). + */ + +static int scc_std_softreset (struct ata_port *ap, unsigned int *classes) +{ + unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS; + unsigned int devmask = 0, err_mask; + u8 err; + + DPRINTK("ENTER\n"); + + if (ata_port_offline(ap)) { + classes[0] = ATA_DEV_NONE; + goto out; + } + + /* determine if device 0/1 are present */ + if (scc_devchk(ap, 0)) + devmask |= (1 << 0); + if (slave_possible && scc_devchk(ap, 1)) + devmask |= (1 << 1); + + /* select device 0 again */ + ap->ops->dev_select(ap, 0); + + /* issue bus reset */ + DPRINTK("about to softreset, devmask=%x\n", devmask); + err_mask = scc_bus_softreset(ap, devmask); + if (err_mask) { + ata_port_printk(ap, KERN_ERR, "SRST failed (err_mask=0x%x)\n", + err_mask); + return -EIO; + } + + /* determine by signature whether we have ATA or ATAPI devices */ + classes[0] = ata_dev_try_classify(ap, 0, &err); + if (slave_possible && err != 0x81) + classes[1] = ata_dev_try_classify(ap, 1, &err); + + out: + DPRINTK("EXIT, classes[0]=%u [1]=%u\n", classes[0], classes[1]); + return 0; +} + +/** + * scc_bmdma_stop - Stop PCI IDE BMDMA transfer + * @qc: Command we are ending DMA for + */ + +static void scc_bmdma_stop (struct ata_queued_cmd *qc) +{ + struct ata_port *ap = qc->ap; + void __iomem *ctrl_base = ap->host->iomap[SCC_CTRL_BAR]; + void __iomem *bmid_base = ap->host->iomap[SCC_BMID_BAR]; + u32 reg; + + while (1) { + reg = in_be32(bmid_base + SCC_DMA_INTST); + + if (reg & INTSTS_SERROR) { + printk(KERN_WARNING "%s: SERROR\n", DRV_NAME); + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_SERROR|INTSTS_BMSINT); + out_be32(bmid_base + SCC_DMA_CMD, + in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); + continue; + } + + if (reg & INTSTS_PRERR) { + u32 maea0, maec0; + maea0 = in_be32(ctrl_base + SCC_CTL_MAEA0); + maec0 = in_be32(ctrl_base + SCC_CTL_MAEC0); + printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", DRV_NAME, maea0, maec0); + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_PRERR|INTSTS_BMSINT); + out_be32(bmid_base + SCC_DMA_CMD, + in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); + continue; + } + + if (reg & INTSTS_RERR) { + printk(KERN_WARNING "%s: Response Error\n", DRV_NAME); + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_RERR|INTSTS_BMSINT); + out_be32(bmid_base + SCC_DMA_CMD, + in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); + continue; + } + + if (reg & INTSTS_ICERR) { + out_be32(bmid_base + SCC_DMA_CMD, + in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); + printk(KERN_WARNING "%s: Illegal Configuration\n", DRV_NAME); + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ICERR|INTSTS_BMSINT); + continue; + } + + if (reg & INTSTS_BMSINT) { + unsigned int classes; + printk(KERN_WARNING "%s: Internal Bus Error\n", DRV_NAME); + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMSINT); + /* TBD: SW reset */ + scc_std_softreset(ap, &classes); + continue; + } + + if (reg & INTSTS_BMHE) { + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_BMHE); + continue; + } + + if (reg & INTSTS_ACTEINT) { + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_ACTEINT); + continue; + } + + if (reg & INTSTS_IOIRQS) { + out_be32(bmid_base + SCC_DMA_INTST, INTSTS_IOIRQS); + continue; + } + break; + } + + /* clear start/stop bit */ + out_be32(bmid_base + SCC_DMA_CMD, + in_be32(bmid_base + SCC_DMA_CMD) & ~ATA_DMA_START); + + /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ + ata_altstatus(ap); /* dummy read */ +} + +/** + * scc_bmdma_status - Read PCI IDE BMDMA status + * @ap: Port associated with this ATA transaction. + */ + +static u8 scc_bmdma_status (struct ata_port *ap) +{ + u8 host_stat; + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + host_stat = in_be32(mmio + SCC_DMA_STATUS); + + /* Workaround for PTERADD: emulate DMA_INTR when + * - IDE_STATUS[ERR] = 1 + * - INT_STATUS[INTRQ] = 1 + * - DMA_STATUS[IORACTA] = 1 + */ + if (!(host_stat & ATA_DMA_INTR)) { + u32 int_status = in_be32(mmio + SCC_DMA_INTST); + if (ata_altstatus(ap) & ATA_ERR && + int_status & INTSTS_INTRQ && + host_stat & ATA_DMA_ACTIVE) + host_stat |= ATA_DMA_INTR; + } + + return host_stat; +} + +/** + * scc_data_xfer - Transfer data by PIO + * @adev: device for this I/O + * @buf: data buffer + * @buflen: buffer length + * @write_data: read/write + * + * Note: Original code is ata_data_xfer(). + */ + +static void scc_data_xfer (struct ata_device *adev, unsigned char *buf, + unsigned int buflen, int write_data) +{ + struct ata_port *ap = adev->ap; + unsigned int words = buflen >> 1; + unsigned int i; + u16 *buf16 = (u16 *) buf; + void __iomem *mmio = ap->ioaddr.data_addr; + + /* Transfer multiple of 2 bytes */ + if (write_data) { + for (i = 0; i < words; i++) + out_be32(mmio, cpu_to_le16(buf16[i])); + } else { + for (i = 0; i < words; i++) + buf16[i] = le16_to_cpu(in_be32(mmio)); + } + + /* Transfer trailing 1 byte, if any. */ + if (unlikely(buflen & 0x01)) { + u16 align_buf[1] = { 0 }; + unsigned char *trailing_buf = buf + buflen - 1; + + if (write_data) { + memcpy(align_buf, trailing_buf, 1); + out_be32(mmio, cpu_to_le16(align_buf[0])); + } else { + align_buf[0] = le16_to_cpu(in_be32(mmio)); + memcpy(trailing_buf, align_buf, 1); + } + } +} + +/** + * scc_irq_on - Enable interrupts on a port. + * @ap: Port on which interrupts are enabled. + * + * Note: Original code is ata_irq_on(). + */ + +static u8 scc_irq_on (struct ata_port *ap) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + u8 tmp; + + ap->ctl &= ~ATA_NIEN; + ap->last_ctl = ap->ctl; + + out_be32(ioaddr->ctl_addr, ap->ctl); + tmp = ata_wait_idle(ap); + + ap->ops->irq_clear(ap); + + return tmp; +} + +/** + * scc_irq_ack - Acknowledge a device interrupt. + * @ap: Port on which interrupts are enabled. + * + * Note: Original code is ata_irq_ack(). + */ + +static u8 scc_irq_ack (struct ata_port *ap, unsigned int chk_drq) +{ + unsigned int bits = chk_drq ? ATA_BUSY | ATA_DRQ : ATA_BUSY; + u8 host_stat, post_stat, status; + + status = ata_busy_wait(ap, bits, 1000); + if (status & bits) + if (ata_msg_err(ap)) + printk(KERN_ERR "abnormal status 0x%X\n", status); + + /* get controller status; clear intr, err bits */ + host_stat = in_be32(ap->ioaddr.bmdma_addr + SCC_DMA_STATUS); + out_be32(ap->ioaddr.bmdma_addr + SCC_DMA_STATUS, + host_stat | ATA_DMA_INTR | ATA_DMA_ERR); + + post_stat = in_be32(ap->ioaddr.bmdma_addr + SCC_DMA_STATUS); + + if (ata_msg_intr(ap)) + printk(KERN_INFO "%s: irq ack: host_stat 0x%X, new host_stat 0x%X, drv_stat 0x%X\n", + __FUNCTION__, + host_stat, post_stat, status); + + return status; +} + +/** + * scc_bmdma_freeze - Freeze BMDMA controller port + * @ap: port to freeze + * + * Note: Original code is ata_bmdma_freeze(). + */ + +static void scc_bmdma_freeze (struct ata_port *ap) +{ + struct ata_ioports *ioaddr = &ap->ioaddr; + + ap->ctl |= ATA_NIEN; + ap->last_ctl = ap->ctl; + + out_be32(ioaddr->ctl_addr, ap->ctl); + + /* Under certain circumstances, some controllers raise IRQ on + * ATA_NIEN manipulation. Also, many controllers fail to mask + * previously pending IRQ on ATA_NIEN assertion. Clear it. + */ + ata_chk_status(ap); + + ap->ops->irq_clear(ap); +} + +/** + * scc_pata_prereset - prepare for reset + * @ap: ATA port to be reset + */ + +static int scc_pata_prereset (struct ata_port *ap) +{ + ap->cbl = ATA_CBL_PATA80; + return ata_std_prereset(ap); +} + +/** + * scc_std_postreset - standard postreset callback + * @ap: the target ata_port + * @classes: classes of attached devices + * + * Note: Original code is ata_std_postreset(). + */ + +static void scc_std_postreset (struct ata_port *ap, unsigned int *classes) +{ + DPRINTK("ENTER\n"); + + /* re-enable interrupts */ + if (!ap->ops->error_handler) + ap->ops->irq_on(ap); + + /* is double-select really necessary? */ + if (classes[0] != ATA_DEV_NONE) + ap->ops->dev_select(ap, 1); + if (classes[1] != ATA_DEV_NONE) + ap->ops->dev_select(ap, 0); + + /* bail out if no device is present */ + if (classes[0] == ATA_DEV_NONE && classes[1] == ATA_DEV_NONE) { + DPRINTK("EXIT, no device\n"); + return; + } + + /* set up device control */ + if (ap->ioaddr.ctl_addr) + out_be32(ap->ioaddr.ctl_addr, ap->ctl); + + DPRINTK("EXIT\n"); +} + +/** + * scc_error_handler - Stock error handler for BMDMA controller + * @ap: port to handle error for + */ + +static void scc_error_handler (struct ata_port *ap) +{ + ata_bmdma_drive_eh(ap, scc_pata_prereset, scc_std_softreset, NULL, + scc_std_postreset); +} + +/** + * scc_bmdma_irq_clear - Clear PCI IDE BMDMA interrupt. + * @ap: Port associated with this ATA transaction. + * + * Note: Original code is ata_bmdma_irq_clear(). + */ + +static void scc_bmdma_irq_clear (struct ata_port *ap) +{ + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + if (!mmio) + return; + + out_be32(mmio + SCC_DMA_STATUS, in_be32(mmio + SCC_DMA_STATUS)); +} + +/** + * scc_port_start - Set port up for dma. + * @ap: Port to initialize + * + * Allocate space for PRD table using ata_port_start(). + * Set PRD table address for PTERADD. (PRD Transfer End Read) + */ + +static int scc_port_start (struct ata_port *ap) +{ + void __iomem *mmio = ap->ioaddr.bmdma_addr; + int rc; + + rc = ata_port_start(ap); + if (rc) + return rc; + + out_be32(mmio + SCC_DMA_PTERADD, ap->prd_dma); + return 0; +} + +/** + * scc_port_stop - Undo scc_port_start() + * @ap: Port to shut down + * + * Reset PTERADD. + */ + +static void scc_port_stop (struct ata_port *ap) +{ + void __iomem *mmio = ap->ioaddr.bmdma_addr; + + out_be32(mmio + SCC_DMA_PTERADD, 0); +} + +static struct scsi_host_template scc_sht = { + .module = THIS_MODULE, + .name = DRV_NAME, + .ioctl = ata_scsi_ioctl, + .queuecommand = ata_scsi_queuecmd, + .can_queue = ATA_DEF_QUEUE, + .this_id = ATA_SHT_THIS_ID, + .sg_tablesize = LIBATA_MAX_PRD, + .cmd_per_lun = ATA_SHT_CMD_PER_LUN, + .emulated = ATA_SHT_EMULATED, + .use_clustering = ATA_SHT_USE_CLUSTERING, + .proc_name = DRV_NAME, + .dma_boundary = ATA_DMA_BOUNDARY, + .slave_configure = ata_scsi_slave_config, + .slave_destroy = ata_scsi_slave_destroy, + .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .resume = ata_scsi_device_resume, + .suspend = ata_scsi_device_suspend, +#endif +}; + +static const struct ata_port_operations scc_pata_ops = { + .port_disable = ata_port_disable, + .set_piomode = scc_set_piomode, + .set_dmamode = scc_set_dmamode, + .mode_filter = ata_pci_default_filter, + + .tf_load = scc_tf_load, + .tf_read = scc_tf_read, + .exec_command = scc_exec_command, + .check_status = scc_check_status, + .check_altstatus = scc_check_altstatus, + .dev_select = scc_std_dev_select, + + .bmdma_setup = scc_bmdma_setup, + .bmdma_start = scc_bmdma_start, + .bmdma_stop = scc_bmdma_stop, + .bmdma_status = scc_bmdma_status, + .data_xfer = scc_data_xfer, + + .qc_prep = ata_qc_prep, + .qc_issue = ata_qc_issue_prot, + + .freeze = scc_bmdma_freeze, + .error_handler = scc_error_handler, + .post_internal_cmd = scc_bmdma_stop, + + .irq_clear = scc_bmdma_irq_clear, + .irq_on = scc_irq_on, + .irq_ack = scc_irq_ack, + + .port_start = scc_port_start, + .port_stop = scc_port_stop, +}; + +static struct ata_port_info scc_port_info[] = { + { + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_MMIO | ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x00, + .udma_mask = ATA_UDMA6, + .port_ops = &scc_pata_ops, + }, +}; + +/** + * scc_reset_controller - initialize SCC PATA controller. + */ + +static int scc_reset_controller(struct ata_host *host) +{ + void __iomem *ctrl_base = host->iomap[SCC_CTRL_BAR]; + void __iomem *bmid_base = host->iomap[SCC_BMID_BAR]; + void __iomem *cckctrl_port = ctrl_base + SCC_CTL_CCKCTRL; + void __iomem *mode_port = ctrl_base + SCC_CTL_MODEREG; + void __iomem *ecmode_port = ctrl_base + SCC_CTL_ECMODE; + void __iomem *intmask_port = bmid_base + SCC_DMA_INTMASK; + void __iomem *dmastatus_port = bmid_base + SCC_DMA_STATUS; + u32 reg = 0; + + out_be32(cckctrl_port, reg); + reg |= CCKCTRL_ATACLKOEN; + out_be32(cckctrl_port, reg); + reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN; + out_be32(cckctrl_port, reg); + reg |= CCKCTRL_CRST; + out_be32(cckctrl_port, reg); + + for (;;) { + reg = in_be32(cckctrl_port); + if (reg & CCKCTRL_CRST) + break; + udelay(5000); + } + + reg |= CCKCTRL_ATARESET; + out_be32(cckctrl_port, reg); + out_be32(ecmode_port, ECMODE_VALUE); + out_be32(mode_port, MODE_JCUSFEN); + out_be32(intmask_port, INTMASK_MSK); + + if (in_be32(dmastatus_port) & QCHSD_STPDIAG) { + printk(KERN_WARNING "%s: failed to detect 80c cable. (PDIAG# is high)\n", DRV_NAME); + return -EIO; + } + + return 0; +} + +/** + * scc_setup_ports - initialize ioaddr with SCC PATA port offsets. + * @ioaddr: IO address structure to be initialized + * @base: base address of BMID region + */ + +static void scc_setup_ports (struct ata_ioports *ioaddr, void __iomem *base) +{ + ioaddr->cmd_addr = base + SCC_REG_CMD_ADDR; + ioaddr->altstatus_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS; + ioaddr->ctl_addr = ioaddr->cmd_addr + SCC_REG_ALTSTATUS; + ioaddr->bmdma_addr = base; + ioaddr->data_addr = ioaddr->cmd_addr + SCC_REG_DATA; + ioaddr->error_addr = ioaddr->cmd_addr + SCC_REG_ERR; + ioaddr->feature_addr = ioaddr->cmd_addr + SCC_REG_FEATURE; + ioaddr->nsect_addr = ioaddr->cmd_addr + SCC_REG_NSECT; + ioaddr->lbal_addr = ioaddr->cmd_addr + SCC_REG_LBAL; + ioaddr->lbam_addr = ioaddr->cmd_addr + SCC_REG_LBAM; + ioaddr->lbah_addr = ioaddr->cmd_addr + SCC_REG_LBAH; + ioaddr->device_addr = ioaddr->cmd_addr + SCC_REG_DEVICE; + ioaddr->status_addr = ioaddr->cmd_addr + SCC_REG_STATUS; + ioaddr->command_addr = ioaddr->cmd_addr + SCC_REG_CMD; +} + +static int scc_host_init(struct ata_host *host) +{ + struct pci_dev *pdev = to_pci_dev(host->dev); + int rc; + + rc = scc_reset_controller(host); + if (rc) + return rc; + + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + + scc_setup_ports(&host->ports[0]->ioaddr, host->iomap[SCC_BMID_BAR]); + + pci_set_master(pdev); + + return 0; +} + +/** + * scc_init_one - Register SCC PATA device with kernel services + * @pdev: PCI device to register + * @ent: Entry in scc_pci_tbl matching with @pdev + * + * LOCKING: + * Inherited from PCI layer (may sleep). + * + * RETURNS: + * Zero on success, or -ERRNO value. + */ + +static int scc_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) +{ + static int printed_version; + unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &scc_port_info[board_idx], NULL }; + struct device *dev = &pdev->dev; + int rc; + + if (!printed_version++) + dev_printk(KERN_DEBUG, &pdev->dev, + "version " DRV_VERSION "\n"); + + host = ata_port_alloc_pinfo(&pdev->dev, ppi, 1); + if (!host) + return -ENOMEM; + + rc = pcim_enable_device(pdev); + if (rc) + return rc; + + rc = pcim_iomap_regions(pdev, (1 << SCC_CTRL_BAR) | (1 << SCC_BMID_BAR), DRV_NAME); + if (rc == -EBUSY) + pcim_pin_device(pdev); + if (rc) + return rc; + host->iomap = pcim_iomap_table(pdev); + + rc = scc_host_init(host); + if (rc) + return rc; + + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &scc_sht); +} + +static struct pci_driver scc_pci_driver = { + .name = DRV_NAME, + .id_table = scc_pci_tbl, + .probe = scc_init_one, + .remove = ata_pci_remove_one, +#ifdef CONFIG_PM + .suspend = ata_pci_device_suspend, + .resume = ata_pci_device_resume, +#endif +}; + +static int __init scc_init (void) +{ + int rc; + + DPRINTK("pci_register_driver\n"); + rc = pci_register_driver(&scc_pci_driver); + if (rc) + return rc; + + DPRINTK("done\n"); + return 0; +} + +static void __exit scc_exit (void) +{ + pci_unregister_driver(&scc_pci_driver); +} + +module_init(scc_init); +module_exit(scc_exit); + +MODULE_AUTHOR("Toshiba corp"); +MODULE_DESCRIPTION("SCSI low-level driver for Toshiba SCC PATA controller"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(pci, scc_pci_tbl); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c index ad5b43f..3956ef2 100644 --- a/drivers/ata/pata_serverworks.c +++ b/drivers/ata/pata_serverworks.c @@ -1,5 +1,5 @@ /* - * ata-serverworks.c - Serverworks PATA for new ATA layer + * pata_serverworks.c - Serverworks PATA for new ATA layer * (C) 2005 Red Hat Inc * Alan Cox <alan@redhat.com> * @@ -41,7 +41,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_serverworks" -#define DRV_VERSION "0.3.9" +#define DRV_VERSION "0.4.0" #define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ #define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ @@ -137,14 +137,14 @@ static struct sv_cable_table cable_detect[] = { }; /** - * serverworks_pre_reset - cable detection + * serverworks_cable_detect - cable detection * @ap: ATA port * * Perform cable detection according to the device and subvendor * identifications */ -static int serverworks_pre_reset(struct ata_port *ap) { +static int serverworks_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); struct sv_cable_table *cb = cable_detect; @@ -152,8 +152,7 @@ static int serverworks_pre_reset(struct ata_port *ap) { if (cb->device == pdev->device && (cb->subvendor == pdev->subsystem_vendor || cb->subvendor == PCI_ANY_ID)) { - ap->cbl = cb->cable_detect(ap); - return ata_std_prereset(ap); + return cb->cable_detect(ap); } cb++; } @@ -162,11 +161,6 @@ static int serverworks_pre_reset(struct ata_port *ap) { return -1; /* kill compiler warning */ } -static void serverworks_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, serverworks_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} - /** * serverworks_is_csb - Check for CSB or OSB * @pdev: PCI device to check @@ -191,31 +185,31 @@ static u8 serverworks_is_csb(struct pci_dev *pdev) /** * serverworks_osb4_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device + * @mask: Mask of proposed modes * * Filter the offered modes for the device to apply controller * specific rules. OSB4 requires no UDMA for disks due to a FIFO * bug we hit. */ -static unsigned long serverworks_osb4_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long serverworks_osb4_filter(struct ata_device *adev, unsigned long mask) { if (adev->class == ATA_DEV_ATA) mask &= ~ATA_MASK_UDMA; - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } /** * serverworks_csb_filter - mode selection filter - * @ap: ATA interface * @adev: ATA device + * @mask: Mask of proposed modes * * Check the blacklist and disable UDMA5 if matched */ -static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct ata_device *adev, unsigned long mask) +static unsigned long serverworks_csb_filter(struct ata_device *adev, unsigned long mask) { const char *p; char model_num[ATA_ID_PROD_LEN + 1]; @@ -223,7 +217,7 @@ static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct at /* Disk, UDMA */ if (adev->class != ATA_DEV_ATA) - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); /* Actually do need to check */ ata_id_c_string(adev->id, model_num, ATA_ID_PROD, sizeof(model_num)); @@ -232,7 +226,7 @@ static unsigned long serverworks_csb_filter(const struct ata_port *ap, struct at if (!strcmp(p, model_num)) mask &= ~(0x1F << ATA_SHIFT_UDMA); } - return ata_pci_default_filter(ap, adev, mask); + return ata_pci_default_filter(adev, mask); } @@ -319,8 +313,10 @@ static struct scsi_host_template serverworks_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations serverworks_osb4_port_ops = { @@ -337,8 +333,9 @@ static struct ata_port_operations serverworks_osb4_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = serverworks_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = serverworks_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -372,8 +369,9 @@ static struct ata_port_operations serverworks_csb_port_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = serverworks_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = serverworks_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -548,6 +546,7 @@ static int serverworks_init_one(struct pci_dev *pdev, const struct pci_device_id return ata_pci_init_one(pdev, port_info, ports); } +#ifdef CONFIG_PM static int serverworks_reinit_one(struct pci_dev *pdev) { /* Force master latency timer to 64 PCI clocks */ @@ -571,6 +570,7 @@ static int serverworks_reinit_one(struct pci_dev *pdev) } return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id serverworks[] = { { PCI_VDEVICE(SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0}, @@ -587,8 +587,10 @@ static struct pci_driver serverworks_pci_driver = { .id_table = serverworks, .probe = serverworks_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = serverworks_reinit_one, +#endif }; static int __init serverworks_init(void) diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c index ed79fabe..6770820 100644 --- a/drivers/ata/pata_sil680.c +++ b/drivers/ata/pata_sil680.c @@ -33,7 +33,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_sil680" -#define DRV_VERSION "0.4.1" +#define DRV_VERSION "0.4.6" /** * sil680_selreg - return register base @@ -91,12 +91,6 @@ static int sil680_cable_detect(struct ata_port *ap) { return ATA_CBL_PATA40; } -static int sil680_pre_reset(struct ata_port *ap) -{ - ap->cbl = sil680_cable_detect(ap); - return ata_std_prereset(ap); -} - /** * sil680_bus_reset - reset the SIL680 bus * @ap: ATA port to reset @@ -119,7 +113,7 @@ static int sil680_bus_reset(struct ata_port *ap,unsigned int *classes) static void sil680_error_handler(struct ata_port *ap) { - ata_bmdma_drive_eh(ap, sil680_pre_reset, sil680_bus_reset, NULL, ata_std_postreset); + ata_bmdma_drive_eh(ap, ata_std_prereset, sil680_bus_reset, NULL, ata_std_postreset); } /** @@ -139,10 +133,13 @@ static void sil680_set_piomode(struct ata_port *ap, struct ata_device *adev) unsigned long tfaddr = sil680_selreg(ap, 0x02); unsigned long addr = sil680_seldev(ap, adev, 0x04); + unsigned long addr_mask = 0x80 + 4 * ap->port_no; struct pci_dev *pdev = to_pci_dev(ap->host->dev); int pio = adev->pio_mode - XFER_PIO_0; int lowest_pio = pio; + int port_shift = 4 * adev->devno; u16 reg; + u8 mode; struct ata_device *pair = ata_dev_pair(adev); @@ -153,10 +150,17 @@ static void sil680_set_piomode(struct ata_port *ap, struct ata_device *adev) pci_write_config_word(pdev, tfaddr, speed_t[lowest_pio]); pci_read_config_word(pdev, tfaddr-2, ®); + pci_read_config_byte(pdev, addr_mask, &mode); + reg &= ~0x0200; /* Clear IORDY */ - if (ata_pio_need_iordy(adev)) + mode &= ~(3 << port_shift); /* Clear IORDY and DMA bits */ + + if (ata_pio_need_iordy(adev)) { reg |= 0x0200; /* Enable IORDY */ + mode |= 1 << port_shift; + } pci_write_config_word(pdev, tfaddr-2, reg); + pci_write_config_byte(pdev, addr_mask, mode); } /** @@ -226,6 +230,10 @@ static struct scsi_host_template sil680_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM + .suspend = ata_scsi_device_suspend, + .resume = ata_scsi_device_resume, +#endif }; static struct ata_port_operations sil680_port_ops = { @@ -243,6 +251,7 @@ static struct ata_port_operations sil680_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = sil680_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sil680_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -367,11 +376,13 @@ static int sil680_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM static int sil680_reinit_one(struct pci_dev *pdev) { sil680_init_chip(pdev); return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id sil680[] = { { PCI_VDEVICE(CMD, PCI_DEVICE_ID_SII_680), }, @@ -384,8 +395,10 @@ static struct pci_driver sil680_pci_driver = { .id_table = sil680, .probe = sil680_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = sil680_reinit_one, +#endif }; static int __init sil680_init(void) diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c index 560103d..a3fbcee 100644 --- a/drivers/ata/pata_sis.c +++ b/drivers/ata/pata_sis.c @@ -32,11 +32,10 @@ #include <scsi/scsi_host.h> #include <linux/libata.h> #include <linux/ata.h> -#include "libata.h" +#include "sis.h" -#undef DRV_NAME /* already defined in libata.h, for libata-core */ #define DRV_NAME "pata_sis" -#define DRV_VERSION "0.4.5" +#define DRV_VERSION "0.5.1" struct sis_chipset { u16 device; /* PCI host ID */ @@ -87,106 +86,55 @@ static int sis_port_base(struct ata_device *adev) } /** - * sis_133_pre_reset - check for 40/80 pin + * sis_133_cable_detect - check for 40/80 pin * @ap: Port * * Perform cable detection for the later UDMA133 capable * SiS chipset. */ -static int sis_133_pre_reset(struct ata_port *ap) +static int sis_133_cable_detect(struct ata_port *ap) { - static const struct pci_bits sis_enable_bits[] = { - { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ - { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */ - }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); u16 tmp; - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) - return -ENOENT; - /* The top bit of this register is the cable detect bit */ pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp); if ((tmp & 0x8000) && !sis_short_ata40(pdev)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - - return ata_std_prereset(ap); -} - -/** - * sis_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * - * LOCKING: - * None (inherited from caller). - */ - -static void sis_133_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, sis_133_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } - /** - * sis_66_pre_reset - check for 40/80 pin + * sis_66_cable_detect - check for 40/80 pin * @ap: Port * * Perform cable detection on the UDMA66, UDMA100 and early UDMA133 * SiS IDE controllers. */ -static int sis_66_pre_reset(struct ata_port *ap) +static int sis_66_cable_detect(struct ata_port *ap) { - static const struct pci_bits sis_enable_bits[] = { - { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ - { 0x4aU, 1U, 0x04UL, 0x04UL }, /* port 1 */ - }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 tmp; - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) { - ata_port_disable(ap); - printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); - return 0; - } /* Older chips keep cable detect in bits 4/5 of reg 0x48 */ pci_read_config_byte(pdev, 0x48, &tmp); tmp >>= ap->port_no; if ((tmp & 0x10) && !sis_short_ata40(pdev)) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - - return ata_std_prereset(ap); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } -/** - * sis_66_error_handler - Probe specified port on PATA host controller - * @ap: Port to probe - * @classes: - * - * LOCKING: - * None (inherited from caller). - */ - -static void sis_66_error_handler(struct ata_port *ap) -{ - ata_bmdma_drive_eh(ap, sis_66_pre_reset, ata_std_softreset, NULL, ata_std_postreset); -} /** - * sis_old_pre_reset - probe begin + * sis_pre_reset - probe begin * @ap: ATA port * * Set up cable type and use generic probe init */ -static int sis_old_pre_reset(struct ata_port *ap) +static int sis_pre_reset(struct ata_port *ap) { static const struct pci_bits sis_enable_bits[] = { { 0x4aU, 1U, 0x02UL, 0x02UL }, /* port 0 */ @@ -195,27 +143,23 @@ static int sis_old_pre_reset(struct ata_port *ap) struct pci_dev *pdev = to_pci_dev(ap->host->dev); - if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) { - ata_port_disable(ap); - printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id); - return 0; - } - ap->cbl = ATA_CBL_PATA40; + if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) + return -ENOENT; return ata_std_prereset(ap); } /** - * sis_old_error_handler - Probe specified port on PATA host controller + * sis_error_handler - Probe specified port on PATA host controller * @ap: Port to probe * * LOCKING: * None (inherited from caller). */ -static void sis_old_error_handler(struct ata_port *ap) +static void sis_error_handler(struct ata_port *ap) { - ata_bmdma_drive_eh(ap, sis_old_pre_reset, ata_std_softreset, NULL, ata_std_postreset); + ata_bmdma_drive_eh(ap, sis_pre_reset, ata_std_softreset, NULL, ata_std_postreset); } /** @@ -495,7 +439,7 @@ static void sis_133_early_set_dmamode (struct ata_port *ap, struct ata_device *a int drive_pci = sis_port_base(adev); u16 timing; - const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100}; + static const u16 udma_bits[] = { 0x8F00, 0x8A00, 0x8700, 0x8500, 0x8300, 0x8200, 0x8100}; pci_read_config_word(pdev, drive_pci, &timing); @@ -532,8 +476,8 @@ static void sis_133_set_dmamode (struct ata_port *ap, struct ata_device *adev) u32 reg54; /* bits 4- cycle time 8 - cvs time */ - const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 }; - const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 }; + static const u32 timing_u100[] = { 0x6B0, 0x470, 0x350, 0x140, 0x120, 0x110, 0x000 }; + static const u32 timing_u133[] = { 0x9F0, 0x6A0, 0x470, 0x250, 0x230, 0x220, 0x210 }; /* If bit 14 is set then the registers are mapped at 0x70 not 0x40 */ pci_read_config_dword(pdev, 0x54, ®54); @@ -576,8 +520,10 @@ static struct scsi_host_template sis_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static const struct ata_port_operations sis_133_ops = { @@ -594,8 +540,9 @@ static const struct ata_port_operations sis_133_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_133_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sis_133_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -627,8 +574,9 @@ static const struct ata_port_operations sis_133_early_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = sis_66_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -660,9 +608,9 @@ static const struct ata_port_operations sis_100_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - + .cable_detect = sis_66_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -691,10 +639,11 @@ static const struct ata_port_operations sis_66_ops = { .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .cable_detect = sis_66_cable_detect, .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_66_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .bmdma_setup = ata_bmdma_setup, @@ -727,8 +676,9 @@ static const struct ata_port_operations sis_old_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = sis_old_error_handler, + .error_handler = sis_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -877,6 +827,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) struct ata_port_info *port; struct pci_dev *host = NULL; struct sis_chipset *chipset = NULL; + struct sis_chipset *sets; static struct sis_chipset sis_chipsets[] = { @@ -931,10 +882,11 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) /* We have to find the bridge first */ - for (chipset = &sis_chipsets[0]; chipset->device; chipset++) { - host = pci_get_device(PCI_VENDOR_ID_SI, chipset->device, NULL); + for (sets = &sis_chipsets[0]; sets->device; sets++) { + host = pci_get_device(PCI_VENDOR_ID_SI, sets->device, NULL); if (host != NULL) { - if (chipset->device == 0x630) { /* SIS630 */ + chipset = sets; /* Match found */ + if (sets->device == 0x630) { /* SIS630 */ u8 host_rev; pci_read_config_byte(host, PCI_REVISION_ID, &host_rev); if (host_rev >= 0x30) /* 630 ET */ @@ -945,7 +897,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) } /* Look for concealed bridges */ - if (host == NULL) { + if (chipset == NULL) { /* Second check */ u32 idemisc; u16 trueid; @@ -1033,8 +985,10 @@ static struct pci_driver sis_pci_driver = { .id_table = sis_pci_tbl, .probe = sis_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init sis_init(void) diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c index 96e890f..da9e22b 100644 --- a/drivers/ata/pata_sl82c105.c +++ b/drivers/ata/pata_sl82c105.c @@ -7,6 +7,13 @@ * SL82C105/Winbond 553 IDE driver * * and in part on the documentation and errata sheet + * + * + * Note: The controller like many controllers has shared timings for + * PIO and DMA. We thus flip to the DMA timings in dma_start and flip back + * in the dma_stop function. Thus we actually don't need a set_dmamode + * method as the PIO method is always called and will set the right PIO + * timing parameters. */ #include <linux/kernel.h> @@ -19,7 +26,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_sl82c105" -#define DRV_VERSION "0.2.3" +#define DRV_VERSION "0.3.0" enum { /* @@ -51,7 +58,6 @@ static int sl82c105_pre_reset(struct ata_port *ap) if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -126,33 +132,6 @@ static void sl82c105_configure_dmamode(struct ata_port *ap, struct ata_device *a } /** - * sl82c105_set_dmamode - set initial DMA mode data - * @ap: ATA interface - * @adev: ATA device - * - * Called to do the DMA mode setup. This replaces the PIO timings - * for the device in question. Set appropriate PIO timings not DMA - * timings at this point. - */ - -static void sl82c105_set_dmamode(struct ata_port *ap, struct ata_device *adev) -{ - switch(adev->dma_mode) { - case XFER_MW_DMA_0: - sl82c105_configure_piomode(ap, adev, 0); - break; - case XFER_MW_DMA_1: - sl82c105_configure_piomode(ap, adev, 3); - break; - case XFER_MW_DMA_2: - sl82c105_configure_piomode(ap, adev, 4); - break; - default: - BUG(); - } -} - -/** * sl82c105_reset_engine - Reset the DMA engine * @ap: ATA interface * @@ -222,7 +201,7 @@ static void sl82c105_bmdma_stop(struct ata_queued_cmd *qc) /* This will redo the initial setup of the DMA device to matching PIO timings */ - sl82c105_set_dmamode(ap, qc->dev); + sl82c105_set_piomode(ap, qc->dev); } static struct scsi_host_template sl82c105_sht = { @@ -246,7 +225,6 @@ static struct scsi_host_template sl82c105_sht = { static struct ata_port_operations sl82c105_port_ops = { .port_disable = ata_port_disable, .set_piomode = sl82c105_set_piomode, - .set_dmamode = sl82c105_set_dmamode, .mode_filter = ata_pci_default_filter, .tf_load = ata_tf_load, @@ -255,7 +233,11 @@ static struct ata_port_operations sl82c105_port_ops = { .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, + .freeze = ata_bmdma_freeze, + .thaw = ata_bmdma_thaw, .error_handler = sl82c105_error_handler, + .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = sl82c105_bmdma_start, diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c index 453ab90..e618ffd 100644 --- a/drivers/ata/pata_triflex.c +++ b/drivers/ata/pata_triflex.c @@ -43,7 +43,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_triflex" -#define DRV_VERSION "0.2.7" +#define DRV_VERSION "0.2.8" /** * triflex_prereset - probe begin @@ -63,7 +63,6 @@ static int triflex_prereset(struct ata_port *ap) if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no])) return -ENOENT; - ap->cbl = ATA_CBL_PATA40; return ata_std_prereset(ap); } @@ -193,8 +192,10 @@ static struct scsi_host_template triflex_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations triflex_port_ops = { @@ -212,6 +213,7 @@ static struct ata_port_operations triflex_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = triflex_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .bmdma_setup = ata_bmdma_setup, .bmdma_start = triflex_bmdma_start, @@ -260,8 +262,10 @@ static struct pci_driver triflex_pci_driver = { .id_table = triflex, .probe = triflex_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = ata_pci_device_resume, +#endif }; static int __init triflex_init(void) diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c index 220fcd6..96b7179 100644 --- a/drivers/ata/pata_via.c +++ b/drivers/ata/pata_via.c @@ -62,7 +62,7 @@ #include <linux/libata.h> #define DRV_NAME "pata_via" -#define DRV_VERSION "0.2.1" +#define DRV_VERSION "0.3.1" /* * The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx @@ -135,16 +135,23 @@ static const struct via_isa_bridge { */ static int via_cable_detect(struct ata_port *ap) { + const struct via_isa_bridge *config = ap->host->private_data; struct pci_dev *pdev = to_pci_dev(ap->host->dev); u32 ata66; + /* Early chips are 40 wire */ + if ((config->flags & VIA_UDMA) < VIA_UDMA_66) + return ATA_CBL_PATA40; + /* UDMA 66 chips have only drive side logic */ + else if((config->flags & VIA_UDMA) < VIA_UDMA_100) + return ATA_CBL_PATA_UNK; + /* UDMA 100 or later */ pci_read_config_dword(pdev, 0x50, &ata66); /* Check both the drive cable reporting bits, we might not have two drives */ if (ata66 & (0x10100000 >> (16 * ap->port_no))) return ATA_CBL_PATA80; - else - return ATA_CBL_PATA40; + return ATA_CBL_PATA40; } static int via_pre_reset(struct ata_port *ap) @@ -156,22 +163,10 @@ static int via_pre_reset(struct ata_port *ap) { 0x40, 1, 0x02, 0x02 }, { 0x40, 1, 0x01, 0x01 } }; - struct pci_dev *pdev = to_pci_dev(ap->host->dev); - if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no])) return -ENOENT; } - - if ((config->flags & VIA_UDMA) >= VIA_UDMA_100) - ap->cbl = via_cable_detect(ap); - /* The UDMA66 series has no cable detect so do drive side detect */ - else if ((config->flags & VIA_UDMA) < VIA_UDMA_66) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA_UNK; - - return ata_std_prereset(ap); } @@ -305,8 +300,10 @@ static struct scsi_host_template via_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .resume = ata_scsi_device_resume, .suspend = ata_scsi_device_suspend, +#endif }; static struct ata_port_operations via_port_ops = { @@ -325,6 +322,7 @@ static struct ata_port_operations via_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = via_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = via_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -360,6 +358,7 @@ static struct ata_port_operations via_port_ops_noirq = { .thaw = ata_bmdma_thaw, .error_handler = via_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = via_cable_detect, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, @@ -560,6 +559,7 @@ static int via_init_one(struct pci_dev *pdev, const struct pci_device_id *id) return ata_pci_init_one(pdev, port_info, 2); } +#ifdef CONFIG_PM /** * via_reinit_one - reinit after resume * @pdev; PCI device @@ -592,6 +592,7 @@ static int via_reinit_one(struct pci_dev *pdev) } return ata_pci_device_resume(pdev); } +#endif static const struct pci_device_id via[] = { { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C576_1), }, @@ -607,8 +608,10 @@ static struct pci_driver via_pci_driver = { .id_table = via, .probe = via_init_one, .remove = ata_pci_remove_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = via_reinit_one, +#endif }; static int __init via_init(void) diff --git a/drivers/ata/pata_winbond.c b/drivers/ata/pata_winbond.c index 0888b4f..cc4ad27 100644 --- a/drivers/ata/pata_winbond.c +++ b/drivers/ata/pata_winbond.c @@ -8,7 +8,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/pci.h> #include <linux/init.h> #include <linux/blkdev.h> #include <linux/delay.h> @@ -17,7 +16,7 @@ #include <linux/platform_device.h> #define DRV_NAME "pata_winbond" -#define DRV_VERSION "0.0.1" +#define DRV_VERSION "0.0.2" #define NR_HOST 4 /* Two winbond controllers, two channels each */ @@ -36,7 +35,7 @@ static int probe_winbond = 1; static int probe_winbond; #endif -static spinlock_t winbond_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_SPINLOCK(winbond_lock); static void winbond_writecfg(unsigned long port, u8 reg, u8 val) { @@ -152,13 +151,13 @@ static struct ata_port_operations winbond_port_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_40wire, .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = winbond_data_xfer, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -179,11 +178,9 @@ static struct ata_port_operations winbond_port_ops = { static __init int winbond_init_one(unsigned long port) { - struct ata_probe_ent ae; struct platform_device *pdev; - int ret; u8 reg; - int i; + int i, rc; reg = winbond_readcfg(port, 0x81); reg |= 0x80; /* jumpered mode off */ @@ -202,58 +199,56 @@ static __init int winbond_init_one(unsigned long port) for (i = 0; i < 2 ; i ++) { unsigned long cmd_port = 0x1F0 - (0x80 * i); + struct ata_host *host; + struct ata_port *ap; void __iomem *cmd_addr, *ctl_addr; - if (reg & (1 << i)) { - /* - * Fill in a probe structure first of all - */ - - pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0); - if (IS_ERR(pdev)) - return PTR_ERR(pdev); - - cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8); - ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1); - if (!cmd_addr || !ctl_addr) { - platform_device_unregister(pdev); - return -ENOMEM; - } - - memset(&ae, 0, sizeof(struct ata_probe_ent)); - INIT_LIST_HEAD(&ae.node); - ae.dev = &pdev->dev; - - ae.port_ops = &winbond_port_ops; - ae.pio_mask = 0x1F; - - ae.sht = &winbond_sht; - - ae.n_ports = 1; - ae.irq = 14 + i; - ae.irq_flags = 0; - ae.port_flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST; - ae.port[0].cmd_addr = cmd_addr; - ae.port[0].altstatus_addr = ctl_addr; - ae.port[0].ctl_addr = ctl_addr; - ata_std_ports(&ae.port[0]); - /* - * Hook in a private data structure per channel - */ - ae.private_data = &winbond_data[nr_winbond_host]; - winbond_data[nr_winbond_host].config = port; - winbond_data[nr_winbond_host].platform_dev = pdev; - - ret = ata_device_add(&ae); - if (ret == 0) { - platform_device_unregister(pdev); - return -ENODEV; - } - winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev); - } + if (!(reg & (1 << i))) + continue; + + pdev = platform_device_register_simple(DRV_NAME, nr_winbond_host, NULL, 0); + if (IS_ERR(pdev)) + return PTR_ERR(pdev); + + rc = -ENOMEM; + host = ata_host_alloc(&pdev->dev, 1); + if (!host) + goto err_unregister; + + rc = -ENOMEM; + cmd_addr = devm_ioport_map(&pdev->dev, cmd_port, 8); + ctl_addr = devm_ioport_map(&pdev->dev, cmd_port + 0x0206, 1); + if (!cmd_addr || !ctl_addr) + goto err_unregister; + + ap = host->ports[0]; + ap->ops = &winbond_port_ops; + ap->pio_mask = 0x1F; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->ioaddr.cmd_addr = cmd_addr; + ap->ioaddr.altstatus_addr = ctl_addr; + ap->ioaddr.ctl_addr = ctl_addr; + ata_std_ports(&ap->ioaddr); + + /* hook in a private data structure per channel */ + host->private_data = &winbond_data[nr_winbond_host]; + winbond_data[nr_winbond_host].config = port; + winbond_data[nr_winbond_host].platform_dev = pdev; + + /* activate */ + rc = ata_host_activate(host, 14 + i, ata_interrupt, 0, + &winbond_sht); + if (rc) + goto err_unregister; + + winbond_host[nr_winbond_host++] = dev_get_drvdata(&pdev->dev); } return 0; + + err_unregister: + platform_device_unregister(pdev); + return rc; } /** diff --git a/drivers/ata/pdc_adma.c b/drivers/ata/pdc_adma.c index 857ac23..52b6953 100644 --- a/drivers/ata/pdc_adma.c +++ b/drivers/ata/pdc_adma.c @@ -44,7 +44,7 @@ #include <linux/libata.h> #define DRV_NAME "pdc_adma" -#define DRV_VERSION "0.04" +#define DRV_VERSION "0.05" /* macro to calculate base address for ATA regs */ #define ADMA_ATA_REGS(base,port_no) ((base) + ((port_no) * 0x40)) @@ -52,9 +52,9 @@ /* macro to calculate base address for ADMA regs */ #define ADMA_REGS(base,port_no) ((base) + 0x80 + ((port_no) * 0x20)) -/* macro to obtain addresses from ata_host */ -#define ADMA_HOST_REGS(host,port_no) \ - ADMA_REGS((host)->iomap[ADMA_MMIO_BAR], port_no) +/* macro to obtain addresses from ata_port */ +#define ADMA_PORT_REGS(ap) \ + ADMA_REGS((ap)->host->iomap[ADMA_MMIO_BAR], ap->port_no) enum { ADMA_MMIO_BAR = 4, @@ -128,7 +128,6 @@ struct adma_port_priv { static int adma_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t adma_intr (int irq, void *dev_instance); static int adma_port_start(struct ata_port *ap); static void adma_host_stop(struct ata_host *host); static void adma_port_stop(struct ata_port *ap); @@ -172,7 +171,6 @@ static const struct ata_port_operations adma_ata_ops = { .qc_issue = adma_qc_issue, .eng_timeout = adma_eng_timeout, .data_xfer = ata_data_xfer, - .irq_handler = adma_intr, .irq_clear = adma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -186,7 +184,6 @@ static const struct ata_port_operations adma_ata_ops = { static struct ata_port_info adma_port_info[] = { /* board_1841_idx */ { - .sht = &adma_ata_sht, .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_SRST | ATA_FLAG_NO_LEGACY | ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, @@ -229,8 +226,10 @@ static void adma_irq_clear(struct ata_port *ap) /* nothing */ } -static void adma_reset_engine(void __iomem *chan) +static void adma_reset_engine(struct ata_port *ap) { + void __iomem *chan = ADMA_PORT_REGS(ap); + /* reset ADMA to idle state */ writew(aPIOMD4 | aNIEN | aRSTADM, chan + ADMA_CONTROL); udelay(2); @@ -241,14 +240,14 @@ static void adma_reset_engine(void __iomem *chan) static void adma_reinit_engine(struct ata_port *ap) { struct adma_port_priv *pp = ap->private_data; - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); /* mask/clear ATA interrupts */ writeb(ATA_NIEN, ap->ioaddr.ctl_addr); ata_check_status(ap); /* reset the ADMA engine */ - adma_reset_engine(chan); + adma_reset_engine(ap); /* set in-FIFO threshold to 0x100 */ writew(0x100, chan + ADMA_FIFO_IN); @@ -268,7 +267,7 @@ static void adma_reinit_engine(struct ata_port *ap) static inline void adma_enter_reg_mode(struct ata_port *ap) { - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); writew(aPIOMD4, chan + ADMA_CONTROL); readb(chan + ADMA_STATUS); /* flush */ @@ -415,7 +414,7 @@ static void adma_qc_prep(struct ata_queued_cmd *qc) static inline void adma_packet_start(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - void __iomem *chan = ADMA_HOST_REGS(ap->host, ap->port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); VPRINTK("ENTER, ap %p\n", ap); @@ -453,7 +452,7 @@ static inline unsigned int adma_intr_pkt(struct ata_host *host) struct ata_port *ap = host->ports[port_no]; struct adma_port_priv *pp; struct ata_queued_cmd *qc; - void __iomem *chan = ADMA_HOST_REGS(host, port_no); + void __iomem *chan = ADMA_PORT_REGS(ap); u8 status = readb(chan + ADMA_STATUS); if (status == 0) @@ -498,7 +497,7 @@ static inline unsigned int adma_intr_mmio(struct ata_host *host) if ((status & ATA_BUSY)) continue; DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, status); + ap->print_id, qc->tf.protocol, status); /* complete taskfile transaction */ pp->state = adma_state_idle; @@ -575,7 +574,7 @@ static int adma_port_start(struct ata_port *ap) static void adma_port_stop(struct ata_port *ap) { - adma_reset_engine(ADMA_HOST_REGS(ap->host, ap->port_no)); + adma_reset_engine(ap); } static void adma_host_stop(struct ata_host *host) @@ -583,21 +582,19 @@ static void adma_host_stop(struct ata_host *host) unsigned int port_no; for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_HOST_REGS(host, port_no)); + adma_reset_engine(host->ports[port_no]); } -static void adma_host_init(unsigned int chip_id, - struct ata_probe_ent *probe_ent) +static void adma_host_init(struct ata_host *host, unsigned int chip_id) { unsigned int port_no; - void __iomem *mmio_base = probe_ent->iomap[ADMA_MMIO_BAR]; /* enable/lock aGO operation */ - writeb(7, mmio_base + ADMA_MODE_LOCK); + writeb(7, host->iomap[ADMA_MMIO_BAR] + ADMA_MODE_LOCK); /* reset the ADMA logic */ for (port_no = 0; port_no < ADMA_PORTS; ++port_no) - adma_reset_engine(ADMA_REGS(mmio_base, port_no)); + adma_reset_engine(host->ports[port_no]); } static int adma_set_dma_masks(struct pci_dev *pdev, void __iomem *mmio_base) @@ -623,14 +620,21 @@ static int adma_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - void __iomem *mmio_base; unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &adma_port_info[board_idx], NULL }; + struct ata_host *host; + void __iomem *mmio_base; int rc, port_no; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, ADMA_PORTS); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -641,46 +645,23 @@ static int adma_ata_init_one(struct pci_dev *pdev, rc = pcim_iomap_regions(pdev, 1 << ADMA_MMIO_BAR, DRV_NAME); if (rc) return rc; - mmio_base = pcim_iomap_table(pdev)[ADMA_MMIO_BAR]; + host->iomap = pcim_iomap_table(pdev); + mmio_base = host->iomap[ADMA_MMIO_BAR]; rc = adma_set_dma_masks(pdev, mmio_base); if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = adma_port_info[board_idx].sht; - probe_ent->port_flags = adma_port_info[board_idx].flags; - probe_ent->pio_mask = adma_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = adma_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = adma_port_info[board_idx].udma_mask; - probe_ent->port_ops = adma_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->n_ports = ADMA_PORTS; - probe_ent->iomap = pcim_iomap_table(pdev); - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { - adma_ata_setup_port(&probe_ent->port[port_no], + for (port_no = 0; port_no < ADMA_PORTS; ++port_no) + adma_ata_setup_port(&host->ports[port_no]->ioaddr, ADMA_ATA_REGS(mmio_base, port_no)); - } - - pci_set_master(pdev); /* initialize adapter */ - adma_host_init(board_idx, probe_ent); + adma_host_init(host, board_idx); - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, adma_intr, IRQF_SHARED, + &adma_ata_sht); } static int __init adma_ata_init(void) diff --git a/drivers/ata/sata_inic162x.c b/drivers/ata/sata_inic162x.c index 31b636f..f099a1d 100644 --- a/drivers/ata/sata_inic162x.c +++ b/drivers/ata/sata_inic162x.c @@ -135,8 +135,10 @@ static struct scsi_host_template inic_sht = { .slave_configure = inic_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static const int scr_map[] = { @@ -486,11 +488,11 @@ static void inic_error_handler(struct ata_port *ap) static void inic_post_internal_cmd(struct ata_queued_cmd *qc) { /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) inic_reset_port(inic_port_base(qc->ap)); } -static void inic_dev_config(struct ata_port *ap, struct ata_device *dev) +static void inic_dev_config(struct ata_device *dev) { /* inic can only handle upto LBA28 max sectors */ if (dev->max_sectors > ATA_MAX_SECTORS) @@ -557,7 +559,6 @@ static struct ata_port_operations inic_port_ops = { .bmdma_stop = inic_bmdma_stop, .bmdma_status = inic_bmdma_status, - .irq_handler = inic_interrupt, .irq_clear = inic_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -578,7 +579,6 @@ static struct ata_port_operations inic_port_ops = { }; static struct ata_port_info inic_port_info = { - .sht = &inic_sht, /* For some reason, ATA_PROT_ATAPI is broken on this * controller, and no, PIO_POLLING does't fix it. It somehow * manages to report the wrong ireason and ignoring ireason @@ -632,6 +632,7 @@ static int init_controller(void __iomem *mmio_base, u16 hctl) return 0; } +#ifdef CONFIG_PM static int inic_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); @@ -639,10 +640,11 @@ static int inic_pci_device_resume(struct pci_dev *pdev) void __iomem *mmio_base = host->iomap[MMIO_BAR]; int rc; - ata_pci_device_do_resume(pdev); + rc = ata_pci_device_do_resume(pdev); + if (rc) + return rc; if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - printk("XXX\n"); rc = init_controller(mmio_base, hpriv->cached_hctl); if (rc) return rc; @@ -652,12 +654,13 @@ static int inic_pci_device_resume(struct pci_dev *pdev) return 0; } +#endif static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_port_info *pinfo = &inic_port_info; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = { &inic_port_info, NULL }; + struct ata_host *host; struct inic_host_priv *hpriv; void __iomem * const *iomap; int i, rc; @@ -665,18 +668,38 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); - rc = pcim_enable_device(pdev); - if (rc) - return rc; + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, NR_PORTS); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + + host->private_data = hpriv; - rc = pci_request_regions(pdev, DRV_NAME); + /* acquire resources and fill host */ + rc = pcim_enable_device(pdev); if (rc) return rc; rc = pcim_iomap_regions(pdev, 0x3f, DRV_NAME); if (rc) return rc; - iomap = pcim_iomap_table(pdev); + host->iomap = iomap = pcim_iomap_table(pdev); + + for (i = 0; i < NR_PORTS; i++) { + struct ata_ioports *port = &host->ports[i]->ioaddr; + void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE; + + port->cmd_addr = iomap[2 * i]; + port->altstatus_addr = + port->ctl_addr = (void __iomem *) + ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS); + port->scr_addr = port_base + PORT_SCR; + + ata_std_ports(port); + } + + hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL); /* Set dma_mask. This devices doesn't support 64bit addressing. */ rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); @@ -693,43 +716,6 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) return rc; } - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); - if (!probe_ent || !hpriv) - return -ENOMEM; - - probe_ent->dev = &pdev->dev; - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = pinfo->sht; - probe_ent->port_flags = pinfo->flags; - probe_ent->pio_mask = pinfo->pio_mask; - probe_ent->mwdma_mask = pinfo->mwdma_mask; - probe_ent->udma_mask = pinfo->udma_mask; - probe_ent->port_ops = pinfo->port_ops; - probe_ent->n_ports = NR_PORTS; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - - probe_ent->iomap = iomap; - - for (i = 0; i < NR_PORTS; i++) { - struct ata_ioports *port = &probe_ent->port[i]; - void __iomem *port_base = iomap[MMIO_BAR] + i * PORT_SIZE; - - port->cmd_addr = iomap[2 * i]; - port->altstatus_addr = - port->ctl_addr = (void __iomem *) - ((unsigned long)iomap[2 * i + 1] | ATA_PCI_CTL_OFS); - port->scr_addr = port_base + PORT_SCR; - - ata_std_ports(port); - } - - probe_ent->private_data = hpriv; - hpriv->cached_hctl = readw(iomap[MMIO_BAR] + HOST_CTL); - rc = init_controller(iomap[MMIO_BAR], hpriv->cached_hctl); if (rc) { dev_printk(KERN_ERR, &pdev->dev, @@ -738,13 +724,8 @@ static int inic_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - - return 0; + return ata_host_activate(host, pdev->irq, inic_interrupt, IRQF_SHARED, + &inic_sht); } static const struct pci_device_id inic_pci_tbl[] = { @@ -755,8 +736,10 @@ static const struct pci_device_id inic_pci_tbl[] = { static struct pci_driver inic_pci_driver = { .name = DRV_NAME, .id_table = inic_pci_tbl, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = inic_pci_device_resume, +#endif .probe = inic_init_one, .remove = ata_pci_remove_one, }; diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index d689df5..cb9b9ac 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -35,7 +35,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_mv" -#define DRV_VERSION "0.7" +#define DRV_VERSION "0.8" enum { /* BAR's are enumerated in terms of pci_resource_start() terms */ @@ -137,14 +137,19 @@ enum { PCI_ERR = (1 << 18), TRAN_LO_DONE = (1 << 19), /* 6xxx: IRQ coalescing */ TRAN_HI_DONE = (1 << 20), /* 6xxx: IRQ coalescing */ + PORTS_0_3_COAL_DONE = (1 << 8), + PORTS_4_7_COAL_DONE = (1 << 17), PORTS_0_7_COAL_DONE = (1 << 21), /* 6xxx: IRQ coalescing */ GPIO_INT = (1 << 22), SELF_INT = (1 << 23), TWSI_INT = (1 << 24), HC_MAIN_RSVD = (0x7f << 25), /* bits 31-25 */ + HC_MAIN_RSVD_5 = (0x1fff << 19), /* bits 31-19 */ HC_MAIN_MASKED_IRQS = (TRAN_LO_DONE | TRAN_HI_DONE | PORTS_0_7_COAL_DONE | GPIO_INT | TWSI_INT | HC_MAIN_RSVD), + HC_MAIN_MASKED_IRQS_5 = (PORTS_0_3_COAL_DONE | PORTS_4_7_COAL_DONE | + HC_MAIN_RSVD_5), /* SATAHC registers */ HC_CFG_OFS = 0, @@ -248,10 +253,7 @@ enum { #define IS_GEN_IIE(hpriv) ((hpriv)->hp_flags & MV_HP_GEN_IIE) enum { - /* Our DMA boundary is determined by an ePRD being unable to handle - * anything larger than 64KB - */ - MV_DMA_BOUNDARY = 0xffffU, + MV_DMA_BOUNDARY = 0xffffffffU, EDMA_REQ_Q_BASE_LO_MASK = 0xfffffc00U, @@ -345,7 +347,6 @@ static void mv_port_stop(struct ata_port *ap); static void mv_qc_prep(struct ata_queued_cmd *qc); static void mv_qc_prep_iie(struct ata_queued_cmd *qc); static unsigned int mv_qc_issue(struct ata_queued_cmd *qc); -static irqreturn_t mv_interrupt(int irq, void *dev_instance); static void mv_eng_timeout(struct ata_port *ap); static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent); @@ -379,10 +380,10 @@ static struct scsi_host_template mv_sht = { .queuecommand = ata_scsi_queuecmd, .can_queue = MV_USE_Q_DEPTH, .this_id = ATA_SHT_THIS_ID, - .sg_tablesize = MV_MAX_SG_CT / 2, + .sg_tablesize = MV_MAX_SG_CT, .cmd_per_lun = ATA_SHT_CMD_PER_LUN, .emulated = ATA_SHT_EMULATED, - .use_clustering = ATA_SHT_USE_CLUSTERING, + .use_clustering = 1, .proc_name = DRV_NAME, .dma_boundary = MV_DMA_BOUNDARY, .slave_configure = ata_scsi_slave_config, @@ -400,6 +401,7 @@ static const struct ata_port_operations mv5_ops = { .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep, .qc_issue = mv_qc_issue, @@ -407,7 +409,6 @@ static const struct ata_port_operations mv5_ops = { .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -429,6 +430,7 @@ static const struct ata_port_operations mv6_ops = { .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep, .qc_issue = mv_qc_issue, @@ -436,7 +438,6 @@ static const struct ata_port_operations mv6_ops = { .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -458,6 +459,7 @@ static const struct ata_port_operations mv_iie_ops = { .dev_select = ata_std_dev_select, .phy_reset = mv_phy_reset, + .cable_detect = ata_cable_sata, .qc_prep = mv_qc_prep_iie, .qc_issue = mv_qc_issue, @@ -465,7 +467,6 @@ static const struct ata_port_operations mv_iie_ops = { .eng_timeout = mv_eng_timeout, - .irq_handler = mv_interrupt, .irq_clear = mv_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -479,35 +480,30 @@ static const struct ata_port_operations mv_iie_ops = { static const struct ata_port_info mv_port_info[] = { { /* chip_504x */ - .sht = &mv_sht, .flags = MV_COMMON_FLAGS, .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_508x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_5080 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv5_ops, }, { /* chip_604x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv6_ops, }, { /* chip_608x */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS | MV_FLAG_DUAL_HC), .pio_mask = 0x1f, /* pio0-4 */ @@ -515,14 +511,12 @@ static const struct ata_port_info mv_port_info[] = { .port_ops = &mv6_ops, }, { /* chip_6042 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ .port_ops = &mv_iie_ops, }, { /* chip_7042 */ - .sht = &mv_sht, .flags = (MV_COMMON_FLAGS | MV_6XXX_FLAGS), .pio_mask = 0x1f, /* pio0-4 */ .udma_mask = 0x7f, /* udma0-6 */ @@ -546,6 +540,9 @@ static const struct pci_device_id mv_pci_tbl[] = { { PCI_VDEVICE(TTI, 0x2310), chip_7042 }, + /* add Marvell 7042 support */ + { PCI_VDEVICE(MARVELL, 0x7042), chip_7042 }, + { } /* terminate list */ }; @@ -580,6 +577,39 @@ static const struct mv_hw_ops mv6xxx_ops = { static int msi; /* Use PCI msi; either zero (off, default) or non-zero */ +/* move to PCI layer or libata core? */ +static int pci_go_64(struct pci_dev *pdev) +{ + int rc; + + if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { + rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + if (rc) { + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "64-bit DMA enable failed\n"); + return rc; + } + } + } else { + rc = pci_set_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit DMA enable failed\n"); + return rc; + } + rc = pci_set_consistent_dma_mask(pdev, DMA_32BIT_MASK); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, + "32-bit consistent DMA enable failed\n"); + return rc; + } + } + + return rc; +} + /* * Functions */ @@ -793,20 +823,18 @@ static u32 mv_scr_read(struct ata_port *ap, unsigned int sc_reg_in) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) { + if (0xffffffffU != ofs) return readl(mv_ap_base(ap) + ofs); - } else { + else return (u32) ofs; - } } static void mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) { unsigned int ofs = mv_scr_offset(sc_reg_in); - if (0xffffffffU != ofs) { + if (0xffffffffU != ofs) writelfl(val, mv_ap_base(ap) + ofs); - } } static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio) @@ -814,23 +842,27 @@ static void mv_edma_cfg(struct mv_host_priv *hpriv, void __iomem *port_mmio) u32 cfg = readl(port_mmio + EDMA_CFG_OFS); /* set up non-NCQ EDMA configuration */ - cfg &= ~0x1f; /* clear queue depth */ - cfg &= ~EDMA_CFG_NCQ; /* clear NCQ mode */ cfg &= ~(1 << 9); /* disable equeue */ - if (IS_GEN_I(hpriv)) + if (IS_GEN_I(hpriv)) { + cfg &= ~0x1f; /* clear queue depth */ cfg |= (1 << 8); /* enab config burst size mask */ + } - else if (IS_GEN_II(hpriv)) + else if (IS_GEN_II(hpriv)) { + cfg &= ~0x1f; /* clear queue depth */ cfg |= EDMA_CFG_RD_BRST_EXT | EDMA_CFG_WR_BUFF_LEN; + cfg &= ~(EDMA_CFG_NCQ | EDMA_CFG_NCQ_GO_ON_ERR); /* clear NCQ */ + } else if (IS_GEN_IIE(hpriv)) { - cfg |= (1 << 23); /* dis RX PM port mask */ - cfg &= ~(1 << 16); /* dis FIS-based switching (for now) */ + cfg |= (1 << 23); /* do not mask PM field in rx'd FIS */ + cfg |= (1 << 22); /* enab 4-entry host queue cache */ cfg &= ~(1 << 19); /* dis 128-entry queue (for now?) */ cfg |= (1 << 18); /* enab early completion */ - cfg |= (1 << 17); /* enab host q cache */ - cfg |= (1 << 22); /* enab cutthrough */ + cfg |= (1 << 17); /* enab cut-through (dis stor&forwrd) */ + cfg &= ~(1 << 16); /* dis FIS-based switching (for now) */ + cfg &= ~(EDMA_CFG_NCQ | EDMA_CFG_NCQ_GO_ON_ERR); /* clear NCQ */ } writelfl(cfg, port_mmio + EDMA_CFG_OFS); @@ -950,38 +982,30 @@ static void mv_port_stop(struct ata_port *ap) * LOCKING: * Inherited from caller. */ -static void mv_fill_sg(struct ata_queued_cmd *qc) +static unsigned int mv_fill_sg(struct ata_queued_cmd *qc) { struct mv_port_priv *pp = qc->ap->private_data; - unsigned int i = 0; + unsigned int n_sg = 0; struct scatterlist *sg; + struct mv_sg *mv_sg; + mv_sg = pp->sg_tbl; ata_for_each_sg(sg, qc) { - dma_addr_t addr; - u32 sg_len, len, offset; - - addr = sg_dma_address(sg); - sg_len = sg_dma_len(sg); - - while (sg_len) { - offset = addr & MV_DMA_BOUNDARY; - len = sg_len; - if ((offset + sg_len) > 0x10000) - len = 0x10000 - offset; + dma_addr_t addr = sg_dma_address(sg); + u32 sg_len = sg_dma_len(sg); - pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff); - pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16); - pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff); + mv_sg->addr = cpu_to_le32(addr & 0xffffffff); + mv_sg->addr_hi = cpu_to_le32((addr >> 16) >> 16); + mv_sg->flags_size = cpu_to_le32(sg_len & 0xffff); - sg_len -= len; - addr += len; + if (ata_sg_is_last(sg, qc)) + mv_sg->flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL); - if (!sg_len && ata_sg_is_last(sg, qc)) - pp->sg_tbl[i].flags_size |= cpu_to_le32(EPRD_FLAG_END_OF_TBL); - - i++; - } + mv_sg++; + n_sg++; } + + return n_sg; } static inline unsigned mv_inc_q_index(unsigned index) @@ -1276,7 +1300,7 @@ static void mv_err_intr(struct ata_port *ap, int reset_allowed) pp->pp_flags &= ~MV_PP_FLAG_EDMA_EN; } DPRINTK(KERN_ERR "ata%u: port error; EDMA err cause: 0x%08x " - "SERR: 0x%08x\n", ap->id, edma_err_cause, serr); + "SERR: 0x%08x\n", ap->print_id, edma_err_cause, serr); /* Clear EDMA now that SERR cleanup done */ writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS); @@ -1311,17 +1335,15 @@ static void mv_host_intr(struct ata_host *host, u32 relevant, unsigned int hc) int shift, port, port0, hard_port, handled; unsigned int err_mask; - if (hc == 0) { + if (hc == 0) port0 = 0; - } else { + else port0 = MV_PORTS_PER_HC; - } /* we'll need the HC success int register in most cases */ hc_irq_cause = readl(hc_mmio + HC_IRQ_CAUSE_OFS); - if (hc_irq_cause) { + if (hc_irq_cause) writelfl(~hc_irq_cause, hc_mmio + HC_IRQ_CAUSE_OFS); - } VPRINTK("ENTER, hc%u relevant=0x%08x HC IRQ cause=0x%08x\n", hc,relevant,hc_irq_cause); @@ -1416,9 +1438,8 @@ static irqreturn_t mv_interrupt(int irq, void *dev_instance) /* check the cases where we either have nothing pending or have read * a bogus register value which can indicate HW removal or PCI fault */ - if (!irq_stat || (0xffffffffU == irq_stat)) { + if (!irq_stat || (0xffffffffU == irq_stat)) return IRQ_NONE; - } n_hcs = mv_get_hc_count(host->ports[0]->flags); spin_lock(&host->lock); @@ -1943,7 +1964,6 @@ comreset_retry: ata_port_disable(ap); return; } - ap->cbl = ATA_CBL_SATA; /* even after SStatus reflects that device is ready, * it seems to take a while for link to be fully @@ -2052,7 +2072,7 @@ static void mv_port_init(struct ata_ioports *port, void __iomem *port_mmio) port->altstatus_addr = port->ctl_addr = shd_base + SHD_CTL_AST_OFS; /* unused: */ - port->cmd_addr = port->bmdma_addr = port->scr_addr = 0; + port->cmd_addr = port->bmdma_addr = port->scr_addr = NULL; /* Clear any currently outstanding port interrupt conditions */ serr_ofs = mv_scr_offset(SCR_ERROR); @@ -2068,9 +2088,10 @@ static void mv_port_init(struct ata_ioports *port, void __iomem *port_mmio) readl(port_mmio + EDMA_ERR_IRQ_MASK_OFS)); } -static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv, - unsigned int board_idx) +static int mv_chip_id(struct ata_host *host, unsigned int board_idx) { + struct pci_dev *pdev = to_pci_dev(host->dev); + struct mv_host_priv *hpriv = host->private_data; u8 rev_id; u32 hp_flags = hpriv->hp_flags; @@ -2168,8 +2189,8 @@ static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv, /** * mv_init_host - Perform some early initialization of the host. - * @pdev: host PCI device - * @probe_ent: early data struct representing the host + * @host: ATA host to initialize + * @board_idx: controller index * * If possible, do an early global reset of the host. Then do * our port init and clear/unmask all/relevant host interrupts. @@ -2177,24 +2198,23 @@ static int mv_chip_id(struct pci_dev *pdev, struct mv_host_priv *hpriv, * LOCKING: * Inherited from caller. */ -static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, - unsigned int board_idx) +static int mv_init_host(struct ata_host *host, unsigned int board_idx) { int rc = 0, n_hc, port, hc; - void __iomem *mmio = probe_ent->iomap[MV_PRIMARY_BAR]; - struct mv_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio = host->iomap[MV_PRIMARY_BAR]; + struct mv_host_priv *hpriv = host->private_data; /* global interrupt mask */ writel(0, mmio + HC_MAIN_IRQ_MASK_OFS); - rc = mv_chip_id(pdev, hpriv, board_idx); + rc = mv_chip_id(host, board_idx); if (rc) goto done; - n_hc = mv_get_hc_count(probe_ent->port_flags); - probe_ent->n_ports = MV_PORTS_PER_HC * n_hc; + n_hc = mv_get_hc_count(host->ports[0]->flags); - for (port = 0; port < probe_ent->n_ports; port++) + for (port = 0; port < host->n_ports; port++) hpriv->ops->read_preamp(hpriv, port, mmio); rc = hpriv->ops->reset_hc(hpriv, mmio, n_hc); @@ -2205,7 +2225,7 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, hpriv->ops->reset_bus(pdev, mmio); hpriv->ops->enable_leds(hpriv, mmio); - for (port = 0; port < probe_ent->n_ports; port++) { + for (port = 0; port < host->n_ports; port++) { if (IS_60XX(hpriv)) { void __iomem *port_mmio = mv_port_base(mmio, port); @@ -2218,9 +2238,9 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, hpriv->ops->phy_errata(hpriv, mmio, port); } - for (port = 0; port < probe_ent->n_ports; port++) { + for (port = 0; port < host->n_ports; port++) { void __iomem *port_mmio = mv_port_base(mmio, port); - mv_port_init(&probe_ent->port[port], port_mmio); + mv_port_init(&host->ports[port]->ioaddr, port_mmio); } for (hc = 0; hc < n_hc; hc++) { @@ -2240,7 +2260,11 @@ static int mv_init_host(struct pci_dev *pdev, struct ata_probe_ent *probe_ent, /* and unmask interrupt generation for host regs */ writelfl(PCI_UNMASK_ALL_IRQS, mmio + PCI_IRQ_MASK_OFS); - writelfl(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); + + if (IS_50XX(hpriv)) + writelfl(~HC_MAIN_MASKED_IRQS_5, mmio + HC_MAIN_IRQ_MASK_OFS); + else + writelfl(~HC_MAIN_MASKED_IRQS, mmio + HC_MAIN_IRQ_MASK_OFS); VPRINTK("HC MAIN IRQ cause/mask=0x%08x/0x%08x " "PCI int cause/mask=0x%08x/0x%08x\n", @@ -2255,17 +2279,17 @@ done: /** * mv_print_info - Dump key info to kernel log for perusal. - * @probe_ent: early data struct representing the host + * @host: ATA host to print info about * * FIXME: complete this. * * LOCKING: * Inherited from caller. */ -static void mv_print_info(struct ata_probe_ent *probe_ent) +static void mv_print_info(struct ata_host *host) { - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); - struct mv_host_priv *hpriv = probe_ent->private_data; + struct pci_dev *pdev = to_pci_dev(host->dev); + struct mv_host_priv *hpriv = host->private_data; u8 rev_id, scc; const char *scc_s; @@ -2284,7 +2308,7 @@ static void mv_print_info(struct ata_probe_ent *probe_ent) dev_printk(KERN_INFO, &pdev->dev, "%u slots %u ports %s mode IRQ via %s\n", - (unsigned)MV_MAX_Q_DEPTH, probe_ent->n_ports, + (unsigned)MV_MAX_Q_DEPTH, host->n_ports, scc_s, (MV_HP_FLAG_MSI & hpriv->hp_flags) ? "MSI" : "INTx"); } @@ -2299,65 +2323,55 @@ static void mv_print_info(struct ata_probe_ent *probe_ent) static int mv_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; - struct mv_host_priv *hpriv; unsigned int board_idx = (unsigned int)ent->driver_data; - int rc; + const struct ata_port_info *ppi[] = { &mv_port_info[board_idx], NULL }; + struct ata_host *host; + struct mv_host_priv *hpriv; + int n_ports, rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = mv_get_hc_count(ppi[0]->flags) * MV_PORTS_PER_HC; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + host->private_data = hpriv; + + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; - pci_set_master(pdev); rc = pcim_iomap_regions(pdev, 1 << MV_PRIMARY_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - hpriv = devm_kzalloc(dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - probe_ent->sht = mv_port_info[board_idx].sht; - probe_ent->port_flags = mv_port_info[board_idx].flags; - probe_ent->pio_mask = mv_port_info[board_idx].pio_mask; - probe_ent->udma_mask = mv_port_info[board_idx].udma_mask; - probe_ent->port_ops = mv_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - probe_ent->private_data = hpriv; + rc = pci_go_64(pdev); + if (rc) + return rc; /* initialize adapter */ - rc = mv_init_host(pdev, probe_ent, board_idx); + rc = mv_init_host(host, board_idx); if (rc) return rc; /* Enable interrupts */ - if (msi && !pci_enable_msi(pdev)) + if (msi && pci_enable_msi(pdev)) pci_intx(pdev, 1); mv_dump_pci_cfg(pdev, 0x68); - mv_print_info(probe_ent); + mv_print_info(host); - if (ata_device_add(probe_ent) == 0) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, mv_interrupt, IRQF_SHARED, + &mv_sht); } static int __init mv_init(void) diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c index ab92f20..0216974 100644 --- a/drivers/ata/sata_nv.c +++ b/drivers/ata/sata_nv.c @@ -219,6 +219,7 @@ struct nv_adma_port_priv { void __iomem * gen_block; void __iomem * notifier_clear_block; u8 flags; + int last_issue_ncq; }; struct nv_host_priv { @@ -229,7 +230,9 @@ struct nv_host_priv { static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); static void nv_remove_one (struct pci_dev *pdev); +#ifdef CONFIG_PM static int nv_pci_device_resume(struct pci_dev *pdev); +#endif static void nv_ck804_host_stop(struct ata_host *host); static irqreturn_t nv_generic_interrupt(int irq, void *dev_instance); static irqreturn_t nv_nf2_interrupt(int irq, void *dev_instance); @@ -250,14 +253,14 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance); static void nv_adma_irq_clear(struct ata_port *ap); static int nv_adma_port_start(struct ata_port *ap); static void nv_adma_port_stop(struct ata_port *ap); +#ifdef CONFIG_PM static int nv_adma_port_suspend(struct ata_port *ap, pm_message_t mesg); static int nv_adma_port_resume(struct ata_port *ap); +#endif static void nv_adma_error_handler(struct ata_port *ap); static void nv_adma_host_stop(struct ata_host *host); -static void nv_adma_bmdma_setup(struct ata_queued_cmd *qc); -static void nv_adma_bmdma_start(struct ata_queued_cmd *qc); -static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc); -static u8 nv_adma_bmdma_status(struct ata_port *ap); +static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc); +static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf); enum nv_host_type { @@ -297,8 +300,10 @@ static struct pci_driver nv_pci_driver = { .name = DRV_NAME, .id_table = nv_pci_tbl, .probe = nv_init_one, +#ifdef CONFIG_PM .suspend = ata_pci_device_suspend, .resume = nv_pci_device_resume, +#endif .remove = nv_remove_one, }; @@ -318,8 +323,10 @@ static struct scsi_host_template nv_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static struct scsi_host_template nv_adma_sht = { @@ -338,8 +345,10 @@ static struct scsi_host_template nv_adma_sht = { .slave_configure = nv_adma_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static const struct ata_port_operations nv_generic_ops = { @@ -360,7 +369,6 @@ static const struct ata_port_operations nv_generic_ops = { .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_generic_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -387,7 +395,6 @@ static const struct ata_port_operations nv_nf2_ops = { .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_nf2_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -414,7 +421,6 @@ static const struct ata_port_operations nv_ck804_ops = { .error_handler = nv_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_ck804_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -427,23 +433,22 @@ static const struct ata_port_operations nv_ck804_ops = { static const struct ata_port_operations nv_adma_ops = { .port_disable = ata_port_disable, .tf_load = ata_tf_load, - .tf_read = ata_tf_read, + .tf_read = nv_adma_tf_read, .check_atapi_dma = nv_adma_check_atapi_dma, .exec_command = ata_exec_command, .check_status = ata_check_status, .dev_select = ata_std_dev_select, - .bmdma_setup = nv_adma_bmdma_setup, - .bmdma_start = nv_adma_bmdma_start, - .bmdma_stop = nv_adma_bmdma_stop, - .bmdma_status = nv_adma_bmdma_status, + .bmdma_setup = ata_bmdma_setup, + .bmdma_start = ata_bmdma_start, + .bmdma_stop = ata_bmdma_stop, + .bmdma_status = ata_bmdma_status, .qc_prep = nv_adma_qc_prep, .qc_issue = nv_adma_qc_issue, .freeze = nv_ck804_freeze, .thaw = nv_ck804_thaw, .error_handler = nv_adma_error_handler, - .post_internal_cmd = nv_adma_bmdma_stop, + .post_internal_cmd = nv_adma_post_internal_cmd, .data_xfer = ata_data_xfer, - .irq_handler = nv_adma_interrupt, .irq_clear = nv_adma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -451,8 +456,10 @@ static const struct ata_port_operations nv_adma_ops = { .scr_write = nv_scr_write, .port_start = nv_adma_port_start, .port_stop = nv_adma_port_stop, +#ifdef CONFIG_PM .port_suspend = nv_adma_port_suspend, .port_resume = nv_adma_port_resume, +#endif .host_stop = nv_adma_host_stop, }; @@ -466,6 +473,7 @@ static struct ata_port_info nv_port_info[] = { .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_generic_ops, + .irq_handler = nv_generic_interrupt, }, /* nforce2/3 */ { @@ -476,6 +484,7 @@ static struct ata_port_info nv_port_info[] = { .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_nf2_ops, + .irq_handler = nv_nf2_interrupt, }, /* ck804 */ { @@ -486,6 +495,7 @@ static struct ata_port_info nv_port_info[] = { .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_ck804_ops, + .irq_handler = nv_ck804_interrupt, }, /* ADMA */ { @@ -497,6 +507,7 @@ static struct ata_port_info nv_port_info[] = { .mwdma_mask = NV_MWDMA_MASK, .udma_mask = NV_UDMA_MASK, .port_ops = &nv_adma_ops, + .irq_handler = nv_adma_interrupt, }, }; @@ -657,34 +668,47 @@ static int nv_adma_check_atapi_dma(struct ata_queued_cmd *qc) return !(pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE); } +static void nv_adma_tf_read(struct ata_port *ap, struct ata_taskfile *tf) +{ + /* Since commands where a result TF is requested are not + executed in ADMA mode, the only time this function will be called + in ADMA mode will be if a command fails. In this case we + don't care about going into register mode with ADMA commands + pending, as the commands will all shortly be aborted anyway. */ + nv_adma_register_mode(ap); + + ata_tf_read(ap, tf); +} + static unsigned int nv_adma_tf_to_cpb(struct ata_taskfile *tf, __le16 *cpb) { unsigned int idx = 0; - cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device | WNB); - - if ((tf->flags & ATA_TFLAG_LBA48) == 0) { - cpb[idx++] = cpu_to_le16(IGN); - cpb[idx++] = cpu_to_le16(IGN); - cpb[idx++] = cpu_to_le16(IGN); - cpb[idx++] = cpu_to_le16(IGN); - cpb[idx++] = cpu_to_le16(IGN); + if(tf->flags & ATA_TFLAG_ISADDR) { + if (tf->flags & ATA_TFLAG_LBA48) { + cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature | WNB); + cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah); + cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature); + } else + cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature | WNB); + + cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam); + cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah); } - else { - cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->hob_feature); - cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->hob_nsect); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->hob_lbal); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->hob_lbam); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->hob_lbah); - } - cpb[idx++] = cpu_to_le16((ATA_REG_ERR << 8) | tf->feature); - cpb[idx++] = cpu_to_le16((ATA_REG_NSECT << 8) | tf->nsect); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAL << 8) | tf->lbal); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAM << 8) | tf->lbam); - cpb[idx++] = cpu_to_le16((ATA_REG_LBAH << 8) | tf->lbah); + + if(tf->flags & ATA_TFLAG_DEVICE) + cpb[idx++] = cpu_to_le16((ATA_REG_DEVICE << 8) | tf->device); cpb[idx++] = cpu_to_le16((ATA_REG_CMD << 8) | tf->command | CMDEND); + while(idx < 12) + cpb[idx++] = cpu_to_le16(IGN); + return idx; } @@ -727,20 +751,23 @@ static int nv_adma_check_cpb(struct ata_port *ap, int cpb_num, int force_err) return 1; } - if (flags & NV_CPB_RESP_DONE) { + if (likely(flags & NV_CPB_RESP_DONE)) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, cpb_num); VPRINTK("CPB flags done, flags=0x%x\n", flags); if (likely(qc)) { - /* Grab the ATA port status for non-NCQ commands. - For NCQ commands the current status may have nothing to do with - the command just completed. */ - if (qc->tf.protocol != ATA_PROT_NCQ) { - u8 ata_status = readb(pp->ctl_block + (ATA_REG_STATUS * 4)); - qc->err_mask |= ac_err_mask(ata_status); - } - DPRINTK("Completing qc from tag %d with err_mask %u\n",cpb_num, - qc->err_mask); + DPRINTK("Completing qc from tag %d\n",cpb_num); ata_qc_complete(qc); + } else { + struct ata_eh_info *ehi = &ap->eh_info; + /* Notifier bits set without a command may indicate the drive + is misbehaving. Raise host state machine violation on this + condition. */ + ata_port_printk(ap, KERN_ERR, "notifier for tag %d with no command?\n", + cpb_num); + ehi->err_mask |= AC_ERR_HSM; + ehi->action |= ATA_EH_SOFTRESET; + ata_port_freeze(ap); + return 1; } } return 0; @@ -852,22 +879,20 @@ static irqreturn_t nv_adma_interrupt(int irq, void *dev_instance) if (status & (NV_ADMA_STAT_DONE | NV_ADMA_STAT_CPBERR)) { - /** Check CPBs for completed commands */ + u32 check_commands; + int pos, error = 0; - if (ata_tag_valid(ap->active_tag)) { - /* Non-NCQ command */ - nv_adma_check_cpb(ap, ap->active_tag, - notifier_error & (1 << ap->active_tag)); - } else { - int pos, error = 0; - u32 active = ap->sactive; - - while ((pos = ffs(active)) && !error) { - pos--; - error = nv_adma_check_cpb(ap, pos, - notifier_error & (1 << pos) ); - active &= ~(1 << pos ); - } + if(ata_tag_valid(ap->active_tag)) + check_commands = 1 << ap->active_tag; + else + check_commands = ap->sactive; + + /** Check CPBs for completed commands */ + while ((pos = ffs(check_commands)) && !error) { + pos--; + error = nv_adma_check_cpb(ap, pos, + notifier_error & (1 << pos) ); + check_commands &= ~(1 << pos ); } } } @@ -905,73 +930,12 @@ static void nv_adma_irq_clear(struct ata_port *ap) iowrite8(ioread8(dma_stat_addr), dma_stat_addr); } -static void nv_adma_bmdma_setup(struct ata_queued_cmd *qc) +static void nv_adma_post_internal_cmd(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; - unsigned int rw = (qc->tf.flags & ATA_TFLAG_WRITE); - struct nv_adma_port_priv *pp = ap->private_data; - u8 dmactl; - - if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { - WARN_ON(1); - return; - } - - /* load PRD table addr. */ - iowrite32(ap->prd_dma, ap->ioaddr.bmdma_addr + ATA_DMA_TABLE_OFS); - - /* specify data direction, triple-check start bit is clear */ - dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - dmactl &= ~(ATA_DMA_WR | ATA_DMA_START); - if (!rw) - dmactl |= ATA_DMA_WR; - - iowrite8(dmactl, ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - - /* issue r/w command */ - ata_exec_command(ap, &qc->tf); -} - -static void nv_adma_bmdma_start(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct nv_adma_port_priv *pp = ap->private_data; - u8 dmactl; - - if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) { - WARN_ON(1); - return; - } - - /* start host DMA transaction */ - dmactl = ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - iowrite8(dmactl | ATA_DMA_START, - ap->ioaddr.bmdma_addr + ATA_DMA_CMD); -} - -static void nv_adma_bmdma_stop(struct ata_queued_cmd *qc) -{ - struct ata_port *ap = qc->ap; - struct nv_adma_port_priv *pp = ap->private_data; - - if(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)) - return; - - /* clear start/stop bit */ - iowrite8(ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_CMD) & ~ATA_DMA_START, - ap->ioaddr.bmdma_addr + ATA_DMA_CMD); - - /* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */ - ata_altstatus(ap); /* dummy read */ -} - -static u8 nv_adma_bmdma_status(struct ata_port *ap) -{ - struct nv_adma_port_priv *pp = ap->private_data; - - WARN_ON(!(pp->flags & NV_ADMA_PORT_REGISTER_MODE)); + struct nv_adma_port_priv *pp = qc->ap->private_data; - return ioread8(ap->ioaddr.bmdma_addr + ATA_DMA_STATUS); + if(pp->flags & NV_ADMA_PORT_REGISTER_MODE) + ata_bmdma_post_internal_cmd(qc); } static int nv_adma_port_start(struct ata_port *ap) @@ -1040,14 +1004,15 @@ static int nv_adma_port_start(struct ata_port *ap) /* clear GO for register mode, enable interrupt */ tmp = readw(mmio + NV_ADMA_CTL); - writew( (tmp & ~NV_ADMA_CTL_GO) | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL); + writew( (tmp & ~NV_ADMA_CTL_GO) | NV_ADMA_CTL_AIEN | + NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ udelay(1); writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ return 0; } @@ -1061,6 +1026,7 @@ static void nv_adma_port_stop(struct ata_port *ap) writew(0, mmio + NV_ADMA_CTL); } +#ifdef CONFIG_PM static int nv_adma_port_suspend(struct ata_port *ap, pm_message_t mesg) { struct nv_adma_port_priv *pp = ap->private_data; @@ -1099,26 +1065,28 @@ static int nv_adma_port_resume(struct ata_port *ap) /* clear GO for register mode, enable interrupt */ tmp = readw(mmio + NV_ADMA_CTL); - writew((tmp & ~NV_ADMA_CTL_GO) | NV_ADMA_CTL_AIEN, mmio + NV_ADMA_CTL); + writew( (tmp & ~NV_ADMA_CTL_GO) | NV_ADMA_CTL_AIEN | + NV_ADMA_CTL_HOTPLUG_IEN, mmio + NV_ADMA_CTL); tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ udelay(1); writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ return 0; } +#endif -static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int port) +static void nv_adma_setup_port(struct ata_port *ap) { - void __iomem *mmio = probe_ent->iomap[NV_MMIO_BAR]; - struct ata_ioports *ioport = &probe_ent->port[port]; + void __iomem *mmio = ap->host->iomap[NV_MMIO_BAR]; + struct ata_ioports *ioport = &ap->ioaddr; VPRINTK("ENTER\n"); - mmio += NV_ADMA_PORT + port * NV_ADMA_PORT_SIZE; + mmio += NV_ADMA_PORT + ap->port_no * NV_ADMA_PORT_SIZE; ioport->cmd_addr = mmio; ioport->data_addr = mmio + (ATA_REG_DATA * 4); @@ -1135,9 +1103,9 @@ static void nv_adma_setup_port(struct ata_probe_ent *probe_ent, unsigned int por ioport->ctl_addr = mmio + 0x20; } -static int nv_adma_host_init(struct ata_probe_ent *probe_ent) +static int nv_adma_host_init(struct ata_host *host) { - struct pci_dev *pdev = to_pci_dev(probe_ent->dev); + struct pci_dev *pdev = to_pci_dev(host->dev); unsigned int i; u32 tmp32; @@ -1152,8 +1120,8 @@ static int nv_adma_host_init(struct ata_probe_ent *probe_ent) pci_write_config_dword(pdev, NV_MCP_SATA_CFG_20, tmp32); - for (i = 0; i < probe_ent->n_ports; i++) - nv_adma_setup_port(probe_ent, i); + for (i = 0; i < host->n_ports; i++) + nv_adma_setup_port(host->ports[i]); return 0; } @@ -1163,11 +1131,7 @@ static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, int idx, struct nv_adma_prd *aprd) { - u8 flags; - - memset(aprd, 0, sizeof(struct nv_adma_prd)); - - flags = 0; + u8 flags = 0; if (qc->tf.flags & ATA_TFLAG_WRITE) flags |= NV_APRD_WRITE; if (idx == qc->n_elem - 1) @@ -1178,6 +1142,7 @@ static void nv_adma_fill_aprd(struct ata_queued_cmd *qc, aprd->addr = cpu_to_le64(((u64)sg_dma_address(sg))); aprd->len = cpu_to_le32(((u32)sg_dma_len(sg))); /* len in bytes */ aprd->flags = flags; + aprd->packet_len = 0; } static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb) @@ -1198,6 +1163,8 @@ static void nv_adma_fill_sg(struct ata_queued_cmd *qc, struct nv_adma_cpb *cpb) } if (idx > 5) cpb->next_aprd = cpu_to_le64(((u64)(pp->aprd_dma + NV_ADMA_SGTBL_SZ * qc->tag))); + else + cpb->next_aprd = cpu_to_le64(0); } static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc) @@ -1205,9 +1172,11 @@ static int nv_adma_use_reg_mode(struct ata_queued_cmd *qc) struct nv_adma_port_priv *pp = qc->ap->private_data; /* ADMA engine can only be used for non-ATAPI DMA commands, - or interrupt-driven no-data commands. */ + or interrupt-driven no-data commands, where a result taskfile + is not required. */ if((pp->flags & NV_ADMA_ATAPI_SETUP_COMPLETE) || - (qc->tf.flags & ATA_TFLAG_POLLING)) + (qc->tf.flags & ATA_TFLAG_POLLING) || + (qc->flags & ATA_QCFLAG_RESULT_TF)) return 1; if((qc->flags & ATA_QCFLAG_DMAMAP) || @@ -1230,7 +1199,10 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc) return; } - memset(cpb, 0, sizeof(struct nv_adma_cpb)); + cpb->resp_flags = NV_CPB_RESP_DONE; + wmb(); + cpb->ctl_flags = 0; + wmb(); cpb->len = 3; cpb->tag = qc->tag; @@ -1254,12 +1226,15 @@ static void nv_adma_qc_prep(struct ata_queued_cmd *qc) finished filling in all of the contents */ wmb(); cpb->ctl_flags = ctl_flags; + wmb(); + cpb->resp_flags = 0; } static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc) { struct nv_adma_port_priv *pp = qc->ap->private_data; void __iomem *mmio = pp->ctl_block; + int curr_ncq = (qc->tf.protocol == ATA_PROT_NCQ); VPRINTK("ENTER\n"); @@ -1274,6 +1249,14 @@ static unsigned int nv_adma_qc_issue(struct ata_queued_cmd *qc) /* write append register, command tag in lower 8 bits and (number of cpbs to append -1) in top 8 bits */ wmb(); + + if(curr_ncq != pp->last_issue_ncq) { + /* Seems to need some delay before switching between NCQ and non-NCQ + commands, else we get command timeouts and such. */ + udelay(20); + pp->last_issue_ncq = curr_ncq; + } + writew(qc->tag, mmio + NV_ADMA_APPEND); DPRINTK("Issued tag %u\n",qc->tag); @@ -1447,6 +1430,30 @@ static void nv_adma_error_handler(struct ata_port *ap) int i; u16 tmp; + if(ata_tag_valid(ap->active_tag) || ap->sactive) { + u32 notifier = readl(mmio + NV_ADMA_NOTIFIER); + u32 notifier_error = readl(mmio + NV_ADMA_NOTIFIER_ERROR); + u32 gen_ctl = readl(pp->gen_block + NV_ADMA_GEN_CTL); + u32 status = readw(mmio + NV_ADMA_STAT); + u8 cpb_count = readb(mmio + NV_ADMA_CPB_COUNT); + u8 next_cpb_idx = readb(mmio + NV_ADMA_NEXT_CPB_IDX); + + ata_port_printk(ap, KERN_ERR, "EH in ADMA mode, notifier 0x%X " + "notifier_error 0x%X gen_ctl 0x%X status 0x%X " + "next cpb count 0x%X next cpb idx 0x%x\n", + notifier, notifier_error, gen_ctl, status, + cpb_count, next_cpb_idx); + + for( i=0;i<NV_ADMA_MAX_CPBS;i++) { + struct nv_adma_cpb *cpb = &pp->cpb[i]; + if( (ata_tag_valid(ap->active_tag) && i == ap->active_tag) || + ap->sactive & (1 << i) ) + ata_port_printk(ap, KERN_ERR, + "CPB %d: ctl_flags 0x%x, resp_flags 0x%x\n", + i, cpb->ctl_flags, cpb->resp_flags); + } + } + /* Push us back into port register mode for error handling. */ nv_adma_register_mode(ap); @@ -1460,10 +1467,10 @@ static void nv_adma_error_handler(struct ata_port *ap) /* Reset channel */ tmp = readw(mmio + NV_ADMA_CTL); writew(tmp | NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ udelay(1); writew(tmp & ~NV_ADMA_CTL_CHANNEL_RESET, mmio + NV_ADMA_CTL); - readl( mmio + NV_ADMA_CTL ); /* flush posted write */ + readw( mmio + NV_ADMA_CTL ); /* flush posted write */ } ata_bmdma_drive_eh(ap, ata_std_prereset, ata_std_softreset, @@ -1473,14 +1480,13 @@ static void nv_adma_error_handler(struct ata_port *ap) static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct ata_port_info *ppi[2]; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[2]; + struct ata_host *host; struct nv_host_priv *hpriv; int rc; u32 bar; void __iomem *base; unsigned long type = ent->driver_data; - int mask_set = 0; // Make sure this is a SATA controller by counting the number of bars // (NVIDIA SATA controllers will always have six bars). Otherwise, @@ -1496,50 +1502,38 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - if(type >= CK804 && adma_enabled) { + /* determine type and allocate host */ + if (type >= CK804 && adma_enabled) { dev_printk(KERN_NOTICE, &pdev->dev, "Using ADMA mode\n"); type = ADMA; - if(!pci_set_dma_mask(pdev, DMA_64BIT_MASK) && - !pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK)) - mask_set = 1; - } - - if(!mask_set) { - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; } - rc = -ENOMEM; + ppi[0] = ppi[1] = &nv_port_info[type]; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + hpriv->type = type; + host->private_data = hpriv; - ppi[0] = ppi[1] = &nv_port_info[type]; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; - - if (!pcim_iomap(pdev, NV_MMIO_BAR, 0)) - return -EIO; - probe_ent->iomap = pcim_iomap_table(pdev); + /* set 64bit dma masks, may fail */ + if (type == ADMA) { + if (pci_set_dma_mask(pdev, DMA_64BIT_MASK) == 0) + pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); + } - probe_ent->private_data = hpriv; - hpriv->type = type; + /* request and iomap NV_MMIO_BAR */ + rc = pcim_iomap_regions(pdev, 1 << NV_MMIO_BAR, DRV_NAME); + if (rc) + return rc; - base = probe_ent->iomap[NV_MMIO_BAR]; - probe_ent->port[0].scr_addr = base + NV_PORT0_SCR_REG_OFFSET; - probe_ent->port[1].scr_addr = base + NV_PORT1_SCR_REG_OFFSET; + /* configure SCR access */ + base = host->iomap[NV_MMIO_BAR]; + host->ports[0]->ioaddr.scr_addr = base + NV_PORT0_SCR_REG_OFFSET; + host->ports[1]->ioaddr.scr_addr = base + NV_PORT1_SCR_REG_OFFSET; /* enable SATA space for CK804 */ if (type >= CK804) { @@ -1550,20 +1544,16 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_write_config_byte(pdev, NV_MCP_SATA_CFG_20, regval); } - pci_set_master(pdev); - + /* init ADMA */ if (type == ADMA) { - rc = nv_adma_host_init(probe_ent); + rc = nv_adma_host_init(host); if (rc) return rc; } - rc = ata_device_add(probe_ent); - if (rc != NV_PORTS) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, ppi[0]->irq_handler, + IRQF_SHARED, ppi[0]->sht); } static void nv_remove_one (struct pci_dev *pdev) @@ -1575,6 +1565,7 @@ static void nv_remove_one (struct pci_dev *pdev) kfree(hpriv); } +#ifdef CONFIG_PM static int nv_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); @@ -1622,6 +1613,7 @@ static int nv_pci_device_resume(struct pci_dev *pdev) return 0; } +#endif static void nv_ck804_host_stop(struct ata_host *host) { diff --git a/drivers/ata/sata_promise.c b/drivers/ata/sata_promise.c index cf9ed8c..f56549b 100644 --- a/drivers/ata/sata_promise.c +++ b/drivers/ata/sata_promise.c @@ -45,10 +45,11 @@ #include "sata_promise.h" #define DRV_NAME "sata_promise" -#define DRV_VERSION "1.05" +#define DRV_VERSION "2.05" enum { + PDC_MAX_PORTS = 4, PDC_MMIO_BAR = 3, /* register offsets */ @@ -70,14 +71,31 @@ enum { PDC_TBG_MODE = 0x41C, /* TBG mode (not SATAII) */ PDC_SLEW_CTL = 0x470, /* slew rate control reg (not SATAII) */ - PDC_ERR_MASK = (1<<19) | (1<<20) | (1<<21) | (1<<22) | - (1<<8) | (1<<9) | (1<<10), + /* PDC_GLOBAL_CTL bit definitions */ + PDC_PH_ERR = (1 << 8), /* PCI error while loading packet */ + PDC_SH_ERR = (1 << 9), /* PCI error while loading S/G table */ + PDC_DH_ERR = (1 << 10), /* PCI error while loading data */ + PDC2_HTO_ERR = (1 << 12), /* host bus timeout */ + PDC2_ATA_HBA_ERR = (1 << 13), /* error during SATA DATA FIS transmission */ + PDC2_ATA_DMA_CNT_ERR = (1 << 14), /* DMA DATA FIS size differs from S/G count */ + PDC_OVERRUN_ERR = (1 << 19), /* S/G byte count larger than HD requires */ + PDC_UNDERRUN_ERR = (1 << 20), /* S/G byte count less than HD requires */ + PDC_DRIVE_ERR = (1 << 21), /* drive error */ + PDC_PCI_SYS_ERR = (1 << 22), /* PCI system error */ + PDC1_PCI_PARITY_ERR = (1 << 23), /* PCI parity error (from SATA150 driver) */ + PDC1_ERR_MASK = PDC1_PCI_PARITY_ERR, + PDC2_ERR_MASK = PDC2_HTO_ERR | PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR, + PDC_ERR_MASK = (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC_OVERRUN_ERR + | PDC_UNDERRUN_ERR | PDC_DRIVE_ERR | PDC_PCI_SYS_ERR + | PDC1_ERR_MASK | PDC2_ERR_MASK), board_2037x = 0, /* FastTrak S150 TX2plus */ - board_20319 = 1, /* FastTrak S150 TX4 */ - board_20619 = 2, /* FastTrak TX4000 */ - board_2057x = 3, /* SATAII150 Tx2plus */ - board_40518 = 4, /* SATAII150 Tx4 */ + board_2037x_pata = 1, /* FastTrak S150 TX2plus PATA port */ + board_20319 = 2, /* FastTrak S150 TX4 */ + board_20619 = 3, /* FastTrak TX4000 */ + board_2057x = 4, /* SATAII150 Tx2plus */ + board_2057x_pata = 5, /* SATAII150 Tx2plus */ + board_40518 = 6, /* SATAII150 Tx4 */ PDC_HAS_PATA = (1 << 1), /* PDC20375/20575 has PATA */ @@ -100,8 +118,10 @@ enum { ATA_FLAG_MMIO | ATA_FLAG_PIO_POLLING, - /* hp->flags bits */ - PDC_FLAG_GEN_II = (1 << 0), + /* ap->flags bits */ + PDC_FLAG_GEN_II = (1 << 24), + PDC_FLAG_SATA_PATA = (1 << 25), /* supports SATA + PATA */ + PDC_FLAG_4_PORTS = (1 << 26), /* 4 ports */ }; @@ -110,28 +130,25 @@ struct pdc_port_priv { dma_addr_t pkt_dma; }; -struct pdc_host_priv { - unsigned long flags; - unsigned long port_flags[ATA_MAX_PORTS]; -}; - static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg); static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc_interrupt (int irq, void *dev_instance); -static int pdc_port_start(struct ata_port *ap); +static int pdc_common_port_start(struct ata_port *ap); +static int pdc_sata_port_start(struct ata_port *ap); static void pdc_qc_prep(struct ata_queued_cmd *qc); static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static int pdc_check_atapi_dma(struct ata_queued_cmd *qc); -static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc); +static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc); static void pdc_irq_clear(struct ata_port *ap); static unsigned int pdc_qc_issue_prot(struct ata_queued_cmd *qc); static void pdc_freeze(struct ata_port *ap); static void pdc_thaw(struct ata_port *ap); -static void pdc_error_handler(struct ata_port *ap); +static void pdc_pata_error_handler(struct ata_port *ap); +static void pdc_sata_error_handler(struct ata_port *ap); static void pdc_post_internal_cmd(struct ata_queued_cmd *qc); - +static int pdc_pata_cable_detect(struct ata_port *ap); +static int pdc_sata_cable_detect(struct ata_port *ap); static struct scsi_host_template pdc_ata_sht = { .module = THIS_MODULE, @@ -164,17 +181,17 @@ static const struct ata_port_operations pdc_sata_ops = { .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_sata_error_handler, .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_sata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, - .port_start = pdc_port_start, + .port_start = pdc_sata_port_start, }; /* First-generation chips need a more restrictive ->check_atapi_dma op */ @@ -185,23 +202,23 @@ static const struct ata_port_operations pdc_old_sata_ops = { .check_status = ata_check_status, .exec_command = pdc_exec_command_mmio, .dev_select = ata_std_dev_select, - .check_atapi_dma = pdc_old_check_atapi_dma, + .check_atapi_dma = pdc_old_sata_check_atapi_dma, .qc_prep = pdc_qc_prep, .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_sata_error_handler, .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_sata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, .scr_read = pdc_sata_scr_read, .scr_write = pdc_sata_scr_write, - .port_start = pdc_port_start, + .port_start = pdc_sata_port_start, }; static const struct ata_port_operations pdc_pata_ops = { @@ -217,31 +234,41 @@ static const struct ata_port_operations pdc_pata_ops = { .qc_issue = pdc_qc_issue_prot, .freeze = pdc_freeze, .thaw = pdc_thaw, - .error_handler = pdc_error_handler, + .error_handler = pdc_pata_error_handler, + .post_internal_cmd = pdc_post_internal_cmd, + .cable_detect = pdc_pata_cable_detect, .data_xfer = ata_data_xfer, - .irq_handler = pdc_interrupt, .irq_clear = pdc_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, - .port_start = pdc_port_start, + .port_start = pdc_common_port_start, }; static const struct ata_port_info pdc_port_info[] = { /* board_2037x */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_SATA_PATA, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &pdc_old_sata_ops, }, + /* board_2037x_pata */ + { + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_pata_ops, + }, + /* board_20319 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -250,8 +277,8 @@ static const struct ata_port_info pdc_port_info[] = { /* board_20619 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS | + PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -260,18 +287,28 @@ static const struct ata_port_info pdc_port_info[] = { /* board_2057x */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_GEN_II | PDC_FLAG_SATA_PATA, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ .port_ops = &pdc_sata_ops, }, + /* board_2057x_pata */ + { + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SLAVE_POSS, + PDC_FLAG_GEN_II, + .pio_mask = 0x1f, /* pio0-4 */ + .mwdma_mask = 0x07, /* mwdma0-2 */ + .udma_mask = 0x7f, /* udma0-6 ; FIXME */ + .port_ops = &pdc_pata_ops, + }, + /* board_40518 */ { - .sht = &pdc_ata_sht, - .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA, + .flags = PDC_COMMON_FLAGS | ATA_FLAG_SATA | + PDC_FLAG_GEN_II | PDC_FLAG_4_PORTS, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ .udma_mask = 0x7f, /* udma0-6 ; FIXME */ @@ -312,18 +349,12 @@ static struct pci_driver pdc_ata_pci_driver = { }; -static int pdc_port_start(struct ata_port *ap) +static int pdc_common_port_start(struct ata_port *ap) { struct device *dev = ap->host->dev; - struct pdc_host_priv *hp = ap->host->private_data; struct pdc_port_priv *pp; int rc; - /* fix up port flags and cable type for SATA+PATA chips */ - ap->flags |= hp->port_flags[ap->port_no]; - if (ap->flags & ATA_FLAG_SATA) - ap->cbl = ATA_CBL_SATA; - rc = ata_port_start(ap); if (rc) return rc; @@ -338,8 +369,19 @@ static int pdc_port_start(struct ata_port *ap) ap->private_data = pp; + return 0; +} + +static int pdc_sata_port_start(struct ata_port *ap) +{ + int rc; + + rc = pdc_common_port_start(ap); + if (rc) + return rc; + /* fix up PHYMODE4 align timing */ - if ((hp->flags & PDC_FLAG_GEN_II) && sata_scr_valid(ap)) { + if (ap->flags & PDC_FLAG_GEN_II) { void __iomem *mmio = (void __iomem *) ap->ioaddr.scr_addr; unsigned int tmp; @@ -373,23 +415,25 @@ static void pdc_reset_port(struct ata_port *ap) readl(mmio); /* flush */ } -static void pdc_pata_cbl_detect(struct ata_port *ap) +static int pdc_pata_cable_detect(struct ata_port *ap) { u8 tmp; void __iomem *mmio = (void __iomem *) ap->ioaddr.cmd_addr + PDC_CTLSTAT + 0x03; tmp = readb(mmio); + if (tmp & 0x01) + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; +} - if (tmp & 0x01) { - ap->cbl = ATA_CBL_PATA40; - ap->udma_mask &= ATA_UDMA_MASK_40C; - } else - ap->cbl = ATA_CBL_PATA80; +static int pdc_sata_cable_detect(struct ata_port *ap) +{ + return ATA_CBL_SATA; } static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { - if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA) + if (sc_reg > SCR_CONTROL) return 0xffffffffU; return readl(ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -398,7 +442,7 @@ static u32 pdc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) static void pdc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) { - if (sc_reg > SCR_CONTROL || ap->cbl != ATA_CBL_SATA) + if (sc_reg > SCR_CONTROL) return; writel(val, ap->ioaddr.scr_addr + (sc_reg * 4)); } @@ -554,52 +598,79 @@ static void pdc_thaw(struct ata_port *ap) readl(mmio + PDC_CTLSTAT); /* flush */ } -static int pdc_pre_reset(struct ata_port *ap) +static void pdc_common_error_handler(struct ata_port *ap, ata_reset_fn_t hardreset) { - if (!sata_scr_valid(ap)) - pdc_pata_cbl_detect(ap); - return ata_std_prereset(ap); -} - -static void pdc_error_handler(struct ata_port *ap) -{ - ata_reset_fn_t hardreset; - if (!(ap->pflags & ATA_PFLAG_FROZEN)) pdc_reset_port(ap); - hardreset = NULL; - if (sata_scr_valid(ap)) - hardreset = sata_std_hardreset; - /* perform recovery */ - ata_do_eh(ap, pdc_pre_reset, ata_std_softreset, hardreset, + ata_do_eh(ap, ata_std_prereset, ata_std_softreset, hardreset, ata_std_postreset); } +static void pdc_pata_error_handler(struct ata_port *ap) +{ + pdc_common_error_handler(ap, NULL); +} + +static void pdc_sata_error_handler(struct ata_port *ap) +{ + pdc_common_error_handler(ap, sata_std_hardreset); +} + static void pdc_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) pdc_reset_port(ap); } +static void pdc_error_intr(struct ata_port *ap, struct ata_queued_cmd *qc, + u32 port_status, u32 err_mask) +{ + struct ata_eh_info *ehi = &ap->eh_info; + unsigned int ac_err_mask = 0; + + ata_ehi_clear_desc(ehi); + ata_ehi_push_desc(ehi, "port_status 0x%08x", port_status); + port_status &= err_mask; + + if (port_status & PDC_DRIVE_ERR) + ac_err_mask |= AC_ERR_DEV; + if (port_status & (PDC_OVERRUN_ERR | PDC_UNDERRUN_ERR)) + ac_err_mask |= AC_ERR_HSM; + if (port_status & (PDC2_ATA_HBA_ERR | PDC2_ATA_DMA_CNT_ERR)) + ac_err_mask |= AC_ERR_ATA_BUS; + if (port_status & (PDC_PH_ERR | PDC_SH_ERR | PDC_DH_ERR | PDC2_HTO_ERR + | PDC_PCI_SYS_ERR | PDC1_PCI_PARITY_ERR)) + ac_err_mask |= AC_ERR_HOST_BUS; + + if (sata_scr_valid(ap)) + ehi->serror |= pdc_sata_scr_read(ap, SCR_ERROR); + + qc->err_mask |= ac_err_mask; + + pdc_reset_port(ap); +} + static inline unsigned int pdc_host_intr( struct ata_port *ap, struct ata_queued_cmd *qc) { unsigned int handled = 0; - u32 tmp; - void __iomem *mmio = ap->ioaddr.cmd_addr + PDC_GLOBAL_CTL; + void __iomem *port_mmio = ap->ioaddr.cmd_addr; + u32 port_status, err_mask; - tmp = readl(mmio); - if (tmp & PDC_ERR_MASK) { - qc->err_mask |= AC_ERR_DEV; - pdc_reset_port(ap); + err_mask = PDC_ERR_MASK; + if (ap->flags & PDC_FLAG_GEN_II) + err_mask &= ~PDC1_ERR_MASK; + else + err_mask &= ~PDC2_ERR_MASK; + port_status = readl(port_mmio + PDC_GLOBAL_CTL); + if (unlikely(port_status & err_mask)) { + pdc_error_intr(ap, qc, port_status, err_mask); + return 1; } switch (qc->tf.protocol) { @@ -766,42 +837,40 @@ static int pdc_check_atapi_dma(struct ata_queued_cmd *qc) return pio; } -static int pdc_old_check_atapi_dma(struct ata_queued_cmd *qc) +static int pdc_old_sata_check_atapi_dma(struct ata_queued_cmd *qc) { - struct ata_port *ap = qc->ap; - /* First generation chips cannot use ATAPI DMA on SATA ports */ - if (sata_scr_valid(ap)) - return 1; - return pdc_check_atapi_dma(qc); + return 1; } -static void pdc_ata_setup_port(struct ata_ioports *port, void __iomem *base) +static void pdc_ata_setup_port(struct ata_port *ap, + void __iomem *base, void __iomem *scr_addr) { - port->cmd_addr = base; - port->data_addr = base; - port->feature_addr = - port->error_addr = base + 0x4; - port->nsect_addr = base + 0x8; - port->lbal_addr = base + 0xc; - port->lbam_addr = base + 0x10; - port->lbah_addr = base + 0x14; - port->device_addr = base + 0x18; - port->command_addr = - port->status_addr = base + 0x1c; - port->altstatus_addr = - port->ctl_addr = base + 0x38; + ap->ioaddr.cmd_addr = base; + ap->ioaddr.data_addr = base; + ap->ioaddr.feature_addr = + ap->ioaddr.error_addr = base + 0x4; + ap->ioaddr.nsect_addr = base + 0x8; + ap->ioaddr.lbal_addr = base + 0xc; + ap->ioaddr.lbam_addr = base + 0x10; + ap->ioaddr.lbah_addr = base + 0x14; + ap->ioaddr.device_addr = base + 0x18; + ap->ioaddr.command_addr = + ap->ioaddr.status_addr = base + 0x1c; + ap->ioaddr.altstatus_addr = + ap->ioaddr.ctl_addr = base + 0x38; + ap->ioaddr.scr_addr = scr_addr; } -static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) +static void pdc_host_init(struct ata_host *host) { - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - struct pdc_host_priv *hp = pe->private_data; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + int is_gen2 = host->ports[0]->flags & PDC_FLAG_GEN_II; int hotplug_offset; u32 tmp; - if (hp->flags & PDC_FLAG_GEN_II) + if (is_gen2) hotplug_offset = PDC2_SATA_PLUG_CSR; else hotplug_offset = PDC_SATA_PLUG_CSR; @@ -815,7 +884,7 @@ static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) /* enable BMR_BURST, maybe change FIFO_SHD to 8 dwords */ tmp = readl(mmio + PDC_FLASH_CTL); tmp |= 0x02000; /* bit 13 (enable bmr burst) */ - if (!(hp->flags & PDC_FLAG_GEN_II)) + if (!is_gen2) tmp |= 0x10000; /* bit 16 (fifo threshold at 8 dw) */ writel(tmp, mmio + PDC_FLASH_CTL); @@ -828,7 +897,7 @@ static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) writel(tmp | 0xff0000, mmio + hotplug_offset); /* don't initialise TBG or SLEW on 2nd generation chips */ - if (hp->flags & PDC_FLAG_GEN_II) + if (is_gen2) return; /* reduce TBG clock to 133 Mhz. */ @@ -850,16 +919,16 @@ static void pdc_host_init(unsigned int chip_id, struct ata_probe_ent *pe) static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - struct pdc_host_priv *hp; + const struct ata_port_info *pi = &pdc_port_info[ent->driver_data]; + const struct ata_port_info *ppi[PDC_MAX_PORTS]; + struct ata_host *host; void __iomem *base; - unsigned int board_idx = (unsigned int) ent->driver_data; - int rc; - u8 tmp; + int n_ports, i, rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* enable and acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -869,100 +938,49 @@ static int pdc_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *e pcim_pin_device(pdev); if (rc) return rc; + base = pcim_iomap_table(pdev)[PDC_MMIO_BAR]; - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - hp = devm_kzalloc(&pdev->dev, sizeof(*hp), GFP_KERNEL); - if (hp == NULL) - return -ENOMEM; - - probe_ent->private_data = hp; + /* determine port configuration and setup host */ + n_ports = 2; + if (pi->flags & PDC_FLAG_4_PORTS) + n_ports = 4; + for (i = 0; i < n_ports; i++) + ppi[i] = pi; - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->port_flags = pdc_port_info[board_idx].flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - base = probe_ent->iomap[PDC_MMIO_BAR]; - - pdc_ata_setup_port(&probe_ent->port[0], base + 0x200); - pdc_ata_setup_port(&probe_ent->port[1], base + 0x280); - - probe_ent->port[0].scr_addr = base + 0x400; - probe_ent->port[1].scr_addr = base + 0x500; - - /* notice 4-port boards */ - switch (board_idx) { - case board_40518: - hp->flags |= PDC_FLAG_GEN_II; - /* Fall through */ - case board_20319: - probe_ent->n_ports = 4; - - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380); - - probe_ent->port[2].scr_addr = base + 0x600; - probe_ent->port[3].scr_addr = base + 0x700; - break; - case board_2057x: - hp->flags |= PDC_FLAG_GEN_II; - /* Fall through */ - case board_2037x: - /* TX2plus boards also have a PATA port */ - tmp = readb(base + PDC_FLASH_CTL+1); + if (pi->flags & PDC_FLAG_SATA_PATA) { + u8 tmp = readb(base + PDC_FLASH_CTL+1); if (!(tmp & 0x80)) { - probe_ent->n_ports = 3; - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300); - hp->port_flags[2] = ATA_FLAG_SLAVE_POSS; - printk(KERN_INFO DRV_NAME " PATA port found\n"); - } else - probe_ent->n_ports = 2; - hp->port_flags[0] = ATA_FLAG_SATA; - hp->port_flags[1] = ATA_FLAG_SATA; - break; - case board_20619: - probe_ent->n_ports = 4; - - pdc_ata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_ata_setup_port(&probe_ent->port[3], base + 0x380); + ppi[n_ports++] = pi + 1; + dev_printk(KERN_INFO, &pdev->dev, "PATA port found\n"); + } + } - probe_ent->port[2].scr_addr = base + 0x600; - probe_ent->port[3].scr_addr = base + 0x700; - break; - default: - BUG(); - break; + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n"); + return -ENOMEM; } + host->iomap = pcim_iomap_table(pdev); - pci_set_master(pdev); + for (i = 0; i < host->n_ports; i++) + pdc_ata_setup_port(host->ports[i], + base + 0x200 + i * 0x80, + base + 0x400 + i * 0x100); /* initialize adapter */ - pdc_host_init(board_idx, probe_ent); + pdc_host_init(host); - if (!ata_device_add(probe_ent)) - return -ENODEV; + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; - devm_kfree(&pdev->dev, probe_ent); - return 0; + /* start host, request IRQ and attach */ + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, pdc_interrupt, IRQF_SHARED, + &pdc_ata_sht); } diff --git a/drivers/ata/sata_qstor.c b/drivers/ata/sata_qstor.c index 6097d8f..f5a05de 100644 --- a/drivers/ata/sata_qstor.c +++ b/drivers/ata/sata_qstor.c @@ -39,7 +39,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_qstor" -#define DRV_VERSION "0.06" +#define DRV_VERSION "0.07" enum { QS_MMIO_BAR = 4, @@ -114,7 +114,6 @@ struct qs_port_priv { static u32 qs_scr_read (struct ata_port *ap, unsigned int sc_reg); static void qs_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static int qs_ata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t qs_intr (int irq, void *dev_instance); static int qs_port_start(struct ata_port *ap); static void qs_host_stop(struct ata_host *host); static void qs_phy_reset(struct ata_port *ap); @@ -158,7 +157,6 @@ static const struct ata_port_operations qs_ata_ops = { .qc_issue = qs_qc_issue, .data_xfer = ata_data_xfer, .eng_timeout = qs_eng_timeout, - .irq_handler = qs_intr, .irq_clear = qs_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -173,7 +171,6 @@ static const struct ata_port_operations qs_ata_ops = { static const struct ata_port_info qs_port_info[] = { /* board_2068_idx */ { - .sht = &qs_ata_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SATA_RESET | //FIXME ATA_FLAG_SRST | @@ -446,7 +443,7 @@ static inline unsigned int qs_intr_mmio(struct ata_host *host) if ((status & ATA_BUSY)) continue; DPRINTK("ata%u: protocol %d (dev_stat 0x%X)\n", - ap->id, qc->tf.protocol, status); + ap->print_id, qc->tf.protocol, status); /* complete taskfile transaction */ pp->state = qs_state_idle; @@ -530,16 +527,16 @@ static void qs_host_stop(struct ata_host *host) writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ } -static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) +static void qs_host_init(struct ata_host *host, unsigned int chip_id) { - void __iomem *mmio_base = pe->iomap[QS_MMIO_BAR]; + void __iomem *mmio_base = host->iomap[QS_MMIO_BAR]; unsigned int port_no; writeb(0, mmio_base + QS_HCT_CTRL); /* disable host interrupts */ writeb(QS_CNFG3_GSRST, mmio_base + QS_HCF_CNFG3); /* global reset */ /* reset each channel in turn */ - for (port_no = 0; port_no < pe->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { u8 __iomem *chan = mmio_base + (port_no * 0x4000); writeb(QS_CTR1_RDEV|QS_CTR1_RCHN, chan + QS_CCT_CTR1); writeb(QS_CTR0_REG, chan + QS_CCT_CTR0); @@ -547,7 +544,7 @@ static void qs_host_init(unsigned int chip_id, struct ata_probe_ent *pe) } writeb(QS_SERD3_PHY_ENA, mmio_base + QS_HVS_SERD3); /* enable phy */ - for (port_no = 0; port_no < pe->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { u8 __iomem *chan = mmio_base + (port_no * 0x4000); /* set FIFO depths to same settings as Windows driver */ writew(32, chan + QS_CFC_HUFT); @@ -607,14 +604,20 @@ static int qs_ata_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - void __iomem * const *iomap; unsigned int board_idx = (unsigned int) ent->driver_data; + const struct ata_port_info *ppi[] = { &qs_port_info[board_idx], NULL }; + struct ata_host *host; int rc, port_no; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* alloc host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, QS_PORTS); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -625,47 +628,24 @@ static int qs_ata_init_one(struct pci_dev *pdev, rc = pcim_iomap_regions(pdev, 1 << QS_MMIO_BAR, DRV_NAME); if (rc) return rc; - iomap = pcim_iomap_table(pdev); + host->iomap = pcim_iomap_table(pdev); - rc = qs_set_dma_masks(pdev, iomap[QS_MMIO_BAR]); + rc = qs_set_dma_masks(pdev, host->iomap[QS_MMIO_BAR]); if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = qs_port_info[board_idx].sht; - probe_ent->port_flags = qs_port_info[board_idx].flags; - probe_ent->pio_mask = qs_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = qs_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = qs_port_info[board_idx].udma_mask; - probe_ent->port_ops = qs_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = iomap; - probe_ent->n_ports = QS_PORTS; - - for (port_no = 0; port_no < probe_ent->n_ports; ++port_no) { + for (port_no = 0; port_no < host->n_ports; ++port_no) { void __iomem *chan = - probe_ent->iomap[QS_MMIO_BAR] + (port_no * 0x4000); - qs_ata_setup_port(&probe_ent->port[port_no], chan); + host->iomap[QS_MMIO_BAR] + (port_no * 0x4000); + qs_ata_setup_port(&host->ports[port_no]->ioaddr, chan); } - pci_set_master(pdev); - /* initialize adapter */ - qs_host_init(board_idx, probe_ent); + qs_host_init(host, board_idx); - if (ata_device_add(probe_ent) != QS_PORTS) - return -EIO; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, qs_intr, IRQF_SHARED, + &qs_ata_sht); } static int __init qs_ata_init(void) diff --git a/drivers/ata/sata_sil.c b/drivers/ata/sata_sil.c index dca3d37..0a1e417 100644 --- a/drivers/ata/sata_sil.c +++ b/drivers/ata/sata_sil.c @@ -46,7 +46,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_sil" -#define DRV_VERSION "2.0" +#define DRV_VERSION "2.2" enum { SIL_MMIO_BAR = 5, @@ -114,11 +114,10 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); #ifdef CONFIG_PM static int sil_pci_device_resume(struct pci_dev *pdev); #endif -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev); +static void sil_dev_config(struct ata_device *dev); static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg); static void sil_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); -static void sil_post_set_mode (struct ata_port *ap); -static irqreturn_t sil_interrupt(int irq, void *dev_instance); +static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed); static void sil_freeze(struct ata_port *ap); static void sil_thaw(struct ata_port *ap); @@ -183,8 +182,10 @@ static struct scsi_host_template sil_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static const struct ata_port_operations sil_ops = { @@ -195,7 +196,7 @@ static const struct ata_port_operations sil_ops = { .check_status = ata_check_status, .exec_command = ata_exec_command, .dev_select = ata_std_dev_select, - .post_set_mode = sil_post_set_mode, + .set_mode = sil_set_mode, .bmdma_setup = ata_bmdma_setup, .bmdma_start = ata_bmdma_start, .bmdma_stop = ata_bmdma_stop, @@ -207,7 +208,6 @@ static const struct ata_port_operations sil_ops = { .thaw = sil_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = sil_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -219,7 +219,6 @@ static const struct ata_port_operations sil_ops = { static const struct ata_port_info sil_port_info[] = { /* sil_3112 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -228,7 +227,6 @@ static const struct ata_port_info sil_port_info[] = { }, /* sil_3112_no_sata_irq */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_MOD15WRITE | SIL_FLAG_NO_SATA_IRQ, .pio_mask = 0x1f, /* pio0-4 */ @@ -238,7 +236,6 @@ static const struct ata_port_info sil_port_info[] = { }, /* sil_3512 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -247,7 +244,6 @@ static const struct ata_port_info sil_port_info[] = { }, /* sil_3114 */ { - .sht = &sil_sht, .flags = SIL_DFL_PORT_FLAGS | SIL_FLAG_RERR_ON_DMA_ACT, .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -295,7 +291,16 @@ static unsigned char sil_get_device_cache_line(struct pci_dev *pdev) return cache_line; } -static void sil_post_set_mode (struct ata_port *ap) +/** + * sil_set_mode - wrap set_mode functions + * @ap: port to set up + * @r_failed: returned device when we fail + * + * Wrap the libata method for device setup as after the setup we need + * to inspect the results and do some configuration work + */ + +static int sil_set_mode (struct ata_port *ap, struct ata_device **r_failed) { struct ata_host *host = ap->host; struct ata_device *dev; @@ -303,6 +308,11 @@ static void sil_post_set_mode (struct ata_port *ap) void __iomem *addr = mmio_base + sil_port[ap->port_no].xfer_mode; u32 tmp, dev_mode[2]; unsigned int i; + int rc; + + rc = ata_do_set_mode(ap, r_failed); + if (rc) + return rc; for (i = 0; i < 2; i++) { dev = &ap->device[i]; @@ -321,6 +331,7 @@ static void sil_post_set_mode (struct ata_port *ap) tmp |= (dev_mode[1] << 4); writel(tmp, addr); readl(addr); /* flush */ + return 0; } static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_reg) @@ -339,7 +350,7 @@ static inline void __iomem *sil_scr_addr(struct ata_port *ap, unsigned int sc_re break; } - return 0; + return NULL; } static u32 sil_scr_read (struct ata_port *ap, unsigned int sc_reg) @@ -386,9 +397,15 @@ static void sil_host_intr(struct ata_port *ap, u32 bmdma2) goto freeze; } - if (unlikely(!qc || qc->tf.ctl & ATA_NIEN)) + if (unlikely(!qc)) goto freeze; + if (unlikely(qc->tf.flags & ATA_TFLAG_POLLING)) { + /* this sometimes happens, just clear IRQ */ + ata_chk_status(ap); + return; + } + /* Check whether we are expecting interrupt in this state */ switch (ap->hsm_task_state) { case HSM_ST_FIRST: @@ -513,7 +530,6 @@ static void sil_thaw(struct ata_port *ap) /** * sil_dev_config - Apply device/host-specific errata fixups - * @ap: Port containing device to be examined * @dev: Device to be examined * * After the IDENTIFY [PACKET] DEVICE step is complete, and a @@ -540,8 +556,9 @@ static void sil_thaw(struct ata_port *ap) * appreciated. * - But then again UDMA5 is hardly anything to complain about */ -static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) +static void sil_dev_config(struct ata_device *dev) { + struct ata_port *ap = dev->ap; int print_info = ap->eh_context.i.flags & ATA_EHI_PRINTINFO; unsigned int n, quirks = 0; unsigned char model_num[ATA_ID_PROD_LEN + 1]; @@ -575,10 +592,10 @@ static void sil_dev_config(struct ata_port *ap, struct ata_device *dev) } } -static void sil_init_controller(struct pci_dev *pdev, - int n_ports, unsigned long port_flags, - void __iomem *mmio_base) +static void sil_init_controller(struct ata_host *host) { + struct pci_dev *pdev = to_pci_dev(host->dev); + void __iomem *mmio_base = host->iomap[SIL_MMIO_BAR]; u8 cls; u32 tmp; int i; @@ -588,7 +605,7 @@ static void sil_init_controller(struct pci_dev *pdev, if (cls) { cls >>= 3; cls++; /* cls = (line_size/8)+1 */ - for (i = 0; i < n_ports; i++) + for (i = 0; i < host->n_ports; i++) writew(cls << 8 | cls, mmio_base + sil_port[i].fifo_cfg); } else @@ -596,10 +613,10 @@ static void sil_init_controller(struct pci_dev *pdev, "cache line size not set. Driver may not function\n"); /* Apply R_ERR on DMA activate FIS errata workaround */ - if (port_flags & SIL_FLAG_RERR_ON_DMA_ACT) { + if (host->ports[0]->flags & SIL_FLAG_RERR_ON_DMA_ACT) { int cnt; - for (i = 0, cnt = 0; i < n_ports; i++) { + for (i = 0, cnt = 0; i < host->n_ports; i++) { tmp = readl(mmio_base + sil_port[i].sfis_cfg); if ((tmp & 0x3) != 0x01) continue; @@ -612,7 +629,7 @@ static void sil_init_controller(struct pci_dev *pdev, } } - if (n_ports == 4) { + if (host->n_ports == 4) { /* flip the magic "make 4 ports work" bit */ tmp = readl(mmio_base + sil_port[2].bmdma); if ((tmp & SIL_INTR_STEERING) == 0) @@ -624,15 +641,26 @@ static void sil_init_controller(struct pci_dev *pdev, static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; + int board_id = ent->driver_data; + const struct ata_port_info *ppi[] = { &sil_port_info[board_id], NULL }; + struct ata_host *host; void __iomem *mmio_base; - int rc; + int n_ports, rc; unsigned int i; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = 2; + if (board_id == sil_3114) + n_ports = 4; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) + return -ENOMEM; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -642,6 +670,7 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -650,45 +679,25 @@ static int sil_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; + mmio_base = host->iomap[SIL_MMIO_BAR]; - INIT_LIST_HEAD(&probe_ent->node); - probe_ent->dev = pci_dev_to_dev(pdev); - probe_ent->port_ops = sil_port_info[ent->driver_data].port_ops; - probe_ent->sht = sil_port_info[ent->driver_data].sht; - probe_ent->n_ports = (ent->driver_data == sil_3114) ? 4 : 2; - probe_ent->pio_mask = sil_port_info[ent->driver_data].pio_mask; - probe_ent->mwdma_mask = sil_port_info[ent->driver_data].mwdma_mask; - probe_ent->udma_mask = sil_port_info[ent->driver_data].udma_mask; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->port_flags = sil_port_info[ent->driver_data].flags; - - probe_ent->iomap = pcim_iomap_table(pdev); - - mmio_base = probe_ent->iomap[SIL_MMIO_BAR]; - - for (i = 0; i < probe_ent->n_ports; i++) { - probe_ent->port[i].cmd_addr = mmio_base + sil_port[i].tf; - probe_ent->port[i].altstatus_addr = - probe_ent->port[i].ctl_addr = mmio_base + sil_port[i].ctl; - probe_ent->port[i].bmdma_addr = mmio_base + sil_port[i].bmdma; - probe_ent->port[i].scr_addr = mmio_base + sil_port[i].scr; - ata_std_ports(&probe_ent->port[i]); + for (i = 0; i < host->n_ports; i++) { + struct ata_ioports *ioaddr = &host->ports[i]->ioaddr; + + ioaddr->cmd_addr = mmio_base + sil_port[i].tf; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = mmio_base + sil_port[i].ctl; + ioaddr->bmdma_addr = mmio_base + sil_port[i].bmdma; + ioaddr->scr_addr = mmio_base + sil_port[i].scr; + ata_std_ports(ioaddr); } - sil_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags, - mmio_base); + /* initialize and activate */ + sil_init_controller(host); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, sil_interrupt, IRQF_SHARED, + &sil_sht); } #ifdef CONFIG_PM @@ -701,8 +710,7 @@ static int sil_pci_device_resume(struct pci_dev *pdev) if (rc) return rc; - sil_init_controller(pdev, host->n_ports, host->ports[0]->flags, - host->iomap[SIL_MMIO_BAR]); + sil_init_controller(host); ata_host_resume(host); return 0; diff --git a/drivers/ata/sata_sil24.c b/drivers/ata/sata_sil24.c index e65e8d5..e6223ba 100644 --- a/drivers/ata/sata_sil24.c +++ b/drivers/ata/sata_sil24.c @@ -30,7 +30,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_sil24" -#define DRV_VERSION "0.3" +#define DRV_VERSION "0.8" /* * Port request block (PRB) 32 bytes @@ -323,7 +323,7 @@ struct sil24_port_priv { struct ata_taskfile tf; /* Cached taskfile registers */ }; -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev); +static void sil24_dev_config(struct ata_device *dev); static u8 sil24_check_status(struct ata_port *ap); static u32 sil24_scr_read(struct ata_port *ap, unsigned sc_reg); static void sil24_scr_write(struct ata_port *ap, unsigned sc_reg, u32 val); @@ -331,7 +331,6 @@ static void sil24_tf_read(struct ata_port *ap, struct ata_taskfile *tf); static void sil24_qc_prep(struct ata_queued_cmd *qc); static unsigned int sil24_qc_issue(struct ata_queued_cmd *qc); static void sil24_irq_clear(struct ata_port *ap); -static irqreturn_t sil24_interrupt(int irq, void *dev_instance); static void sil24_freeze(struct ata_port *ap); static void sil24_thaw(struct ata_port *ap); static void sil24_error_handler(struct ata_port *ap); @@ -346,6 +345,7 @@ static const struct pci_device_id sil24_pci_tbl[] = { { PCI_VDEVICE(CMD, 0x3124), BID_SIL3124 }, { PCI_VDEVICE(INTEL, 0x3124), BID_SIL3124 }, { PCI_VDEVICE(CMD, 0x3132), BID_SIL3132 }, + { PCI_VDEVICE(CMD, 0x0242), BID_SIL3132 }, { PCI_VDEVICE(CMD, 0x3131), BID_SIL3131 }, { PCI_VDEVICE(CMD, 0x3531), BID_SIL3131 }, @@ -380,8 +380,10 @@ static struct scsi_host_template sil24_sht = { .slave_configure = ata_scsi_slave_config, .slave_destroy = ata_scsi_slave_destroy, .bios_param = ata_std_bios_param, +#ifdef CONFIG_PM .suspend = ata_scsi_device_suspend, .resume = ata_scsi_device_resume, +#endif }; static const struct ata_port_operations sil24_ops = { @@ -398,7 +400,6 @@ static const struct ata_port_operations sil24_ops = { .qc_prep = sil24_qc_prep, .qc_issue = sil24_qc_issue, - .irq_handler = sil24_interrupt, .irq_clear = sil24_irq_clear, .irq_on = ata_dummy_irq_on, .irq_ack = ata_dummy_irq_ack, @@ -421,10 +422,9 @@ static const struct ata_port_operations sil24_ops = { #define SIL24_NPORTS2FLAG(nports) ((((unsigned)(nports) - 1) & 0x3) << 30) #define SIL24_FLAG2NPORTS(flag) ((((flag) >> 30) & 0x3) + 1) -static struct ata_port_info sil24_port_info[] = { +static const struct ata_port_info sil24_port_info[] = { /* sil_3124 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(4) | SIL24_FLAG_PCIX_IRQ_WOC, .pio_mask = 0x1f, /* pio0-4 */ @@ -434,7 +434,6 @@ static struct ata_port_info sil24_port_info[] = { }, /* sil_3132 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(2), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -443,7 +442,6 @@ static struct ata_port_info sil24_port_info[] = { }, /* sil_3131/sil_3531 */ { - .sht = &sil24_sht, .flags = SIL24_COMMON_FLAGS | SIL24_NPORTS2FLAG(1), .pio_mask = 0x1f, /* pio0-4 */ .mwdma_mask = 0x07, /* mwdma0-2 */ @@ -459,9 +457,9 @@ static int sil24_tag(int tag) return tag; } -static void sil24_dev_config(struct ata_port *ap, struct ata_device *dev) +static void sil24_dev_config(struct ata_device *dev) { - void __iomem *port = ap->ioaddr.cmd_addr; + void __iomem *port = dev->ap->ioaddr.cmd_addr; if (dev->cdb_len == 16) writel(PORT_CS_CDB16, port + PORT_CTRL_STAT); @@ -647,7 +645,6 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc, struct sil24_sge *sge) { struct scatterlist *sg; - unsigned int idx = 0; ata_for_each_sg(sg, qc) { sge->addr = cpu_to_le64(sg_dma_address(sg)); @@ -656,9 +653,7 @@ static inline void sil24_fill_sg(struct ata_queued_cmd *qc, sge->flags = cpu_to_le32(SGE_TRM); else sge->flags = 0; - sge++; - idx++; } } @@ -924,11 +919,8 @@ static void sil24_post_internal_cmd(struct ata_queued_cmd *qc) { struct ata_port *ap = qc->ap; - if (qc->flags & ATA_QCFLAG_FAILED) - qc->err_mask |= AC_ERR_OTHER; - /* make DMA engine forget about the failed command */ - if (qc->err_mask) + if (qc->flags & ATA_QCFLAG_FAILED) sil24_init_port(ap); } @@ -964,11 +956,10 @@ static int sil24_port_start(struct ata_port *ap) return 0; } -static void sil24_init_controller(struct pci_dev *pdev, int n_ports, - unsigned long port_flags, - void __iomem *host_base, - void __iomem *port_base) +static void sil24_init_controller(struct ata_host *host) { + void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; + void __iomem *port_base = host->iomap[SIL24_PORT_BAR]; u32 tmp; int i; @@ -979,7 +970,7 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports, writel(0, host_base + HOST_CTRL); /* init ports */ - for (i = 0; i < n_ports; i++) { + for (i = 0; i < host->n_ports; i++) { void __iomem *port = port_base + i * PORT_REGS_SIZE; /* Initial PHY setting */ @@ -993,12 +984,12 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports, PORT_CS_PORT_RST, PORT_CS_PORT_RST, 10, 100); if (tmp & PORT_CS_PORT_RST) - dev_printk(KERN_ERR, &pdev->dev, + dev_printk(KERN_ERR, host->dev, "failed to clear port RST\n"); } /* Configure IRQ WoC */ - if (port_flags & SIL24_FLAG_PCIX_IRQ_WOC) + if (host->ports[0]->flags & SIL24_FLAG_PCIX_IRQ_WOC) writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_STAT); else writel(PORT_CS_IRQ_WOC, port + PORT_CTRL_CLR); @@ -1026,18 +1017,17 @@ static void sil24_init_controller(struct pci_dev *pdev, int n_ports, static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version = 0; - struct device *dev = &pdev->dev; - unsigned int board_id = (unsigned int)ent->driver_data; - struct ata_port_info *pinfo = &sil24_port_info[board_id]; - struct ata_probe_ent *probe_ent; - void __iomem *host_base; - void __iomem *port_base; + struct ata_port_info pi = sil24_port_info[ent->driver_data]; + const struct ata_port_info *ppi[] = { &pi, NULL }; + void __iomem * const *iomap; + struct ata_host *host; int i, rc; u32 tmp; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* acquire resources */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1047,33 +1037,36 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) DRV_NAME); if (rc) return rc; + iomap = pcim_iomap_table(pdev); - /* allocate & init probe_ent */ - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return -ENOMEM; + /* apply workaround for completion IRQ loss on PCI-X errata */ + if (pi.flags & SIL24_FLAG_PCIX_IRQ_WOC) { + tmp = readl(iomap[SIL24_HOST_BAR] + HOST_CTRL); + if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) + dev_printk(KERN_INFO, &pdev->dev, + "Applying completion IRQ loss on PCI-X " + "errata fix\n"); + else + pi.flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; + } - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); + /* allocate and fill host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, + SIL24_FLAG2NPORTS(ppi[0]->flags)); + if (!host) + return -ENOMEM; + host->iomap = iomap; - probe_ent->sht = pinfo->sht; - probe_ent->port_flags = pinfo->flags; - probe_ent->pio_mask = pinfo->pio_mask; - probe_ent->mwdma_mask = pinfo->mwdma_mask; - probe_ent->udma_mask = pinfo->udma_mask; - probe_ent->port_ops = pinfo->port_ops; - probe_ent->n_ports = SIL24_FLAG2NPORTS(pinfo->flags); + for (i = 0; i < host->n_ports; i++) { + void __iomem *port = iomap[SIL24_PORT_BAR] + i * PORT_REGS_SIZE; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); + host->ports[i]->ioaddr.cmd_addr = port; + host->ports[i]->ioaddr.scr_addr = port + PORT_SCONTROL; - host_base = probe_ent->iomap[SIL24_HOST_BAR]; - port_base = probe_ent->iomap[SIL24_PORT_BAR]; + ata_std_ports(&host->ports[i]->ioaddr); + } - /* - * Configure the device - */ + /* configure and activate the device */ if (!pci_set_dma_mask(pdev, DMA_64BIT_MASK)) { rc = pci_set_consistent_dma_mask(pdev, DMA_64BIT_MASK); if (rc) { @@ -1099,36 +1092,11 @@ static int sil24_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) } } - /* Apply workaround for completion IRQ loss on PCI-X errata */ - if (probe_ent->port_flags & SIL24_FLAG_PCIX_IRQ_WOC) { - tmp = readl(host_base + HOST_CTRL); - if (tmp & (HOST_CTRL_TRDY | HOST_CTRL_STOP | HOST_CTRL_DEVSEL)) - dev_printk(KERN_INFO, &pdev->dev, - "Applying completion IRQ loss on PCI-X " - "errata fix\n"); - else - probe_ent->port_flags &= ~SIL24_FLAG_PCIX_IRQ_WOC; - } - - for (i = 0; i < probe_ent->n_ports; i++) { - void __iomem *port = port_base + i * PORT_REGS_SIZE; - - probe_ent->port[i].cmd_addr = port; - probe_ent->port[i].scr_addr = port + PORT_SCONTROL; - - ata_std_ports(&probe_ent->port[i]); - } - - sil24_init_controller(pdev, probe_ent->n_ports, probe_ent->port_flags, - host_base, port_base); + sil24_init_controller(host); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, sil24_interrupt, IRQF_SHARED, + &sil24_sht); } #ifdef CONFIG_PM @@ -1136,7 +1104,6 @@ static int sil24_pci_device_resume(struct pci_dev *pdev) { struct ata_host *host = dev_get_drvdata(&pdev->dev); void __iomem *host_base = host->iomap[SIL24_HOST_BAR]; - void __iomem *port_base = host->iomap[SIL24_PORT_BAR]; int rc; rc = ata_pci_device_do_resume(pdev); @@ -1146,8 +1113,7 @@ static int sil24_pci_device_resume(struct pci_dev *pdev) if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) writel(HOST_CTRL_GLOBAL_RST, host_base + HOST_CTRL); - sil24_init_controller(pdev, host->n_ports, host->ports[0]->flags, - host_base, port_base); + sil24_init_controller(host); ata_host_resume(host); diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c index 49c9e2b..d8ee062 100644 --- a/drivers/ata/sata_sis.c +++ b/drivers/ata/sata_sis.c @@ -40,9 +40,8 @@ #include <linux/device.h> #include <scsi/scsi_host.h> #include <linux/libata.h> -#include "libata.h" +#include "sis.h" -#undef DRV_NAME /* already defined in libata.h, for libata-core */ #define DRV_NAME "sata_sis" #define DRV_VERSION "0.7" @@ -122,7 +121,6 @@ static const struct ata_port_operations sis_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -132,7 +130,6 @@ static const struct ata_port_operations sis_ops = { }; static struct ata_port_info sis_port_info = { - .sht = &sis_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, .mwdma_mask = 0x7, @@ -257,12 +254,13 @@ static void sis_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent = NULL; - int rc; + struct ata_port_info pi = sis_port_info; + const struct ata_port_info *ppi[2] = { &pi, &pi }; + struct ata_host *host; u32 genctl, val; - struct ata_port_info pi = sis_port_info, *ppi[2] = { &pi, &pi }; u8 pmr; u8 port2_start = 0x20; + int rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); @@ -271,19 +269,6 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - /* check and see if the SCRs are in IO space or PCI cfg space */ pci_read_config_dword(pdev, SIS_GENCTL, &genctl); if ((genctl & GENCTL_IOMAPPED_SCR) == 0) @@ -310,7 +295,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) case 0x10: ppi[1] = &sis_info133; break; - + case 0x30: ppi[0] = &sis_info133; break; @@ -350,30 +335,26 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) break; } - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; - if (!(probe_ent->port_flags & SIS_FLAG_CFGSCR)) { - void *mmio; + if (!(pi.flags & SIS_FLAG_CFGSCR)) { + void __iomem *mmio; - mmio = pcim_iomap(pdev, SIS_SCR_PCI_BAR, 0); - if (!mmio) - return -ENOMEM; + rc = pcim_iomap_regions(pdev, 1 << SIS_SCR_PCI_BAR, DRV_NAME); + if (rc) + return rc; + mmio = host->iomap[SIS_SCR_PCI_BAR]; - probe_ent->port[0].scr_addr = mmio; - probe_ent->port[1].scr_addr = mmio + port2_start; + host->ports[0]->ioaddr.scr_addr = mmio; + host->ports[1]->ioaddr.scr_addr = mmio + port2_start; } pci_set_master(pdev); pci_intx(pdev, 1); - - if (!ata_device_add(probe_ent)) - return -EIO; - - devm_kfree(&pdev->dev, probe_ent); - return 0; - + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &sis_sht); } static int __init sis_init(void) diff --git a/drivers/ata/sata_svw.c b/drivers/ata/sata_svw.c index 4e42899..cc07aac 100644 --- a/drivers/ata/sata_svw.c +++ b/drivers/ata/sata_svw.c @@ -53,10 +53,12 @@ #endif /* CONFIG_PPC_OF */ #define DRV_NAME "sata_svw" -#define DRV_VERSION "2.0" +#define DRV_VERSION "2.1" enum { - K2_FLAG_NO_ATAPI_DMA = (1 << 29), + /* ap->flags bits */ + K2_FLAG_SATA_8_PORTS = (1 << 24), + K2_FLAG_NO_ATAPI_DMA = (1 << 25), /* Taskfile registers offsets */ K2_SATA_TF_CMD_OFFSET = 0x00, @@ -90,17 +92,6 @@ enum { board_svw8 = 1, }; -static const struct k2_board_info { - unsigned int n_ports; - unsigned long port_flags; -} k2_board_info[] = { - /* board_svw4 */ - { 4, K2_FLAG_NO_ATAPI_DMA }, - - /* board_svw8 */ - { 8, K2_FLAG_NO_ATAPI_DMA }, -}; - static u8 k2_stat_check_status(struct ata_port *ap); @@ -354,7 +345,6 @@ static const struct ata_port_operations k2_sata_ops = { .thaw = ata_bmdma_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -363,6 +353,28 @@ static const struct ata_port_operations k2_sata_ops = { .port_start = ata_port_start, }; +static const struct ata_port_info k2_port_info[] = { + /* board_svw4 */ + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &k2_sata_ops, + }, + /* board_svw8 */ + { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO | K2_FLAG_NO_ATAPI_DMA | + K2_FLAG_SATA_8_PORTS, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &k2_sata_ops, + }, +}; + static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base) { port->cmd_addr = base + K2_SATA_TF_CMD_OFFSET; @@ -386,17 +398,24 @@ static void k2_sata_setup_port(struct ata_ioports *port, void __iomem *base) static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct device *dev = &pdev->dev; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &k2_port_info[ent->driver_data], NULL }; + struct ata_host *host; void __iomem *mmio_base; - const struct k2_board_info *board_info = - &k2_board_info[ent->driver_data]; - int rc; - int i; + int n_ports, i, rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + n_ports = 4; + if (ppi[0]->flags & K2_FLAG_SATA_8_PORTS) + n_ports = 8; + + host = ata_host_alloc_pinfo(&pdev->dev, ppi, n_ports); + if (!host) + return -ENOMEM; + /* * If this driver happens to only be useful on Apple's K2, then * we should check that here as it has a normal Serverworks ID @@ -404,6 +423,7 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e rc = pcim_enable_device(pdev); if (rc) return rc; + /* * Check if we have resources mapped at all (second function may * have been disabled by firmware) @@ -417,6 +437,15 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + mmio_base = host->iomap[5]; + + /* different controllers have different number of ports - currently 4 or 8 */ + /* All ports are on the same function. Multi-function device is no + * longer available. This should not be seen in any system. */ + for (i = 0; i < host->n_ports; i++) + k2_sata_setup_port(&host->ports[i]->ioaddr, + mmio_base + i * K2_SATA_PORT_OFFSET); rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) @@ -425,38 +454,6 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e if (rc) return rc; - probe_ent = devm_kzalloc(dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - probe_ent->sht = &k2_sata_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO | board_info->port_flags; - probe_ent->port_ops = &k2_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x7; - probe_ent->udma_mask = 0x7f; - - mmio_base = probe_ent->iomap[5]; - - /* different controllers have different number of ports - currently 4 or 8 */ - /* All ports are on the same function. Multi-function device is no - * longer available. This should not be seen in any system. */ - for (i = 0; i < board_info->n_ports; i++) - k2_sata_setup_port(&probe_ent->port[i], - mmio_base + i * K2_SATA_PORT_OFFSET); - /* Clear a magic bit in SCR1 according to Darwin, those help * some funky seagate drives (though so far, those were already * set by the firmware on the machines I had access to) @@ -469,12 +466,8 @@ static int k2_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *e writel(0x0, mmio_base + K2_SATA_SIM_OFFSET); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &k2_sata_sht); } /* 0x240 is device ID for Apple K2 device diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 0ebd77b..3a4f445 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -44,7 +44,7 @@ #include "sata_promise.h" #define DRV_NAME "sata_sx4" -#define DRV_VERSION "0.9" +#define DRV_VERSION "0.10" enum { @@ -151,24 +151,23 @@ struct pdc_host_priv { static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static irqreturn_t pdc20621_interrupt (int irq, void *dev_instance); static void pdc_eng_timeout(struct ata_port *ap); static void pdc_20621_phy_reset (struct ata_port *ap); static int pdc_port_start(struct ata_port *ap); static void pdc20621_qc_prep(struct ata_queued_cmd *qc); static void pdc_tf_load_mmio(struct ata_port *ap, const struct ata_taskfile *tf); static void pdc_exec_command_mmio(struct ata_port *ap, const struct ata_taskfile *tf); -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe); -static int pdc20621_detect_dimm(struct ata_probe_ent *pe); -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, +static unsigned int pdc20621_dimm_init(struct ata_host *host); +static int pdc20621_detect_dimm(struct ata_host *host); +static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device, u32 subaddr, u32 *pdata); -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe); -static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe); +static int pdc20621_prog_dimm0(struct ata_host *host); +static unsigned int pdc20621_prog_dimm_global(struct ata_host *host); #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, +static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, u32 offset, u32 size); #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, +static void pdc20621_put_to_dimm(struct ata_host *host, void *psource, u32 offset, u32 size); static void pdc20621_irq_clear(struct ata_port *ap); static unsigned int pdc20621_qc_issue_prot(struct ata_queued_cmd *qc); @@ -204,7 +203,6 @@ static const struct ata_port_operations pdc_20621_ops = { .qc_issue = pdc20621_qc_issue_prot, .data_xfer = ata_data_xfer, .eng_timeout = pdc_eng_timeout, - .irq_handler = pdc20621_interrupt, .irq_clear = pdc20621_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -214,7 +212,6 @@ static const struct ata_port_operations pdc_20621_ops = { static const struct ata_port_info pdc_port_info[] = { /* board_20621 */ { - .sht = &pdc_sata_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST | ATA_FLAG_MMIO | ATA_FLAG_NO_ATAPI | ATA_FLAG_PIO_POLLING, @@ -421,7 +418,7 @@ static void pdc20621_dma_prep(struct ata_queued_cmd *qc) WARN_ON(!(qc->flags & ATA_QCFLAG_DMAMAP)); - VPRINTK("ata%u: ENTER\n", ap->id); + VPRINTK("ata%u: ENTER\n", ap->print_id); /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -478,7 +475,7 @@ static void pdc20621_nodata_prep(struct ata_queued_cmd *qc) unsigned int portno = ap->port_no; unsigned int i; - VPRINTK("ata%u: ENTER\n", ap->id); + VPRINTK("ata%u: ENTER\n", ap->print_id); /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -605,7 +602,7 @@ static void pdc20621_packet_start(struct ata_queued_cmd *qc) /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; - VPRINTK("ata%u: ENTER\n", ap->id); + VPRINTK("ata%u: ENTER\n", ap->print_id); wmb(); /* flush PRD, pkt writes */ @@ -672,7 +669,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, /* step two - DMA from DIMM to host */ if (doing_hdma) { - VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->id, + VPRINTK("ata%u: read hdma, 0x%x 0x%x\n", ap->print_id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* get drive status; clear intr; complete txn */ qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); @@ -683,7 +680,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, /* step one - exec ATA command */ else { u8 seq = (u8) (port_no + 1 + 4); - VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->id, + VPRINTK("ata%u: read ata, 0x%x 0x%x\n", ap->print_id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* submit hdma pkt */ @@ -698,7 +695,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, /* step one - DMA from host to DIMM */ if (doing_hdma) { u8 seq = (u8) (port_no + 1); - VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->id, + VPRINTK("ata%u: write hdma, 0x%x 0x%x\n", ap->print_id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* submit ata pkt */ @@ -711,7 +708,7 @@ static inline unsigned int pdc20621_host_intr( struct ata_port *ap, /* step two - execute ATA command */ else { - VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->id, + VPRINTK("ata%u: write ata, 0x%x 0x%x\n", ap->print_id, readl(mmio + 0x104), readl(mmio + PDC_HDMA_CTLSTAT)); /* get drive status; clear intr; complete txn */ qc->err_mask |= ac_err_mask(ata_wait_idle(ap)); @@ -882,15 +879,15 @@ static void pdc_sata_setup_port(struct ata_ioports *port, void __iomem *base) #ifdef ATA_VERBOSE_DEBUG -static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_get_from_dimm(struct ata_host *host, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -937,15 +934,15 @@ static void pdc20621_get_from_dimm(struct ata_probe_ent *pe, void *psource, #endif -static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, +static void pdc20621_put_to_dimm(struct ata_host *host, void *psource, u32 offset, u32 size) { u32 window_size; u16 idx; u8 page_mask; long dist; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; - void __iomem *dimm_mmio = pe->iomap[PDC_DIMM_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; + void __iomem *dimm_mmio = host->iomap[PDC_DIMM_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -987,10 +984,10 @@ static void pdc20621_put_to_dimm(struct ata_probe_ent *pe, void *psource, } -static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, +static unsigned int pdc20621_i2c_read(struct ata_host *host, u32 device, u32 subaddr, u32 *pdata) { - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; u32 i2creg = 0; u32 status; u32 count =0; @@ -1023,17 +1020,17 @@ static unsigned int pdc20621_i2c_read(struct ata_probe_ent *pe, u32 device, } -static int pdc20621_detect_dimm(struct ata_probe_ent *pe) +static int pdc20621_detect_dimm(struct ata_host *host) { u32 data=0 ; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_SYSTEM_FREQ, &data)) { if (data == 100) return 100; } else return 0; - if (pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { + if (pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, 9, &data)) { if(data <= 0x75) return 133; } else @@ -1043,13 +1040,13 @@ static int pdc20621_detect_dimm(struct ata_probe_ent *pe) } -static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) +static int pdc20621_prog_dimm0(struct ata_host *host) { u32 spd0[50]; u32 data = 0; int size, i; u8 bdimmsize; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; static const struct { unsigned int reg; unsigned int ofs; @@ -1072,7 +1069,7 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) mmio += PDC_CHIP0_OFS; for(i=0; i<ARRAY_SIZE(pdc_i2c_read_data); i++) - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, pdc_i2c_read_data[i].reg, &spd0[pdc_i2c_read_data[i].ofs]); @@ -1108,11 +1105,11 @@ static int pdc20621_prog_dimm0(struct ata_probe_ent *pe) } -static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) +static unsigned int pdc20621_prog_dimm_global(struct ata_host *host) { u32 data, spd0; int error, i; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1129,7 +1126,7 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) readl(mmio + PDC_SDRAM_CONTROL_OFFSET); /* Turn on for ECC */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { data |= (0x01 << 16); @@ -1156,7 +1153,7 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_probe_ent *pe) } -static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) +static unsigned int pdc20621_dimm_init(struct ata_host *host) { int speed, size, length; u32 addr,spd0,pci_status; @@ -1166,7 +1163,7 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) u32 ticks=0; u32 clock=0; u32 fparam=0; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1225,18 +1222,18 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) Read SPD of DIMM by I2C interface, and program the DIMM Module Controller. */ - if (!(speed = pdc20621_detect_dimm(pe))) { + if (!(speed = pdc20621_detect_dimm(host))) { printk(KERN_ERR "Detect Local DIMM Fail\n"); return 1; /* DIMM error */ } VPRINTK("Local DIMM Speed = %d\n", speed); /* Programming DIMM0 Module Control Register (index_CID0:80h) */ - size = pdc20621_prog_dimm0(pe); + size = pdc20621_prog_dimm0(host); VPRINTK("Local DIMM Size = %dMB\n",size); /* Programming DIMM Module Global Control Register (index_CID0:88h) */ - if (pdc20621_prog_dimm_global(pe)) { + if (pdc20621_prog_dimm_global(host)) { printk(KERN_ERR "Programming DIMM Module Global Control Register Fail\n"); return 1; } @@ -1249,20 +1246,20 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) '9','8','0','3','1','6','1','2',0,0}; u8 test_parttern2[40] = {0}; - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x10040, 40); - pdc20621_put_to_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x10040, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern2, 0x40, 40); - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x10040, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x10040, 40); + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x10040, + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x10040, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); - pdc20621_put_to_dimm(pe, (void *) test_parttern1, 0x40, 40); - pdc20621_get_from_dimm(pe, (void *) test_parttern2, 0x40, 40); + pdc20621_put_to_dimm(host, (void *) test_parttern1, 0x40, 40); + pdc20621_get_from_dimm(host, (void *) test_parttern2, 0x40, 40); printk(KERN_ERR "%x, %x, %s\n", test_parttern2[0], test_parttern2[1], &(test_parttern2[2])); } @@ -1270,14 +1267,14 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) /* ECC initiliazation. */ - pdc20621_i2c_read(pe, PDC_DIMM0_SPD_DEV_ADDRESS, + pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE, &spd0); if (spd0 == 0x02) { VPRINTK("Start ECC initialization\n"); addr = 0; length = size * 1024 * 1024; while (addr < length) { - pdc20621_put_to_dimm(pe, (void *) &tmp, addr, + pdc20621_put_to_dimm(host, (void *) &tmp, addr, sizeof(u32)); addr += sizeof(u32); } @@ -1287,10 +1284,10 @@ static unsigned int pdc20621_dimm_init(struct ata_probe_ent *pe) } -static void pdc_20621_init(struct ata_probe_ent *pe) +static void pdc_20621_init(struct ata_host *host) { u32 tmp; - void __iomem *mmio = pe->iomap[PDC_MMIO_BAR]; + void __iomem *mmio = host->iomap[PDC_MMIO_BAR]; /* hard-code chip #0 */ mmio += PDC_CHIP0_OFS; @@ -1321,15 +1318,25 @@ static void pdc_20621_init(struct ata_probe_ent *pe) static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; + const struct ata_port_info *ppi[] = + { &pdc_port_info[ent->driver_data], NULL }; + struct ata_host *host; void __iomem *base; struct pdc_host_priv *hpriv; - unsigned int board_idx = (unsigned int) ent->driver_data; int rc; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); + hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); + if (!host || !hpriv) + return -ENOMEM; + + host->private_data = hpriv; + + /* acquire resources and fill host */ rc = pcim_enable_device(pdev); if (rc) return rc; @@ -1340,7 +1347,15 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + + base = host->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS; + pdc_sata_setup_port(&host->ports[0]->ioaddr, base + 0x200); + pdc_sata_setup_port(&host->ports[1]->ioaddr, base + 0x280); + pdc_sata_setup_port(&host->ports[2]->ioaddr, base + 0x300); + pdc_sata_setup_port(&host->ports[3]->ioaddr, base + 0x380); + /* configure and activate */ rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); if (rc) return rc; @@ -1348,50 +1363,13 @@ static int pdc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id * if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) + if (pdc20621_dimm_init(host)) return -ENOMEM; - - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); - if (!hpriv) - return -ENOMEM; - - probe_ent->sht = pdc_port_info[board_idx].sht; - probe_ent->port_flags = pdc_port_info[board_idx].flags; - probe_ent->pio_mask = pdc_port_info[board_idx].pio_mask; - probe_ent->mwdma_mask = pdc_port_info[board_idx].mwdma_mask; - probe_ent->udma_mask = pdc_port_info[board_idx].udma_mask; - probe_ent->port_ops = pdc_port_info[board_idx].port_ops; - - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->iomap = pcim_iomap_table(pdev); - - probe_ent->private_data = hpriv; - base = probe_ent->iomap[PDC_MMIO_BAR] + PDC_CHIP0_OFS; - - probe_ent->n_ports = 4; - pdc_sata_setup_port(&probe_ent->port[0], base + 0x200); - pdc_sata_setup_port(&probe_ent->port[1], base + 0x280); - pdc_sata_setup_port(&probe_ent->port[2], base + 0x300); - pdc_sata_setup_port(&probe_ent->port[3], base + 0x380); + pdc_20621_init(host); pci_set_master(pdev); - - /* initialize adapter */ - /* initialize local dimm */ - if (pdc20621_dimm_init(probe_ent)) - return -ENOMEM; - pdc_20621_init(probe_ent); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, pdc20621_interrupt, + IRQF_SHARED, &pdc_sata_sht); } diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c index 80131ee..f74e383 100644 --- a/drivers/ata/sata_uli.c +++ b/drivers/ata/sata_uli.c @@ -36,7 +36,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_uli" -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.1" enum { uli_5289 = 0, @@ -115,7 +115,6 @@ static const struct ata_port_operations uli_ops = { .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -127,7 +126,6 @@ static const struct ata_port_operations uli_ops = { }; static struct ata_port_info uli_port_info = { - .sht = &uli_sht, .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | ATA_FLAG_IGN_SIMPLEX, .pio_mask = 0x1f, /* pio0-4 */ @@ -185,12 +183,13 @@ static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val) static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { static int printed_version; - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi[2]; - int rc; + const struct ata_port_info *ppi[] = { &uli_port_info, NULL }; unsigned int board_idx = (unsigned int) ent->driver_data; + struct ata_host *host; struct uli_priv *hpriv; void __iomem * const *iomap; + struct ata_ioports *ioaddr; + int n_ports, rc; if (!printed_version++) dev_printk(KERN_INFO, &pdev->dev, "version " DRV_VERSION "\n"); @@ -199,54 +198,42 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + n_ports = 2; + if (board_idx == uli_5287) + n_ports = 4; + rc = ata_pci_prepare_native_host(pdev, ppi, n_ports, &host); if (rc) return rc; - ppi[0] = ppi[1] = &uli_port_info; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return -ENOMEM; - hpriv = devm_kzalloc(&pdev->dev, sizeof(*hpriv), GFP_KERNEL); if (!hpriv) return -ENOMEM; + host->private_data = hpriv; - probe_ent->private_data = hpriv; - - iomap = pcim_iomap_table(pdev); + iomap = host->iomap; switch (board_idx) { case uli_5287: hpriv->scr_cfg_addr[0] = ULI5287_BASE; hpriv->scr_cfg_addr[1] = ULI5287_BASE + ULI5287_OFFS; - probe_ent->n_ports = 4; - probe_ent->port[2].cmd_addr = iomap[0] + 8; - probe_ent->port[2].altstatus_addr = - probe_ent->port[2].ctl_addr = (void __iomem *) + ioaddr = &host->ports[2]->ioaddr; + ioaddr->cmd_addr = iomap[0] + 8; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)iomap[1] | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[2].bmdma_addr = iomap[4] + 16; + ioaddr->bmdma_addr = iomap[4] + 16; hpriv->scr_cfg_addr[2] = ULI5287_BASE + ULI5287_OFFS*4; + ata_std_ports(ioaddr); - probe_ent->port[3].cmd_addr = iomap[2] + 8; - probe_ent->port[3].altstatus_addr = - probe_ent->port[3].ctl_addr = (void __iomem *) + ioaddr = &host->ports[3]->ioaddr; + ioaddr->cmd_addr = iomap[2] + 8; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)iomap[3] | ATA_PCI_CTL_OFS) + 4; - probe_ent->port[3].bmdma_addr = iomap[4] + 24; + ioaddr->bmdma_addr = iomap[4] + 24; hpriv->scr_cfg_addr[3] = ULI5287_BASE + ULI5287_OFFS*5; - - ata_std_ports(&probe_ent->port[2]); - ata_std_ports(&probe_ent->port[3]); + ata_std_ports(ioaddr); break; case uli_5289: @@ -266,12 +253,8 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) pci_set_master(pdev); pci_intx(pdev, 1); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &uli_sht); } static int __init uli_init(void) diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c index baca6d7..1d855f5 100644 --- a/drivers/ata/sata_via.c +++ b/drivers/ata/sata_via.c @@ -46,7 +46,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_via" -#define DRV_VERSION "2.0" +#define DRV_VERSION "2.1" enum board_ids_enum { vt6420, @@ -60,12 +60,10 @@ enum { SATA_PATA_SHARING = 0x49, /* PATA/SATA sharing func ctrl */ PATA_UDMA_TIMING = 0xB3, /* PATA timing for DMA/ cable detect */ PATA_PIO_TIMING = 0xAB, /* PATA timing register */ - + PORT0 = (1 << 1), PORT1 = (1 << 0), ALL_PORTS = PORT0 | PORT1, - PATA_PORT = 2, /* PATA is port 2 */ - N_PORTS = 3, NATIVE_MODE_ALL = (1 << 7) | (1 << 6) | (1 << 5) | (1 << 4), @@ -78,11 +76,9 @@ static u32 svia_scr_read (struct ata_port *ap, unsigned int sc_reg); static void svia_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val); static void svia_noop_freeze(struct ata_port *ap); static void vt6420_error_handler(struct ata_port *ap); -static void vt6421_sata_error_handler(struct ata_port *ap); -static void vt6421_pata_error_handler(struct ata_port *ap); +static int vt6421_pata_cable_detect(struct ata_port *ap); static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev); static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev); -static int vt6421_port_start(struct ata_port *ap); static const struct pci_device_id svia_pci_tbl[] = { { PCI_VDEVICE(VIA, 0x5337), vt6420 }, @@ -141,7 +137,6 @@ static const struct ata_port_operations vt6420_sata_ops = { .error_handler = vt6420_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -151,7 +146,7 @@ static const struct ata_port_operations vt6420_sata_ops = { static const struct ata_port_operations vt6421_pata_ops = { .port_disable = ata_port_disable, - + .set_piomode = vt6421_set_pio_mode, .set_dmamode = vt6421_set_dma_mode, @@ -172,20 +167,20 @@ static const struct ata_port_operations vt6421_pata_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = vt6421_pata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = vt6421_pata_cable_detect, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, - .port_start = vt6421_port_start, + .port_start = ata_port_start, }; static const struct ata_port_operations vt6421_sata_ops = { .port_disable = ata_port_disable, - + .tf_load = ata_tf_load, .tf_read = ata_tf_read, .check_status = ata_check_status, @@ -203,10 +198,10 @@ static const struct ata_port_operations vt6421_sata_ops = { .freeze = ata_bmdma_freeze, .thaw = ata_bmdma_thaw, - .error_handler = vt6421_sata_error_handler, + .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, + .cable_detect = ata_cable_sata, - .irq_handler = ata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -214,11 +209,10 @@ static const struct ata_port_operations vt6421_sata_ops = { .scr_read = svia_scr_read, .scr_write = svia_scr_write, - .port_start = vt6421_port_start, + .port_start = ata_port_start, }; -static struct ata_port_info vt6420_port_info = { - .sht = &svia_sht, +static const struct ata_port_info vt6420_port_info = { .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, .pio_mask = 0x1f, .mwdma_mask = 0x07, @@ -226,6 +220,22 @@ static struct ata_port_info vt6420_port_info = { .port_ops = &vt6420_sata_ops, }; +static struct ata_port_info vt6421_sport_info = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &vt6421_sata_ops, +}; + +static struct ata_port_info vt6421_pport_info = { + .flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY, + .pio_mask = 0x1f, + .mwdma_mask = 0, + .udma_mask = 0x7f, + .port_ops = &vt6421_pata_ops, +}; + MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("SCSI low-level driver for VIA SATA controllers"); MODULE_LICENSE("GPL"); @@ -330,35 +340,15 @@ static void vt6420_error_handler(struct ata_port *ap) NULL, ata_std_postreset); } -static int vt6421_pata_prereset(struct ata_port *ap) +static int vt6421_pata_cable_detect(struct ata_port *ap) { struct pci_dev *pdev = to_pci_dev(ap->host->dev); u8 tmp; pci_read_config_byte(pdev, PATA_UDMA_TIMING, &tmp); if (tmp & 0x10) - ap->cbl = ATA_CBL_PATA40; - else - ap->cbl = ATA_CBL_PATA80; - return 0; -} - -static void vt6421_pata_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, vt6421_pata_prereset, ata_std_softreset, - NULL, ata_std_postreset); -} - -static int vt6421_sata_prereset(struct ata_port *ap) -{ - ap->cbl = ATA_CBL_SATA; - return 0; -} - -static void vt6421_sata_error_handler(struct ata_port *ap) -{ - return ata_bmdma_drive_eh(ap, vt6421_sata_prereset, ata_std_softreset, - NULL, ata_std_postreset); + return ATA_CBL_PATA40; + return ATA_CBL_PATA80; } static void vt6421_set_pio_mode(struct ata_port *ap, struct ata_device *adev) @@ -375,16 +365,6 @@ static void vt6421_set_dma_mode(struct ata_port *ap, struct ata_device *adev) pci_write_config_byte(pdev, PATA_UDMA_TIMING, udma_bits[adev->pio_mode - XFER_UDMA_0]); } -static int vt6421_port_start(struct ata_port *ap) -{ - if (ap->port_no == PATA_PORT) { - ap->ops = &vt6421_pata_ops; - ap->mwdma_mask = 0; - ap->flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_LEGACY | ATA_FLAG_SRST; - } - return ata_port_start(ap); -} - static const unsigned int svia_bar_sizes[] = { 8, 4, 8, 4, 16, 256 }; @@ -403,67 +383,78 @@ static void __iomem * vt6421_scr_addr(void __iomem *addr, unsigned int port) return addr + (port * 64); } -static void vt6421_init_addrs(struct ata_probe_ent *probe_ent, - void __iomem * const *iomap, unsigned int port) +static void vt6421_init_addrs(struct ata_port *ap) { - void __iomem *reg_addr = iomap[port]; - void __iomem *bmdma_addr = iomap[4] + (port * 8); - - probe_ent->port[port].cmd_addr = reg_addr; - probe_ent->port[port].altstatus_addr = - probe_ent->port[port].ctl_addr = (void __iomem *) + void __iomem * const * iomap = ap->host->iomap; + void __iomem *reg_addr = iomap[ap->port_no]; + void __iomem *bmdma_addr = iomap[4] + (ap->port_no * 8); + struct ata_ioports *ioaddr = &ap->ioaddr; + + ioaddr->cmd_addr = reg_addr; + ioaddr->altstatus_addr = + ioaddr->ctl_addr = (void __iomem *) ((unsigned long)(reg_addr + 8) | ATA_PCI_CTL_OFS); - probe_ent->port[port].bmdma_addr = bmdma_addr; - probe_ent->port[port].scr_addr = vt6421_scr_addr(iomap[5], port); + ioaddr->bmdma_addr = bmdma_addr; + ioaddr->scr_addr = vt6421_scr_addr(iomap[5], ap->port_no); - ata_std_ports(&probe_ent->port[port]); + ata_std_ports(ioaddr); } -static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev) +static int vt6420_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) { - struct ata_probe_ent *probe_ent; - struct ata_port_info *ppi[2]; - void __iomem * const *iomap; + const struct ata_port_info *ppi[] = { &vt6420_port_info, NULL }; + struct ata_host *host; + int rc; - ppi[0] = ppi[1] = &vt6420_port_info; - probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY); - if (!probe_ent) - return NULL; + rc = ata_pci_prepare_native_host(pdev, ppi, 2, &host); + if (rc) + return rc; + *r_host = host; - iomap = pcim_iomap_table(pdev); - probe_ent->port[0].scr_addr = svia_scr_addr(iomap[5], 0); - probe_ent->port[1].scr_addr = svia_scr_addr(iomap[5], 1); + rc = pcim_iomap_regions(pdev, 1 << 5, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, "failed to iomap PCI BAR 5\n"); + return rc; + } + + host->ports[0]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 0); + host->ports[1]->ioaddr.scr_addr = svia_scr_addr(host->iomap[5], 1); - return probe_ent; + return 0; } -static struct ata_probe_ent *vt6421_init_probe_ent(struct pci_dev *pdev) +static int vt6421_prepare_host(struct pci_dev *pdev, struct ata_host **r_host) { - struct ata_probe_ent *probe_ent; - unsigned int i; - - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (!probe_ent) - return NULL; + const struct ata_port_info *ppi[] = + { &vt6421_sport_info, &vt6421_sport_info, &vt6421_pport_info }; + struct ata_host *host; + int i, rc; + + *r_host = host = ata_host_alloc_pinfo(&pdev->dev, ppi, ARRAY_SIZE(ppi)); + if (!host) { + dev_printk(KERN_ERR, &pdev->dev, "failed to allocate host\n"); + return -ENOMEM; + } - memset(probe_ent, 0, sizeof(*probe_ent)); - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); + rc = pcim_iomap_regions(pdev, 0x1f, DRV_NAME); + if (rc) { + dev_printk(KERN_ERR, &pdev->dev, "failed to request/iomap " + "PCI BARs (errno=%d)\n", rc); + return rc; + } + host->iomap = pcim_iomap_table(pdev); - probe_ent->sht = &svia_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY; - probe_ent->port_ops = &vt6421_sata_ops; - probe_ent->n_ports = N_PORTS; - probe_ent->irq = pdev->irq; - probe_ent->irq_flags = IRQF_SHARED; - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; + for (i = 0; i < host->n_ports; i++) + vt6421_init_addrs(host->ports[i]); - for (i = 0; i < N_PORTS; i++) - vt6421_init_addrs(probe_ent, pcim_iomap_table(pdev), i); + rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; + rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); + if (rc) + return rc; - return probe_ent; + return 0; } static void svia_configure(struct pci_dev *pdev) @@ -510,7 +501,7 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) static int printed_version; unsigned int i; int rc; - struct ata_probe_ent *probe_ent; + struct ata_host *host; int board_id = (int) ent->driver_data; const int *bar_sizes; u8 tmp8; @@ -522,12 +513,6 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) return rc; - rc = pcim_iomap_regions(pdev, 0x1f, DRV_NAME); - if (rc) { - pcim_pin_device(pdev); - return rc; - } - if (board_id == vt6420) { pci_read_config_byte(pdev, SATA_PATA_SHARING, &tmp8); if (tmp8 & SATA_2DEV) { @@ -553,32 +538,18 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) return -ENODEV; } - rc = pci_set_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK); - if (rc) - return rc; - if (board_id == vt6420) - probe_ent = vt6420_init_probe_ent(pdev); + rc = vt6420_prepare_host(pdev, &host); else - probe_ent = vt6421_init_probe_ent(pdev); - - if (!probe_ent) { - dev_printk(KERN_ERR, &pdev->dev, "out of memory\n"); - return -ENOMEM; - } + rc = vt6421_prepare_host(pdev, &host); + if (rc) + return rc; svia_configure(pdev); pci_set_master(pdev); - - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + return ata_host_activate(host, pdev->irq, ata_interrupt, IRQF_SHARED, + &svia_sht); } static int __init svia_init(void) diff --git a/drivers/ata/sata_vsc.c b/drivers/ata/sata_vsc.c index 2fd037b..80126f8 100644 --- a/drivers/ata/sata_vsc.c +++ b/drivers/ata/sata_vsc.c @@ -47,7 +47,7 @@ #include <linux/libata.h> #define DRV_NAME "sata_vsc" -#define DRV_VERSION "2.0" +#define DRV_VERSION "2.1" enum { VSC_MMIO_BAR = 0, @@ -98,10 +98,6 @@ enum { VSC_SATA_INT_PHY_CHANGE), }; -#define is_vsc_sata_int_err(port_idx, int_status) \ - (int_status & (VSC_SATA_INT_ERROR << (8 * port_idx))) - - static u32 vsc_sata_scr_read (struct ata_port *ap, unsigned int sc_reg) { if (sc_reg > SCR_CONTROL) @@ -119,6 +115,28 @@ static void vsc_sata_scr_write (struct ata_port *ap, unsigned int sc_reg, } +static void vsc_freeze(struct ata_port *ap) +{ + void __iomem *mask_addr; + + mask_addr = ap->host->iomap[VSC_MMIO_BAR] + + VSC_SATA_INT_MASK_OFFSET + ap->port_no; + + writeb(0, mask_addr); +} + + +static void vsc_thaw(struct ata_port *ap) +{ + void __iomem *mask_addr; + + mask_addr = ap->host->iomap[VSC_MMIO_BAR] + + VSC_SATA_INT_MASK_OFFSET + ap->port_no; + + writeb(0xff, mask_addr); +} + + static void vsc_intr_mask_update(struct ata_port *ap, u8 ctl) { void __iomem *mask_addr; @@ -203,6 +221,36 @@ static void vsc_sata_tf_read(struct ata_port *ap, struct ata_taskfile *tf) } } +static inline void vsc_error_intr(u8 port_status, struct ata_port *ap) +{ + if (port_status & (VSC_SATA_INT_PHY_CHANGE | VSC_SATA_INT_ERROR_M)) + ata_port_freeze(ap); + else + ata_port_abort(ap); +} + +static void vsc_port_intr(u8 port_status, struct ata_port *ap) +{ + struct ata_queued_cmd *qc; + int handled = 0; + + if (unlikely(port_status & VSC_SATA_INT_ERROR)) { + vsc_error_intr(port_status, ap); + return; + } + + qc = ata_qc_from_tag(ap, ap->active_tag); + if (qc && likely(!(qc->tf.flags & ATA_TFLAG_POLLING))) + handled = ata_host_intr(ap, qc); + + /* We received an interrupt during a polled command, + * or some other spurious condition. Interrupt reporting + * with this hardware is fairly reliable so it is safe to + * simply clear the interrupt + */ + if (unlikely(!handled)) + ata_chk_status(ap); +} /* * vsc_sata_interrupt @@ -214,59 +262,36 @@ static irqreturn_t vsc_sata_interrupt (int irq, void *dev_instance) struct ata_host *host = dev_instance; unsigned int i; unsigned int handled = 0; - u32 int_status; - - spin_lock(&host->lock); + u32 status; - int_status = readl(host->iomap[VSC_MMIO_BAR] + - VSC_SATA_INT_STAT_OFFSET); + status = readl(host->iomap[VSC_MMIO_BAR] + VSC_SATA_INT_STAT_OFFSET); - for (i = 0; i < host->n_ports; i++) { - if (int_status & ((u32) 0xFF << (8 * i))) { - struct ata_port *ap; + if (unlikely(status == 0xffffffff || status == 0)) { + if (status) + dev_printk(KERN_ERR, host->dev, + ": IRQ status == 0xffffffff, " + "PCI fault or device removal?\n"); + goto out; + } - ap = host->ports[i]; + spin_lock(&host->lock); - if (is_vsc_sata_int_err(i, int_status)) { - u32 err_status; - printk(KERN_DEBUG "%s: ignoring interrupt(s)\n", __FUNCTION__); - err_status = ap ? vsc_sata_scr_read(ap, SCR_ERROR) : 0; - vsc_sata_scr_write(ap, SCR_ERROR, err_status); - handled++; - } + for (i = 0; i < host->n_ports; i++) { + u8 port_status = (status >> (8 * i)) & 0xff; + if (port_status) { + struct ata_port *ap = host->ports[i]; if (ap && !(ap->flags & ATA_FLAG_DISABLED)) { - struct ata_queued_cmd *qc; - - qc = ata_qc_from_tag(ap, ap->active_tag); - if (qc && (!(qc->tf.flags & ATA_TFLAG_POLLING))) - handled += ata_host_intr(ap, qc); - else if (is_vsc_sata_int_err(i, int_status)) { - /* - * On some chips (i.e. Intel 31244), an error - * interrupt will sneak in at initialization - * time (phy state changes). Clearing the SCR - * error register is not required, but it prevents - * the phy state change interrupts from recurring - * later. - */ - u32 err_status; - err_status = vsc_sata_scr_read(ap, SCR_ERROR); - printk(KERN_DEBUG "%s: clearing interrupt, " - "status %x; sata err status %x\n", - __FUNCTION__, - int_status, err_status); - vsc_sata_scr_write(ap, SCR_ERROR, err_status); - /* Clear interrupt status */ - ata_chk_status(ap); - handled++; - } - } + vsc_port_intr(port_status, ap); + handled++; + } else + dev_printk(KERN_ERR, host->dev, + ": interrupt from disabled port %d\n", i); } } spin_unlock(&host->lock); - +out: return IRQ_RETVAL(handled); } @@ -304,11 +329,10 @@ static const struct ata_port_operations vsc_sata_ops = { .qc_prep = ata_qc_prep, .qc_issue = ata_qc_issue_prot, .data_xfer = ata_data_xfer, - .freeze = ata_bmdma_freeze, - .thaw = ata_bmdma_thaw, + .freeze = vsc_freeze, + .thaw = vsc_thaw, .error_handler = ata_bmdma_error_handler, .post_internal_cmd = ata_bmdma_post_internal_cmd, - .irq_handler = vsc_sata_interrupt, .irq_clear = ata_bmdma_irq_clear, .irq_on = ata_irq_on, .irq_ack = ata_irq_ack, @@ -342,30 +366,50 @@ static void __devinit vsc_sata_setup_port(struct ata_ioports *port, static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static const struct ata_port_info pi = { + .flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | + ATA_FLAG_MMIO, + .pio_mask = 0x1f, + .mwdma_mask = 0x07, + .udma_mask = 0x7f, + .port_ops = &vsc_sata_ops, + }; + const struct ata_port_info *ppi[] = { &pi, NULL }; static int printed_version; - struct ata_probe_ent *probe_ent; + struct ata_host *host; void __iomem *mmio_base; - int rc; + int i, rc; u8 cls; if (!printed_version++) dev_printk(KERN_DEBUG, &pdev->dev, "version " DRV_VERSION "\n"); + /* allocate host */ + host = ata_host_alloc_pinfo(&pdev->dev, ppi, 4); + if (!host) + return -ENOMEM; + rc = pcim_enable_device(pdev); if (rc) return rc; - /* - * Check if we have needed resource mapped. - */ + /* check if we have needed resource mapped */ if (pci_resource_len(pdev, 0) == 0) return -ENODEV; + /* map IO regions and intialize host accordingly */ rc = pcim_iomap_regions(pdev, 1 << VSC_MMIO_BAR, DRV_NAME); if (rc == -EBUSY) pcim_pin_device(pdev); if (rc) return rc; + host->iomap = pcim_iomap_table(pdev); + + mmio_base = host->iomap[VSC_MMIO_BAR]; + + for (i = 0; i < host->n_ports; i++) + vsc_sata_setup_port(&host->ports[i]->ioaddr, + mmio_base + (i + 1) * VSC_SATA_PORT_OFFSET); /* * Use 32 bit DMA mask, because 64 bit address support is poor. @@ -377,12 +421,6 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d if (rc) return rc; - probe_ent = devm_kzalloc(&pdev->dev, sizeof(*probe_ent), GFP_KERNEL); - if (probe_ent == NULL) - return -ENOMEM; - probe_ent->dev = pci_dev_to_dev(pdev); - INIT_LIST_HEAD(&probe_ent->node); - /* * Due to a bug in the chip, the default cache line size can't be * used (unless the default is non-zero). @@ -393,33 +431,6 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d if (pci_enable_msi(pdev) == 0) pci_intx(pdev, 0); - else - probe_ent->irq_flags = IRQF_SHARED; - - probe_ent->sht = &vsc_sata_sht; - probe_ent->port_flags = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY | - ATA_FLAG_MMIO; - probe_ent->port_ops = &vsc_sata_ops; - probe_ent->n_ports = 4; - probe_ent->irq = pdev->irq; - probe_ent->iomap = pcim_iomap_table(pdev); - - /* We don't care much about the PIO/UDMA masks, but the core won't like us - * if we don't fill these - */ - probe_ent->pio_mask = 0x1f; - probe_ent->mwdma_mask = 0x07; - probe_ent->udma_mask = 0x7f; - - mmio_base = probe_ent->iomap[VSC_MMIO_BAR]; - - /* We have 4 ports per PCI function */ - vsc_sata_setup_port(&probe_ent->port[0], mmio_base + 1 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[1], mmio_base + 2 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[2], mmio_base + 3 * VSC_SATA_PORT_OFFSET); - vsc_sata_setup_port(&probe_ent->port[3], mmio_base + 4 * VSC_SATA_PORT_OFFSET); - - pci_set_master(pdev); /* * Config offset 0x98 is "Extended Control and Status Register 0" @@ -429,11 +440,9 @@ static int __devinit vsc_sata_init_one (struct pci_dev *pdev, const struct pci_d */ pci_write_config_dword(pdev, 0x98, 0); - if (!ata_device_add(probe_ent)) - return -ENODEV; - - devm_kfree(&pdev->dev, probe_ent); - return 0; + pci_set_master(pdev); + return ata_host_activate(host, pdev->irq, vsc_sata_interrupt, + IRQF_SHARED, &vsc_sata_sht); } static const struct pci_device_id vsc_sata_pci_tbl[] = { diff --git a/drivers/ata/sis.h b/drivers/ata/sis.h new file mode 100644 index 0000000..231da8f --- /dev/null +++ b/drivers/ata/sis.h @@ -0,0 +1,5 @@ + +struct ata_port_info; + +/* pata_sis.c */ +extern struct ata_port_info sis_info133; diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c index 3c372e0..59651ab 100644 --- a/drivers/atm/ambassador.c +++ b/drivers/atm/ambassador.c @@ -821,7 +821,7 @@ static inline void fill_rx_pool (amb_dev * dev, unsigned char pool, } // cast needed as there is no %? for pointer differences PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li", - skb, skb->head, (long) (skb->end - skb->head)); + skb, skb->head, (long) (skb_end_pointer(skb) - skb->head)); rx.handle = virt_to_bus (skb); rx.host_address = cpu_to_be32 (virt_to_bus (skb->data)); if (rx_give (dev, &rx, pool)) diff --git a/drivers/atm/atmtcp.c b/drivers/atm/atmtcp.c index fc518d8..02ad83d 100644 --- a/drivers/atm/atmtcp.c +++ b/drivers/atm/atmtcp.c @@ -221,7 +221,7 @@ static int atmtcp_v_send(struct atm_vcc *vcc,struct sk_buff *skb) hdr->vpi = htons(vcc->vpi); hdr->vci = htons(vcc->vci); hdr->length = htonl(skb->len); - memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); + skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); if (vcc->pop) vcc->pop(vcc,skb); else dev_kfree_skb(skb); out_vcc->push(out_vcc,new_skb); @@ -310,7 +310,7 @@ static int atmtcp_c_send(struct atm_vcc *vcc,struct sk_buff *skb) goto done; } __net_timestamp(new_skb); - memcpy(skb_put(new_skb,skb->len),skb->data,skb->len); + skb_copy_from_linear_data(skb, skb_put(new_skb, skb->len), skb->len); out_vcc->push(out_vcc,new_skb); atomic_inc(&vcc->stats->tx); atomic_inc(&out_vcc->stats->rx); @@ -352,7 +352,7 @@ static struct atm_dev atmtcp_control_dev = { .ops = &atmtcp_c_dev_ops, .type = "atmtcp", .number = 999, - .lock = SPIN_LOCK_UNLOCKED + .lock = __SPIN_LOCK_UNLOCKED(atmtcp_control_dev.lock) }; diff --git a/drivers/atm/eni.c b/drivers/atm/eni.c index 8fccf01..0d3a38b 100644 --- a/drivers/atm/eni.c +++ b/drivers/atm/eni.c @@ -536,7 +536,7 @@ static int rx_aal0(struct atm_vcc *vcc) return 0; } skb_put(skb,length); - skb_set_timestamp(skb, &eni_vcc->timestamp); + skb->tstamp = eni_vcc->timestamp; DPRINTK("got len %ld\n",length); if (do_rx_dma(vcc,skb,1,length >> 2,length >> 2)) return 1; eni_vcc->rxing++; @@ -701,7 +701,7 @@ static void get_service(struct atm_dev *dev) DPRINTK("Grr, servicing VCC %ld twice\n",vci); continue; } - do_gettimeofday(&ENI_VCC(vcc)->timestamp); + ENI_VCC(vcc)->timestamp = ktime_get_real(); ENI_VCC(vcc)->next = NULL; if (vcc->qos.rxtp.traffic_class == ATM_CBR) { if (eni_dev->fast) diff --git a/drivers/atm/eni.h b/drivers/atm/eni.h index 385090c..d04fefb 100644 --- a/drivers/atm/eni.h +++ b/drivers/atm/eni.h @@ -59,7 +59,7 @@ struct eni_vcc { int rxing; /* number of pending PDUs */ int servicing; /* number of waiting VCs (0 or 1) */ int txing; /* number of pending TX bytes */ - struct timeval timestamp; /* for RX timing */ + ktime_t timestamp; /* for RX timing */ struct atm_vcc *next; /* next pending RX */ struct sk_buff *last; /* last PDU being DMAed (used to carry discard information) */ diff --git a/drivers/atm/fore200e.c b/drivers/atm/fore200e.c index a7c0ed3..405ee5e 100644 --- a/drivers/atm/fore200e.c +++ b/drivers/atm/fore200e.c @@ -1,6 +1,4 @@ /* - $Id: fore200e.c,v 1.5 2000/04/14 10:10:34 davem Exp $ - A FORE Systems 200E-series driver for ATM on Linux. Christophe Lizzi (lizzi@cnam.fr), October 1999-March 2003. @@ -1502,9 +1500,9 @@ fore200e_open(struct atm_vcc *vcc) /* pseudo-CBR bandwidth requested? */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); if (fore200e->available_cell_rate < vcc->qos.txtp.max_pcr) { - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); kfree(fore200e_vcc); vc_map->vcc = NULL; @@ -1513,7 +1511,7 @@ fore200e_open(struct atm_vcc *vcc) /* reserve bandwidth */ fore200e->available_cell_rate -= vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); } vcc->itf = vcc->dev->number; @@ -1599,9 +1597,9 @@ fore200e_close(struct atm_vcc* vcc) /* release reserved bandwidth, if any */ if ((vcc->qos.txtp.traffic_class == ATM_CBR) && (vcc->qos.txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); clear_bit(ATM_VF_HASQOS, &vcc->flags); } @@ -2064,16 +2062,16 @@ fore200e_change_qos(struct atm_vcc* vcc,struct atm_qos* qos, int flags) if ((qos->txtp.traffic_class == ATM_CBR) && (qos->txtp.max_pcr > 0)) { - down(&fore200e->rate_sf); + mutex_lock(&fore200e->rate_mtx); if (fore200e->available_cell_rate + vcc->qos.txtp.max_pcr < qos->txtp.max_pcr) { - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); return -EAGAIN; } fore200e->available_cell_rate += vcc->qos.txtp.max_pcr; fore200e->available_cell_rate -= qos->txtp.max_pcr; - up(&fore200e->rate_sf); + mutex_unlock(&fore200e->rate_mtx); memcpy(&vcc->qos, qos, sizeof(struct atm_qos)); @@ -2459,7 +2457,7 @@ fore200e_initialize(struct fore200e* fore200e) DPRINTK(2, "device %s being initialized\n", fore200e->name); - init_MUTEX(&fore200e->rate_sf); + mutex_init(&fore200e->rate_mtx); spin_lock_init(&fore200e->q_lock); cpq = fore200e->cp_queues = fore200e->virt_base + FORE200E_CP_QUEUES_OFFSET; diff --git a/drivers/atm/fore200e.h b/drivers/atm/fore200e.h index f9abfda..b85a546 100644 --- a/drivers/atm/fore200e.h +++ b/drivers/atm/fore200e.h @@ -869,7 +869,7 @@ typedef struct fore200e { struct stats* stats; /* last snapshot of the stats */ - struct semaphore rate_sf; /* protects rate reservation ops */ + struct mutex rate_mtx; /* protects rate reservation ops */ spinlock_t q_lock; /* protects queue ops */ #ifdef FORE200E_USE_TASKLET struct tasklet_struct tx_tasklet; /* performs tx interrupt work */ diff --git a/drivers/atm/he.c b/drivers/atm/he.c index db33f6f..d33aba6 100644 --- a/drivers/atm/he.c +++ b/drivers/atm/he.c @@ -1901,13 +1901,13 @@ he_service_rbrq(struct he_dev *he_dev, int group) case ATM_AAL0: /* 2.10.1.5 raw cell receive */ skb->len = ATM_AAL0_SDU; - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); break; case ATM_AAL5: /* 2.10.1.2 aal5 receive */ skb->len = AAL5_LEN(skb->data, he_vcc->pdu_len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); #ifdef USE_CHECKSUM_HW if (vcc->vpi == 0 && vcc->vci >= ATM_NOT_RSV_VCI) { skb->ip_summed = CHECKSUM_COMPLETE; @@ -3017,7 +3017,7 @@ read_prom_byte(struct he_dev *he_dev, int addr) he_writel(he_dev, val, HOST_CNTL); /* Send READ instruction */ - for (i = 0; i < sizeof(readtab)/sizeof(readtab[0]); i++) { + for (i = 0; i < ARRAY_SIZE(readtab); i++) { he_writel(he_dev, val | readtab[i], HOST_CNTL); udelay(EEPROM_DELAY); } diff --git a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c index f407861..057efbc 100644 --- a/drivers/atm/idt77252.c +++ b/drivers/atm/idt77252.c @@ -388,7 +388,7 @@ idt77252_eeprom_read_status(struct idt77252_dev *card) gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - for (i = 0; i < sizeof(rdsrtab)/sizeof(rdsrtab[0]); i++) { + for (i = 0; i < ARRAY_SIZE(rdsrtab); i++) { idt77252_write_gp(card, gp | rdsrtab[i]); udelay(5); } @@ -422,7 +422,7 @@ idt77252_eeprom_read_byte(struct idt77252_dev *card, u8 offset) gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - for (i = 0; i < sizeof(rdtab)/sizeof(rdtab[0]); i++) { + for (i = 0; i < ARRAY_SIZE(rdtab); i++) { idt77252_write_gp(card, gp | rdtab[i]); udelay(5); } @@ -469,14 +469,14 @@ idt77252_eeprom_write_byte(struct idt77252_dev *card, u8 offset, u8 data) gp = idt77252_read_gp(card) & ~(SAR_GP_EESCLK|SAR_GP_EECS|SAR_GP_EEDO); - for (i = 0; i < sizeof(wrentab)/sizeof(wrentab[0]); i++) { + for (i = 0; i < ARRAY_SIZE(wrentab); i++) { idt77252_write_gp(card, gp | wrentab[i]); udelay(5); } idt77252_write_gp(card, gp | SAR_GP_EECS); udelay(5); - for (i = 0; i < sizeof(wrtab)/sizeof(wrtab[0]); i++) { + for (i = 0; i < ARRAY_SIZE(wrtab); i++) { idt77252_write_gp(card, gp | wrtab[i]); udelay(5); } @@ -1065,7 +1065,8 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) vcc = vc->rx_vcc; pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); if ((vcc->qos.aal == ATM_AAL0) || (vcc->qos.aal == ATM_AAL34)) { @@ -1194,7 +1195,8 @@ dequeue_rx(struct idt77252_dev *card, struct rsq_entry *rsqe) } pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); sb_pool_remove(card, skb); skb_trim(skb, len); @@ -1267,7 +1269,7 @@ idt77252_rx_raw(struct idt77252_dev *card) tail = readl(SAR_REG_RAWCT); pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue), - queue->end - queue->head - 16, + skb_end_pointer(queue) - queue->head - 16, PCI_DMA_FROMDEVICE); while (head != tail) { @@ -1363,7 +1365,8 @@ drop: queue = card->raw_cell_head; pci_dma_sync_single_for_cpu(card->pcidev, IDT77252_PRV_PADDR(queue), - queue->end - queue->data, + (skb_end_pointer(queue) - + queue->data), PCI_DMA_FROMDEVICE); } else { card->raw_cell_head = NULL; @@ -1816,7 +1819,8 @@ push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue) u32 handle; u32 addr; - skb->data = skb->tail = skb->head; + skb->data = skb->head; + skb_reset_tail_pointer(skb); skb->len = 0; skb_reserve(skb, 16); @@ -1835,7 +1839,6 @@ push_rx_skb(struct idt77252_dev *card, struct sk_buff *skb, int queue) skb_put(skb, SAR_FB_SIZE_3); break; default: - dev_kfree_skb(skb); return -1; } @@ -1874,7 +1877,7 @@ add_rx_skb(struct idt77252_dev *card, int queue, } paddr = pci_map_single(card->pcidev, skb->data, - skb->end - skb->data, + skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE); IDT77252_PRV_PADDR(skb) = paddr; @@ -1888,7 +1891,7 @@ add_rx_skb(struct idt77252_dev *card, int queue, outunmap: pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, PCI_DMA_FROMDEVICE); handle = IDT77252_PRV_POOL(skb); card->sbpool[POOL_QUEUE(handle)].skb[POOL_INDEX(handle)] = NULL; @@ -1905,12 +1908,14 @@ recycle_rx_skb(struct idt77252_dev *card, struct sk_buff *skb) int err; pci_dma_sync_single_for_device(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); err = push_rx_skb(card, skb, POOL_QUEUE(handle)); if (err) { pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, PCI_DMA_FROMDEVICE); + skb_end_pointer(skb) - skb->data, + PCI_DMA_FROMDEVICE); sb_pool_remove(card, skb); dev_kfree_skb(skb); } @@ -3122,7 +3127,8 @@ deinit_card(struct idt77252_dev *card) if (skb) { pci_unmap_single(card->pcidev, IDT77252_PRV_PADDR(skb), - skb->end - skb->data, + (skb_end_pointer(skb) - + skb->data), PCI_DMA_FROMDEVICE); card->sbpool[i].skb[j] = NULL; dev_kfree_skb(skb); diff --git a/drivers/atm/nicstar.c b/drivers/atm/nicstar.c index aab9b37..14ced85 100644 --- a/drivers/atm/nicstar.c +++ b/drivers/atm/nicstar.c @@ -2208,7 +2208,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) if (i == 1 && ns_rsqe_eopdu(rsqe)) *((u32 *) sb->data) |= 0x00000002; skb_put(sb, NS_AAL0_HEADER); - memcpy(sb->tail, cell, ATM_CELL_PAYLOAD); + memcpy(skb_tail_pointer(sb), cell, ATM_CELL_PAYLOAD); skb_put(sb, ATM_CELL_PAYLOAD); ATM_SKB(sb)->vcc = vcc; __net_timestamp(sb); @@ -2252,7 +2252,8 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) vc->rx_iov = iovb; NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; - iovb->tail = iovb->data = iovb->head; + iovb->data = iovb->head; + skb_reset_tail_pointer(iovb); NS_SKB(iovb)->vcc = vcc; /* IMPORTANT: a pointer to the sk_buff containing the small or large buffer is stored as iovec base, NOT a pointer to the @@ -2265,7 +2266,8 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) recycle_iovec_rx_bufs(card, (struct iovec *) iovb->data, NS_MAX_IOVECS); NS_SKB(iovb)->iovcnt = 0; iovb->len = 0; - iovb->tail = iovb->data = iovb->head; + iovb->data = iovb->head; + skb_reset_tail_pointer(iovb); NS_SKB(iovb)->vcc = vcc; } iov = &((struct iovec *) iovb->data)[NS_SKB(iovb)->iovcnt++]; @@ -2393,7 +2395,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) skb->destructor = ns_lb_destructor; #endif /* NS_USE_DESTRUCTORS */ skb_push(skb, NS_SMBUFSIZE); - memcpy(skb->data, sb->data, NS_SMBUFSIZE); + skb_copy_from_linear_data(sb, skb->data, NS_SMBUFSIZE); skb_put(skb, len - NS_SMBUFSIZE); ATM_SKB(skb)->vcc = vcc; __net_timestamp(skb); @@ -2477,7 +2479,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) { /* Copy the small buffer to the huge buffer */ sb = (struct sk_buff *) iov->iov_base; - memcpy(hb->data, sb->data, iov->iov_len); + skb_copy_from_linear_data(sb, hb->data, iov->iov_len); skb_put(hb, iov->iov_len); remaining = len - iov->iov_len; iov++; @@ -2489,7 +2491,7 @@ static void dequeue_rx(ns_dev *card, ns_rsqe *rsqe) { lb = (struct sk_buff *) iov->iov_base; tocopy = min_t(int, remaining, iov->iov_len); - memcpy(hb->tail, lb->data, tocopy); + skb_copy_from_linear_data(lb, skb_tail_pointer(hb), tocopy); skb_put(hb, tocopy); iov++; remaining -= tocopy; diff --git a/drivers/atm/nicstarmac.c b/drivers/atm/nicstarmac.c index 2c5e3ae..480947f 100644 --- a/drivers/atm/nicstarmac.c +++ b/drivers/atm/nicstarmac.c @@ -7,6 +7,8 @@ * Read this ForeRunner's MAC address from eprom/eeprom */ +#include <linux/kernel.h> + typedef void __iomem *virt_addr_t; #define CYCLE_DELAY 5 @@ -176,7 +178,7 @@ read_eprom_byte(virt_addr_t base, u_int8_t offset) val = NICSTAR_REG_READ( base, NICSTAR_REG_GENERAL_PURPOSE ) & 0xFFFFFFF0; /* Send READ instruction */ - for (i=0; i<sizeof readtab/sizeof readtab[0]; i++) + for (i=0; i<ARRAY_SIZE(readtab); i++) { NICSTAR_REG_WRITE( base, NICSTAR_REG_GENERAL_PURPOSE, (val | readtab[i]) ); diff --git a/drivers/atm/zatm.c b/drivers/atm/zatm.c index 0d7091e..2ad2527 100644 --- a/drivers/atm/zatm.c +++ b/drivers/atm/zatm.c @@ -1177,7 +1177,7 @@ static void __devinit eprom_get_esi(struct atm_dev *dev) /*--------------------------------- entries ---------------------------------*/ -static int __init zatm_init(struct atm_dev *dev) +static int __devinit zatm_init(struct atm_dev *dev) { struct zatm_dev *zatm_dev; struct pci_dev *pci_dev; @@ -1256,7 +1256,7 @@ static int __init zatm_init(struct atm_dev *dev) } -static int __init zatm_start(struct atm_dev *dev) +static int __devinit zatm_start(struct atm_dev *dev) { struct zatm_dev *zatm_dev = ZATM_DEV(dev); struct pci_dev *pdev = zatm_dev->pci_dev; diff --git a/drivers/auxdisplay/cfag12864b.c b/drivers/auxdisplay/cfag12864b.c index 889583d..cb44cb4 100644 --- a/drivers/auxdisplay/cfag12864b.c +++ b/drivers/auxdisplay/cfag12864b.c @@ -312,6 +312,17 @@ EXPORT_SYMBOL_GPL(cfag12864b_disable); EXPORT_SYMBOL_GPL(cfag12864b_isenabled); /* + * Is the module inited? + */ + +static unsigned char cfag12864b_inited; +unsigned char cfag12864b_isinited(void) +{ + return cfag12864b_inited; +} +EXPORT_SYMBOL_GPL(cfag12864b_isinited); + +/* * Module Init & Exit */ @@ -319,6 +330,13 @@ static int __init cfag12864b_init(void) { int ret = -EINVAL; + /* ks0108_init() must be called first */ + if (!ks0108_isinited()) { + printk(KERN_ERR CFAG12864B_NAME ": ERROR: " + "ks0108 is not initialized\n"); + goto none; + } + if (PAGE_SIZE < CFAG12864B_SIZE) { printk(KERN_ERR CFAG12864B_NAME ": ERROR: " "page size (%i) < cfag12864b size (%i)\n", @@ -354,6 +372,7 @@ static int __init cfag12864b_init(void) cfag12864b_clear(); cfag12864b_on(); + cfag12864b_inited = 1; return 0; cachealloced: diff --git a/drivers/auxdisplay/cfag12864bfb.c b/drivers/auxdisplay/cfag12864bfb.c index 94765e7..66fafbb 100644 --- a/drivers/auxdisplay/cfag12864bfb.c +++ b/drivers/auxdisplay/cfag12864bfb.c @@ -137,7 +137,14 @@ static struct platform_device *cfag12864bfb_device; static int __init cfag12864bfb_init(void) { - int ret; + int ret = -EINVAL; + + /* cfag12864b_init() must be called first */ + if (!cfag12864b_isinited()) { + printk(KERN_ERR CFAG12864BFB_NAME ": ERROR: " + "cfag12864b is not initialized\n"); + goto none; + } if (cfag12864b_enable()) { printk(KERN_ERR CFAG12864BFB_NAME ": ERROR: " @@ -162,6 +169,7 @@ static int __init cfag12864bfb_init(void) } } +none: return ret; } diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c index a637575..e6c3646 100644 --- a/drivers/auxdisplay/ks0108.c +++ b/drivers/auxdisplay/ks0108.c @@ -111,6 +111,17 @@ EXPORT_SYMBOL_GPL(ks0108_address); EXPORT_SYMBOL_GPL(ks0108_page); /* + * Is the module inited? + */ + +static unsigned char ks0108_inited; +unsigned char ks0108_isinited(void) +{ + return ks0108_inited; +} +EXPORT_SYMBOL_GPL(ks0108_isinited); + +/* * Module Init & Exit */ @@ -142,6 +153,7 @@ static int __init ks0108_init(void) goto registered; } + ks0108_inited = 1; return 0; registered: diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 2222073..1ec0654 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -62,7 +62,7 @@ EXPORT_SYMBOL_GPL(attribute_container_classdev_to_container); static struct list_head attribute_container_list; -static DECLARE_MUTEX(attribute_container_mutex); +static DEFINE_MUTEX(attribute_container_mutex); /** * attribute_container_register - register an attribute container @@ -77,9 +77,9 @@ attribute_container_register(struct attribute_container *cont) klist_init(&cont->containers,internal_container_klist_get, internal_container_klist_put); - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_add_tail(&cont->node, &attribute_container_list); - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); return 0; } @@ -94,7 +94,7 @@ int attribute_container_unregister(struct attribute_container *cont) { int retval = -EBUSY; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); spin_lock(&cont->containers.k_lock); if (!list_empty(&cont->containers.k_list)) goto out; @@ -102,7 +102,7 @@ attribute_container_unregister(struct attribute_container *cont) list_del(&cont->node); out: spin_unlock(&cont->containers.k_lock); - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); return retval; } @@ -145,7 +145,7 @@ attribute_container_add_device(struct device *dev, { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; @@ -173,7 +173,7 @@ attribute_container_add_device(struct device *dev, attribute_container_add_class_device(&ic->classdev); klist_add_tail(&ic->node, &cont->containers); } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /* FIXME: can't break out of this unless klist_iter_exit is also @@ -211,7 +211,7 @@ attribute_container_remove_device(struct device *dev, { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; struct klist_iter iter; @@ -234,7 +234,7 @@ attribute_container_remove_device(struct device *dev, } } } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** @@ -255,7 +255,7 @@ attribute_container_device_trigger(struct device *dev, { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { struct internal_container *ic; struct klist_iter iter; @@ -273,7 +273,7 @@ attribute_container_device_trigger(struct device *dev, fn(cont, dev, &ic->classdev); } } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** @@ -295,12 +295,12 @@ attribute_container_trigger(struct device *dev, { struct attribute_container *cont; - down(&attribute_container_mutex); + mutex_lock(&attribute_container_mutex); list_for_each_entry(cont, &attribute_container_list, node) { if (cont->match(cont, dev)) fn(cont, dev); } - up(&attribute_container_mutex); + mutex_unlock(&attribute_container_mutex); } /** diff --git a/drivers/base/base.h b/drivers/base/base.h index de7e144..d597f26 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -16,7 +16,7 @@ extern int cpu_dev_init(void); extern int attribute_container_init(void); extern int bus_add_device(struct device * dev); -extern int bus_attach_device(struct device * dev); +extern void bus_attach_device(struct device * dev); extern void bus_remove_device(struct device * dev); extern struct bus_type *get_bus(struct bus_type * bus); extern void put_bus(struct bus_type * bus); diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 472810f..1d76e23 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -27,6 +27,9 @@ #define to_driver(obj) container_of(obj, struct device_driver, kobj) +static int __must_check bus_rescan_devices_helper(struct device *dev, + void *data); + static ssize_t drv_attr_show(struct kobject * kobj, struct attribute * attr, char * buf) { @@ -60,8 +63,19 @@ static struct sysfs_ops driver_sysfs_ops = { static void driver_release(struct kobject * kobj) { - struct device_driver * drv = to_driver(kobj); - complete(&drv->unloaded); + /* + * Yes this is an empty release function, it is this way because struct + * device is always a static object, not a dynamic one. Yes, this is + * not nice and bad, but remember, drivers are code, reference counted + * by the module count, not a device, which is really data. And yes, + * in the future I do want to have all drivers be created dynamically, + * and am working toward that goal, but it will take a bit longer... + * + * But do not let this example give _anyone_ the idea that they can + * create a release function without any code in it at all, to do that + * is almost always wrong. If you have any questions about this, + * please send an email to <greg@kroah.com> + */ } static struct kobj_type ktype_driver = { @@ -133,7 +147,6 @@ static decl_subsys(bus, &ktype_bus, NULL); #ifdef CONFIG_HOTPLUG - /* Manually detach a device from its associated driver. */ static int driver_helper(struct device *dev, void *data) { @@ -199,6 +212,33 @@ static ssize_t driver_bind(struct device_driver *drv, } static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); +static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) +{ + return sprintf(buf, "%d\n", bus->drivers_autoprobe); +} + +static ssize_t store_drivers_autoprobe(struct bus_type *bus, + const char *buf, size_t count) +{ + if (buf[0] == '0') + bus->drivers_autoprobe = 0; + else + bus->drivers_autoprobe = 1; + return count; +} + +static ssize_t store_drivers_probe(struct bus_type *bus, + const char *buf, size_t count) +{ + struct device *dev; + + dev = bus_find_device(bus, NULL, (void *)buf, driver_helper); + if (!dev) + return -ENODEV; + if (bus_rescan_devices_helper(dev, NULL) != 0) + return -EINVAL; + return count; +} #endif static struct device * next_device(struct klist_iter * i) @@ -324,27 +364,25 @@ int bus_for_each_drv(struct bus_type * bus, struct device_driver * start, return error; } -static int device_add_attrs(struct bus_type * bus, struct device * dev) +static int device_add_attrs(struct bus_type *bus, struct device *dev) { int error = 0; int i; - if (bus->dev_attrs) { - for (i = 0; attr_name(bus->dev_attrs[i]); i++) { - error = device_create_file(dev,&bus->dev_attrs[i]); - if (error) - goto Err; + if (!bus->dev_attrs) + return 0; + + for (i = 0; attr_name(bus->dev_attrs[i]); i++) { + error = device_create_file(dev,&bus->dev_attrs[i]); + if (error) { + while (--i >= 0) + device_remove_file(dev, &bus->dev_attrs[i]); + break; } } - Done: return error; - Err: - while (--i >= 0) - device_remove_file(dev,&bus->dev_attrs[i]); - goto Done; } - static void device_remove_attrs(struct bus_type * bus, struct device * dev) { int i; @@ -420,21 +458,21 @@ out_put: * - Add device to bus's list of devices. * - Try to attach to driver. */ -int bus_attach_device(struct device * dev) +void bus_attach_device(struct device * dev) { struct bus_type *bus = dev->bus; int ret = 0; if (bus) { dev->is_registered = 1; - ret = device_attach(dev); - if (ret >= 0) { + if (bus->drivers_autoprobe) + ret = device_attach(dev); + WARN_ON(ret < 0); + if (ret >= 0) klist_add_tail(&dev->knode_bus, &bus->klist_devices); - ret = 0; - } else + else dev->is_registered = 0; } - return ret; } /** @@ -517,9 +555,41 @@ static void remove_bind_files(struct device_driver *drv) driver_remove_file(drv, &driver_attr_bind); driver_remove_file(drv, &driver_attr_unbind); } + +static int add_probe_files(struct bus_type *bus) +{ + int retval; + + bus->drivers_probe_attr.attr.name = "drivers_probe"; + bus->drivers_probe_attr.attr.mode = S_IWUSR; + bus->drivers_probe_attr.attr.owner = bus->owner; + bus->drivers_probe_attr.store = store_drivers_probe; + retval = bus_create_file(bus, &bus->drivers_probe_attr); + if (retval) + goto out; + + bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; + bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; + bus->drivers_autoprobe_attr.attr.owner = bus->owner; + bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; + bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; + retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); + if (retval) + bus_remove_file(bus, &bus->drivers_probe_attr); +out: + return retval; +} + +static void remove_probe_files(struct bus_type *bus) +{ + bus_remove_file(bus, &bus->drivers_autoprobe_attr); + bus_remove_file(bus, &bus->drivers_probe_attr); +} #else static inline int add_bind_files(struct device_driver *drv) { return 0; } static inline void remove_bind_files(struct device_driver *drv) {} +static inline int add_probe_files(struct bus_type *bus) { return 0; } +static inline void remove_probe_files(struct bus_type *bus) {} #endif /** @@ -533,7 +603,7 @@ int bus_add_driver(struct device_driver *drv) int error = 0; if (!bus) - return 0; + return -EINVAL; pr_debug("bus %s: add driver %s\n", bus->name, drv->name); error = kobject_set_name(&drv->kobj, "%s", drv->name); @@ -543,9 +613,11 @@ int bus_add_driver(struct device_driver *drv) if ((error = kobject_register(&drv->kobj))) goto out_put_bus; - error = driver_attach(drv); - if (error) - goto out_unregister; + if (drv->bus->drivers_autoprobe) { + error = driver_attach(drv); + if (error) + goto out_unregister; + } klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv); @@ -607,8 +679,6 @@ static int __must_check bus_rescan_devices_helper(struct device *dev, ret = device_attach(dev); if (dev->parent) up(&dev->parent->sem); - if (ret > 0) - ret = 0; } return ret < 0 ? ret : 0; } @@ -764,6 +834,12 @@ int bus_register(struct bus_type * bus) klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put); klist_init(&bus->klist_drivers, NULL, NULL); + + bus->drivers_autoprobe = 1; + retval = add_probe_files(bus); + if (retval) + goto bus_probe_files_fail; + retval = bus_add_attrs(bus); if (retval) goto bus_attrs_fail; @@ -772,6 +848,8 @@ int bus_register(struct bus_type * bus) return 0; bus_attrs_fail: + remove_probe_files(bus); +bus_probe_files_fail: kset_unregister(&bus->drivers); bus_drivers_fail: kset_unregister(&bus->devices); @@ -781,7 +859,6 @@ out: return retval; } - /** * bus_unregister - remove a bus from the system * @bus: bus. @@ -793,6 +870,7 @@ void bus_unregister(struct bus_type * bus) { pr_debug("bus %s: unregistering\n", bus->name); bus_remove_attrs(bus); + remove_probe_files(bus); kset_unregister(&bus->drivers); kset_unregister(&bus->devices); subsystem_unregister(&bus->subsys); diff --git a/drivers/base/class.c b/drivers/base/class.c index 96def1d..80bbb20 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -145,6 +145,7 @@ int class_register(struct class * cls) INIT_LIST_HEAD(&cls->children); INIT_LIST_HEAD(&cls->devices); INIT_LIST_HEAD(&cls->interfaces); + kset_init(&cls->class_dirs); init_MUTEX(&cls->sem); error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name); if (error) @@ -163,8 +164,6 @@ int class_register(struct class * cls) void class_unregister(struct class * cls) { pr_debug("device class '%s': unregistering\n", cls->name); - if (cls->virtual_dir) - kobject_unregister(cls->virtual_dir); remove_class_attrs(cls); subsystem_unregister(&cls->subsys); } @@ -841,48 +840,6 @@ void class_device_destroy(struct class *cls, dev_t devt) class_device_unregister(class_dev); } -int class_device_rename(struct class_device *class_dev, char *new_name) -{ - int error = 0; - char *old_class_name = NULL, *new_class_name = NULL; - - class_dev = class_device_get(class_dev); - if (!class_dev) - return -EINVAL; - - pr_debug("CLASS: renaming '%s' to '%s'\n", class_dev->class_id, - new_name); - -#ifdef CONFIG_SYSFS_DEPRECATED - if (class_dev->dev) - old_class_name = make_class_name(class_dev->class->name, - &class_dev->kobj); -#endif - - strlcpy(class_dev->class_id, new_name, KOBJ_NAME_LEN); - - error = kobject_rename(&class_dev->kobj, new_name); - -#ifdef CONFIG_SYSFS_DEPRECATED - if (class_dev->dev) { - new_class_name = make_class_name(class_dev->class->name, - &class_dev->kobj); - if (new_class_name) - sysfs_create_link(&class_dev->dev->kobj, - &class_dev->kobj, new_class_name); - if (old_class_name) - sysfs_remove_link(&class_dev->dev->kobj, - old_class_name); - } -#endif - class_device_put(class_dev); - - kfree(old_class_name); - kfree(new_class_name); - - return error; -} - struct class_device * class_device_get(struct class_device *class_dev) { if (class_dev) diff --git a/drivers/base/core.c b/drivers/base/core.c index a8ac34b..8aa090d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -43,7 +43,8 @@ int (*platform_notify_remove)(struct device * dev) = NULL; const char *dev_driver_string(struct device *dev) { return dev->driver ? dev->driver->name : - (dev->bus ? dev->bus->name : ""); + (dev->bus ? dev->bus->name : + (dev->class ? dev->class->name : "")); } EXPORT_SYMBOL(dev_driver_string); @@ -119,6 +120,8 @@ static int dev_uevent_filter(struct kset *kset, struct kobject *kobj) if (ktype == &ktype_device) { struct device *dev = to_dev(kobj); + if (dev->uevent_suppress) + return 0; if (dev->bus) return 1; if (dev->class) @@ -156,6 +159,11 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp, "MINOR=%u", MINOR(dev->devt)); } + if (dev->type && dev->type->name) + add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEVTYPE=%s", dev->type->name); + if (dev->driver) add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, @@ -238,71 +246,152 @@ static struct kset_uevent_ops device_uevent_ops = { .uevent = dev_uevent, }; +static ssize_t show_uevent(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct kobject *top_kobj; + struct kset *kset; + char *envp[32]; + char data[PAGE_SIZE]; + char *pos; + int i; + size_t count = 0; + int retval; + + /* search the kset, the device belongs to */ + top_kobj = &dev->kobj; + if (!top_kobj->kset && top_kobj->parent) { + do { + top_kobj = top_kobj->parent; + } while (!top_kobj->kset && top_kobj->parent); + } + if (!top_kobj->kset) + goto out; + kset = top_kobj->kset; + if (!kset->uevent_ops || !kset->uevent_ops->uevent) + goto out; + + /* respect filter */ + if (kset->uevent_ops && kset->uevent_ops->filter) + if (!kset->uevent_ops->filter(kset, &dev->kobj)) + goto out; + + /* let the kset specific function add its keys */ + pos = data; + retval = kset->uevent_ops->uevent(kset, &dev->kobj, + envp, ARRAY_SIZE(envp), + pos, PAGE_SIZE); + if (retval) + goto out; + + /* copy keys to file */ + for (i = 0; envp[i]; i++) { + pos = &buf[count]; + count += sprintf(pos, "%s\n", envp[i]); + } +out: + return count; +} + static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + if (memcmp(buf, "add", 3) != 0) + dev_err(dev, "uevent: unsupported action-string; this will " + "be ignored in a future kernel version"); kobject_uevent(&dev->kobj, KOBJ_ADD); return count; } -static int device_add_groups(struct device *dev) +static int device_add_attributes(struct device *dev, + struct device_attribute *attrs) { + int error = 0; int i; + + if (attrs) { + for (i = 0; attr_name(attrs[i]); i++) { + error = device_create_file(dev, &attrs[i]); + if (error) + break; + } + if (error) + while (--i >= 0) + device_remove_file(dev, &attrs[i]); + } + return error; +} + +static void device_remove_attributes(struct device *dev, + struct device_attribute *attrs) +{ + int i; + + if (attrs) + for (i = 0; attr_name(attrs[i]); i++) + device_remove_file(dev, &attrs[i]); +} + +static int device_add_groups(struct device *dev, + struct attribute_group **groups) +{ int error = 0; + int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - error = sysfs_create_group(&dev->kobj, dev->groups[i]); + if (groups) { + for (i = 0; groups[i]; i++) { + error = sysfs_create_group(&dev->kobj, groups[i]); if (error) { while (--i >= 0) - sysfs_remove_group(&dev->kobj, dev->groups[i]); - goto out; + sysfs_remove_group(&dev->kobj, groups[i]); + break; } } } -out: return error; } -static void device_remove_groups(struct device *dev) +static void device_remove_groups(struct device *dev, + struct attribute_group **groups) { int i; - if (dev->groups) { - for (i = 0; dev->groups[i]; i++) { - sysfs_remove_group(&dev->kobj, dev->groups[i]); - } - } + + if (groups) + for (i = 0; groups[i]; i++) + sysfs_remove_group(&dev->kobj, groups[i]); } static int device_add_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int error = 0; - int i; + int error; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) { - error = device_create_file(dev, &class->dev_attrs[i]); - if (error) - break; - } + if (class) { + error = device_add_attributes(dev, class->dev_attrs); if (error) - while (--i >= 0) - device_remove_file(dev, &class->dev_attrs[i]); + return error; } - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) { - error = device_create_file(dev, &type->attrs[i]); - if (error) - break; - } + if (type) { + error = device_add_groups(dev, type->groups); if (error) - while (--i >= 0) - device_remove_file(dev, &type->attrs[i]); + goto err_remove_class_attrs; } + error = device_add_groups(dev, dev->groups); + if (error) + goto err_remove_type_groups; + + return 0; + + err_remove_type_groups: + if (type) + device_remove_groups(dev, type->groups); + err_remove_class_attrs: + if (class) + device_remove_attributes(dev, class->dev_attrs); + return error; } @@ -310,17 +399,14 @@ static void device_remove_attrs(struct device *dev) { struct class *class = dev->class; struct device_type *type = dev->type; - int i; - if (class && class->dev_attrs) { - for (i = 0; attr_name(class->dev_attrs[i]); i++) - device_remove_file(dev, &class->dev_attrs[i]); - } + device_remove_groups(dev, dev->groups); - if (type && type->attrs) { - for (i = 0; attr_name(type->attrs[i]); i++) - device_remove_file(dev, &type->attrs[i]); - } + if (type) + device_remove_groups(dev, type->groups); + + if (class) + device_remove_attributes(dev, class->dev_attrs); } @@ -393,6 +479,39 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) } EXPORT_SYMBOL_GPL(device_remove_bin_file); +/** + * device_schedule_callback_owner - helper to schedule a callback for a device + * @dev: device. + * @func: callback function to invoke later. + * @owner: module owning the callback routine + * + * Attribute methods must not unregister themselves or their parent device + * (which would amount to the same thing). Attempts to do so will deadlock, + * since unregistration is mutually exclusive with driver callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @dev as its + * argument in the workqueue's process context. @dev will be pinned until + * @func returns. + * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated, -ENODEV if a reference to @owner isn't available. + * + * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an + * underlying sysfs routine (since it is intended for use by attribute + * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. + */ +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) +{ + return sysfs_schedule_callback(&dev->kobj, + (void (*)(void *)) func, dev, owner); +} +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); + static void klist_children_get(struct klist_node *n) { struct device *dev = container_of(n, struct device, knode_parent); @@ -448,34 +567,58 @@ static struct kobject * get_device_parent(struct device *dev, return NULL; } #else -static struct kobject * virtual_device_parent(struct device *dev) +static struct kobject *virtual_device_parent(struct device *dev) { - if (!dev->class) - return ERR_PTR(-ENODEV); + static struct kobject *virtual_dir = NULL; - if (!dev->class->virtual_dir) { - static struct kobject *virtual_dir = NULL; - - if (!virtual_dir) - virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name); - } + if (!virtual_dir) + virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual"); - return dev->class->virtual_dir; + return virtual_dir; } static struct kobject * get_device_parent(struct device *dev, struct device *parent) { - /* if this is a class device, and has no parent, create one */ - if ((dev->class) && (parent == NULL)) { - return virtual_device_parent(dev); - } else if (parent) + if (dev->class) { + struct kobject *kobj = NULL; + struct kobject *parent_kobj; + struct kobject *k; + + /* + * If we have no parent, we live in "virtual". + * Class-devices with a bus-device as parent, live + * in a class-directory to prevent namespace collisions. + */ + if (parent == NULL) + parent_kobj = virtual_device_parent(dev); + else if (parent->class) + return &parent->kobj; + else + parent_kobj = &parent->kobj; + + /* find our class-directory at the parent and reference it */ + spin_lock(&dev->class->class_dirs.list_lock); + list_for_each_entry(k, &dev->class->class_dirs.list, entry) + if (k->parent == parent_kobj) { + kobj = kobject_get(k); + break; + } + spin_unlock(&dev->class->class_dirs.list_lock); + if (kobj) + return kobj; + + /* or create a new class-directory at the parent device */ + return kobject_kset_add_dir(&dev->class->class_dirs, + parent_kobj, dev->class->name); + } + + if (parent) return &parent->kobj; return NULL; } - #endif + static int setup_parent(struct device *dev, struct device *parent) { struct kobject *kobj; @@ -512,7 +655,6 @@ int device_add(struct device *dev) pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id); parent = get_device(dev->parent); - error = setup_parent(dev, parent); if (error) goto Error; @@ -533,10 +675,11 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); dev->uevent_attr.attr.name = "uevent"; - dev->uevent_attr.attr.mode = S_IWUSR; + dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; if (dev->driver) dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; + dev->uevent_attr.show = show_uevent; error = device_create_file(dev, &dev->uevent_attr); if (error) goto attrError; @@ -570,31 +713,27 @@ int device_add(struct device *dev) if (dev->kobj.parent != &dev->class->subsys.kset.kobj) sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj, dev->bus_id); -#ifdef CONFIG_SYSFS_DEPRECATED if (parent) { sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device"); +#ifdef CONFIG_SYSFS_DEPRECATED class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name); - } #endif + } } if ((error = device_add_attrs(dev))) goto AttrsError; - if ((error = device_add_groups(dev))) - goto GroupError; if ((error = device_pm_add(dev))) goto PMError; if ((error = bus_add_device(dev))) goto BusError; - if (!dev->uevent_suppress) - kobject_uevent(&dev->kobj, KOBJ_ADD); - if ((error = bus_attach_device(dev))) - goto AttachError; + kobject_uevent(&dev->kobj, KOBJ_ADD); + bus_attach_device(dev); if (parent) klist_add_tail(&dev->knode_parent, &parent->klist_children); @@ -610,25 +749,41 @@ int device_add(struct device *dev) up(&dev->class->sem); } Done: - kfree(class_name); + kfree(class_name); put_device(dev); return error; - AttachError: - bus_remove_device(dev); BusError: device_pm_remove(dev); PMError: if (dev->bus) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_DEL_DEVICE, dev); - device_remove_groups(dev); - GroupError: - device_remove_attrs(dev); + device_remove_attrs(dev); AttrsError: if (dev->devt_attr) { device_remove_file(dev, dev->devt_attr); kfree(dev->devt_attr); } + + if (dev->class) { + sysfs_remove_link(&dev->kobj, "subsystem"); + /* If this is not a "fake" compatible device, remove the + * symlink from the class to the device. */ + if (dev->kobj.parent != &dev->class->subsys.kset.kobj) + sysfs_remove_link(&dev->class->subsys.kset.kobj, + dev->bus_id); + if (parent) { +#ifdef CONFIG_SYSFS_DEPRECATED + char *class_name = make_class_name(dev->class->name, + &dev->kobj); + if (class_name) + sysfs_remove_link(&dev->parent->kobj, + class_name); + kfree(class_name); +#endif + sysfs_remove_link(&dev->kobj, "device"); + } + } ueventattrError: device_remove_file(dev, &dev->uevent_attr); attrError: @@ -718,17 +873,17 @@ void device_del(struct device * dev) if (dev->kobj.parent != &dev->class->subsys.kset.kobj) sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id); -#ifdef CONFIG_SYSFS_DEPRECATED if (parent) { +#ifdef CONFIG_SYSFS_DEPRECATED char *class_name = make_class_name(dev->class->name, &dev->kobj); if (class_name) sysfs_remove_link(&dev->parent->kobj, class_name); kfree(class_name); +#endif sysfs_remove_link(&dev->kobj, "device"); } -#endif down(&dev->class->sem); /* notify any interfaces that the device is now gone */ @@ -738,12 +893,43 @@ void device_del(struct device * dev) /* remove the device from the class list */ list_del_init(&dev->node); up(&dev->class->sem); + + /* If we live in a parent class-directory, unreference it */ + if (dev->kobj.parent->kset == &dev->class->class_dirs) { + struct device *d; + int other = 0; + + /* + * if we are the last child of our class, delete + * our class-directory at this parent + */ + down(&dev->class->sem); + list_for_each_entry(d, &dev->class->devices, node) { + if (d == dev) + continue; + if (d->kobj.parent == dev->kobj.parent) { + other = 1; + break; + } + } + if (!other) + kobject_del(dev->kobj.parent); + + kobject_put(dev->kobj.parent); + up(&dev->class->sem); + } } device_remove_file(dev, &dev->uevent_attr); - device_remove_groups(dev); device_remove_attrs(dev); bus_remove_device(dev); + /* + * Some platform devices are driven without driver attached + * and managed resources may have been acquired. Make sure + * all resources are released. + */ + devres_release_all(dev); + /* Notify the platform of the removal, in case they * need to do anything... */ @@ -1015,14 +1201,14 @@ int device_rename(struct device *dev, char *new_name) return error; } - +EXPORT_SYMBOL_GPL(device_rename); static int device_move_class_links(struct device *dev, struct device *old_parent, struct device *new_parent) { + int error = 0; #ifdef CONFIG_SYSFS_DEPRECATED - int error; char *class_name; class_name = make_class_name(dev->class->name, &dev->kobj); @@ -1050,7 +1236,12 @@ out: kfree(class_name); return error; #else - return 0; + if (old_parent) + sysfs_remove_link(&dev->kobj, "device"); + if (new_parent) + error = sysfs_create_link(&dev->kobj, &new_parent->kobj, + "device"); + return error; #endif } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 7fd095e..fe7ef33 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -103,7 +103,7 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL); #endif /* - * register_cpu - Setup a driverfs device for a CPU. + * register_cpu - Setup a sysfs device for a CPU. * @cpu - cpu->hotpluggable field set to 1 will generate a control file in * sysfs for this CPU. * @num - CPU number to use when creating the device. diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 6a48824..18dba8e 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -94,19 +94,11 @@ int device_bind_driver(struct device *dev) return ret; } -struct stupid_thread_structure { - struct device_driver *drv; - struct device *dev; -}; - static atomic_t probe_count = ATOMIC_INIT(0); static DECLARE_WAIT_QUEUE_HEAD(probe_waitqueue); -static int really_probe(void *void_data) +static int really_probe(struct device *dev, struct device_driver *drv) { - struct stupid_thread_structure *data = void_data; - struct device_driver *drv = data->drv; - struct device *dev = data->dev; int ret = 0; atomic_inc(&probe_count); @@ -154,7 +146,6 @@ probe_failed: */ ret = 0; done: - kfree(data); atomic_dec(&probe_count); wake_up(&probe_waitqueue); return ret; @@ -186,16 +177,14 @@ int driver_probe_done(void) * format of the ID structures, nor what is to be considered a match and * what is not. * - * This function returns 1 if a match is found, an error if one occurs - * (that is not -ENODEV or -ENXIO), and 0 otherwise. + * This function returns 1 if a match is found, -ENODEV if the device is + * not registered, and 0 otherwise. * * This function must be called with @dev->sem held. When called for a * USB interface, @dev->parent->sem must be held as well. */ int driver_probe_device(struct device_driver * drv, struct device * dev) { - struct stupid_thread_structure *data; - struct task_struct *probe_task; int ret = 0; if (!device_is_registered(dev)) @@ -206,19 +195,7 @@ int driver_probe_device(struct device_driver * drv, struct device * dev) pr_debug("%s: Matched Device %s with Driver %s\n", drv->bus->name, dev->bus_id, drv->name); - data = kmalloc(sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - data->drv = drv; - data->dev = dev; - - if (drv->multithread_probe) { - probe_task = kthread_run(really_probe, data, - "probe-%s", dev->bus_id); - if (IS_ERR(probe_task)) - ret = really_probe(data); - } else - ret = really_probe(data); + ret = really_probe(dev, drv); done: return ret; @@ -230,30 +207,57 @@ static int __device_attach(struct device_driver * drv, void * data) return driver_probe_device(drv, dev); } +static int device_probe_drivers(void *data) +{ + struct device *dev = data; + int ret = 0; + + if (dev->bus) { + down(&dev->sem); + ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + up(&dev->sem); + } + return ret; +} + /** * device_attach - try to attach device to a driver. * @dev: device. * * Walk the list of drivers that the bus has and call * driver_probe_device() for each pair. If a compatible - * pair is found, break out and return. + * pair is found, break out and return. If the bus specifies + * multithreaded probing, walking the list of drivers is done + * on a probing thread. * * Returns 1 if the device was bound to a driver; - * 0 if no matching device was found; error code otherwise. + * 0 if no matching device was found or multithreaded probing is done; + * -ENODEV if the device is not registered. * * When called for a USB interface, @dev->parent->sem must be held. */ int device_attach(struct device * dev) { int ret = 0; + struct task_struct *probe_task = ERR_PTR(-ENOMEM); down(&dev->sem); if (dev->driver) { ret = device_bind_driver(dev); if (ret == 0) ret = 1; - } else - ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + else { + dev->driver = NULL; + ret = 0; + } + } else { + if (dev->bus->multithread_probe) + probe_task = kthread_run(device_probe_drivers, dev, + "probe-%s", dev->bus_id); + if(IS_ERR(probe_task)) + ret = bus_for_each_drv(dev->bus, NULL, dev, + __device_attach); + } up(&dev->sem); return ret; } diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c index cd467c9..9406259 100644 --- a/drivers/base/dmapool.c +++ b/drivers/base/dmapool.c @@ -37,7 +37,7 @@ struct dma_page { /* cacheable header for 'allocation' bytes */ #define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000) -static DECLARE_MUTEX (pools_lock); +static DEFINE_MUTEX (pools_lock); static ssize_t show_pools (struct device *dev, struct device_attribute *attr, char *buf) @@ -55,7 +55,7 @@ show_pools (struct device *dev, struct device_attribute *attr, char *buf) size -= temp; next += temp; - down (&pools_lock); + mutex_lock(&pools_lock); list_for_each_entry(pool, &dev->dma_pools, pools) { unsigned pages = 0; unsigned blocks = 0; @@ -73,7 +73,7 @@ show_pools (struct device *dev, struct device_attribute *attr, char *buf) size -= temp; next += temp; } - up (&pools_lock); + mutex_unlock(&pools_lock); return PAGE_SIZE - size; } @@ -143,7 +143,7 @@ dma_pool_create (const char *name, struct device *dev, if (dev) { int ret; - down (&pools_lock); + mutex_lock(&pools_lock); if (list_empty (&dev->dma_pools)) ret = device_create_file (dev, &dev_attr_pools); else @@ -155,7 +155,7 @@ dma_pool_create (const char *name, struct device *dev, kfree(retval); retval = NULL; } - up (&pools_lock); + mutex_unlock(&pools_lock); } else INIT_LIST_HEAD (&retval->pools); @@ -231,11 +231,11 @@ pool_free_page (struct dma_pool *pool, struct dma_page *page) void dma_pool_destroy (struct dma_pool *pool) { - down (&pools_lock); + mutex_lock(&pools_lock); list_del (&pool->pools); if (pool->dev && list_empty (&pool->dev->dma_pools)) device_remove_file (pool->dev, &dev_attr_pools); - up (&pools_lock); + mutex_unlock(&pools_lock); while (!list_empty (&pool->page_list)) { struct dma_page *page; diff --git a/drivers/base/driver.c b/drivers/base/driver.c index 1214cbd..eb11475 100644 --- a/drivers/base/driver.c +++ b/drivers/base/driver.c @@ -149,10 +149,6 @@ void put_driver(struct device_driver * drv) * We pass off most of the work to the bus_add_driver() call, * since most of the things we have to do deal with the bus * structures. - * - * The one interesting aspect is that we setup @drv->unloaded - * as a completion that gets complete when the driver reference - * count reaches 0. */ int driver_register(struct device_driver * drv) { @@ -162,28 +158,19 @@ int driver_register(struct device_driver * drv) printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL); - init_completion(&drv->unloaded); return bus_add_driver(drv); } - /** * driver_unregister - remove driver from system. * @drv: driver. * * Again, we pass off most of the work to the bus-level call. - * - * Though, once that is done, we wait until @drv->unloaded is completed. - * This will block until the driver refcount reaches 0, and it is - * released. Only modular drivers will call this function, and we - * have to guarantee that it won't complete, letting the driver - * unload until all references are gone. */ void driver_unregister(struct device_driver * drv) { bus_remove_driver(drv); - wait_for_completion(&drv->unloaded); } /** diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index c0a979a5..97ab5bd 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -31,8 +31,6 @@ enum { FW_STATUS_LOADING, FW_STATUS_DONE, FW_STATUS_ABORT, - FW_STATUS_READY, - FW_STATUS_READY_NOHOTPLUG, }; static int loading_timeout = 60; /* In seconds */ @@ -96,9 +94,6 @@ static int firmware_uevent(struct device *dev, char **envp, int num_envp, struct firmware_priv *fw_priv = dev_get_drvdata(dev); int i = 0, len = 0; - if (!test_bit(FW_STATUS_READY, &fw_priv->status)) - return -ENODEV; - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, "FIRMWARE=%s", fw_priv->fw_id)) return -ENOMEM; @@ -333,6 +328,7 @@ static int fw_register_device(struct device **dev_p, const char *fw_name, f_dev->parent = device; f_dev->class = &firmware_class; dev_set_drvdata(f_dev, fw_priv); + f_dev->uevent_suppress = 1; retval = device_register(f_dev); if (retval) { printk(KERN_ERR "%s: device_register failed\n", @@ -382,9 +378,7 @@ static int fw_setup_device(struct firmware *fw, struct device **dev_p, } if (uevent) - set_bit(FW_STATUS_READY, &fw_priv->status); - else - set_bit(FW_STATUS_READY_NOHOTPLUG, &fw_priv->status); + f_dev->uevent_suppress = 0; *dev_p = f_dev; goto out; diff --git a/drivers/base/node.c b/drivers/base/node.c index 475e33f..cae346e 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -133,7 +133,7 @@ static SYSDEV_ATTR(distance, S_IRUGO, node_read_distance, NULL); /* - * register_node - Setup a driverfs device for a node. + * register_node - Setup a sysfs device for a node. * @num - Node number to use when creating the device. * * Initialize and register the node device. diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index fdfa3d0..05dc876 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -29,6 +29,9 @@ LIST_HEAD(dpm_off_irq); DECLARE_MUTEX(dpm_sem); DECLARE_MUTEX(dpm_list_sem); +int (*platform_enable_wakeup)(struct device *dev, int is_on); + + /** * device_pm_set_parent - Specify power dependency. * @dev: Device who needs power. @@ -54,7 +57,8 @@ int device_pm_add(struct device * dev) int error; pr_debug("PM: Adding info for %s:%s\n", - dev->bus ? dev->bus->name : "No Bus", dev->kobj.name); + dev->bus ? dev->bus->name : "No Bus", + kobject_name(&dev->kobj)); down(&dpm_list_sem); list_add_tail(&dev->power.entry, &dpm_active); device_pm_set_parent(dev, dev->parent); @@ -67,7 +71,8 @@ int device_pm_add(struct device * dev) void device_pm_remove(struct device * dev) { pr_debug("PM: Removing info for %s:%s\n", - dev->bus ? dev->bus->name : "No Bus", dev->kobj.name); + dev->bus ? dev->bus->name : "No Bus", + kobject_name(&dev->kobj)); down(&dpm_list_sem); dpm_sysfs_remove(dev); put_device(dev->power.pm_parent); diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 020be36..a2c6418 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -26,7 +26,9 @@ int resume_device(struct device * dev) TRACE_DEVICE(dev); TRACE_RESUME(0); + down(&dev->sem); + if (dev->power.pm_parent && dev->power.pm_parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", @@ -34,15 +36,24 @@ int resume_device(struct device * dev) dev->power.pm_parent->bus_id, dev->power.pm_parent->power.power_state.event); } + if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); } - if (dev->class && dev->class->resume) { + + if (!error && dev->type && dev->type->resume) { + dev_dbg(dev,"resuming\n"); + error = dev->type->resume(dev); + } + + if (!error && dev->class && dev->class->resume) { dev_dbg(dev,"class resume\n"); error = dev->class->resume(dev); } + up(&dev->sem); + TRACE_RESUME(error); return error; } diff --git a/drivers/base/power/shutdown.c b/drivers/base/power/shutdown.c index 3483ae4..58b6f77 100644 --- a/drivers/base/power/shutdown.c +++ b/drivers/base/power/shutdown.c @@ -36,7 +36,6 @@ void device_shutdown(void) { struct device * dev, *devn; - down_write(&devices_subsys.rwsem); list_for_each_entry_safe_reverse(dev, devn, &devices_subsys.kset.list, kobj.entry) { if (dev->bus && dev->bus->shutdown) { @@ -47,7 +46,6 @@ void device_shutdown(void) dev->driver->shutdown(dev); } } - up_write(&devices_subsys.rwsem); sysdev_shutdown(); } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index ece136b..42d2b86 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -78,6 +78,18 @@ int suspend_device(struct device * dev, pm_message_t state) suspend_report_result(dev->class->suspend, error); } + if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { + dev_dbg(dev, "%s%s\n", + suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) + && device_may_wakeup(dev)) + ? ", may wakeup" + : "" + ); + error = dev->type->suspend(dev, state); + suspend_report_result(dev->type->suspend, error); + } + if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { dev_dbg(dev, "%s%s\n", suspend_verb(state.event), diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index cacb1c8..17ee97f 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -406,22 +406,6 @@ config BLK_DEV_RAM_BLOCKSIZE setups function - apparently needed by the rd_load_image routine that supposes the filesystem in the image uses a 1024 blocksize. -config BLK_DEV_INITRD - bool "Initial RAM filesystem and RAM disk (initramfs/initrd) support" - depends on BROKEN || !FRV - help - The initial RAM filesystem is a ramfs which is loaded by the - boot loader (loadlin or lilo) and that is mounted as root - before the normal boot procedure. It is typically used to - load modules needed to mount the "real" root file system, - etc. See <file:Documentation/initrd.txt> for details. - - If RAM disk support (BLK_DEV_RAM) is also included, this - also enables initial RAM disk (initrd) support and adds - 15 Kbytes (more on some other architectures) to the kernel size. - - If unsure say Y. - config CDROM_PKTCDVD tristate "Packet writing on CD/DVD media" depends on !UML diff --git a/drivers/block/aoe/aoe.h b/drivers/block/aoe/aoe.h index 2308e83..1d84668 100644 --- a/drivers/block/aoe/aoe.h +++ b/drivers/block/aoe/aoe.h @@ -48,6 +48,15 @@ struct aoe_hdr { __be32 tag; }; +#ifdef __KERNEL__ +#include <linux/skbuff.h> + +static inline struct aoe_hdr *aoe_hdr(const struct sk_buff *skb) +{ + return (struct aoe_hdr *)skb_mac_header(skb); +} +#endif + struct aoe_atahdr { unsigned char aflags; unsigned char errfeat; diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c index bb022ed..1a6aeac 100644 --- a/drivers/block/aoe/aoecmd.c +++ b/drivers/block/aoe/aoecmd.c @@ -27,7 +27,8 @@ new_skb(ulong len) skb = alloc_skb(len, GFP_ATOMIC); if (skb) { - skb->nh.raw = skb->mac.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); skb->protocol = __constant_htons(ETH_P_AOE); skb->priority = 0; skb->next = skb->prev = NULL; @@ -118,7 +119,7 @@ aoecmd_ata_rw(struct aoedev *d, struct frame *f) /* initialize the headers & frame */ skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); skb_put(skb, sizeof *h + sizeof *ah); memset(h, 0, skb->len); @@ -207,7 +208,7 @@ aoecmd_cfg_pkts(ushort aoemajor, unsigned char aoeminor, struct sk_buff **tail) skb->dev = ifp; if (sl_tail == NULL) sl_tail = skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); memset(h, 0, sizeof *h + sizeof *ch); memset(h->dst, 0xff, sizeof h->dst); @@ -300,7 +301,7 @@ rexmit(struct aoedev *d, struct frame *f) aoechr_error(buf); skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); f->tag = n; h->tag = cpu_to_be32(n); @@ -529,8 +530,8 @@ aoecmd_ata_rsp(struct sk_buff *skb) char ebuf[128]; u16 aoemajor; - hin = (struct aoe_hdr *) skb->mac.raw; - aoemajor = be16_to_cpu(hin->major); + hin = aoe_hdr(skb); + aoemajor = be16_to_cpu(get_unaligned(&hin->major)); d = aoedev_by_aoeaddr(aoemajor, hin->minor); if (d == NULL) { snprintf(ebuf, sizeof ebuf, "aoecmd_ata_rsp: ata response " @@ -542,7 +543,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) spin_lock_irqsave(&d->lock, flags); - n = be32_to_cpu(hin->tag); + n = be32_to_cpu(get_unaligned(&hin->tag)); f = getframe(d, n); if (f == NULL) { calc_rttavg(d, -tsince(n)); @@ -550,9 +551,9 @@ aoecmd_ata_rsp(struct sk_buff *skb) snprintf(ebuf, sizeof ebuf, "%15s e%d.%d tag=%08x@%08lx\n", "unexpected rsp", - be16_to_cpu(hin->major), + be16_to_cpu(get_unaligned(&hin->major)), hin->minor, - be32_to_cpu(hin->tag), + be32_to_cpu(get_unaligned(&hin->tag)), jiffies); aoechr_error(ebuf); return; @@ -561,7 +562,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) calc_rttavg(d, tsince(f->tag)); ahin = (struct aoe_atahdr *) (hin+1); - hout = (struct aoe_hdr *) f->skb->mac.raw; + hout = aoe_hdr(f->skb); ahout = (struct aoe_atahdr *) (hout+1); buf = f->buf; @@ -631,7 +632,7 @@ aoecmd_ata_rsp(struct sk_buff *skb) printk(KERN_INFO "aoe: unrecognized ata command %2.2Xh for %d.%d\n", ahout->cmdstat, - be16_to_cpu(hin->major), + be16_to_cpu(get_unaligned(&hin->major)), hin->minor); } } @@ -695,7 +696,7 @@ aoecmd_ata_id(struct aoedev *d) /* initialize the headers & frame */ skb = f->skb; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ah = (struct aoe_atahdr *) (h+1); skb_put(skb, sizeof *h + sizeof *ah); memset(h, 0, skb->len); @@ -726,14 +727,14 @@ aoecmd_cfg_rsp(struct sk_buff *skb) enum { MAXFRAMES = 16 }; u16 n; - h = (struct aoe_hdr *) skb->mac.raw; + h = aoe_hdr(skb); ch = (struct aoe_cfghdr *) (h+1); /* * Enough people have their dip switches set backwards to * warrant a loud message for this special case. */ - aoemajor = be16_to_cpu(h->major); + aoemajor = be16_to_cpu(get_unaligned(&h->major)); if (aoemajor == 0xfff) { printk(KERN_ERR "aoe: Warning: shelf address is all ones. " "Check shelf dip switches.\n"); diff --git a/drivers/block/aoe/aoenet.c b/drivers/block/aoe/aoenet.c index 9626e0f..f9ddfda 100644 --- a/drivers/block/aoe/aoenet.c +++ b/drivers/block/aoe/aoenet.c @@ -8,6 +8,7 @@ #include <linux/blkdev.h> #include <linux/netdevice.h> #include <linux/moduleparam.h> +#include <asm/unaligned.h> #include "aoe.h" #define NECODES 5 @@ -122,8 +123,8 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, goto exit; skb_push(skb, ETH_HLEN); /* (1) */ - h = (struct aoe_hdr *) skb->mac.raw; - n = be32_to_cpu(h->tag); + h = aoe_hdr(skb); + n = be32_to_cpu(get_unaligned(&h->tag)); if ((h->verfl & AOEFL_RSP) == 0 || (n & 1<<31)) goto exit; @@ -133,7 +134,7 @@ aoenet_rcv(struct sk_buff *skb, struct net_device *ifp, struct packet_type *pt, n = 0; if (net_ratelimit()) printk(KERN_ERR "aoe: error packet from %d.%d; ecode=%d '%s'\n", - be16_to_cpu(h->major), h->minor, + be16_to_cpu(get_unaligned(&h->major)), h->minor, h->err, aoe_errlist[n]); goto exit; } diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c index 05dfe35..65a725c 100644 --- a/drivers/block/cciss.c +++ b/drivers/block/cciss.c @@ -1291,13 +1291,19 @@ static void cciss_update_drive_info(int ctlr, int drv_index) if (inq_buff == NULL) goto mem_msg; + /* testing to see if 16-byte CDBs are already being used */ + if (h->cciss_read == CCISS_READ_16) { + cciss_read_capacity_16(h->ctlr, drv_index, 1, + &total_size, &block_size); + goto geo_inq; + } + cciss_read_capacity(ctlr, drv_index, 1, &total_size, &block_size); - /* total size = last LBA + 1 */ - /* FFFFFFFF + 1 = 0, cannot have a logical volume of size 0 */ - /* so we assume this volume this must be >2TB in size */ - if (total_size == (__u32) 0) { + /* if read_capacity returns all F's this volume is >2TB in size */ + /* so we switch to 16-byte CDB's for all read/write ops */ + if (total_size == 0xFFFFFFFFULL) { cciss_read_capacity_16(ctlr, drv_index, 1, &total_size, &block_size); h->cciss_read = CCISS_READ_16; @@ -1306,6 +1312,7 @@ static void cciss_update_drive_info(int ctlr, int drv_index) h->cciss_read = CCISS_READ_10; h->cciss_write = CCISS_WRITE_10; } +geo_inq: cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size, inq_buff, &h->drv[drv_index]); @@ -1432,7 +1439,7 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk) if (return_code == IO_OK) { listlength = - be32_to_cpu(*(__u32 *) ld_buff->LUNListLength); + be32_to_cpu(*(__be32 *) ld_buff->LUNListLength); } else { /* reading number of logical volumes failed */ printk(KERN_WARNING "cciss: report logical volume" " command failed\n"); @@ -1908,6 +1915,7 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, "does not support reading geometry\n"); drv->heads = 255; drv->sectors = 32; // Sectors per track + drv->cylinders = total_size + 1; drv->raid_level = RAID_UNKNOWN; } else { drv->heads = inq_buff->data_byte[6]; @@ -1917,13 +1925,14 @@ static void cciss_geometry_inquiry(int ctlr, int logvol, drv->raid_level = inq_buff->data_byte[8]; } drv->block_size = block_size; - drv->nr_blocks = total_size; + drv->nr_blocks = total_size + 1; t = drv->heads * drv->sectors; if (t > 1) { - unsigned rem = sector_div(total_size, t); + sector_t real_size = total_size + 1; + unsigned long rem = sector_div(real_size, t); if (rem) - total_size++; - drv->cylinders = total_size; + real_size++; + drv->cylinders = real_size; } } else { /* Get geometry failed */ printk(KERN_WARNING "cciss: reading geometry failed\n"); @@ -1953,16 +1962,16 @@ cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size, ctlr, buf, sizeof(ReadCapdata_struct), 1, logvol, 0, NULL, TYPE_CMD); if (return_code == IO_OK) { - *total_size = be32_to_cpu(*(__u32 *) buf->total_size)+1; - *block_size = be32_to_cpu(*(__u32 *) buf->block_size); + *total_size = be32_to_cpu(*(__be32 *) buf->total_size); + *block_size = be32_to_cpu(*(__be32 *) buf->block_size); } else { /* read capacity command failed */ printk(KERN_WARNING "cciss: read capacity failed\n"); *total_size = 0; *block_size = BLOCK_SIZE; } - if (*total_size != (__u32) 0) + if (*total_size != 0) printk(KERN_INFO " blocks= %llu block_size= %d\n", - (unsigned long long)*total_size, *block_size); + (unsigned long long)*total_size+1, *block_size); kfree(buf); return; } @@ -1989,15 +1998,15 @@ cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, 1, logvol, 0, NULL, TYPE_CMD); } if (return_code == IO_OK) { - *total_size = be64_to_cpu(*(__u64 *) buf->total_size)+1; - *block_size = be32_to_cpu(*(__u32 *) buf->block_size); + *total_size = be64_to_cpu(*(__be64 *) buf->total_size); + *block_size = be32_to_cpu(*(__be32 *) buf->block_size); } else { /* read capacity command failed */ printk(KERN_WARNING "cciss: read capacity failed\n"); *total_size = 0; *block_size = BLOCK_SIZE; } printk(KERN_INFO " blocks= %llu block_size= %d\n", - (unsigned long long)*total_size, *block_size); + (unsigned long long)*total_size+1, *block_size); kfree(buf); return; } @@ -3119,8 +3128,9 @@ static void cciss_getgeometry(int cntl_num) } cciss_read_capacity(cntl_num, i, 0, &total_size, &block_size); - /* total_size = last LBA + 1 */ - if(total_size == (__u32) 0) { + /* If read_capacity returns all F's the logical is >2TB */ + /* so we switch to 16-byte CDBs for all read/write ops */ + if(total_size == 0xFFFFFFFFULL) { cciss_read_capacity_16(cntl_num, i, 0, &total_size, &block_size); hba[cntl_num]->cciss_read = CCISS_READ_16; @@ -3395,7 +3405,7 @@ static int __devinit cciss_init_one(struct pci_dev *pdev, return -1; } -static void __devexit cciss_remove_one(struct pci_dev *pdev) +static void cciss_remove_one(struct pci_dev *pdev) { ctlr_info_t *tmp_ptr; int i, j; @@ -3413,15 +3423,35 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) "already be removed \n"); return; } + + remove_proc_entry(hba[i]->devname, proc_cciss); + unregister_blkdev(hba[i]->major, hba[i]->devname); + + /* remove it from the disk list */ + for (j = 0; j < CISS_MAX_LUN; j++) { + struct gendisk *disk = hba[i]->gendisk[j]; + if (disk) { + request_queue_t *q = disk->queue; + + if (disk->flags & GENHD_FL_UP) + del_gendisk(disk); + if (q) + blk_cleanup_queue(q); + } + } + + cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ + /* Turn board interrupts off and send the flush cache command */ /* sendcmd will turn off interrupt, and send the flush... * To write all data in the battery backed cache to disks */ memset(flush_buf, 0, 4); return_code = sendcmd(CCISS_CACHE_FLUSH, i, flush_buf, 4, 0, 0, 0, NULL, TYPE_CMD); - if (return_code != IO_OK) { - printk(KERN_WARNING "Error Flushing cache on controller %d\n", - i); + if (return_code == IO_OK) { + printk(KERN_INFO "Completed flushing cache on controller %d\n", i); + } else { + printk(KERN_WARNING "Error flushing cache on controller %d\n", i); } free_irq(hba[i]->intr[2], hba[i]); @@ -3433,22 +3463,6 @@ static void __devexit cciss_remove_one(struct pci_dev *pdev) #endif /* CONFIG_PCI_MSI */ iounmap(hba[i]->vaddr); - cciss_unregister_scsi(i); /* unhook from SCSI subsystem */ - unregister_blkdev(hba[i]->major, hba[i]->devname); - remove_proc_entry(hba[i]->devname, proc_cciss); - - /* remove it from the disk list */ - for (j = 0; j < CISS_MAX_LUN; j++) { - struct gendisk *disk = hba[i]->gendisk[j]; - if (disk) { - request_queue_t *q = disk->queue; - - if (disk->flags & GENHD_FL_UP) - del_gendisk(disk); - if (q) - blk_cleanup_queue(q); - } - } pci_free_consistent(hba[i]->pdev, hba[i]->nr_cmds * sizeof(CommandList_struct), hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); @@ -3472,6 +3486,7 @@ static struct pci_driver cciss_pci_driver = { .probe = cciss_init_one, .remove = __devexit_p(cciss_remove_one), .id_table = cciss_pci_device_id, /* id_table */ + .shutdown = cciss_remove_one, }; /* diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 3f1b382..5231ed7 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -297,17 +297,17 @@ static int initialising = 1; #define DRS (&drive_state[current_drive]) #define DRWE (&write_errors[current_drive]) #define FDCS (&fdc_state[fdc]) -#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags)) -#define SETF(x) (set_bit(x##_BIT, &DRS->flags)) -#define TESTF(x) (test_bit(x##_BIT, &DRS->flags)) +#define CLEARF(x) clear_bit(x##_BIT, &DRS->flags) +#define SETF(x) set_bit(x##_BIT, &DRS->flags) +#define TESTF(x) test_bit(x##_BIT, &DRS->flags) #define UDP (&drive_params[drive]) #define UDRS (&drive_state[drive]) #define UDRWE (&write_errors[drive]) #define UFDCS (&fdc_state[FDC(drive)]) -#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags)) -#define USETF(x) (set_bit(x##_BIT, &UDRS->flags)) -#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags)) +#define UCLEARF(x) clear_bit(x##_BIT, &UDRS->flags) +#define USETF(x) set_bit(x##_BIT, &UDRS->flags) +#define UTESTF(x) test_bit(x##_BIT, &UDRS->flags) #define DPRINT(format, args...) printk(DEVICE_NAME "%d: " format, current_drive , ## args) diff --git a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c index c852eed..1eeb8f2 100644 --- a/drivers/block/paride/pcd.c +++ b/drivers/block/paride/pcd.c @@ -140,7 +140,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_DLY}; #include <linux/blkdev.h> #include <asm/uaccess.h> -static spinlock_t pcd_lock; +static DEFINE_SPINLOCK(pcd_lock); module_param(verbose, bool, 0644); module_param(major, int, 0); diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c index 99e2c8c..31e0148 100644 --- a/drivers/block/paride/pd.c +++ b/drivers/block/paride/pd.c @@ -663,11 +663,11 @@ static enum action pd_identify(struct pd_unit *disk) return Fail; pi_read_block(disk->pi, pd_scratch, 512); disk->can_lba = pd_scratch[99] & 2; - disk->sectors = le16_to_cpu(*(u16 *) (pd_scratch + 12)); - disk->heads = le16_to_cpu(*(u16 *) (pd_scratch + 6)); - disk->cylinders = le16_to_cpu(*(u16 *) (pd_scratch + 2)); + disk->sectors = le16_to_cpu(*(__le16 *) (pd_scratch + 12)); + disk->heads = le16_to_cpu(*(__le16 *) (pd_scratch + 6)); + disk->cylinders = le16_to_cpu(*(__le16 *) (pd_scratch + 2)); if (disk->can_lba) - disk->capacity = le32_to_cpu(*(u32 *) (pd_scratch + 120)); + disk->capacity = le32_to_cpu(*(__le32 *) (pd_scratch + 120)); else disk->capacity = disk->sectors * disk->heads * disk->cylinders; diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c index 7cdaa19..5826508 100644 --- a/drivers/block/paride/pf.c +++ b/drivers/block/paride/pf.c @@ -154,7 +154,7 @@ enum {D_PRT, D_PRO, D_UNI, D_MOD, D_SLV, D_LUN, D_DLY}; #include <linux/blkpg.h> #include <asm/uaccess.h> -static spinlock_t pf_spin_lock; +static DEFINE_SPINLOCK(pf_spin_lock); module_param(verbose, bool, 0644); module_param(major, int, 0); diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 93fb6ed..f1b9dd7 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -765,7 +765,7 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command * goto out; } - rq->cmd_len = COMMAND_SIZE(rq->cmd[0]); + rq->cmd_len = COMMAND_SIZE(cgc->cmd[0]); memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE); if (sizeof(rq->cmd) > CDROM_PACKET_SIZE) memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE); @@ -777,7 +777,8 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command * rq->cmd_flags |= REQ_QUIET; blk_execute_rq(rq->q, pd->bdev->bd_disk, rq, 0); - ret = rq->errors; + if (rq->errors) + ret = -EIO; out: blk_put_request(rq); return ret; diff --git a/drivers/block/ub.c b/drivers/block/ub.c index 2098eff..746a118 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -2132,10 +2132,13 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev, if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { /* BULK in or out? */ - if (ep->bEndpointAddress & USB_DIR_IN) - ep_in = ep; - else - ep_out = ep; + if (ep->bEndpointAddress & USB_DIR_IN) { + if (ep_in == NULL) + ep_in = ep; + } else { + if (ep_out == NULL) + ep_out = ep; + } } } diff --git a/drivers/block/umem.c b/drivers/block/umem.c index dff3766..5872036 100644 --- a/drivers/block/umem.c +++ b/drivers/block/umem.c @@ -1179,8 +1179,10 @@ static int __init mm_init(void) return -ENOMEM; err = major_nr = register_blkdev(0, "umem"); - if (err < 0) + if (err < 0) { + pci_unregister_driver(&mm_pci_driver); return -EIO; + } for (i = 0; i < num_cards; i++) { mm_gendisk[i] = alloc_disk(1 << MM_SHIFT); @@ -1207,6 +1209,7 @@ static int __init mm_init(void) return 0; out: + pci_unregister_driver(&mm_pci_driver); unregister_blkdev(major_nr, "umem"); while (i--) put_disk(mm_gendisk[i]); diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 9256985..8919ccf 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -307,3 +307,5 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("Broadcom Blutonium firmware driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("BCM2033-MD.hex"); +MODULE_FIRMWARE("BCM2033-FW.bin"); diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 27cceb6..b990805 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -527,7 +527,7 @@ static int bfusb_send_frame(struct sk_buff *skb) buf[2] = (size == BFUSB_MAX_BLOCK_SIZE) ? 0 : size; memcpy(skb_put(nskb, 3), buf, 3); - memcpy(skb_put(nskb, size), skb->data + sent, size); + skb_copy_from_linear_data_offset(skb, sent, skb_put(nskb, size), size); sent += size; count -= size; @@ -801,3 +801,4 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); MODULE_DESCRIPTION("BlueFRITZ! USB driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("bfubase.frm"); diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index acfb6a4..851de4d 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -461,20 +461,20 @@ static void bluecard_receive(bluecard_info_t *info, unsigned int offset) switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/bpa10x.c b/drivers/bluetooth/bpa10x.c index 9fca651..e8ebd5d 100644 --- a/drivers/bluetooth/bpa10x.c +++ b/drivers/bluetooth/bpa10x.c @@ -231,7 +231,7 @@ static void bpa10x_wakeup(struct bpa10x_data *data) cr = (struct usb_ctrlrequest *) urb->setup_packet; cr->wLength = __cpu_to_le16(skb->len); - memcpy(urb->transfer_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); urb->transfer_buffer_length = skb->len; err = usb_submit_urb(urb, GFP_ATOMIC); @@ -250,7 +250,7 @@ static void bpa10x_wakeup(struct bpa10x_data *data) skb = skb_dequeue(&data->tx_queue); if (skb) { - memcpy(urb->transfer_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, urb->transfer_buffer, skb->len); urb->transfer_buffer_length = skb->len; err = usb_submit_urb(urb, GFP_ATOMIC); diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 34e5555..3951607 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -63,6 +63,7 @@ MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>, Jose Orlando Pereira <jop@di.uminho.pt>"); MODULE_DESCRIPTION("Bluetooth driver for the 3Com Bluetooth PCMCIA card"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("BT3CPCC.bin"); @@ -302,20 +303,20 @@ static void bt3c_receive(bt3c_info_t *info) switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index c1bce75..d7d2ea0d 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -250,20 +250,20 @@ static void btuart_receive(btuart_info_t *info) switch (info->rx_state) { case RECV_WAIT_EVENT_HEADER: - eh = (struct hci_event_hdr *)(info->rx_skb->data); + eh = hci_event_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = eh->plen; break; case RECV_WAIT_ACL_HEADER: - ah = (struct hci_acl_hdr *)(info->rx_skb->data); + ah = hci_acl_hdr(info->rx_skb); dlen = __le16_to_cpu(ah->dlen); info->rx_state = RECV_WAIT_DATA; info->rx_count = dlen; break; case RECV_WAIT_SCO_HEADER: - sh = (struct hci_sco_hdr *)(info->rx_skb->data); + sh = hci_sco_hdr(info->rx_skb); info->rx_state = RECV_WAIT_DATA; info->rx_count = sh->dlen; break; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 459aa97..7f9c54b 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -425,7 +425,7 @@ static int dtl1_hci_send_frame(struct sk_buff *skb) return -ENOMEM; skb_reserve(s, NSHL); - memcpy(skb_put(s, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(s, skb->len), skb->len); if (skb->len & 0x0001) *skb_put(s, 1) = 0; /* PAD */ diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 34f0afc..bfbae14 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -188,7 +188,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count) continue; case H4_W4_EVENT_HDR: - eh = (struct hci_event_hdr *) h4->rx_skb->data; + eh = hci_event_hdr(h4->rx_skb); BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); @@ -196,7 +196,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count) continue; case H4_W4_ACL_HDR: - ah = (struct hci_acl_hdr *) h4->rx_skb->data; + ah = hci_acl_hdr(h4->rx_skb); dlen = __le16_to_cpu(ah->dlen); BT_DBG("ACL header: dlen %d", dlen); @@ -205,7 +205,7 @@ static int h4_recv(struct hci_uart *hu, void *data, int count) continue; case H4_W4_SCO_HDR: - sh = (struct hci_sco_hdr *) h4->rx_skb->data; + sh = hci_sco_hdr(h4->rx_skb); BT_DBG("SCO header: dlen %d", sh->dlen); diff --git a/drivers/cdrom/viocd.c b/drivers/cdrom/viocd.c index dc13eba..44cd7b2 100644 --- a/drivers/cdrom/viocd.c +++ b/drivers/cdrom/viocd.c @@ -376,6 +376,25 @@ static int send_request(struct request *req) return 0; } +static void viocd_end_request(struct request *req, int uptodate) +{ + int nsectors = req->hard_nr_sectors; + + /* + * Make sure it's fully ended, and ensure that we process + * at least one sector. + */ + if (blk_pc_request(req)) + nsectors = (req->data_len + 511) >> 9; + if (!nsectors) + nsectors = 1; + + if (end_that_request_first(req, uptodate, nsectors)) + BUG(); + add_disk_randomness(req->rq_disk); + blkdev_dequeue_request(req); + end_that_request_last(req, uptodate); +} static int rwreq; @@ -385,11 +404,11 @@ static void do_viocd_request(request_queue_t *q) while ((rwreq == 0) && ((req = elv_next_request(q)) != NULL)) { if (!blk_fs_request(req)) - end_request(req, 0); + viocd_end_request(req, 0); else if (send_request(req) < 0) { printk(VIOCD_KERN_WARNING "unable to send message to OS/400!"); - end_request(req, 0); + viocd_end_request(req, 0); } else rwreq++; } @@ -601,9 +620,9 @@ return_complete: "with rc %d:0x%04X: %s\n", req, event->xRc, bevent->sub_result, err->msg); - end_request(req, 0); + viocd_end_request(req, 0); } else - end_request(req, 1); + viocd_end_request(req, 1); /* restart handling of incoming requests */ spin_unlock_irqrestore(&viocd_reqlock, flags); diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d0a6dc5..d0c978f 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -386,6 +386,39 @@ config AU1000_SERIAL_CONSOLE If you have an Alchemy AU1000 processor (MIPS based) and you want to use a console on a serial port, say Y. Otherwise, say N. +config SERIAL_DEC + bool "DECstation serial support" + depends on MACH_DECSTATION + default y + help + This selects whether you want to be asked about drivers for + DECstation serial ports. + + Note that the answer to this question won't directly affect the + kernel: saying N will just cause the configurator to skip all + the questions about DECstation serial ports. + +config SERIAL_DEC_CONSOLE + bool "Support for console on a DECstation serial port" + depends on SERIAL_DEC + default y + help + If you say Y here, it will be possible to use a serial port as the + system console (the system console is the device which receives all + kernel messages and warnings and which allows logins in single user + mode). Note that the firmware uses ttyS0 as the serial console on + the Maxine and ttyS2 on the others. + + If unsure, say Y. + +config ZS + bool "Z85C30 Serial Support" + depends on SERIAL_DEC + default y + help + Documentation on the Zilog 85C350 serial communications controller + is downloadable at <http://www.zilog.com/pdfs/serial/z85c30.pdf> + config A2232 tristate "Commodore A2232 serial support (EXPERIMENTAL)" depends on EXPERIMENTAL && ZORRO && BROKEN_ON_SMP @@ -1026,16 +1059,17 @@ config MMTIMER source "drivers/char/tpm/Kconfig" config TELCLOCK - tristate "Telecom clock driver for MPBL0010 ATCA SBC" + tristate "Telecom clock driver for ATCA SBC" depends on EXPERIMENTAL && X86 default n help - The telecom clock device is specific to the MPBL0010 ATCA computer and - allows direct userspace access to the configuration of the telecom clock - configuration settings. This device is used for hardware synchronization - across the ATCA backplane fabric. Upon loading, the driver exports a - sysfs directory, /sys/devices/platform/telco_clock, with a number of - files for controlling the behavior of this hardware. + The telecom clock device is specific to the MPCBL0010 and MPCBL0050 + ATCA computers and allows direct userspace access to the + configuration of the telecom clock configuration settings. This + device is used for hardware synchronization across the ATCA backplane + fabric. Upon loading, the driver exports a sysfs directory, + /sys/devices/platform/telco_clock, with a number of files for + controlling the behavior of this hardware. endmenu diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile index a0d04a2..627f542 100644 --- a/drivers/char/agp/Makefile +++ b/drivers/char/agp/Makefile @@ -1,7 +1,8 @@ agpgart-y := backend.o frontend.o generic.o isoch.o +agpgart-$(CONFIG_COMPAT) += compat_ioctl.o + obj-$(CONFIG_AGP) += agpgart.o -obj-$(CONFIG_COMPAT) += compat_ioctl.o obj-$(CONFIG_AGP_ALI) += ali-agp.o obj-$(CONFIG_AGP_ATI) += ati-agp.o obj-$(CONFIG_AGP_AMD) += amd-k7-agp.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h index 9bd68d9..fdbca25 100644 --- a/drivers/char/agp/agp.h +++ b/drivers/char/agp/agp.h @@ -93,12 +93,12 @@ struct aper_size_info_fixed { struct agp_bridge_driver { struct module *owner; - void *aperture_sizes; + const void *aperture_sizes; int num_aperture_sizes; enum aper_size_type size_type; int cant_use_aperture; int needs_scratch_page; - struct gatt_mask *masks; + const struct gatt_mask *masks; int (*fetch_size)(void); int (*configure)(void); void (*agp_enable)(struct agp_bridge_data *, u32); @@ -119,7 +119,7 @@ struct agp_bridge_driver { struct agp_bridge_data { const struct agp_version *version; - struct agp_bridge_driver *driver; + const struct agp_bridge_driver *driver; struct vm_operations_struct *vm_ops; void *previous_size; void *current_size; @@ -290,7 +290,7 @@ void agp3_generic_cleanup(void); /* aperture sizes have been standardised since v3 */ #define AGP_GENERIC_SIZES_ENTRIES 11 -extern struct aper_size_info_16 agp3_generic_sizes[]; +extern const struct aper_size_info_16 agp3_generic_sizes[]; #define virt_to_gart(x) (phys_to_gart(virt_to_phys(x))) #define gart_to_virt(x) (phys_to_virt(gart_to_phys(x))) diff --git a/drivers/char/agp/ali-agp.c b/drivers/char/agp/ali-agp.c index 98177a9..5b684fd 100644 --- a/drivers/char/agp/ali-agp.c +++ b/drivers/char/agp/ali-agp.c @@ -182,7 +182,7 @@ static void m1541_destroy_page(void * addr) /* Setup function */ -static struct aper_size_info_32 ali_generic_sizes[7] = +static const struct aper_size_info_32 ali_generic_sizes[7] = { {256, 65536, 6, 10}, {128, 32768, 5, 9}, @@ -193,7 +193,7 @@ static struct aper_size_info_32 ali_generic_sizes[7] = {4, 1024, 0, 3} }; -static struct agp_bridge_driver ali_generic_bridge = { +static const struct agp_bridge_driver ali_generic_bridge = { .owner = THIS_MODULE, .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, @@ -217,7 +217,7 @@ static struct agp_bridge_driver ali_generic_bridge = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver ali_m1541_bridge = { +static const struct agp_bridge_driver ali_m1541_bridge = { .owner = THIS_MODULE, .aperture_sizes = ali_generic_sizes, .size_type = U32_APER_SIZE, diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c index 3d8d448..e6c534e 100644 --- a/drivers/char/agp/amd-k7-agp.c +++ b/drivers/char/agp/amd-k7-agp.c @@ -344,7 +344,7 @@ static int amd_remove_memory(struct agp_memory *mem, off_t pg_start, int type) return 0; } -static struct aper_size_info_lvl2 amd_irongate_sizes[7] = +static const struct aper_size_info_lvl2 amd_irongate_sizes[7] = { {2048, 524288, 0x0000000c}, {1024, 262144, 0x0000000a}, @@ -355,12 +355,12 @@ static struct aper_size_info_lvl2 amd_irongate_sizes[7] = {32, 8192, 0x00000000} }; -static struct gatt_mask amd_irongate_masks[] = +static const struct gatt_mask amd_irongate_masks[] = { {.mask = 1, .type = 0} }; -static struct agp_bridge_driver amd_irongate_driver = { +static const struct agp_bridge_driver amd_irongate_driver = { .owner = THIS_MODULE, .aperture_sizes = amd_irongate_sizes, .size_type = LVL2_APER_SIZE, diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c index 636d984..4857204 100644 --- a/drivers/char/agp/amd64-agp.c +++ b/drivers/char/agp/amd64-agp.c @@ -192,7 +192,7 @@ static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table) } -static struct aper_size_info_32 amd_8151_sizes[7] = +static const struct aper_size_info_32 amd_8151_sizes[7] = { {2048, 524288, 9, 0x00000000 }, /* 0 0 0 0 0 0 */ {1024, 262144, 8, 0x00000400 }, /* 1 0 0 0 0 0 */ @@ -232,7 +232,7 @@ static void amd64_cleanup(void) } -static struct agp_bridge_driver amd_8151_driver = { +static const struct agp_bridge_driver amd_8151_driver = { .owner = THIS_MODULE, .aperture_sizes = amd_8151_sizes, .size_type = U32_APER_SIZE, diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c index 77c9ad6..780e59e 100644 --- a/drivers/char/agp/ati-agp.c +++ b/drivers/char/agp/ati-agp.c @@ -24,7 +24,7 @@ #define ATI_GART_CACHE_ENTRY_CNTRL 0x10 -static struct aper_size_info_lvl2 ati_generic_sizes[7] = +static const struct aper_size_info_lvl2 ati_generic_sizes[7] = { {2048, 524288, 0x0000000c}, {1024, 262144, 0x0000000a}, @@ -410,7 +410,7 @@ static int ati_free_gatt_table(struct agp_bridge_data *bridge) return 0; } -static struct agp_bridge_driver ati_generic_bridge = { +static const struct agp_bridge_driver ati_generic_bridge = { .owner = THIS_MODULE, .aperture_sizes = ati_generic_sizes, .size_type = LVL2_APER_SIZE, diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c index 658cb1a..df8da72 100644 --- a/drivers/char/agp/efficeon-agp.c +++ b/drivers/char/agp/efficeon-agp.c @@ -59,7 +59,7 @@ static struct _efficeon_private { unsigned long l1_table[EFFICEON_L1_SIZE]; } efficeon_private; -static struct gatt_mask efficeon_generic_masks[] = +static const struct gatt_mask efficeon_generic_masks[] = { {.mask = 0x00000001, .type = 0} }; @@ -70,7 +70,7 @@ static inline unsigned long efficeon_mask_memory(unsigned long addr) return addr | 0x00000001; } -static struct aper_size_info_lvl2 efficeon_generic_sizes[4] = +static const struct aper_size_info_lvl2 efficeon_generic_sizes[4] = { {256, 65536, 0}, {128, 32768, 32}, @@ -309,7 +309,7 @@ static int efficeon_remove_memory(struct agp_memory * mem, off_t pg_start, int t } -static struct agp_bridge_driver efficeon_driver = { +static const struct agp_bridge_driver efficeon_driver = { .owner = THIS_MODULE, .aperture_sizes = efficeon_generic_sizes, .size_type = LVL2_APER_SIZE, diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c index 7923337..f902d71 100644 --- a/drivers/char/agp/generic.c +++ b/drivers/char/agp/generic.c @@ -1340,7 +1340,7 @@ void agp3_generic_cleanup(void) } EXPORT_SYMBOL(agp3_generic_cleanup); -struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = +const struct aper_size_info_16 agp3_generic_sizes[AGP_GENERIC_SIZES_ENTRIES] = { {4096, 1048576, 10,0x000}, {2048, 524288, 9, 0x800}, diff --git a/drivers/char/agp/hp-agp.c b/drivers/char/agp/hp-agp.c index 847deab..bcdb149 100644 --- a/drivers/char/agp/hp-agp.c +++ b/drivers/char/agp/hp-agp.c @@ -419,7 +419,7 @@ hp_zx1_enable (struct agp_bridge_data *bridge, u32 mode) agp_device_command(command, (mode & AGP8X_MODE) != 0); } -struct agp_bridge_driver hp_zx1_driver = { +const struct agp_bridge_driver hp_zx1_driver = { .owner = THIS_MODULE, .size_type = FIXED_APER_SIZE, .configure = hp_zx1_configure, diff --git a/drivers/char/agp/i460-agp.c b/drivers/char/agp/i460-agp.c index 3e76186..53354bf 100644 --- a/drivers/char/agp/i460-agp.c +++ b/drivers/char/agp/i460-agp.c @@ -78,7 +78,7 @@ static struct { } *lp_desc; } i460; -static struct aper_size_info_8 i460_sizes[3] = +static const struct aper_size_info_8 i460_sizes[3] = { /* * The 32GB aperture is only available with a 4M GART page size. Due to the @@ -550,7 +550,7 @@ static unsigned long i460_mask_memory (struct agp_bridge_data *bridge, | (((addr & ~((1 << I460_IO_PAGE_SHIFT) - 1)) & 0xfffff000) >> 12); } -struct agp_bridge_driver intel_i460_driver = { +const struct agp_bridge_driver intel_i460_driver = { .owner = THIS_MODULE, .aperture_sizes = i460_sizes, .size_type = U8_APER_SIZE, diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c index 06b0bb6..55392a4 100644 --- a/drivers/char/agp/intel-agp.c +++ b/drivers/char/agp/intel-agp.c @@ -18,11 +18,14 @@ #define PCI_DEVICE_ID_INTEL_82965Q_IG 0x2992 #define PCI_DEVICE_ID_INTEL_82965G_HB 0x29A0 #define PCI_DEVICE_ID_INTEL_82965G_IG 0x29A2 +#define PCI_DEVICE_ID_INTEL_82965GM_HB 0x2A00 +#define PCI_DEVICE_ID_INTEL_82965GM_IG 0x2A02 #define IS_I965 (agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82946GZ_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_1_HB || \ agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965Q_HB || \ - agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB) + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965G_HB || \ + agp_bridge->dev->device == PCI_DEVICE_ID_INTEL_82965GM_HB) extern int agp_memory_reserved; @@ -63,7 +66,7 @@ extern int agp_memory_reserved; #define INTEL_I7505_AGPCTRL 0x70 #define INTEL_I7505_MCHCFG 0x50 -static struct aper_size_info_fixed intel_i810_sizes[] = +static const struct aper_size_info_fixed intel_i810_sizes[] = { {64, 16384, 4}, /* The 32M mode still requires a 64k gatt */ @@ -428,9 +431,8 @@ static void intel_i830_init_gtt_entries(void) if (IS_I965) { u32 pgetbl_ctl; + pgetbl_ctl = readl(intel_i830_private.registers+I810_PGETBL_CTL); - pci_read_config_dword(agp_bridge->dev, I810_PGETBL_CTL, - &pgetbl_ctl); /* The 965 has a field telling us the size of the GTT, * which may be larger than what is necessary to map the * aperture. @@ -1365,18 +1367,18 @@ static int intel_7505_configure(void) } /* Setup function */ -static struct gatt_mask intel_generic_masks[] = +static const struct gatt_mask intel_generic_masks[] = { {.mask = 0x00000017, .type = 0} }; -static struct aper_size_info_8 intel_815_sizes[2] = +static const struct aper_size_info_8 intel_815_sizes[2] = { {64, 16384, 4, 0}, {32, 8192, 3, 8}, }; -static struct aper_size_info_8 intel_8xx_sizes[7] = +static const struct aper_size_info_8 intel_8xx_sizes[7] = { {256, 65536, 6, 0}, {128, 32768, 5, 32}, @@ -1387,7 +1389,7 @@ static struct aper_size_info_8 intel_8xx_sizes[7] = {4, 1024, 0, 63} }; -static struct aper_size_info_16 intel_generic_sizes[7] = +static const struct aper_size_info_16 intel_generic_sizes[7] = { {256, 65536, 6, 0}, {128, 32768, 5, 32}, @@ -1398,7 +1400,7 @@ static struct aper_size_info_16 intel_generic_sizes[7] = {4, 1024, 0, 63} }; -static struct aper_size_info_8 intel_830mp_sizes[4] = +static const struct aper_size_info_8 intel_830mp_sizes[4] = { {256, 65536, 6, 0}, {128, 32768, 5, 32}, @@ -1406,7 +1408,7 @@ static struct aper_size_info_8 intel_830mp_sizes[4] = {32, 8192, 3, 56} }; -static struct agp_bridge_driver intel_generic_driver = { +static const struct agp_bridge_driver intel_generic_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_generic_sizes, .size_type = U16_APER_SIZE, @@ -1430,7 +1432,7 @@ static struct agp_bridge_driver intel_generic_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_810_driver = { +static const struct agp_bridge_driver intel_810_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i810_sizes, .size_type = FIXED_APER_SIZE, @@ -1455,7 +1457,7 @@ static struct agp_bridge_driver intel_810_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_815_driver = { +static const struct agp_bridge_driver intel_815_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_815_sizes, .size_type = U8_APER_SIZE, @@ -1479,7 +1481,7 @@ static struct agp_bridge_driver intel_815_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_830_driver = { +static const struct agp_bridge_driver intel_830_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, @@ -1504,7 +1506,7 @@ static struct agp_bridge_driver intel_830_driver = { .agp_type_to_mask_type = intel_i830_type_to_mask_type, }; -static struct agp_bridge_driver intel_820_driver = { +static const struct agp_bridge_driver intel_820_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1528,7 +1530,7 @@ static struct agp_bridge_driver intel_820_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_830mp_driver = { +static const struct agp_bridge_driver intel_830mp_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_830mp_sizes, .size_type = U8_APER_SIZE, @@ -1552,7 +1554,7 @@ static struct agp_bridge_driver intel_830mp_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_840_driver = { +static const struct agp_bridge_driver intel_840_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1576,7 +1578,7 @@ static struct agp_bridge_driver intel_840_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_845_driver = { +static const struct agp_bridge_driver intel_845_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1600,7 +1602,7 @@ static struct agp_bridge_driver intel_845_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_850_driver = { +static const struct agp_bridge_driver intel_850_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1624,7 +1626,7 @@ static struct agp_bridge_driver intel_850_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_860_driver = { +static const struct agp_bridge_driver intel_860_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1648,7 +1650,7 @@ static struct agp_bridge_driver intel_860_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver intel_915_driver = { +static const struct agp_bridge_driver intel_915_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, @@ -1673,7 +1675,7 @@ static struct agp_bridge_driver intel_915_driver = { .agp_type_to_mask_type = intel_i830_type_to_mask_type, }; -static struct agp_bridge_driver intel_i965_driver = { +static const struct agp_bridge_driver intel_i965_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_i830_sizes, .size_type = FIXED_APER_SIZE, @@ -1698,7 +1700,7 @@ static struct agp_bridge_driver intel_i965_driver = { .agp_type_to_mask_type = intel_i830_type_to_mask_type, }; -static struct agp_bridge_driver intel_7505_driver = { +static const struct agp_bridge_driver intel_7505_driver = { .owner = THIS_MODULE, .aperture_sizes = intel_8xx_sizes, .size_type = U8_APER_SIZE, @@ -1921,7 +1923,13 @@ static int __devinit agp_intel_probe(struct pci_dev *pdev, bridge->driver = &intel_845_driver; name = "965G"; break; - + case PCI_DEVICE_ID_INTEL_82965GM_HB: + if (find_i830(PCI_DEVICE_ID_INTEL_82965GM_IG)) + bridge->driver = &intel_i965_driver; + else + bridge->driver = &intel_845_driver; + name = "965GM"; + break; case PCI_DEVICE_ID_INTEL_7505_0: bridge->driver = &intel_7505_driver; name = "E7505"; @@ -2080,6 +2088,7 @@ static struct pci_device_id agp_intel_pci_table[] = { ID(PCI_DEVICE_ID_INTEL_82965G_1_HB), ID(PCI_DEVICE_ID_INTEL_82965Q_HB), ID(PCI_DEVICE_ID_INTEL_82965G_HB), + ID(PCI_DEVICE_ID_INTEL_82965GM_HB), { } }; diff --git a/drivers/char/agp/nvidia-agp.c b/drivers/char/agp/nvidia-agp.c index 2563286..0c9dab5 100644 --- a/drivers/char/agp/nvidia-agp.c +++ b/drivers/char/agp/nvidia-agp.c @@ -272,7 +272,7 @@ static void nvidia_tlbflush(struct agp_memory *mem) } -static struct aper_size_info_8 nvidia_generic_sizes[5] = +static const struct aper_size_info_8 nvidia_generic_sizes[5] = { {512, 131072, 7, 0}, {256, 65536, 6, 8}, @@ -283,13 +283,13 @@ static struct aper_size_info_8 nvidia_generic_sizes[5] = }; -static struct gatt_mask nvidia_generic_masks[] = +static const struct gatt_mask nvidia_generic_masks[] = { { .mask = 1, .type = 0} }; -static struct agp_bridge_driver nvidia_driver = { +static const struct agp_bridge_driver nvidia_driver = { .owner = THIS_MODULE, .aperture_sizes = nvidia_generic_sizes, .size_type = U8_APER_SIZE, diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index b7b4590..3d83b46 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -210,7 +210,7 @@ parisc_agp_enable(struct agp_bridge_data *bridge, u32 mode) agp_device_command(command, (mode & AGP8X_MODE) != 0); } -struct agp_bridge_driver parisc_agp_driver = { +static const struct agp_bridge_driver parisc_agp_driver = { .owner = THIS_MODULE, .size_type = FIXED_APER_SIZE, .configure = parisc_agp_configure, @@ -236,7 +236,7 @@ static int __init agp_ioc_init(void __iomem *ioc_regs) { struct _parisc_agp_info *info = &parisc_agp_info; - u64 *iova_base, *io_pdir, io_tlb_ps; + u64 iova_base, *io_pdir, io_tlb_ps; int io_tlb_shift; printk(KERN_INFO DRVPFX "IO PDIR shared with sba_iommu\n"); diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c index 92d1dc4..ee8f50e 100644 --- a/drivers/char/agp/sgi-agp.c +++ b/drivers/char/agp/sgi-agp.c @@ -247,7 +247,7 @@ static struct agp_bridge_data *sgi_tioca_find_bridge(struct pci_dev *pdev) return bridge; } -struct agp_bridge_driver sgi_tioca_driver = { +const struct agp_bridge_driver sgi_tioca_driver = { .owner = THIS_MODULE, .size_type = U16_APER_SIZE, .configure = sgi_tioca_configure, diff --git a/drivers/char/agp/sis-agp.c b/drivers/char/agp/sis-agp.c index 60342b7..125f428 100644 --- a/drivers/char/agp/sis-agp.c +++ b/drivers/char/agp/sis-agp.c @@ -108,7 +108,7 @@ static void sis_delayed_enable(struct agp_bridge_data *bridge, u32 mode) } } -static struct aper_size_info_8 sis_generic_sizes[7] = +static const struct aper_size_info_8 sis_generic_sizes[7] = { {256, 65536, 6, 99}, {128, 32768, 5, 83}, diff --git a/drivers/char/agp/sworks-agp.c b/drivers/char/agp/sworks-agp.c index 9f5ae77..55212a3 100644 --- a/drivers/char/agp/sworks-agp.c +++ b/drivers/char/agp/sworks-agp.c @@ -385,12 +385,12 @@ static int serverworks_remove_memory(struct agp_memory *mem, off_t pg_start, return 0; } -static struct gatt_mask serverworks_masks[] = +static const struct gatt_mask serverworks_masks[] = { {.mask = 1, .type = 0} }; -static struct aper_size_info_lvl2 serverworks_sizes[7] = +static const struct aper_size_info_lvl2 serverworks_sizes[7] = { {2048, 524288, 0x80000000}, {1024, 262144, 0xc0000000}, @@ -423,7 +423,7 @@ static void serverworks_agp_enable(struct agp_bridge_data *bridge, u32 mode) agp_device_command(command, 0); } -static struct agp_bridge_driver sworks_driver = { +static const struct agp_bridge_driver sworks_driver = { .owner = THIS_MODULE, .aperture_sizes = serverworks_sizes, .size_type = LVL2_APER_SIZE, diff --git a/drivers/char/agp/uninorth-agp.c b/drivers/char/agp/uninorth-agp.c index 6c45702..91b0621 100644 --- a/drivers/char/agp/uninorth-agp.c +++ b/drivers/char/agp/uninorth-agp.c @@ -460,7 +460,7 @@ void null_cache_flush(void) /* Setup function */ -static struct aper_size_info_32 uninorth_sizes[7] = +static const struct aper_size_info_32 uninorth_sizes[7] = { #if 0 /* Not sure uninorth supports that high aperture sizes */ {256, 65536, 6, 64}, @@ -477,7 +477,7 @@ static struct aper_size_info_32 uninorth_sizes[7] = * Not sure that u3 supports that high aperture sizes but it * would strange if it did not :) */ -static struct aper_size_info_32 u3_sizes[8] = +static const struct aper_size_info_32 u3_sizes[8] = { {512, 131072, 7, 128}, {256, 65536, 6, 64}, @@ -489,7 +489,7 @@ static struct aper_size_info_32 u3_sizes[8] = {4, 1024, 0, 1} }; -struct agp_bridge_driver uninorth_agp_driver = { +const struct agp_bridge_driver uninorth_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)uninorth_sizes, .size_type = U32_APER_SIZE, @@ -514,7 +514,7 @@ struct agp_bridge_driver uninorth_agp_driver = { .cant_use_aperture = 1, }; -struct agp_bridge_driver u3_agp_driver = { +const struct agp_bridge_driver u3_agp_driver = { .owner = THIS_MODULE, .aperture_sizes = (void *)u3_sizes, .size_type = U32_APER_SIZE, diff --git a/drivers/char/agp/via-agp.c b/drivers/char/agp/via-agp.c index 2e7c043..a2bb4ec 100644 --- a/drivers/char/agp/via-agp.c +++ b/drivers/char/agp/via-agp.c @@ -89,7 +89,7 @@ static void via_tlbflush(struct agp_memory *mem) } -static struct aper_size_info_8 via_generic_sizes[9] = +static const struct aper_size_info_8 via_generic_sizes[9] = { {256, 65536, 6, 0}, {128, 32768, 5, 128}, @@ -170,7 +170,7 @@ static void via_tlbflush_agp3(struct agp_memory *mem) } -static struct agp_bridge_driver via_agp3_driver = { +static const struct agp_bridge_driver via_agp3_driver = { .owner = THIS_MODULE, .aperture_sizes = agp3_generic_sizes, .size_type = U8_APER_SIZE, @@ -194,7 +194,7 @@ static struct agp_bridge_driver via_agp3_driver = { .agp_type_to_mask_type = agp_generic_type_to_mask_type, }; -static struct agp_bridge_driver via_driver = { +static const struct agp_bridge_driver via_driver = { .owner = THIS_MODULE, .aperture_sizes = via_generic_sizes, .size_type = U8_APER_SIZE, diff --git a/drivers/char/briq_panel.c b/drivers/char/briq_panel.c index 8dcf9d2..c70d52a 100644 --- a/drivers/char/briq_panel.c +++ b/drivers/char/briq_panel.c @@ -202,13 +202,16 @@ static struct miscdevice briq_panel_miscdev = { static int __init briq_panel_init(void) { - struct device_node *root = find_path_device("/"); + struct device_node *root = of_find_node_by_path("/"); const char *machine; int i; machine = get_property(root, "model", NULL); - if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) + if (!machine || strncmp(machine, "TotalImpact,BRIQ-1", 18) != 0) { + of_node_put(root); return -ENODEV; + } + of_node_put(root); printk(KERN_INFO "briq_panel: v%s Dr. Karsten Jeppesen (kj@totalimpact.com)\n", diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c index 54df355..16dc5d1 100644 --- a/drivers/char/cyclades.c +++ b/drivers/char/cyclades.c @@ -3501,6 +3501,7 @@ get_serial_info(struct cyclades_port *info, tmp.irq = cinfo->irq; tmp.flags = info->flags; tmp.close_delay = info->close_delay; + tmp.closing_wait = info->closing_wait; tmp.baud_base = info->baud; tmp.custom_divisor = info->custom_divisor; tmp.hub6 = 0; /*!!! */ diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile index 3ad0f64..6915a05 100644 --- a/drivers/char/drm/Makefile +++ b/drivers/char/drm/Makefile @@ -15,7 +15,6 @@ i810-objs := i810_drv.o i810_dma.o i830-objs := i830_drv.o i830_dma.o i830_irq.o i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o -ffb-objs := ffb_drv.o ffb_context.o sis-objs := sis_drv.o sis_mm.o savage-objs := savage_drv.o savage_bci.o savage_state.o via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o @@ -36,7 +35,6 @@ obj-$(CONFIG_DRM_MGA) += mga.o obj-$(CONFIG_DRM_I810) += i810.o obj-$(CONFIG_DRM_I830) += i830.o obj-$(CONFIG_DRM_I915) += i915.o -obj-$(CONFIG_DRM_FFB) += ffb.o obj-$(CONFIG_DRM_SIS) += sis.o obj-$(CONFIG_DRM_SAVAGE)+= savage.o obj-$(CONFIG_DRM_VIA) +=via.o diff --git a/drivers/char/drm/ffb_context.c b/drivers/char/drm/ffb_context.c deleted file mode 100644 index ac9ab40..0000000 --- a/drivers/char/drm/ffb_context.c +++ /dev/null @@ -1,544 +0,0 @@ -/* $Id: ffb_context.c,v 1.5 2001/08/09 17:47:51 davem Exp $ - * ffb_context.c: Creator/Creator3D DRI/DRM context switching. - * - * Copyright (C) 2000 David S. Miller (davem@redhat.com) - * - * Almost entirely stolen from tdfx_context.c, see there - * for authors. - */ - -#include <asm/upa.h> - -#include "ffb.h" -#include "drmP.h" - -#include "ffb_drv.h" - -static int DRM(alloc_queue) (drm_device_t * dev, int is_2d_only) { - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - int i; - - for (i = 0; i < FFB_MAX_CTXS; i++) { - if (fpriv->hw_state[i] == NULL) - break; - } - if (i == FFB_MAX_CTXS) - return -1; - - fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL); - if (fpriv->hw_state[i] == NULL) - return -1; - - fpriv->hw_state[i]->is_2d_only = is_2d_only; - - /* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */ - return i + 1; -} - -static void ffb_save_context(ffb_dev_priv_t * fpriv, int idx) -{ - ffb_fbcPtr ffb = fpriv->regs; - struct ffb_hw_context *ctx; - int i; - - ctx = fpriv->hw_state[idx - 1]; - if (idx == 0 || ctx == NULL) - return; - - if (ctx->is_2d_only) { - /* 2D applications only care about certain pieces - * of state. - */ - ctx->drawop = upa_readl(&ffb->drawop); - ctx->ppc = upa_readl(&ffb->ppc); - ctx->wid = upa_readl(&ffb->wid); - ctx->fg = upa_readl(&ffb->fg); - ctx->bg = upa_readl(&ffb->bg); - ctx->xclip = upa_readl(&ffb->xclip); - ctx->fbc = upa_readl(&ffb->fbc); - ctx->rop = upa_readl(&ffb->rop); - ctx->cmp = upa_readl(&ffb->cmp); - ctx->matchab = upa_readl(&ffb->matchab); - ctx->magnab = upa_readl(&ffb->magnab); - ctx->pmask = upa_readl(&ffb->pmask); - ctx->xpmask = upa_readl(&ffb->xpmask); - ctx->lpat = upa_readl(&ffb->lpat); - ctx->fontxy = upa_readl(&ffb->fontxy); - ctx->fontw = upa_readl(&ffb->fontw); - ctx->fontinc = upa_readl(&ffb->fontinc); - - /* stencil/stencilctl only exists on FFB2+ and later - * due to the introduction of 3DRAM-III. - */ - if (fpriv->ffb_type == ffb2_vertical_plus || - fpriv->ffb_type == ffb2_horizontal_plus) { - ctx->stencil = upa_readl(&ffb->stencil); - ctx->stencilctl = upa_readl(&ffb->stencilctl); - } - - for (i = 0; i < 32; i++) - ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); - ctx->ucsr = upa_readl(&ffb->ucsr); - return; - } - - /* Fetch drawop. */ - ctx->drawop = upa_readl(&ffb->drawop); - - /* If we were saving the vertex registers, this is where - * we would do it. We would save 32 32-bit words starting - * at ffb->suvtx. - */ - - /* Capture rendering attributes. */ - - ctx->ppc = upa_readl(&ffb->ppc); /* Pixel Processor Control */ - ctx->wid = upa_readl(&ffb->wid); /* Current WID */ - ctx->fg = upa_readl(&ffb->fg); /* Constant FG color */ - ctx->bg = upa_readl(&ffb->bg); /* Constant BG color */ - ctx->consty = upa_readl(&ffb->consty); /* Constant Y */ - ctx->constz = upa_readl(&ffb->constz); /* Constant Z */ - ctx->xclip = upa_readl(&ffb->xclip); /* X plane clip */ - ctx->dcss = upa_readl(&ffb->dcss); /* Depth Cue Scale Slope */ - ctx->vclipmin = upa_readl(&ffb->vclipmin); /* Primary XY clip, minimum */ - ctx->vclipmax = upa_readl(&ffb->vclipmax); /* Primary XY clip, maximum */ - ctx->vclipzmin = upa_readl(&ffb->vclipzmin); /* Primary Z clip, minimum */ - ctx->vclipzmax = upa_readl(&ffb->vclipzmax); /* Primary Z clip, maximum */ - ctx->dcsf = upa_readl(&ffb->dcsf); /* Depth Cue Scale Front Bound */ - ctx->dcsb = upa_readl(&ffb->dcsb); /* Depth Cue Scale Back Bound */ - ctx->dczf = upa_readl(&ffb->dczf); /* Depth Cue Scale Z Front */ - ctx->dczb = upa_readl(&ffb->dczb); /* Depth Cue Scale Z Back */ - ctx->blendc = upa_readl(&ffb->blendc); /* Alpha Blend Control */ - ctx->blendc1 = upa_readl(&ffb->blendc1); /* Alpha Blend Color 1 */ - ctx->blendc2 = upa_readl(&ffb->blendc2); /* Alpha Blend Color 2 */ - ctx->fbc = upa_readl(&ffb->fbc); /* Frame Buffer Control */ - ctx->rop = upa_readl(&ffb->rop); /* Raster Operation */ - ctx->cmp = upa_readl(&ffb->cmp); /* Compare Controls */ - ctx->matchab = upa_readl(&ffb->matchab); /* Buffer A/B Match Ops */ - ctx->matchc = upa_readl(&ffb->matchc); /* Buffer C Match Ops */ - ctx->magnab = upa_readl(&ffb->magnab); /* Buffer A/B Magnitude Ops */ - ctx->magnc = upa_readl(&ffb->magnc); /* Buffer C Magnitude Ops */ - ctx->pmask = upa_readl(&ffb->pmask); /* RGB Plane Mask */ - ctx->xpmask = upa_readl(&ffb->xpmask); /* X Plane Mask */ - ctx->ypmask = upa_readl(&ffb->ypmask); /* Y Plane Mask */ - ctx->zpmask = upa_readl(&ffb->zpmask); /* Z Plane Mask */ - - /* Auxiliary Clips. */ - ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min); - ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max); - ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min); - ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max); - ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min); - ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max); - ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min); - ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max); - - ctx->lpat = upa_readl(&ffb->lpat); /* Line Pattern */ - ctx->fontxy = upa_readl(&ffb->fontxy); /* XY Font Coordinate */ - ctx->fontw = upa_readl(&ffb->fontw); /* Font Width */ - ctx->fontinc = upa_readl(&ffb->fontinc); /* Font X/Y Increment */ - - /* These registers/features only exist on FFB2 and later chips. */ - if (fpriv->ffb_type >= ffb2_prototype) { - ctx->dcss1 = upa_readl(&ffb->dcss1); /* Depth Cue Scale Slope 1 */ - ctx->dcss2 = upa_readl(&ffb->dcss2); /* Depth Cue Scale Slope 2 */ - ctx->dcss2 = upa_readl(&ffb->dcss3); /* Depth Cue Scale Slope 3 */ - ctx->dcs2 = upa_readl(&ffb->dcs2); /* Depth Cue Scale 2 */ - ctx->dcs3 = upa_readl(&ffb->dcs3); /* Depth Cue Scale 3 */ - ctx->dcs4 = upa_readl(&ffb->dcs4); /* Depth Cue Scale 4 */ - ctx->dcd2 = upa_readl(&ffb->dcd2); /* Depth Cue Depth 2 */ - ctx->dcd3 = upa_readl(&ffb->dcd3); /* Depth Cue Depth 3 */ - ctx->dcd4 = upa_readl(&ffb->dcd4); /* Depth Cue Depth 4 */ - - /* And stencil/stencilctl only exists on FFB2+ and later - * due to the introduction of 3DRAM-III. - */ - if (fpriv->ffb_type == ffb2_vertical_plus || - fpriv->ffb_type == ffb2_horizontal_plus) { - ctx->stencil = upa_readl(&ffb->stencil); - ctx->stencilctl = upa_readl(&ffb->stencilctl); - } - } - - /* Save the 32x32 area pattern. */ - for (i = 0; i < 32; i++) - ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); - - /* Finally, stash away the User Constol/Status Register. */ - ctx->ucsr = upa_readl(&ffb->ucsr); -} - -static void ffb_restore_context(ffb_dev_priv_t * fpriv, int old, int idx) -{ - ffb_fbcPtr ffb = fpriv->regs; - struct ffb_hw_context *ctx; - int i; - - ctx = fpriv->hw_state[idx - 1]; - if (idx == 0 || ctx == NULL) - return; - - if (ctx->is_2d_only) { - /* 2D applications only care about certain pieces - * of state. - */ - upa_writel(ctx->drawop, &ffb->drawop); - - /* If we were restoring the vertex registers, this is where - * we would do it. We would restore 32 32-bit words starting - * at ffb->suvtx. - */ - - upa_writel(ctx->ppc, &ffb->ppc); - upa_writel(ctx->wid, &ffb->wid); - upa_writel(ctx->fg, &ffb->fg); - upa_writel(ctx->bg, &ffb->bg); - upa_writel(ctx->xclip, &ffb->xclip); - upa_writel(ctx->fbc, &ffb->fbc); - upa_writel(ctx->rop, &ffb->rop); - upa_writel(ctx->cmp, &ffb->cmp); - upa_writel(ctx->matchab, &ffb->matchab); - upa_writel(ctx->magnab, &ffb->magnab); - upa_writel(ctx->pmask, &ffb->pmask); - upa_writel(ctx->xpmask, &ffb->xpmask); - upa_writel(ctx->lpat, &ffb->lpat); - upa_writel(ctx->fontxy, &ffb->fontxy); - upa_writel(ctx->fontw, &ffb->fontw); - upa_writel(ctx->fontinc, &ffb->fontinc); - - /* stencil/stencilctl only exists on FFB2+ and later - * due to the introduction of 3DRAM-III. - */ - if (fpriv->ffb_type == ffb2_vertical_plus || - fpriv->ffb_type == ffb2_horizontal_plus) { - upa_writel(ctx->stencil, &ffb->stencil); - upa_writel(ctx->stencilctl, &ffb->stencilctl); - upa_writel(0x80000000, &ffb->fbc); - upa_writel((ctx->stencilctl | 0x80000), - &ffb->rawstencilctl); - upa_writel(ctx->fbc, &ffb->fbc); - } - - for (i = 0; i < 32; i++) - upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); - upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); - return; - } - - /* Restore drawop. */ - upa_writel(ctx->drawop, &ffb->drawop); - - /* If we were restoring the vertex registers, this is where - * we would do it. We would restore 32 32-bit words starting - * at ffb->suvtx. - */ - - /* Restore rendering attributes. */ - - upa_writel(ctx->ppc, &ffb->ppc); /* Pixel Processor Control */ - upa_writel(ctx->wid, &ffb->wid); /* Current WID */ - upa_writel(ctx->fg, &ffb->fg); /* Constant FG color */ - upa_writel(ctx->bg, &ffb->bg); /* Constant BG color */ - upa_writel(ctx->consty, &ffb->consty); /* Constant Y */ - upa_writel(ctx->constz, &ffb->constz); /* Constant Z */ - upa_writel(ctx->xclip, &ffb->xclip); /* X plane clip */ - upa_writel(ctx->dcss, &ffb->dcss); /* Depth Cue Scale Slope */ - upa_writel(ctx->vclipmin, &ffb->vclipmin); /* Primary XY clip, minimum */ - upa_writel(ctx->vclipmax, &ffb->vclipmax); /* Primary XY clip, maximum */ - upa_writel(ctx->vclipzmin, &ffb->vclipzmin); /* Primary Z clip, minimum */ - upa_writel(ctx->vclipzmax, &ffb->vclipzmax); /* Primary Z clip, maximum */ - upa_writel(ctx->dcsf, &ffb->dcsf); /* Depth Cue Scale Front Bound */ - upa_writel(ctx->dcsb, &ffb->dcsb); /* Depth Cue Scale Back Bound */ - upa_writel(ctx->dczf, &ffb->dczf); /* Depth Cue Scale Z Front */ - upa_writel(ctx->dczb, &ffb->dczb); /* Depth Cue Scale Z Back */ - upa_writel(ctx->blendc, &ffb->blendc); /* Alpha Blend Control */ - upa_writel(ctx->blendc1, &ffb->blendc1); /* Alpha Blend Color 1 */ - upa_writel(ctx->blendc2, &ffb->blendc2); /* Alpha Blend Color 2 */ - upa_writel(ctx->fbc, &ffb->fbc); /* Frame Buffer Control */ - upa_writel(ctx->rop, &ffb->rop); /* Raster Operation */ - upa_writel(ctx->cmp, &ffb->cmp); /* Compare Controls */ - upa_writel(ctx->matchab, &ffb->matchab); /* Buffer A/B Match Ops */ - upa_writel(ctx->matchc, &ffb->matchc); /* Buffer C Match Ops */ - upa_writel(ctx->magnab, &ffb->magnab); /* Buffer A/B Magnitude Ops */ - upa_writel(ctx->magnc, &ffb->magnc); /* Buffer C Magnitude Ops */ - upa_writel(ctx->pmask, &ffb->pmask); /* RGB Plane Mask */ - upa_writel(ctx->xpmask, &ffb->xpmask); /* X Plane Mask */ - upa_writel(ctx->ypmask, &ffb->ypmask); /* Y Plane Mask */ - upa_writel(ctx->zpmask, &ffb->zpmask); /* Z Plane Mask */ - - /* Auxiliary Clips. */ - upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min); - upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max); - upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min); - upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max); - upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min); - upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max); - upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min); - upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max); - - upa_writel(ctx->lpat, &ffb->lpat); /* Line Pattern */ - upa_writel(ctx->fontxy, &ffb->fontxy); /* XY Font Coordinate */ - upa_writel(ctx->fontw, &ffb->fontw); /* Font Width */ - upa_writel(ctx->fontinc, &ffb->fontinc); /* Font X/Y Increment */ - - /* These registers/features only exist on FFB2 and later chips. */ - if (fpriv->ffb_type >= ffb2_prototype) { - upa_writel(ctx->dcss1, &ffb->dcss1); /* Depth Cue Scale Slope 1 */ - upa_writel(ctx->dcss2, &ffb->dcss2); /* Depth Cue Scale Slope 2 */ - upa_writel(ctx->dcss3, &ffb->dcss2); /* Depth Cue Scale Slope 3 */ - upa_writel(ctx->dcs2, &ffb->dcs2); /* Depth Cue Scale 2 */ - upa_writel(ctx->dcs3, &ffb->dcs3); /* Depth Cue Scale 3 */ - upa_writel(ctx->dcs4, &ffb->dcs4); /* Depth Cue Scale 4 */ - upa_writel(ctx->dcd2, &ffb->dcd2); /* Depth Cue Depth 2 */ - upa_writel(ctx->dcd3, &ffb->dcd3); /* Depth Cue Depth 3 */ - upa_writel(ctx->dcd4, &ffb->dcd4); /* Depth Cue Depth 4 */ - - /* And stencil/stencilctl only exists on FFB2+ and later - * due to the introduction of 3DRAM-III. - */ - if (fpriv->ffb_type == ffb2_vertical_plus || - fpriv->ffb_type == ffb2_horizontal_plus) { - /* Unfortunately, there is a hardware bug on - * the FFB2+ chips which prevents a normal write - * to the stencil control register from working - * as it should. - * - * The state controlled by the FFB stencilctl register - * really gets transferred to the per-buffer instances - * of the stencilctl register in the 3DRAM chips. - * - * The bug is that FFB does not update buffer C correctly, - * so we have to do it by hand for them. - */ - - /* This will update buffers A and B. */ - upa_writel(ctx->stencil, &ffb->stencil); - upa_writel(ctx->stencilctl, &ffb->stencilctl); - - /* Force FFB to use buffer C 3dram regs. */ - upa_writel(0x80000000, &ffb->fbc); - upa_writel((ctx->stencilctl | 0x80000), - &ffb->rawstencilctl); - - /* Now restore the correct FBC controls. */ - upa_writel(ctx->fbc, &ffb->fbc); - } - } - - /* Restore the 32x32 area pattern. */ - for (i = 0; i < 32; i++) - upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); - - /* Finally, stash away the User Constol/Status Register. - * The only state we really preserve here is the picking - * control. - */ - upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); -} - -#define FFB_UCSR_FB_BUSY 0x01000000 -#define FFB_UCSR_RP_BUSY 0x02000000 -#define FFB_UCSR_ALL_BUSY (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY) - -static void FFBWait(ffb_fbcPtr ffb) -{ - int limit = 100000; - - do { - u32 regval = upa_readl(&ffb->ucsr); - - if ((regval & FFB_UCSR_ALL_BUSY) == 0) - break; - } while (--limit); -} - -int ffb_driver_context_switch(drm_device_t * dev, int old, int new) -{ - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - -#ifdef DRM_DMA_HISTOGRAM - dev->ctx_start = get_cycles(); -#endif - - DRM_DEBUG("Context switch from %d to %d\n", old, new); - - if (new == dev->last_context || dev->last_context == 0) { - dev->last_context = new; - return 0; - } - - FFBWait(fpriv->regs); - ffb_save_context(fpriv, old); - ffb_restore_context(fpriv, old, new); - FFBWait(fpriv->regs); - - dev->last_context = new; - - return 0; -} - -int ffb_driver_resctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_ctx_res_t res; - drm_ctx_t ctx; - int i; - - DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); - if (copy_from_user(&res, (drm_ctx_res_t __user *) arg, sizeof(res))) - return -EFAULT; - if (res.count >= DRM_RESERVED_CONTEXTS) { - memset(&ctx, 0, sizeof(ctx)); - for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { - ctx.handle = i; - if (copy_to_user(&res.contexts[i], &i, sizeof(i))) - return -EFAULT; - } - } - res.count = DRM_RESERVED_CONTEXTS; - if (copy_to_user((drm_ctx_res_t __user *) arg, &res, sizeof(res))) - return -EFAULT; - return 0; -} - -int ffb_driver_addctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - drm_ctx_t ctx; - int idx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - idx = DRM(alloc_queue) (dev, (ctx.flags & _DRM_CONTEXT_2DONLY)); - if (idx < 0) - return -ENFILE; - - DRM_DEBUG("%d\n", ctx.handle); - ctx.handle = idx; - if (copy_to_user((drm_ctx_t __user *) arg, &ctx, sizeof(ctx))) - return -EFAULT; - return 0; -} - -int ffb_driver_modctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - struct ffb_hw_context *hwctx; - drm_ctx_t ctx; - int idx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - - idx = ctx.handle; - if (idx <= 0 || idx >= FFB_MAX_CTXS) - return -EINVAL; - - hwctx = fpriv->hw_state[idx - 1]; - if (hwctx == NULL) - return -EINVAL; - - if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0) - hwctx->is_2d_only = 0; - else - hwctx->is_2d_only = 1; - - return 0; -} - -int ffb_driver_getctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - struct ffb_hw_context *hwctx; - drm_ctx_t ctx; - int idx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - - idx = ctx.handle; - if (idx <= 0 || idx >= FFB_MAX_CTXS) - return -EINVAL; - - hwctx = fpriv->hw_state[idx - 1]; - if (hwctx == NULL) - return -EINVAL; - - if (hwctx->is_2d_only != 0) - ctx.flags = _DRM_CONTEXT_2DONLY; - else - ctx.flags = 0; - - if (copy_to_user((drm_ctx_t __user *) arg, &ctx, sizeof(ctx))) - return -EFAULT; - - return 0; -} - -int ffb_driver_switchctx(struct inode *inode, struct file *filp, - unsigned int cmd, unsigned long arg) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - drm_ctx_t ctx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - DRM_DEBUG("%d\n", ctx.handle); - return ffb_driver_context_switch(dev, dev->last_context, ctx.handle); -} - -int ffb_driver_newctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_ctx_t ctx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - DRM_DEBUG("%d\n", ctx.handle); - - return 0; -} - -int ffb_driver_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, - unsigned long arg) -{ - drm_ctx_t ctx; - drm_file_t *priv = filp->private_data; - drm_device_t *dev = priv->dev; - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - int idx; - - if (copy_from_user(&ctx, (drm_ctx_t __user *) arg, sizeof(ctx))) - return -EFAULT; - DRM_DEBUG("%d\n", ctx.handle); - - idx = ctx.handle - 1; - if (idx < 0 || idx >= FFB_MAX_CTXS) - return -EINVAL; - - kfree(fpriv->hw_state[idx]); - fpriv->hw_state[idx] = NULL; - return 0; -} - -void ffb_set_context_ioctls(void) -{ - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)].func = ffb_driver_addctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)].func = ffb_driver_rmctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)].func = ffb_driver_modctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)].func = ffb_driver_getctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)].func = - ffb_driver_switchctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)].func = ffb_driver_newctx; - DRM(ioctls)[DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)].func = ffb_driver_resctx; - -} diff --git a/drivers/char/drm/ffb_drv.c b/drivers/char/drm/ffb_drv.c deleted file mode 100644 index 9a19879..0000000 --- a/drivers/char/drm/ffb_drv.c +++ /dev/null @@ -1,355 +0,0 @@ -/* $Id: ffb_drv.c,v 1.16 2001/10/18 16:00:24 davem Exp $ - * ffb_drv.c: Creator/Creator3D direct rendering driver. - * - * Copyright (C) 2000 David S. Miller (davem@redhat.com) - */ - -#include "ffb.h" -#include "drmP.h" - -#include "ffb_drv.h" - -#include <linux/smp_lock.h> -#include <asm/shmparam.h> -#include <asm/oplib.h> -#include <asm/upa.h> - -#define DRIVER_AUTHOR "David S. Miller" - -#define DRIVER_NAME "ffb" -#define DRIVER_DESC "Creator/Creator3D" -#define DRIVER_DATE "20000517" - -#define DRIVER_MAJOR 0 -#define DRIVER_MINOR 0 -#define DRIVER_PATCHLEVEL 1 - -typedef struct _ffb_position_t { - int node; - int root; -} ffb_position_t; - -static ffb_position_t *ffb_position; - -static void get_ffb_type(ffb_dev_priv_t * ffb_priv, int instance) -{ - volatile unsigned char *strap_bits; - unsigned char val; - - strap_bits = (volatile unsigned char *) - (ffb_priv->card_phys_base + 0x00200000UL); - - /* Don't ask, you have to read the value twice for whatever - * reason to get correct contents. - */ - val = upa_readb(strap_bits); - val = upa_readb(strap_bits); - switch (val & 0x78) { - case (0x0 << 5) | (0x0 << 3): - ffb_priv->ffb_type = ffb1_prototype; - printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance); - break; - case (0x0 << 5) | (0x1 << 3): - ffb_priv->ffb_type = ffb1_standard; - printk("ffb%d: Detected FFB1\n", instance); - break; - case (0x0 << 5) | (0x3 << 3): - ffb_priv->ffb_type = ffb1_speedsort; - printk("ffb%d: Detected FFB1-SpeedSort\n", instance); - break; - case (0x1 << 5) | (0x0 << 3): - ffb_priv->ffb_type = ffb2_prototype; - printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", - instance); - break; - case (0x1 << 5) | (0x1 << 3): - ffb_priv->ffb_type = ffb2_vertical; - printk("ffb%d: Detected FFB2/vertical\n", instance); - break; - case (0x1 << 5) | (0x2 << 3): - ffb_priv->ffb_type = ffb2_vertical_plus; - printk("ffb%d: Detected FFB2+/vertical\n", instance); - break; - case (0x2 << 5) | (0x0 << 3): - ffb_priv->ffb_type = ffb2_horizontal; - printk("ffb%d: Detected FFB2/horizontal\n", instance); - break; - case (0x2 << 5) | (0x2 << 3): - ffb_priv->ffb_type = ffb2_horizontal; - printk("ffb%d: Detected FFB2+/horizontal\n", instance); - break; - default: - ffb_priv->ffb_type = ffb2_vertical; - printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", - instance, val); - break; - }; -} - -static void ffb_apply_upa_parent_ranges(int parent, - struct linux_prom64_registers *regs) -{ - struct linux_prom64_ranges ranges[PROMREG_MAX]; - char name[128]; - int len, i; - - prom_getproperty(parent, "name", name, sizeof(name)); - if (strcmp(name, "upa") != 0) - return; - - len = - prom_getproperty(parent, "ranges", (void *)ranges, sizeof(ranges)); - if (len <= 0) - return; - - len /= sizeof(struct linux_prom64_ranges); - for (i = 0; i < len; i++) { - struct linux_prom64_ranges *rng = &ranges[i]; - u64 phys_addr = regs->phys_addr; - - if (phys_addr >= rng->ot_child_base && - phys_addr < (rng->ot_child_base + rng->or_size)) { - regs->phys_addr -= rng->ot_child_base; - regs->phys_addr += rng->ot_parent_base; - return; - } - } - - return; -} - -static int ffb_init_one(drm_device_t * dev, int prom_node, int parent_node, - int instance) -{ - struct linux_prom64_registers regs[2 * PROMREG_MAX]; - ffb_dev_priv_t *ffb_priv = (ffb_dev_priv_t *) dev->dev_private; - int i; - - ffb_priv->prom_node = prom_node; - if (prom_getproperty(ffb_priv->prom_node, "reg", - (void *)regs, sizeof(regs)) <= 0) { - return -EINVAL; - } - ffb_apply_upa_parent_ranges(parent_node, ®s[0]); - ffb_priv->card_phys_base = regs[0].phys_addr; - ffb_priv->regs = (ffb_fbcPtr) - (regs[0].phys_addr + 0x00600000UL); - get_ffb_type(ffb_priv, instance); - for (i = 0; i < FFB_MAX_CTXS; i++) - ffb_priv->hw_state[i] = NULL; - - return 0; -} - -static drm_map_t *ffb_find_map(struct file *filp, unsigned long off) -{ - drm_file_t *priv = filp->private_data; - drm_device_t *dev; - drm_map_list_t *r_list; - struct list_head *list; - drm_map_t *map; - - if (!priv || (dev = priv->dev) == NULL) - return NULL; - - list_for_each(list, &dev->maplist->head) { - r_list = (drm_map_list_t *) list; - map = r_list->map; - if (!map) - continue; - if (r_list->user_token == off) - return map; - } - - return NULL; -} - -unsigned long ffb_get_unmapped_area(struct file *filp, - unsigned long hint, - unsigned long len, - unsigned long pgoff, unsigned long flags) -{ - drm_map_t *map = ffb_find_map(filp, pgoff << PAGE_SHIFT); - unsigned long addr = -ENOMEM; - - if (!map) - return get_unmapped_area(NULL, hint, len, pgoff, flags); - - if (map->type == _DRM_FRAME_BUFFER || map->type == _DRM_REGISTERS) { -#ifdef HAVE_ARCH_FB_UNMAPPED_AREA - addr = get_fb_unmapped_area(filp, hint, len, pgoff, flags); -#else - addr = get_unmapped_area(NULL, hint, len, pgoff, flags); -#endif - } else if (map->type == _DRM_SHM && SHMLBA > PAGE_SIZE) { - unsigned long slack = SHMLBA - PAGE_SIZE; - - addr = get_unmapped_area(NULL, hint, len + slack, pgoff, flags); - if (!(addr & ~PAGE_MASK)) { - unsigned long kvirt = (unsigned long)map->handle; - - if ((kvirt & (SHMLBA - 1)) != (addr & (SHMLBA - 1))) { - unsigned long koff, aoff; - - koff = kvirt & (SHMLBA - 1); - aoff = addr & (SHMLBA - 1); - if (koff < aoff) - koff += SHMLBA; - - addr += (koff - aoff); - } - } - } else { - addr = get_unmapped_area(NULL, hint, len, pgoff, flags); - } - - return addr; -} - -static int ffb_presetup(drm_device_t * dev) -{ - ffb_dev_priv_t *ffb_priv; - int ret = 0; - int i = 0; - - /* Check for the case where no device was found. */ - if (ffb_position == NULL) - return -ENODEV; - - /* code used to use numdevs no numdevs anymore */ - ffb_priv = kmalloc(sizeof(ffb_dev_priv_t), GFP_KERNEL); - if (!ffb_priv) - return -ENOMEM; - memset(ffb_priv, 0, sizeof(*ffb_priv)); - dev->dev_private = ffb_priv; - - ret = ffb_init_one(dev, ffb_position[i].node, ffb_position[i].root, i); - return ret; -} - -static void ffb_driver_release(drm_device_t * dev, struct file *filp) -{ - ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) dev->dev_private; - int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock); - int idx; - - idx = context - 1; - if (fpriv && - context != DRM_KERNEL_CONTEXT && fpriv->hw_state[idx] != NULL) { - kfree(fpriv->hw_state[idx]); - fpriv->hw_state[idx] = NULL; - } -} - -static void ffb_driver_pretakedown(drm_device_t * dev) -{ - kfree(dev->dev_private); -} - -static int ffb_driver_postcleanup(drm_device_t * dev) -{ - kfree(ffb_position); - return 0; -} - -static void ffb_driver_kernel_context_switch_unlock(struct drm_device *dev, - drm_lock_t * lock) -{ - dev->lock.filp = 0; - { - __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; - unsigned int old, new, prev, ctx; - - ctx = lock->context; - do { - old = *plock; - new = ctx; - prev = cmpxchg(plock, old, new); - } while (prev != old); - } - wake_up_interruptible(&dev->lock.lock_queue); -} - -static unsigned long ffb_driver_get_map_ofs(drm_map_t * map) -{ - return (map->offset & 0xffffffff); -} - -static unsigned long ffb_driver_get_reg_ofs(drm_device_t * dev) -{ - ffb_dev_priv_t *ffb_priv = (ffb_dev_priv_t *) dev->dev_private; - - if (ffb_priv) - return ffb_priv->card_phys_base; - - return 0; -} - -static int postinit(struct drm_device *dev, unsigned long flags) -{ - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", - DRIVER_NAME, - DRIVER_MAJOR, - DRIVER_MINOR, DRIVER_PATCHLEVEL, DRIVER_DATE, dev->minor); - return 0; -} - -static int version(drm_version_t * version) -{ - int len; - - version->version_major = DRIVER_MAJOR; - version->version_minor = DRIVER_MINOR; - version->version_patchlevel = DRIVER_PATCHLEVEL; - DRM_COPY(version->name, DRIVER_NAME); - DRM_COPY(version->date, DRIVER_DATE); - DRM_COPY(version->desc, DRIVER_DESC); - return 0; -} - -static drm_ioctl_desc_t ioctls[] = { - -}; - -static struct drm_driver driver = { - .driver_features = 0, - .dev_priv_size = sizeof(u32), - .release = ffb_driver_release, - .presetup = ffb_presetup, - .pretakedown = ffb_driver_pretakedown, - .postcleanup = ffb_driver_postcleanup, - .kernel_context_switch = ffb_driver_context_switch, - .kernel_context_switch_unlock = ffb_driver_kernel_context_switch_unlock, - .get_map_ofs = ffb_driver_get_map_ofs, - .get_reg_ofs = ffb_driver_get_reg_ofs, - .postinit = postinit, - .version = version, - .ioctls = ioctls, - .num_ioctls = DRM_ARRAY_SIZE(ioctls), - .fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .ioctl = drm_ioctl, - .mmap = drm_mmap, - .poll = drm_poll, - .fasync = drm_fasync, - } - , -}; - -static int __init ffb_init(void) -{ - return -ENODEV; -} - -static void __exit ffb_exit(void) -{ -} - -module_init(ffb_init); -module_exit(ffb_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL and additional rights"); diff --git a/drivers/char/drm/ffb_drv.h b/drivers/char/drm/ffb_drv.h deleted file mode 100644 index 582afa6..0000000 --- a/drivers/char/drm/ffb_drv.h +++ /dev/null @@ -1,379 +0,0 @@ -/* $Id: ffb_drv.h,v 1.1 2000/06/01 04:24:39 davem Exp $ - * ffb_drv.h: Creator/Creator3D direct rendering driver. - * - * Copyright (C) 2000 David S. Miller (davem@redhat.com) - */ - -/* Auxilliary clips. */ -typedef struct { - volatile unsigned int min; - volatile unsigned int max; -} ffb_auxclip, *ffb_auxclipPtr; - -/* FFB register set. */ -typedef struct _ffb_fbc { - /* Next vertex registers, on the right we list which drawops - * use said register and the logical name the register has in - * that context. - *//* DESCRIPTION DRAWOP(NAME) */ - /*0x00*/ unsigned int pad1[3]; - /* Reserved */ - /*0x0c*/ volatile unsigned int alpha; - /* ALPHA Transparency */ - /*0x10*/ volatile unsigned int red; - /* RED */ - /*0x14*/ volatile unsigned int green; - /* GREEN */ - /*0x18*/ volatile unsigned int blue; - /* BLUE */ - /*0x1c*/ volatile unsigned int z; - /* DEPTH */ - /*0x20*/ volatile unsigned int y; - /* Y triangle(DOYF) */ - /* aadot(DYF) */ - /* ddline(DYF) */ - /* aaline(DYF) */ - /*0x24*/ volatile unsigned int x; - /* X triangle(DOXF) */ - /* aadot(DXF) */ - /* ddline(DXF) */ - /* aaline(DXF) */ - /*0x28*/ unsigned int pad2[2]; - /* Reserved */ - /*0x30*/ volatile unsigned int ryf; - /* Y (alias to DOYF) ddline(RYF) */ - /* aaline(RYF) */ - /* triangle(RYF) */ - /*0x34*/ volatile unsigned int rxf; - /* X ddline(RXF) */ - /* aaline(RXF) */ - /* triangle(RXF) */ - /*0x38*/ unsigned int pad3[2]; - /* Reserved */ - /*0x40*/ volatile unsigned int dmyf; - /* Y (alias to DOYF) triangle(DMYF) */ - /*0x44*/ volatile unsigned int dmxf; - /* X triangle(DMXF) */ - /*0x48*/ unsigned int pad4[2]; - /* Reserved */ - /*0x50*/ volatile unsigned int ebyi; - /* Y (alias to RYI) polygon(EBYI) */ - /*0x54*/ volatile unsigned int ebxi; - /* X polygon(EBXI) */ - /*0x58*/ unsigned int pad5[2]; - /* Reserved */ - /*0x60*/ volatile unsigned int by; - /* Y brline(RYI) */ - /* fastfill(OP) */ - /* polygon(YI) */ - /* rectangle(YI) */ - /* bcopy(SRCY) */ - /* vscroll(SRCY) */ - /*0x64*/ volatile unsigned int bx; - /* X brline(RXI) */ - /* polygon(XI) */ - /* rectangle(XI) */ - /* bcopy(SRCX) */ - /* vscroll(SRCX) */ - /* fastfill(GO) */ - /*0x68*/ volatile unsigned int dy; - /* destination Y fastfill(DSTY) */ - /* bcopy(DSRY) */ - /* vscroll(DSRY) */ - /*0x6c*/ volatile unsigned int dx; - /* destination X fastfill(DSTX) */ - /* bcopy(DSTX) */ - /* vscroll(DSTX) */ - /*0x70*/ volatile unsigned int bh; - /* Y (alias to RYI) brline(DYI) */ - /* dot(DYI) */ - /* polygon(ETYI) */ - /* Height fastfill(H) */ - /* bcopy(H) */ - /* vscroll(H) */ - /* Y count fastfill(NY) */ - /*0x74*/ volatile unsigned int bw; - /* X dot(DXI) */ - /* brline(DXI) */ - /* polygon(ETXI) */ - /* fastfill(W) */ - /* bcopy(W) */ - /* vscroll(W) */ - /* fastfill(NX) */ - /*0x78*/ unsigned int pad6[2]; - /* Reserved */ - /*0x80*/ unsigned int pad7[32]; - /* Reserved */ - - /* Setup Unit's vertex state register */ -/*100*/ volatile unsigned int suvtx; - /*104*/ unsigned int pad8[63]; - /* Reserved */ - - /* Frame Buffer Control Registers */ - /*200*/ volatile unsigned int ppc; - /* Pixel Processor Control */ - /*204*/ volatile unsigned int wid; - /* Current WID */ - /*208*/ volatile unsigned int fg; - /* FG data */ - /*20c*/ volatile unsigned int bg; - /* BG data */ - /*210*/ volatile unsigned int consty; - /* Constant Y */ - /*214*/ volatile unsigned int constz; - /* Constant Z */ - /*218*/ volatile unsigned int xclip; - /* X Clip */ - /*21c*/ volatile unsigned int dcss; - /* Depth Cue Scale Slope */ - /*220*/ volatile unsigned int vclipmin; - /* Viewclip XY Min Bounds */ - /*224*/ volatile unsigned int vclipmax; - /* Viewclip XY Max Bounds */ - /*228*/ volatile unsigned int vclipzmin; - /* Viewclip Z Min Bounds */ - /*22c*/ volatile unsigned int vclipzmax; - /* Viewclip Z Max Bounds */ - /*230*/ volatile unsigned int dcsf; - /* Depth Cue Scale Front Bound */ - /*234*/ volatile unsigned int dcsb; - /* Depth Cue Scale Back Bound */ - /*238*/ volatile unsigned int dczf; - /* Depth Cue Z Front */ - /*23c*/ volatile unsigned int dczb; - /* Depth Cue Z Back */ - /*240*/ unsigned int pad9; - /* Reserved */ - /*244*/ volatile unsigned int blendc; - /* Alpha Blend Control */ - /*248*/ volatile unsigned int blendc1; - /* Alpha Blend Color 1 */ - /*24c*/ volatile unsigned int blendc2; - /* Alpha Blend Color 2 */ - /*250*/ volatile unsigned int fbramitc; - /* FB RAM Interleave Test Control */ - /*254*/ volatile unsigned int fbc; - /* Frame Buffer Control */ - /*258*/ volatile unsigned int rop; - /* Raster OPeration */ - /*25c*/ volatile unsigned int cmp; - /* Frame Buffer Compare */ - /*260*/ volatile unsigned int matchab; - /* Buffer AB Match Mask */ - /*264*/ volatile unsigned int matchc; - /* Buffer C(YZ) Match Mask */ - /*268*/ volatile unsigned int magnab; - /* Buffer AB Magnitude Mask */ - /*26c*/ volatile unsigned int magnc; - /* Buffer C(YZ) Magnitude Mask */ - /*270*/ volatile unsigned int fbcfg0; - /* Frame Buffer Config 0 */ - /*274*/ volatile unsigned int fbcfg1; - /* Frame Buffer Config 1 */ - /*278*/ volatile unsigned int fbcfg2; - /* Frame Buffer Config 2 */ - /*27c*/ volatile unsigned int fbcfg3; - /* Frame Buffer Config 3 */ - /*280*/ volatile unsigned int ppcfg; - /* Pixel Processor Config */ - /*284*/ volatile unsigned int pick; - /* Picking Control */ - /*288*/ volatile unsigned int fillmode; - /* FillMode */ - /*28c*/ volatile unsigned int fbramwac; - /* FB RAM Write Address Control */ - /*290*/ volatile unsigned int pmask; - /* RGB PlaneMask */ - /*294*/ volatile unsigned int xpmask; - /* X PlaneMask */ - /*298*/ volatile unsigned int ypmask; - /* Y PlaneMask */ - /*29c*/ volatile unsigned int zpmask; - /* Z PlaneMask */ - /*2a0*/ ffb_auxclip auxclip[4]; - /* Auxilliary Viewport Clip */ - - /* New 3dRAM III support regs */ -/*2c0*/ volatile unsigned int rawblend2; -/*2c4*/ volatile unsigned int rawpreblend; -/*2c8*/ volatile unsigned int rawstencil; -/*2cc*/ volatile unsigned int rawstencilctl; -/*2d0*/ volatile unsigned int threedram1; -/*2d4*/ volatile unsigned int threedram2; -/*2d8*/ volatile unsigned int passin; -/*2dc*/ volatile unsigned int rawclrdepth; -/*2e0*/ volatile unsigned int rawpmask; -/*2e4*/ volatile unsigned int rawcsrc; -/*2e8*/ volatile unsigned int rawmatch; -/*2ec*/ volatile unsigned int rawmagn; -/*2f0*/ volatile unsigned int rawropblend; -/*2f4*/ volatile unsigned int rawcmp; -/*2f8*/ volatile unsigned int rawwac; -/*2fc*/ volatile unsigned int fbramid; - - /*300*/ volatile unsigned int drawop; - /* Draw OPeration */ - /*304*/ unsigned int pad10[2]; - /* Reserved */ - /*30c*/ volatile unsigned int lpat; - /* Line Pattern control */ - /*310*/ unsigned int pad11; - /* Reserved */ - /*314*/ volatile unsigned int fontxy; - /* XY Font coordinate */ - /*318*/ volatile unsigned int fontw; - /* Font Width */ - /*31c*/ volatile unsigned int fontinc; - /* Font Increment */ - /*320*/ volatile unsigned int font; - /* Font bits */ - /*324*/ unsigned int pad12[3]; - /* Reserved */ -/*330*/ volatile unsigned int blend2; -/*334*/ volatile unsigned int preblend; -/*338*/ volatile unsigned int stencil; -/*33c*/ volatile unsigned int stencilctl; - - /*340*/ unsigned int pad13[4]; - /* Reserved */ - /*350*/ volatile unsigned int dcss1; - /* Depth Cue Scale Slope 1 */ - /*354*/ volatile unsigned int dcss2; - /* Depth Cue Scale Slope 2 */ - /*358*/ volatile unsigned int dcss3; - /* Depth Cue Scale Slope 3 */ -/*35c*/ volatile unsigned int widpmask; -/*360*/ volatile unsigned int dcs2; -/*364*/ volatile unsigned int dcs3; -/*368*/ volatile unsigned int dcs4; - /*36c*/ unsigned int pad14; - /* Reserved */ -/*370*/ volatile unsigned int dcd2; -/*374*/ volatile unsigned int dcd3; -/*378*/ volatile unsigned int dcd4; - /*37c*/ unsigned int pad15; - /* Reserved */ - /*380*/ volatile unsigned int pattern[32]; - /* area Pattern */ - /*400*/ unsigned int pad16[8]; - /* Reserved */ - /*420*/ volatile unsigned int reset; - /* chip RESET */ - /*424*/ unsigned int pad17[247]; - /* Reserved */ - /*800*/ volatile unsigned int devid; - /* Device ID */ - /*804*/ unsigned int pad18[63]; - /* Reserved */ - /*900*/ volatile unsigned int ucsr; - /* User Control & Status Register */ - /*904*/ unsigned int pad19[31]; - /* Reserved */ - /*980*/ volatile unsigned int mer; - /* Mode Enable Register */ - /*984*/ unsigned int pad20[1439]; - /* Reserved */ -} ffb_fbc, *ffb_fbcPtr; - -struct ffb_hw_context { - int is_2d_only; - - unsigned int ppc; - unsigned int wid; - unsigned int fg; - unsigned int bg; - unsigned int consty; - unsigned int constz; - unsigned int xclip; - unsigned int dcss; - unsigned int vclipmin; - unsigned int vclipmax; - unsigned int vclipzmin; - unsigned int vclipzmax; - unsigned int dcsf; - unsigned int dcsb; - unsigned int dczf; - unsigned int dczb; - unsigned int blendc; - unsigned int blendc1; - unsigned int blendc2; - unsigned int fbc; - unsigned int rop; - unsigned int cmp; - unsigned int matchab; - unsigned int matchc; - unsigned int magnab; - unsigned int magnc; - unsigned int pmask; - unsigned int xpmask; - unsigned int ypmask; - unsigned int zpmask; - unsigned int auxclip0min; - unsigned int auxclip0max; - unsigned int auxclip1min; - unsigned int auxclip1max; - unsigned int auxclip2min; - unsigned int auxclip2max; - unsigned int auxclip3min; - unsigned int auxclip3max; - unsigned int drawop; - unsigned int lpat; - unsigned int fontxy; - unsigned int fontw; - unsigned int fontinc; - unsigned int area_pattern[32]; - unsigned int ucsr; - unsigned int stencil; - unsigned int stencilctl; - unsigned int dcss1; - unsigned int dcss2; - unsigned int dcss3; - unsigned int dcs2; - unsigned int dcs3; - unsigned int dcs4; - unsigned int dcd2; - unsigned int dcd3; - unsigned int dcd4; - unsigned int mer; -}; - -#define FFB_MAX_CTXS 32 - -enum ffb_chip_type { - ffb1_prototype = 0, /* Early pre-FCS FFB */ - ffb1_standard, /* First FCS FFB, 100Mhz UPA, 66MHz gclk */ - ffb1_speedsort, /* Second FCS FFB, 100Mhz UPA, 75MHz gclk */ - ffb2_prototype, /* Early pre-FCS vertical FFB2 */ - ffb2_vertical, /* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk, - 75(SingleBuffer)/83(DoubleBuffer) MHz fclk */ - ffb2_vertical_plus, /* Second FCS FFB2/vertical, same timings */ - ffb2_horizontal, /* First FCS FFB2/horizontal, same timings as FFB2/vert */ - ffb2_horizontal_plus, /* Second FCS FFB2/horizontal, same timings */ - afb_m3, /* FCS Elite3D, 3 float chips */ - afb_m6 /* FCS Elite3D, 6 float chips */ -}; - -typedef struct ffb_dev_priv { - /* Misc software state. */ - int prom_node; - enum ffb_chip_type ffb_type; - u64 card_phys_base; - struct miscdevice miscdev; - - /* Controller registers. */ - ffb_fbcPtr regs; - - /* Context table. */ - struct ffb_hw_context *hw_state[FFB_MAX_CTXS]; -} ffb_dev_priv_t; - -extern unsigned long ffb_get_unmapped_area(struct file *filp, - unsigned long hint, - unsigned long len, - unsigned long pgoff, - unsigned long flags); -extern void ffb_set_context_ioctls(void); -extern drm_ioctl_desc_t DRM(ioctls)[]; - -extern int ffb_driver_context_switch(drm_device_t * dev, int old, int new); diff --git a/drivers/char/ds1286.c b/drivers/char/ds1286.c index 6d58b03..59146e3 100644 --- a/drivers/char/ds1286.c +++ b/drivers/char/ds1286.c @@ -197,6 +197,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, hrs = alm_tm.tm_hour; min = alm_tm.tm_min; + sec = alm_tm.tm_sec; if (hrs >= 24) hrs = 0xff; @@ -204,9 +205,11 @@ static int ds1286_ioctl(struct inode *inode, struct file *file, if (min >= 60) min = 0xff; - BIN_TO_BCD(sec); - BIN_TO_BCD(min); - BIN_TO_BCD(hrs); + if (sec != 0) + return -EINVAL; + + min = BIN2BCD(min); + min = BIN2BCD(hrs); spin_lock(&ds1286_lock); rtc_write(hrs, RTC_HOURS_ALARM); diff --git a/drivers/char/epca.c b/drivers/char/epca.c index 88fc24f..de5be30 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c @@ -209,7 +209,6 @@ static void digi_send_break(struct channel *ch, int msec); static void setup_empty_event(struct tty_struct *tty, struct channel *ch); void epca_setup(char *, int *); -static int get_termio(struct tty_struct *, struct termio __user *); static int pc_write(struct tty_struct *, const unsigned char *, int); static int pc_init(void); static int init_PCI(void); @@ -2362,15 +2361,6 @@ static int pc_ioctl(struct tty_struct *tty, struct file * file, switch (cmd) { /* Begin switch cmd */ - -#if 0 /* Handled by calling layer properly */ - case TCGETS: - if (copy_to_user(argp, tty->termios, sizeof(struct ktermios))) - return -EFAULT; - return 0; - case TCGETA: - return get_termio(tty, argp); -#endif case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) @@ -2735,13 +2725,6 @@ static void setup_empty_event(struct tty_struct *tty, struct channel *ch) memoff(ch); } /* End setup_empty_event */ -/* --------------------- Begin get_termio ----------------------- */ - -static int get_termio(struct tty_struct * tty, struct termio __user * termio) -{ /* Begin get_termio */ - return kernel_termios_to_user_termio(termio, tty->termios); -} /* End get_termio */ - /* ---------------------- Begin epca_setup -------------------------- */ void epca_setup(char *str, int *ints) { /* Begin epca_setup */ diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c index 337bbcd..8ea0275 100644 --- a/drivers/char/generic_serial.c +++ b/drivers/char/generic_serial.c @@ -710,12 +710,6 @@ void gs_close(struct tty_struct * tty, struct file * filp) } -static unsigned int gs_baudrates[] = { - 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 -}; - - void gs_set_termios (struct tty_struct * tty, struct ktermios * old_termios) { @@ -771,7 +765,6 @@ void gs_set_termios (struct tty_struct * tty, baudrate = tty_get_baud_rate(tty); - baudrate = gs_baudrates[baudrate]; if ((tiosp->c_cflag & CBAUD) == B38400) { if ( (port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baudrate = 57600; diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c index cc2cd46..0f9ed7b 100644 --- a/drivers/char/hvc_console.c +++ b/drivers/char/hvc_console.c @@ -47,8 +47,6 @@ #define HVC_MAJOR 229 #define HVC_MINOR 0 -#define TIMEOUT (10) - /* * Wait this long per iteration while trying to push buffered data to the * hypervisor before allowing the tty to complete a close operation. @@ -104,12 +102,12 @@ static DEFINE_SPINLOCK(hvc_structs_lock); /* * This value is used to assign a tty->index value to a hvc_struct based * upon order of exposure via hvc_probe(), when we can not match it to - * a console canidate registered with hvc_instantiate(). + * a console candidate registered with hvc_instantiate(). */ static int last_hvc = -1; /* - * Do not call this function with either the hvc_strucst_lock or the hvc_struct + * Do not call this function with either the hvc_structs_lock or the hvc_struct * lock held. If successful, this function increments the kobject reference * count against the target hvc_struct so it should be released when finished. */ @@ -162,7 +160,7 @@ void hvc_console_print(struct console *co, const char *b, unsigned count) if (index >= MAX_NR_HVC_CONSOLES) return; - /* This console adapter was removed so it is not useable. */ + /* This console adapter was removed so it is not usable. */ if (vtermnos[index] < 0) return; @@ -220,7 +218,7 @@ struct console hvc_con_driver = { }; /* - * Early console initialization. Preceeds driver initialization. + * Early console initialization. Precedes driver initialization. * * (1) we are first, and the user specified another driver * -- index will remain -1 @@ -257,7 +255,7 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) if (vtermnos[index] != -1) return -1; - /* make sure no no tty has been registerd in this index */ + /* make sure no no tty has been registered in this index */ hp = hvc_get_by_index(index); if (hp) { kobject_put(&hp->kobj); @@ -267,7 +265,7 @@ int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops) vtermnos[index] = vtermno; cons_ops[index] = ops; - /* reserve all indices upto and including this index */ + /* reserve all indices up to and including this index */ if (last_hvc < index) last_hvc = index; @@ -316,7 +314,7 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) { struct hvc_struct *hp; unsigned long flags; - int irq = NO_IRQ; + int irq = 0; int rc = 0; struct kobject *kobjp; @@ -338,14 +336,14 @@ static int hvc_open(struct tty_struct *tty, struct file * filp) hp->tty = tty; /* Save for request_irq outside of spin_lock. */ irq = hp->irq; - if (irq != NO_IRQ) + if (irq) hp->irq_requested = 1; kobjp = &hp->kobj; spin_unlock_irqrestore(&hp->lock, flags); /* check error, fallback to non-irq */ - if (irq != NO_IRQ) + if (irq) rc = request_irq(irq, hvc_handle_interrupt, IRQF_DISABLED, "hvc_console", hp); /* @@ -373,7 +371,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) { struct hvc_struct *hp; struct kobject *kobjp; - int irq = NO_IRQ; + int irq = 0; unsigned long flags; if (tty_hung_up_p(filp)) @@ -407,7 +405,7 @@ static void hvc_close(struct tty_struct *tty, struct file * filp) */ tty_wait_until_sent(tty, HVC_CLOSE_WAIT); - if (irq != NO_IRQ) + if (irq) free_irq(irq, hp); } else { @@ -424,7 +422,7 @@ static void hvc_hangup(struct tty_struct *tty) { struct hvc_struct *hp = tty->driver_data; unsigned long flags; - int irq = NO_IRQ; + int irq = 0; int temp_open_count; struct kobject *kobjp; @@ -453,7 +451,7 @@ static void hvc_hangup(struct tty_struct *tty) irq = hp->irq; hp->irq_requested = 0; spin_unlock_irqrestore(&hp->lock, flags); - if (irq != NO_IRQ) + if (irq) free_irq(irq, hp); while(temp_open_count) { --temp_open_count; @@ -528,7 +526,7 @@ static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count /* * This is actually a contract between the driver and the tty layer outlining - * how much write room the driver can guarentee will be sent OR BUFFERED. This + * how much write room the driver can guarantee will be sent OR BUFFERED. This * driver MUST honor the return value. */ static int hvc_write_room(struct tty_struct *tty) @@ -550,6 +548,18 @@ static int hvc_chars_in_buffer(struct tty_struct *tty) return hp->n_outbuf; } +/* + * timeout will vary between the MIN and MAX values defined here. By default + * and during console activity we will use a default MIN_TIMEOUT of 10. When + * the console is idle, we increase the timeout value on each pass through + * msleep until we reach the max. This may be noticeable as a brief (average + * one second) delay on the console before the console responds to input when + * there has been no input for some time. + */ +#define MIN_TIMEOUT (10) +#define MAX_TIMEOUT (2000) +static u32 timeout = MIN_TIMEOUT; + #define HVC_POLL_READ 0x00000001 #define HVC_POLL_WRITE 0x00000002 @@ -583,7 +593,7 @@ static int hvc_poll(struct hvc_struct *hp) /* If we aren't interrupt driven and aren't throttled, we always * request a reschedule */ - if (hp->irq == NO_IRQ) + if (hp->irq == 0) poll_mask |= HVC_POLL_READ; /* Read data if any */ @@ -642,9 +652,14 @@ static int hvc_poll(struct hvc_struct *hp) bail: spin_unlock_irqrestore(&hp->lock, flags); - if (read_total) + if (read_total) { + /* Activity is occurring, so reset the polling backoff value to + a minimum for performance. */ + timeout = MIN_TIMEOUT; + tty_flip_buffer_push(tty); - + } + return poll_mask; } @@ -688,8 +703,12 @@ int khvcd(void *unused) if (!hvc_kicked) { if (poll_mask == 0) schedule(); - else - msleep_interruptible(TIMEOUT); + else { + if (timeout < MAX_TIMEOUT) + timeout += (timeout >> 6) + 1; + + msleep_interruptible(timeout); + } } __set_current_state(TASK_RUNNING); } while (!kthread_should_stop()); @@ -794,7 +813,7 @@ int __devexit hvc_remove(struct hvc_struct *hp) /* * We 'put' the instance that was grabbed when the kobject instance - * was intialized using kobject_init(). Let the last holder of this + * was initialized using kobject_init(). Let the last holder of this * kobject cause it to be removed, which will probably be the tty_hangup * below. */ @@ -850,7 +869,7 @@ int __init hvc_init(void) } module_init(hvc_init); -/* This isn't particularily necessary due to this being a console driver +/* This isn't particularly necessary due to this being a console driver * but it is nice to be thorough. */ static void __exit hvc_exit(void) diff --git a/drivers/char/hvc_iseries.c b/drivers/char/hvc_iseries.c index f144a94..ec420fe 100644 --- a/drivers/char/hvc_iseries.c +++ b/drivers/char/hvc_iseries.c @@ -575,7 +575,7 @@ static int hvc_find_vtys(void) (num_found >= VTTY_PORTS)) break; - vtermno = get_property(vty, "reg", NULL); + vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c index f9c0084..94a542e 100644 --- a/drivers/char/hvc_vio.c +++ b/drivers/char/hvc_vio.c @@ -153,7 +153,7 @@ static int hvc_find_vtys(void) if (num_found >= MAX_NR_HVC_CONSOLES) break; - vtermno = get_property(vty, "reg", NULL); + vtermno = of_get_property(vty, "reg", NULL); if (!vtermno) continue; diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c index 50315d6..d5a752d 100644 --- a/drivers/char/hvsi.c +++ b/drivers/char/hvsi.c @@ -1279,8 +1279,8 @@ static int __init hvsi_console_init(void) struct hvsi_struct *hp; const uint32_t *vtermno, *irq; - vtermno = get_property(vty, "reg", NULL); - irq = get_property(vty, "interrupts", NULL); + vtermno = of_get_property(vty, "reg", NULL); + irq = of_get_property(vty, "interrupts", NULL); if (!vtermno || !irq) continue; diff --git a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c index f86fa0c..e46120d 100644 --- a/drivers/char/ip2/i2lib.c +++ b/drivers/char/ip2/i2lib.c @@ -80,7 +80,7 @@ static int i2RetryFlushOutput(i2ChanStrPtr); // Not a documented part of the library routines (careful...) but the Diagnostic // i2diag.c finds them useful to help the throughput in certain limited // single-threaded operations. -static inline void iiSendPendingMail(i2eBordStrPtr); +static void iiSendPendingMail(i2eBordStrPtr); static void serviceOutgoingFifo(i2eBordStrPtr); // Functions defined in ip2.c as part of interrupt handling @@ -166,7 +166,7 @@ static void iiSendPendingMail_t(unsigned long data) // If any outgoing mail bits are set and there is outgoing mailbox is empty, // send the mail and clear the bits. //****************************************************************************** -static inline void +static void iiSendPendingMail(i2eBordStrPtr pB) { if (pB->i2eOutMailWaiting && (!pB->i2eWaitingForEmptyFifo) ) diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index a7b33d2..e221465 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -2478,6 +2478,11 @@ static __devinit void default_find_bmc(void) if (!info) return; +#ifdef CONFIG_PPC_MERGE + if (check_legacy_ioport(ipmi_defaults[i].port)) + continue; +#endif + info->addr_source = NULL; info->si_type = ipmi_defaults[i].type; diff --git a/drivers/char/lcd.c b/drivers/char/lcd.c index d649abb..1f09626 100644 --- a/drivers/char/lcd.c +++ b/drivers/char/lcd.c @@ -11,9 +11,6 @@ * March 2001: Ported from 2.0.34 by Liam Davies * */ - -#define RTC_IO_EXTENT 0x10 /*Only really two ports, but... */ - #include <linux/types.h> #include <linux/errno.h> #include <linux/miscdevice.h> @@ -32,8 +29,6 @@ #include "lcd.h" -static DEFINE_SPINLOCK(lcd_lock); - static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); @@ -409,138 +404,6 @@ static int lcd_ioctl(struct inode *inode, struct file *file, break; } -// Erase the flash - - case FLASH_Erase:{ - - int ctr = 0; - - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - - pr_info(LCD "Erasing Flash\n"); - - // Chip Erase Sequence - WRITE_FLASH(kFlash_Addr1, kFlash_Data1); - WRITE_FLASH(kFlash_Addr2, kFlash_Data2); - WRITE_FLASH(kFlash_Addr1, kFlash_Erase3); - WRITE_FLASH(kFlash_Addr1, kFlash_Data1); - WRITE_FLASH(kFlash_Addr2, kFlash_Data2); - WRITE_FLASH(kFlash_Addr1, kFlash_Erase6); - - while ((!dqpoll(0x00000000, 0xFF)) - && (!timeout(0x00000000))) { - ctr++; - } - - if (READ_FLASH(0x07FFF0) == 0xFF) { - pr_info(LCD "Erase Successful\n"); - } else if (timeout) { - pr_info(LCD "Erase Timed Out\n"); - } - - break; - } - -// burn the flash - - case FLASH_Burn:{ - - volatile unsigned long burn_addr; - unsigned long flags; - unsigned int i, index; - unsigned char *rom; - - - struct lcd_display display; - - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - - if (copy_from_user - (&display, (struct lcd_display *) arg, - sizeof(struct lcd_display))) - return -EFAULT; - rom = kmalloc((128), GFP_ATOMIC); - if (rom == NULL) { - printk(KERN_ERR LCD "kmalloc() failed in %s\n", - __FUNCTION__); - return -ENOMEM; - } - - pr_info(LCD "Starting Flash burn\n"); - for (i = 0; i < FLASH_SIZE; i = i + 128) { - - if (copy_from_user - (rom, display.RomImage + i, 128)) { - kfree(rom); - return -EFAULT; - } - burn_addr = kFlashBase + i; - spin_lock_irqsave(&lcd_lock, flags); - for (index = 0; index < (128); index++) { - - WRITE_FLASH(kFlash_Addr1, - kFlash_Data1); - WRITE_FLASH(kFlash_Addr2, - kFlash_Data2); - WRITE_FLASH(kFlash_Addr1, - kFlash_Prog); - *((volatile unsigned char *)burn_addr) = - (volatile unsigned char) rom[index]; - - while ((!dqpoll (burn_addr, - (volatile unsigned char) - rom[index])) && - (!timeout(burn_addr))) { } - burn_addr++; - } - spin_unlock_irqrestore(&lcd_lock, flags); - if (* ((volatile unsigned char *) - (burn_addr - 1)) == - (volatile unsigned char) - rom[index - 1]) { - } else if (timeout) { - pr_info(LCD "Flash burn timed out\n"); - } - - - } - kfree(rom); - - pr_info(LCD "Flash successfully burned\n"); - - break; - } - -// read the flash all at once - - case FLASH_Read:{ - - unsigned char *user_bytes; - volatile unsigned long read_addr; - unsigned int i; - - user_bytes = - &(((struct lcd_display *) arg)->RomImage[0]); - - if (!access_ok - (VERIFY_WRITE, user_bytes, FLASH_SIZE)) - return -EFAULT; - - pr_info(LCD "Reading Flash"); - for (i = 0; i < FLASH_SIZE; i++) { - unsigned char tmp_byte; - read_addr = kFlashBase + i; - tmp_byte = - *((volatile unsigned char *) - read_addr); - if (__put_user(tmp_byte, &user_bytes[i])) - return -EFAULT; - } - - - break; - } - default: return -EINVAL; @@ -644,42 +507,6 @@ static void __exit lcd_exit(void) misc_deregister(&lcd_dev); } -// -// Function: dqpoll -// -// Description: Polls the data lines to see if the flash is busy -// -// In: address, byte data -// -// Out: 0 = busy, 1 = write or erase complete -// -// - -static int dqpoll(volatile unsigned long address, volatile unsigned char data) -{ - volatile unsigned char dq7; - - dq7 = data & 0x80; - - return ((READ_FLASH(address) & 0x80) == dq7); -} - -// -// Function: timeout -// -// Description: Checks to see if erase or write has timed out -// By polling dq5 -// -// In: address -// -// -// Out: 0 = not timed out, 1 = timed out - -static int timeout(volatile unsigned long address) -{ - return (READ_FLASH(address) & 0x20) == 0x20; -} - module_init(lcd_init); module_exit(lcd_exit); diff --git a/drivers/char/lcd.h b/drivers/char/lcd.h index a8d4ae7..290b3ff 100644 --- a/drivers/char/lcd.h +++ b/drivers/char/lcd.h @@ -14,11 +14,7 @@ // function headers -static int dqpoll(volatile unsigned long, volatile unsigned char ); -static int timeout(volatile unsigned long); - #define LCD_CHARS_PER_LINE 40 -#define FLASH_SIZE 524288 #define MAX_IDLE_TIME 120 struct lcd_display { @@ -54,26 +50,6 @@ struct lcd_display { #define LCDTimeoutValue 0xfff -// Flash definitions AMD 29F040 -#define kFlashBase 0x0FC00000 - -#define kFlash_Addr1 0x5555 -#define kFlash_Addr2 0x2AAA -#define kFlash_Data1 0xAA -#define kFlash_Data2 0x55 -#define kFlash_Prog 0xA0 -#define kFlash_Erase3 0x80 -#define kFlash_Erase6 0x10 -#define kFlash_Read 0xF0 - -#define kFlash_ID 0x90 -#define kFlash_VenAddr 0x00 -#define kFlash_DevAddr 0x01 -#define kFlash_VenID 0x01 -#define kFlash_DevID 0xA4 // 29F040 -//#define kFlash_DevID 0xAD // 29F016 - - // Macros #define LCDWriteData(x) outl((x << 24), kLCD_DR) @@ -89,9 +65,6 @@ struct lcd_display { #define WRITE_GAL(x,y) outl(y, 0x04000000 | (x)) #define BusyCheck() while ((LCDReadInst & 0x80) == 0x80) -#define WRITE_FLASH(x,y) outb((char)y, kFlashBase | (x)) -#define READ_FLASH(x) (inb(kFlashBase | (x))) - /* @@ -124,11 +97,6 @@ struct lcd_display { // Button defs #define BUTTON_Read 50 -// Flash command codes -#define FLASH_Erase 60 -#define FLASH_Burn 61 -#define FLASH_Read 62 - // Ethernet LINK check hackaroo #define LINK_Check 90 diff --git a/drivers/char/mem.c b/drivers/char/mem.c index f5c160c..5f06696 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -248,7 +248,7 @@ static unsigned long get_unmapped_area_mem(struct file *file, { if (!valid_mmap_phys_addr_range(pgoff, len)) return (unsigned long) -EINVAL; - return pgoff; + return pgoff << PAGE_SHIFT; } /* can't do an in-place private mapping if there's no MMU */ diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c index 4e4865e..492dbfb 100644 --- a/drivers/char/mwave/3780i.c +++ b/drivers/char/mwave/3780i.c @@ -63,8 +63,6 @@ #include "3780i.h" static DEFINE_SPINLOCK(dsp_lock); -static unsigned long flags; - static void PaceMsaAccess(unsigned short usDspBaseIO) { @@ -76,6 +74,7 @@ static void PaceMsaAccess(unsigned short usDspBaseIO) unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, unsigned long ulMsaAddr) { + unsigned long flags; unsigned short val; PRINTK_3(TRACE_3780I, @@ -96,6 +95,7 @@ unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO, unsigned long ulMsaAddr, unsigned short usValue) { + unsigned long flags; PRINTK_4(TRACE_3780I, "3780i::dsp3780i_WriteMsaCfg entry usDspBaseIO %x ulMsaAddr %lx usValue %x\n", @@ -175,6 +175,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, unsigned short *pIrqMap, unsigned short *pDmaMap) { + unsigned long flags; unsigned short usDspBaseIO = pSettings->usDspBaseIO; int i; DSP_UART_CFG_1 rUartCfg1; @@ -354,6 +355,7 @@ int dsp3780I_EnableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings, int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings) { + unsigned long flags; unsigned short usDspBaseIO = pSettings->usDspBaseIO; DSP_ISA_SLAVE_CONTROL rSlaveControl; @@ -383,6 +385,7 @@ int dsp3780I_DisableDSP(DSP_3780I_CONFIG_SETTINGS * pSettings) int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings) { + unsigned long flags; unsigned short usDspBaseIO = pSettings->usDspBaseIO; DSP_BOOT_DOMAIN rBootDomain; DSP_HBRIDGE_CONTROL rHBridgeControl; @@ -427,6 +430,7 @@ int dsp3780I_Reset(DSP_3780I_CONFIG_SETTINGS * pSettings) int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings) { + unsigned long flags; unsigned short usDspBaseIO = pSettings->usDspBaseIO; DSP_BOOT_DOMAIN rBootDomain; DSP_HBRIDGE_CONTROL rHBridgeControl; @@ -473,6 +477,7 @@ int dsp3780I_Run(DSP_3780I_CONFIG_SETTINGS * pSettings) int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, unsigned uCount, unsigned long ulDSPAddr) { + unsigned long flags; unsigned short __user *pusBuffer = pvBuffer; unsigned short val; @@ -514,6 +519,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, void __user *pvBuffer, unsigned uCount, unsigned long ulDSPAddr) { + unsigned long flags; unsigned short __user *pusBuffer = pvBuffer; unsigned short val; @@ -555,6 +561,7 @@ int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, unsigned uCount, unsigned long ulDSPAddr) { + unsigned long flags; unsigned short __user *pusBuffer = pvBuffer; @@ -596,6 +603,7 @@ int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, unsigned uCount, unsigned long ulDSPAddr) { + unsigned long flags; unsigned short __user *pusBuffer = pvBuffer; PRINTK_5(TRACE_3780I, @@ -643,6 +651,7 @@ int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, unsigned uCount, unsigned long ulDSPAddr) { + unsigned long flags; unsigned short __user *pusBuffer = pvBuffer; PRINTK_5(TRACE_3780I, @@ -691,6 +700,7 @@ int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, unsigned short *pusIPCSource) { + unsigned long flags; DSP_HBRIDGE_CONTROL rHBridgeControl; unsigned short temp; diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index a61fb6d..80a0115 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1338,43 +1338,23 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, unsigned int c * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ - case TIOCMIWAIT: { - DECLARE_WAITQUEUE(wait, current); - int ret; + case TIOCMIWAIT: + spin_lock_irqsave(&info->slock, flags); + cnow = info->icount; /* note the counters on entry */ + spin_unlock_irqrestore(&info->slock, flags); + + wait_event_interruptible(info->delta_msr_wait, ({ + cprev = cnow; spin_lock_irqsave(&info->slock, flags); - cprev = info->icount; /* note the counters on entry */ + cnow = info->icount; /* atomic copy */ spin_unlock_irqrestore(&info->slock, flags); - add_wait_queue(&info->delta_msr_wait, &wait); - while (1) { - spin_lock_irqsave(&info->slock, flags); - cnow = info->icount; /* atomic copy */ - spin_unlock_irqrestore(&info->slock, flags); - - set_current_state(TASK_INTERRUPTIBLE); - if (((arg & TIOCM_RNG) && - (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && - (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && - (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && - (cnow.cts != cprev.cts))) { - ret = 0; - break; - } - /* see if a signal did it */ - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - cprev = cnow; - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->delta_msr_wait, &wait); - break; - } - /* NOTREACHED */ + ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); + })); + break; /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct diff --git a/drivers/char/mxser_new.c b/drivers/char/mxser_new.c index 9af07e4..f7603b6 100644 --- a/drivers/char/mxser_new.c +++ b/drivers/char/mxser_new.c @@ -1758,43 +1758,23 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file, * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ - case TIOCMIWAIT: { - DECLARE_WAITQUEUE(wait, current); - int ret; + case TIOCMIWAIT: spin_lock_irqsave(&info->slock, flags); - cprev = info->icount; /* note the counters on entry */ + cnow = info->icount; /* note the counters on entry */ spin_unlock_irqrestore(&info->slock, flags); - add_wait_queue(&info->delta_msr_wait, &wait); - while (1) { + wait_event_interruptible(info->delta_msr_wait, ({ + cprev = cnow; spin_lock_irqsave(&info->slock, flags); cnow = info->icount; /* atomic copy */ spin_unlock_irqrestore(&info->slock, flags); - set_current_state(TASK_INTERRUPTIBLE); - if (((arg & TIOCM_RNG) && - (cnow.rng != cprev.rng)) || - ((arg & TIOCM_DSR) && - (cnow.dsr != cprev.dsr)) || - ((arg & TIOCM_CD) && - (cnow.dcd != cprev.dcd)) || - ((arg & TIOCM_CTS) && - (cnow.cts != cprev.cts))) { - ret = 0; - break; - } - /* see if a signal did it */ - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - cprev = cnow; - } - current->state = TASK_RUNNING; - remove_wait_queue(&info->delta_msr_wait, &wait); + ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || + ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || + ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || + ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)); + })); break; - } - /* NOTREACHED */ /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct @@ -2230,7 +2210,14 @@ end_intr: port->mon_data.rxcnt += cnt; port->mon_data.up_rxcnt += cnt; + /* + * We are called from an interrupt context with &port->slock + * being held. Drop it temporarily in order to prevent + * recursive locking. + */ + spin_unlock(&port->slock); tty_flip_buffer_push(tty); + spin_lock(&port->slock); } static void mxser_transmit_chars(struct mxser_port *port) diff --git a/drivers/char/pcmcia/cm4040_cs.c b/drivers/char/pcmcia/cm4040_cs.c index 0e82968..f2e4ec4 100644 --- a/drivers/char/pcmcia/cm4040_cs.c +++ b/drivers/char/pcmcia/cm4040_cs.c @@ -273,6 +273,7 @@ static ssize_t cm4040_read(struct file *filp, char __user *buf, DEBUGP(6, dev, "BytesToRead=%lu\n", bytes_to_read); min_bytes_to_read = min(count, bytes_to_read + 5); + min_bytes_to_read = min_t(size_t, min_bytes_to_read, READ_WRITE_BUFFER_SIZE); DEBUGP(6, dev, "Min=%lu\n", min_bytes_to_read); @@ -340,7 +341,7 @@ static ssize_t cm4040_write(struct file *filp, const char __user *buf, return 0; } - if (count < 5) { + if ((count < 5) || (count > READ_WRITE_BUFFER_SIZE)) { DEBUGP(2, dev, "<- cm4040_write buffersize=%Zd < 5\n", count); return -EIO; } diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c index 8d025e9..157b1d0 100644 --- a/drivers/char/pcmcia/synclink_cs.c +++ b/drivers/char/pcmcia/synclink_cs.c @@ -4169,7 +4169,7 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); /* copy data to device buffers */ - memcpy(info->tx_buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, info->tx_buf, skb->len); info->tx_get = 0; info->tx_put = info->tx_count = skb->len; diff --git a/drivers/char/random.c b/drivers/char/random.c index b9dc7aa..46c1b9774 100644 --- a/drivers/char/random.c +++ b/drivers/char/random.c @@ -881,15 +881,15 @@ EXPORT_SYMBOL(get_random_bytes); */ static void init_std_data(struct entropy_store *r) { - struct timeval tv; + ktime_t now; unsigned long flags; spin_lock_irqsave(&r->lock, flags); r->entropy_count = 0; spin_unlock_irqrestore(&r->lock, flags); - do_gettimeofday(&tv); - add_entropy_words(r, (__u32 *)&tv, sizeof(tv)/4); + now = ktime_get_real(); + add_entropy_words(r, (__u32 *)&now, sizeof(now)/4); add_entropy_words(r, (__u32 *)utsname(), sizeof(*(utsname()))/4); } @@ -911,14 +911,12 @@ void rand_initialize_irq(int irq) return; /* - * If kmalloc returns null, we just won't use that entropy + * If kzalloc returns null, we just won't use that entropy * source. */ - state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) { - memset(state, 0, sizeof(struct timer_rand_state)); + state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) irq_timer_state[irq] = state; - } } #ifdef CONFIG_BLOCK @@ -927,14 +925,12 @@ void rand_initialize_disk(struct gendisk *disk) struct timer_rand_state *state; /* - * If kmalloc returns null, we just won't use that entropy + * If kzalloc returns null, we just won't use that entropy * source. */ - state = kmalloc(sizeof(struct timer_rand_state), GFP_KERNEL); - if (state) { - memset(state, 0, sizeof(struct timer_rand_state)); + state = kzalloc(sizeof(struct timer_rand_state), GFP_KERNEL); + if (state) disk->random = state; - } } #endif @@ -1469,7 +1465,6 @@ late_initcall(seqgen_init); __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, __be16 sport, __be16 dport) { - struct timeval tv; __u32 seq; __u32 hash[12]; struct keydata *keyptr = get_keyptr(); @@ -1485,8 +1480,7 @@ __u32 secure_tcpv6_sequence_number(__be32 *saddr, __be32 *daddr, seq = twothirdsMD4Transform((const __u32 *)daddr, hash) & HASH_MASK; seq += keyptr->count; - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; return seq; } @@ -1521,7 +1515,6 @@ __u32 secure_ip_id(__be32 daddr) __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) { - struct timeval tv; __u32 seq; __u32 hash[4]; struct keydata *keyptr = get_keyptr(); @@ -1543,12 +1536,11 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, * As close as possible to RFC 793, which * suggests using a 250 kHz clock. * Further reading shows this assumes 2 Mb/s networks. - * For 10 Mb/s Ethernet, a 1 MHz clock is appropriate. + * For 10 Gb/s Ethernet, a 1 GHz clock is appropriate. * That's funny, Linux has one built in! Use it! * (Networks are faster now - should this be increased?) */ - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; #if 0 printk("init_seq(%lx, %lx, %d, %d) = %d\n", saddr, daddr, sport, dport, seq); @@ -1556,8 +1548,6 @@ __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, return seq; } -EXPORT_SYMBOL(secure_tcp_sequence_number); - /* Generate secure starting point for ephemeral IPV4 transport port search */ u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport) { @@ -1598,7 +1588,6 @@ u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr, __be16 u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport) { - struct timeval tv; u64 seq; __u32 hash[4]; struct keydata *keyptr = get_keyptr(); @@ -1611,8 +1600,7 @@ u64 secure_dccp_sequence_number(__be32 saddr, __be32 daddr, seq = half_md4_transform(hash, keyptr->secret); seq |= ((u64)keyptr->count) << (32 - HASH_BITS); - do_gettimeofday(&tv); - seq += tv.tv_usec + tv.tv_sec * 1000000; + seq += ktime_get_real().tv64; seq &= (1ull << 48) - 1; #if 0 printk("dccp init_seq(%lx, %lx, %d, %d) = %d\n", diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c index 85c1618..294e9cb 100644 --- a/drivers/char/rio/rio_linux.c +++ b/drivers/char/rio/rio_linux.c @@ -1146,7 +1146,7 @@ static int __init rio_init(void) rio_dprintk(RIO_DEBUG_INIT, "Enabling interrupts on rio card.\n"); hp->Mode |= RIO_PCI_INT_ENABLE; } else - hp->Mode &= !RIO_PCI_INT_ENABLE; + hp->Mode &= ~RIO_PCI_INT_ENABLE; rio_dprintk(RIO_DEBUG_INIT, "New Mode: %x\n", hp->Mode); rio_start_card_running(hp); } diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 7823757..3ef593a 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -1,6 +1,8 @@ /* * Sony Programmable I/O Control Device driver for VAIO * + * Copyright (C) 2007 Mattia Dongili <malattia@linux.it> + * * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net> * * Copyright (C) 2005 Narayanan R S <nars@kadamba.org> @@ -95,6 +97,11 @@ module_param(useinput, int, 0444); MODULE_PARM_DESC(useinput, "set this if you would like sonypi to feed events to the input subsystem"); +static int check_ioport = 1; +module_param(check_ioport, int, 0444); +MODULE_PARM_DESC(check_ioport, + "set this to 0 if you think the automatic ioport check for sony-laptop is wrong"); + #define SONYPI_DEVICE_MODEL_TYPE1 1 #define SONYPI_DEVICE_MODEL_TYPE2 2 #define SONYPI_DEVICE_MODEL_TYPE3 3 @@ -477,7 +484,7 @@ static struct sonypi_device { u16 evtype_offset; int camera_power; int bluetooth_power; - struct semaphore lock; + struct mutex lock; struct kfifo *fifo; spinlock_t fifo_lock; wait_queue_head_t fifo_proc_list; @@ -884,7 +891,7 @@ int sonypi_camera_command(int command, u8 value) if (!camera) return -EIO; - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); switch (command) { case SONYPI_COMMAND_SETCAMERA: @@ -919,7 +926,7 @@ int sonypi_camera_command(int command, u8 value) command); break; } - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } @@ -938,20 +945,20 @@ static int sonypi_misc_fasync(int fd, struct file *filp, int on) static int sonypi_misc_release(struct inode *inode, struct file *file) { sonypi_misc_fasync(-1, file, 0); - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); sonypi_device.open_count--; - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } static int sonypi_misc_open(struct inode *inode, struct file *file) { - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); /* Flush input queue on first open */ if (!sonypi_device.open_count) kfifo_reset(sonypi_device.fifo); sonypi_device.open_count++; - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return 0; } @@ -1001,7 +1008,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, u8 val8; u16 val16; - down(&sonypi_device.lock); + mutex_lock(&sonypi_device.lock); switch (cmd) { case SONYPI_IOCGBRT: if (sonypi_ec_read(SONYPI_LCD_LIGHT, &val8)) { @@ -1101,7 +1108,7 @@ static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, default: ret = -EINVAL; } - up(&sonypi_device.lock); + mutex_unlock(&sonypi_device.lock); return ret; } @@ -1260,6 +1267,28 @@ static int __devinit sonypi_create_input_devices(void) static int __devinit sonypi_setup_ioports(struct sonypi_device *dev, const struct sonypi_ioport_list *ioport_list) { + /* try to detect if sony-laptop is being used and thus + * has already requested one of the known ioports. + * As in the deprecated check_region this is racy has we have + * multiple ioports available and one of them can be requested + * between this check and the subsequent request. Anyway, as an + * attempt to be some more user-friendly as we currently are, + * this is enough. + */ + const struct sonypi_ioport_list *check = ioport_list; + while (check_ioport && check->port1) { + if (!request_region(check->port1, + sonypi_device.region_size, + "Sony Programable I/O Device Check")) { + printk(KERN_ERR "sonypi: ioport 0x%.4x busy, using sony-laptop? " + "if not use check_ioport=0\n", + check->port1); + return -EBUSY; + } + release_region(check->port1, sonypi_device.region_size); + check++; + } + while (ioport_list->port1) { if (request_region(ioport_list->port1, @@ -1321,6 +1350,10 @@ static int __devinit sonypi_probe(struct platform_device *dev) struct pci_dev *pcidev; int error; + printk(KERN_WARNING "sonypi: please try the sony-laptop module instead " + "and report failures, see also " + "http://www.linux.it/~malattia/wiki/index.php/Sony_drivers\n"); + spin_lock_init(&sonypi_device.fifo_lock); sonypi_device.fifo = kfifo_alloc(SONYPI_BUF_SIZE, GFP_KERNEL, &sonypi_device.fifo_lock); @@ -1330,7 +1363,7 @@ static int __devinit sonypi_probe(struct platform_device *dev) } init_waitqueue_head(&sonypi_device.fifo_proc_list); - init_MUTEX(&sonypi_device.lock); + mutex_init(&sonypi_device.lock); sonypi_device.bluetooth_power = -1; if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, diff --git a/drivers/char/tpm/tpm_atmel.h b/drivers/char/tpm/tpm_atmel.h index aefd683..3c85200 100644 --- a/drivers/char/tpm/tpm_atmel.h +++ b/drivers/char/tpm/tpm_atmel.h @@ -53,8 +53,8 @@ static void __iomem * atmel_get_base_addr(unsigned long *base, int *region_size) } reg = get_property(dn, "reg", ®len); - naddrc = prom_n_addr_cells(dn); - nsizec = prom_n_size_cells(dn); + naddrc = of_n_addr_cells(dn); + nsizec = of_n_size_cells(dn); of_node_put(dn); diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 5289254..7a32df5 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1376,6 +1376,8 @@ static void do_tty_hangup(struct work_struct *work) read_unlock(&tasklist_lock); tty->flags = 0; + put_pid(tty->session); + put_pid(tty->pgrp); tty->session = NULL; tty->pgrp = NULL; tty->ctrl_status = 0; @@ -1533,7 +1535,7 @@ void disassociate_ctty(int on_exit) spin_lock_irq(¤t->sighand->siglock); tty_pgrp = current->signal->tty_old_pgrp; - current->signal->tty_old_pgrp = 0; + current->signal->tty_old_pgrp = NULL; spin_unlock_irq(¤t->sighand->siglock); put_pid(tty_pgrp); @@ -1901,6 +1903,20 @@ static int init_dev(struct tty_driver *driver, int idx, /* check whether we're reopening an existing tty */ if (driver->flags & TTY_DRIVER_DEVPTS_MEM) { tty = devpts_get_tty(idx); + /* + * If we don't have a tty here on a slave open, it's because + * the master already started the close process and there's + * no relation between devpts file and tty anymore. + */ + if (!tty && driver->subtype == PTY_TYPE_SLAVE) { + retval = -EIO; + goto end_init; + } + /* + * It's safe from now on because init_dev() is called with + * tty_mutex held and release_dev() won't change tty->count + * or tty->flags without having to grab tty_mutex + */ if (tty && driver->subtype == PTY_TYPE_MASTER) tty = tty->link; } else { @@ -3713,15 +3729,14 @@ int tty_register_driver(struct tty_driver *driver) if (!driver->major) { error = alloc_chrdev_region(&dev, driver->minor_start, driver->num, - (char*)driver->name); + driver->name); if (!error) { driver->major = MAJOR(dev); driver->minor_start = MINOR(dev); } } else { dev = MKDEV(driver->major, driver->minor_start); - error = register_chrdev_region(dev, driver->num, - (char*)driver->name); + error = register_chrdev_region(dev, driver->num, driver->name); } if (error < 0) { kfree(p); @@ -3828,6 +3843,9 @@ static struct pid *__proc_set_tty(struct task_struct *tsk, struct tty_struct *tt { struct pid *old_pgrp; if (tty) { + /* We should not have a session or pgrp to here but.... */ + put_pid(tty->session); + put_pid(tty->pgrp); tty->session = get_pid(task_session(tsk)); tty->pgrp = get_pid(task_pgrp(tsk)); } diff --git a/drivers/char/vt.c b/drivers/char/vt.c index c3f8e38..1bbb45b 100644 --- a/drivers/char/vt.c +++ b/drivers/char/vt.c @@ -724,6 +724,7 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */ return -ENOMEM; memset(vc, 0, sizeof(*vc)); vc_cons[currcons].d = vc; + INIT_WORK(&vc_cons[currcons].SAK_work, vc_SAK); visual_init(vc, currcons, 1); if (!*vc->vc_uni_pagedir_loc) con_set_default_unimap(vc); @@ -2185,10 +2186,28 @@ static void console_callback(struct work_struct *ignored) release_console_sem(); } -void set_console(int nr) +int set_console(int nr) { + struct vc_data *vc = vc_cons[fg_console].d; + + if (!vc_cons_allocated(nr) || vt_dont_switch || + (vc->vt_mode.mode == VT_AUTO && vc->vc_mode == KD_GRAPHICS)) { + + /* + * Console switch will fail in console_callback() or + * change_console() so there is no point scheduling + * the callback + * + * Existing set_console() users don't check the return + * value so this shouldn't break anything + */ + return -EINVAL; + } + want_console = nr; schedule_console_callback(); + + return 0; } struct tty_driver *console_driver; diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index 3a5d301..c9f2dd6 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -34,7 +34,7 @@ #include <linux/kbd_diacr.h> #include <linux/selection.h> -static char vt_dont_switch; +char vt_dont_switch; extern struct tty_driver *console_driver; #define VT_IS_IN_USE(i) (console_driver->ttys[i] && console_driver->ttys[i]->count) @@ -1039,10 +1039,22 @@ int vt_waitactive(int vt) add_wait_queue(&vt_activate_queue, &wait); for (;;) { - set_current_state(TASK_INTERRUPTIBLE); retval = 0; - if (vt == fg_console) + + /* + * Synchronize with redraw_screen(). By acquiring the console + * semaphore we make sure that the console switch is completed + * before we return. If we didn't wait for the semaphore, we + * could return at a point where fg_console has already been + * updated, but the console switch hasn't been completed. + */ + acquire_console_sem(); + set_current_state(TASK_INTERRUPTIBLE); + if (vt == fg_console) { + release_console_sem(); break; + } + release_console_sem(); retval = -EINTR; if (signal_pending(current)) break; diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index ea09d0c..60198a7 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -301,6 +301,7 @@ config I6300ESB_WDT config I8XX_TCO tristate "Intel i8xx TCO Timer/Watchdog" depends on WATCHDOG && (X86 || IA64) && PCI + default n ---help--- Hardware driver for the TCO timer built into the Intel 82801 I/O Controller Hub family. The TCO (Total Cost of Ownership) @@ -547,7 +548,7 @@ config MV64X60_WDT depends on WATCHDOG && MV64X60 config BOOKE_WDT - tristate "PowerPC Book-E Watchdog Timer" + bool "PowerPC Book-E Watchdog Timer" depends on WATCHDOG && (BOOKE || 4xx) ---help--- Please see Documentation/watchdog/watchdog-api.txt for diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c index 81fb3de..76c7fa3 100644 --- a/drivers/char/watchdog/machzwd.c +++ b/drivers/char/watchdog/machzwd.c @@ -314,21 +314,21 @@ static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd, { void __user *argp = (void __user *)arg; int __user *p = argp; - switch(cmd){ - case WDIOC_GETSUPPORT: - if (copy_to_user(argp, &zf_info, sizeof(zf_info))) - return -EFAULT; - break; + switch (cmd) { + case WDIOC_GETSUPPORT: + if (copy_to_user(argp, &zf_info, sizeof(zf_info))) + return -EFAULT; + break; - case WDIOC_GETSTATUS: - return put_user(0, p); + case WDIOC_GETSTATUS: + return put_user(0, p); - case WDIOC_KEEPALIVE: - zf_ping(NULL); - break; + case WDIOC_KEEPALIVE: + zf_ping(0); + break; - default: - return -ENOTTY; + default: + return -ENOTTY; } return 0; diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c index ccaa6a3..5cfcff5 100644 --- a/drivers/clocksource/acpi_pm.c +++ b/drivers/clocksource/acpi_pm.c @@ -26,7 +26,7 @@ /* * The I/O port the PMTMR resides at. * The location is detected during setup_arch(), - * in arch/i386/acpi/boot.c + * in arch/i386/kernel/acpi/boot.c */ u32 pmtmr_ioport __read_mostly; @@ -90,7 +90,7 @@ __setup("acpi_pm_good", acpi_pm_good_setup); static inline void acpi_pm_need_workaround(void) { clocksource_acpi_pm.read = acpi_pm_read_slow; - clocksource_acpi_pm.rating = 110; + clocksource_acpi_pm.rating = 120; } /* @@ -214,4 +214,7 @@ pm_good: return clocksource_register(&clocksource_acpi_pm); } -module_init(init_acpi_pm_clocksource); +/* We use fs_initcall because we want the PCI fixups to have run + * but we still need to load before device_initcall + */ +fs_initcall(init_acpi_pm_clocksource); diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c index 4f3925c..1bde303 100644 --- a/drivers/clocksource/cyclone.c +++ b/drivers/clocksource/cyclone.c @@ -116,4 +116,4 @@ static int __init init_cyclone_clocksource(void) return clocksource_register(&clocksource_cyclone); } -module_init(init_cyclone_clocksource); +arch_initcall(init_cyclone_clocksource); diff --git a/drivers/connector/connector.c b/drivers/connector/connector.c index a44db75..a7b9e9b 100644 --- a/drivers/connector/connector.c +++ b/drivers/connector/connector.c @@ -128,7 +128,7 @@ EXPORT_SYMBOL_GPL(cn_netlink_send); */ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), void *data) { - struct cn_callback_entry *__cbq; + struct cn_callback_entry *__cbq, *__new_cbq; struct cn_dev *dev = &cdev; int err = -ENODEV; @@ -148,27 +148,27 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v } else { struct cn_callback_data *d; - __cbq = kzalloc(sizeof(*__cbq), GFP_ATOMIC); - if (__cbq) { - d = &__cbq->data; + err = -ENOMEM; + __new_cbq = kzalloc(sizeof(struct cn_callback_entry), GFP_ATOMIC); + if (__new_cbq) { + d = &__new_cbq->data; d->callback_priv = msg; d->callback = __cbq->data.callback; d->ddata = data; d->destruct_data = destruct_data; - d->free = __cbq; + d->free = __new_cbq; - INIT_WORK(&__cbq->work, + INIT_WORK(&__new_cbq->work, &cn_queue_wrapper); - + if (queue_work(dev->cbdev->cn_queue, - &__cbq->work)) + &__new_cbq->work)) err = 0; else { - kfree(__cbq); + kfree(__new_cbq); err = -EINVAL; } - } else - err = -ENOMEM; + } } break; } @@ -212,7 +212,7 @@ static void cn_rx_skb(struct sk_buff *__skb) skb = skb_get(__skb); if (skb->len >= NLMSG_SPACE(0)) { - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(struct cn_msg) || skb->len < nlh->nlmsg_len || @@ -448,7 +448,7 @@ static int __devinit cn_init(void) dev->nls = netlink_kernel_create(NETLINK_CONNECTOR, CN_NETLINK_USERS + 0xf, - dev->input, THIS_MODULE); + dev->input, NULL, THIS_MODULE); if (!dev->nls) return -EIO; diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index f52facc..3162010 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1015,6 +1015,10 @@ static int cpufreq_remove_dev (struct sys_device * sys_dev) { unsigned int cpu = sys_dev->id; int retval; + + if (cpu_is_offline(cpu)) + return 0; + if (unlikely(lock_policy_rwsem_write(cpu))) BUG(); diff --git a/drivers/cpufreq/cpufreq_ondemand.c b/drivers/cpufreq/cpufreq_ondemand.c index d60bcb9..8d053f5 100644 --- a/drivers/cpufreq/cpufreq_ondemand.c +++ b/drivers/cpufreq/cpufreq_ondemand.c @@ -432,9 +432,6 @@ static void do_dbs_timer(struct work_struct *work) /* We want all CPUs to do sampling nearly on same jiffy */ int delay = usecs_to_jiffies(dbs_tuners_ins.sampling_rate); - /* Permit rescheduling of this work item */ - work_release(work); - delay -= jiffies % delay; if (lock_policy_rwsem_write(cpu) < 0) @@ -473,7 +470,7 @@ static inline void dbs_timer_init(struct cpu_dbs_info_s *dbs_info) dbs_info->enable = 1; ondemand_powersave_bias_init(); dbs_info->sample_type = DBS_NORMAL_SAMPLE; - INIT_DELAYED_WORK_NAR(&dbs_info->work, do_dbs_timer); + INIT_DELAYED_WORK(&dbs_info->work, do_dbs_timer); queue_delayed_work_on(dbs_info->cpu, kondemand_wq, &dbs_info->work, delay); } diff --git a/drivers/crypto/geode-aes.c b/drivers/crypto/geode-aes.c index 0eb6284..6d3840e 100644 --- a/drivers/crypto/geode-aes.c +++ b/drivers/crypto/geode-aes.c @@ -99,9 +99,8 @@ do_crypt(void *src, void *dst, int len, u32 flags) static unsigned int geode_aes_crypt(struct geode_aes_op *op) { - u32 flags = 0; - int iflags; + unsigned long iflags; if (op->len == 0 || op->src == op->dst) return 0; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 1527804..322ee29 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -176,6 +176,7 @@ void dma_chan_cleanup(struct kref *kref) chan->client = NULL; kref_put(&chan->device->refcount, dma_async_device_cleanup); } +EXPORT_SYMBOL(dma_chan_cleanup); static void dma_chan_free_rcu(struct rcu_head *rcu) { @@ -261,6 +262,7 @@ struct dma_client *dma_async_client_register(dma_event_callback event_callback) return client; } +EXPORT_SYMBOL(dma_async_client_register); /** * dma_async_client_unregister - unregister a client and free the &dma_client @@ -287,6 +289,7 @@ void dma_async_client_unregister(struct dma_client *client) kfree(client); dma_chans_rebalance(); } +EXPORT_SYMBOL(dma_async_client_unregister); /** * dma_async_client_chan_request - request DMA channels @@ -304,6 +307,7 @@ void dma_async_client_chan_request(struct dma_client *client, client->chans_desired = number; dma_chans_rebalance(); } +EXPORT_SYMBOL(dma_async_client_chan_request); /** * dma_async_device_register - registers DMA devices found @@ -346,6 +350,7 @@ int dma_async_device_register(struct dma_device *device) return 0; } +EXPORT_SYMBOL(dma_async_device_register); /** * dma_async_device_cleanup - function called when all references are released @@ -390,23 +395,12 @@ void dma_async_device_unregister(struct dma_device *device) kref_put(&device->refcount, dma_async_device_cleanup); wait_for_completion(&device->done); } +EXPORT_SYMBOL(dma_async_device_unregister); static int __init dma_bus_init(void) { mutex_init(&dma_list_mutex); return class_register(&dma_devclass); } - subsys_initcall(dma_bus_init); -EXPORT_SYMBOL(dma_async_client_register); -EXPORT_SYMBOL(dma_async_client_unregister); -EXPORT_SYMBOL(dma_async_client_chan_request); -EXPORT_SYMBOL(dma_async_memcpy_buf_to_buf); -EXPORT_SYMBOL(dma_async_memcpy_buf_to_pg); -EXPORT_SYMBOL(dma_async_memcpy_pg_to_pg); -EXPORT_SYMBOL(dma_async_memcpy_complete); -EXPORT_SYMBOL(dma_async_memcpy_issue_pending); -EXPORT_SYMBOL(dma_async_device_register); -EXPORT_SYMBOL(dma_async_device_unregister); -EXPORT_SYMBOL(dma_chan_cleanup); diff --git a/drivers/eisa/pci_eisa.c b/drivers/eisa/pci_eisa.c index 9e91362..74edb1d 100644 --- a/drivers/eisa/pci_eisa.c +++ b/drivers/eisa/pci_eisa.c @@ -19,8 +19,8 @@ /* There is only *one* pci_eisa device per machine, right ? */ static struct eisa_root_device pci_eisa_root; -static int __devinit pci_eisa_init (struct pci_dev *pdev, - const struct pci_device_id *ent) +static int __init pci_eisa_init(struct pci_dev *pdev, + const struct pci_device_id *ent) { int rc; diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 850788f..8fbe9fd 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -36,5 +36,7 @@ config HID_DEBUG If unsure, say N +source "drivers/hid/usbhid/Kconfig" + endmenu diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 52e97d8..68d1376 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -6,3 +6,7 @@ hid-objs := hid-core.o hid-input.o obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o +obj-$(CONFIG_USB_HID) += usbhid/ +obj-$(CONFIG_USB_MOUSE) += usbhid/ +obj-$(CONFIG_USB_KBD) += usbhid/ + diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 7452399..62e21cc 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006 Jiri Kosina + * Copyright (c) 2006-2007 Jiri Kosina */ /* @@ -26,6 +26,7 @@ #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> +#include <linux/vmalloc.h> #include <linux/hid.h> #include <linux/hiddev.h> @@ -36,7 +37,7 @@ */ #define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik" +#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" #define DRIVER_DESC "HID core driver" #define DRIVER_LICENSE "GPL" @@ -654,12 +655,13 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) memcpy(device->rdesc, start, size); device->rsize = size; - if (!(parser = kzalloc(sizeof(struct hid_parser), GFP_KERNEL))) { + if (!(parser = vmalloc(sizeof(struct hid_parser)))) { kfree(device->rdesc); kfree(device->collection); kfree(device); return NULL; } + memset(parser, 0, sizeof(struct hid_parser)); parser->device = device; end = start + size; @@ -667,45 +669,40 @@ struct hid_device *hid_parse_report(__u8 *start, unsigned size) if (item.format != HID_ITEM_FORMAT_SHORT) { dbg("unexpected long global item"); - kfree(device->collection); hid_free_device(device); - kfree(parser); + vfree(parser); return NULL; } if (dispatch_type[item.type](parser, &item)) { dbg("item %u %u %u %u parsing failed\n", item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); - kfree(device->collection); hid_free_device(device); - kfree(parser); + vfree(parser); return NULL; } if (start == end) { if (parser->collection_stack_ptr) { dbg("unbalanced collection at end of report description"); - kfree(device->collection); hid_free_device(device); - kfree(parser); + vfree(parser); return NULL; } if (parser->local.delimiter_depth) { dbg("unbalanced delimiter at end of report description"); - kfree(device->collection); hid_free_device(device); - kfree(parser); + vfree(parser); return NULL; } - kfree(parser); + vfree(parser); return device; } } dbg("item fetching failed at offset %d\n", (int)(end - start)); - kfree(device->collection); hid_free_device(device); - kfree(parser); + vfree(parser); return NULL; } EXPORT_SYMBOL_GPL(hid_parse_report); @@ -758,8 +755,7 @@ static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) report += offset >> 3; /* adjust byte index */ offset &= 7; /* now only need bit offset into one byte */ - x = get_unaligned((u64 *) report); - x = le64_to_cpu(x); + x = le64_to_cpu(get_unaligned((__le64 *) report)); x = (x >> offset) & ((1ULL << n) - 1); /* extract bit field */ return (u32) x; } @@ -774,7 +770,7 @@ static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) */ static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) { - u64 x; + __le64 x; u64 m = (1ULL << n) - 1; WARN_ON(n > 32); @@ -785,10 +781,10 @@ static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u3 report += offset >> 3; offset &= 7; - x = get_unaligned((u64 *)report); + x = get_unaligned((__le64 *)report); x &= cpu_to_le64(~(m << offset)); x |= cpu_to_le64(((u64) value) << offset); - put_unaligned(x, (u64 *) report); + put_unaligned(x, (__le64 *) report); } /* @@ -876,11 +872,12 @@ static void hid_output_field(struct hid_field *field, __u8 *data) unsigned count = field->report_count; unsigned offset = field->report_offset; unsigned size = field->report_size; + unsigned bitsused = offset + count * size; unsigned n; /* make sure the unused bits in the last byte are zeros */ - if (count > 0 && size > 0) - data[(count*size-1)/8] = 0; + if (count > 0 && size > 0 && (bitsused % 8) != 0) + data[(bitsused-1)/8] &= (1 << (bitsused % 8)) - 1; for (n = 0; n < count; n++) { if (field->logical_minimum < 0) /* signed values */ @@ -977,7 +974,7 @@ int hid_input_report(struct hid_device *hid, int type, u8 *data, int size, int i if (size < rsize) { dbg("report %d is too short, (%d < %d)", report->id, size, rsize); - return -1; + memset(data + size, 0, rsize - size); } if ((hid->claimed & HID_CLAIMED_HIDDEV) && hid->hiddev_report_event) diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 89241be..83c4126 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -29,6 +29,7 @@ */ #include <linux/hid.h> +#include <linux/hid-debug.h> struct hid_usage_entry { unsigned page; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 25d180a..a19b65e 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -2,7 +2,7 @@ * $Id: hid-input.c,v 1.2 2002/04/23 00:59:25 rdamazio Exp $ * * Copyright (c) 2000-2001 Vojtech Pavlik - * Copyright (c) 2006 Jiri Kosina + * Copyright (c) 2006-2007 Jiri Kosina * * HID to Linux Input mapping */ @@ -71,7 +71,6 @@ static const struct { #define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0) #define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0) -#define map_rel_clear(c) do { map_rel(c); clear_bit(c, bit); } while (0) #define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0) #ifdef CONFIG_USB_HIDINPUT_POWERBOOK @@ -296,7 +295,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel } } - map_key_clear(code); + map_key(code); break; @@ -347,9 +346,9 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case HID_GD_RX: case HID_GD_RY: case HID_GD_RZ: case HID_GD_SLIDER: case HID_GD_DIAL: case HID_GD_WHEEL: if (field->flags & HID_MAIN_ITEM_RELATIVE) - map_rel_clear(usage->hid & 0xf); + map_rel(usage->hid & 0xf); else - map_abs_clear(usage->hid & 0xf); + map_abs(usage->hid & 0xf); break; case HID_GD_HATSWITCH: @@ -432,6 +431,15 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x000: goto ignore; case 0x034: map_key_clear(KEY_SLEEP); break; case 0x036: map_key_clear(BTN_MISC); break; + /* + * The next three are reported by Belkin wireless + * keyboard (1020:0006). These values are "reserved" + * in HUT 1.12. + */ + case 0x03a: map_key_clear(KEY_SOUND); break; + case 0x03b: map_key_clear(KEY_CAMERA); break; + case 0x03c: map_key_clear(KEY_DOCUMENTS); break; + case 0x040: map_key_clear(KEY_MENU); break; case 0x045: map_key_clear(KEY_RADIO); break; @@ -519,7 +527,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x22f: map_key_clear(KEY_ZOOMRESET); break; case 0x233: map_key_clear(KEY_SCROLLUP); break; case 0x234: map_key_clear(KEY_SCROLLDOWN); break; - case 0x238: map_rel_clear(REL_HWHEEL); break; + case 0x238: map_rel(REL_HWHEEL); break; case 0x25f: map_key_clear(KEY_CANCEL); break; case 0x279: map_key_clear(KEY_REDO); break; @@ -532,6 +540,42 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x302: map_key_clear(KEY_PROG2); break; case 0x303: map_key_clear(KEY_PROG3); break; + /* Reported on certain Logitech wireless keyboards */ + case 0x1001: map_key_clear(KEY_MESSENGER); break; + case 0x1003: map_key_clear(KEY_SOUND); break; + case 0x1004: map_key_clear(KEY_VIDEO); break; + case 0x1005: map_key_clear(KEY_AUDIO); break; + case 0x100a: map_key_clear(KEY_DOCUMENTS); break; + case 0x1011: map_key_clear(KEY_PREVIOUSSONG); break; + case 0x1012: map_key_clear(KEY_NEXTSONG); break; + case 0x1013: map_key_clear(KEY_CAMERA); break; + case 0x1014: map_key_clear(KEY_MESSENGER); break; + case 0x1015: map_key_clear(KEY_RECORD); break; + case 0x1016: map_key_clear(KEY_PLAYER); break; + case 0x1017: map_key_clear(KEY_EJECTCD); break; + case 0x1019: map_key_clear(KEY_PROG1); break; + case 0x101a: map_key_clear(KEY_PROG2); break; + case 0x101b: map_key_clear(KEY_PROG3); break; + case 0x101f: map_key_clear(KEY_ZOOMIN); break; + case 0x1020: map_key_clear(KEY_ZOOMOUT); break; + case 0x1021: map_key_clear(KEY_ZOOMRESET); break; + case 0x1023: map_key_clear(KEY_CLOSE); break; + /* this one is marked as 'Rotate' */ + case 0x1028: map_key_clear(KEY_ANGLE); break; + case 0x1029: map_key_clear(KEY_SHUFFLE); break; + case 0x1041: map_key_clear(KEY_BATTERY); break; + case 0x1042: map_key_clear(KEY_WORDPROCESSOR); break; + case 0x1043: map_key_clear(KEY_SPREADSHEET); break; + case 0x1044: map_key_clear(KEY_PRESENTATION); break; + case 0x1045: map_key_clear(KEY_UNDO); break; + case 0x1046: map_key_clear(KEY_REDO); break; + case 0x1047: map_key_clear(KEY_PRINT); break; + case 0x1048: map_key_clear(KEY_SAVE); break; + case 0x1049: map_key_clear(KEY_PROG1); break; + case 0x104a: map_key_clear(KEY_PROG2); break; + case 0x104b: map_key_clear(KEY_PROG3); break; + case 0x104c: map_key_clear(KEY_PROG4); break; + default: goto ignore; } break; @@ -647,6 +691,12 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel set_bit(usage->type, input->evbit); + if (device->quirks & HID_QUIRK_DUPLICATE_USAGES && + (usage->type == EV_KEY || + usage->type == EV_REL || + usage->type == EV_ABS)) + clear_bit(usage->code, bit); + while (usage->code <= max && test_and_set_bit(usage->code, bit)) usage->code = find_next_zero_bit(bit, max + 1, usage->code); diff --git a/drivers/hid/usbhid/Kconfig b/drivers/hid/usbhid/Kconfig new file mode 100644 index 0000000..7c87bdc --- /dev/null +++ b/drivers/hid/usbhid/Kconfig @@ -0,0 +1,149 @@ +comment "USB Input Devices" + depends on USB + +config USB_HID + tristate "USB Human Interface Device (full HID) support" + default y + depends on USB && INPUT + select HID + ---help--- + Say Y here if you want full HID support to connect USB keyboards, + mice, joysticks, graphic tablets, or any other HID based devices + to your computer via USB, as well as Uninterruptible Power Supply + (UPS) and monitor control devices. + + You can't use this driver and the HIDBP (Boot Protocol) keyboard + and mouse drivers at the same time. More information is available: + <file:Documentation/input/input.txt>. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called usbhid. + +comment "Input core support is needed for USB HID input layer or HIDBP support" + depends on USB_HID && INPUT=n + +config USB_HIDINPUT_POWERBOOK + bool "Enable support for iBook/PowerBook special keys" + default n + depends on USB_HID + help + Say Y here if you want support for the special keys (Fn, Numlock) on + Apple iBooks and PowerBooks. + + If unsure, say N. + +config HID_FF + bool "Force feedback support (EXPERIMENTAL)" + depends on USB_HID && EXPERIMENTAL + help + Say Y here is you want force feedback support for a few HID devices. + See below for a list of supported devices. + + See <file:Documentation/input/ff.txt> for a description of the force + feedback API. + + If unsure, say N. + +config HID_PID + bool "PID device support" + depends on HID_FF + help + Say Y here if you have a PID-compliant device and wish to enable force + feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such + devices. + +config LOGITECH_FF + bool "Logitech devices support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have one of these devices: + - Logitech WingMan Cordless RumblePad + - Logitech WingMan Cordless RumblePad 2 + - Logitech WingMan Force 3D + - Logitech Formula Force EX + - Logitech MOMO Force wheel + + and if you want to enable force feedback for them. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config PANTHERLORD_FF + bool "PantherLord USB/PS2 2in1 Adapter support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want + to enable force feedback support for it. + +config THRUSTMASTER_FF + bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" + depends on HID_FF && EXPERIMENTAL + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, + and want to enable force feedback support for it. + Note: if you say N here, this device will still be supported, but without + force feedback. + +config ZEROPLUS_FF + bool "Zeroplus based game controller support" + depends on HID_FF + select INPUT_FF_MEMLESS if USB_HID + help + Say Y here if you have a Zeroplus based game controller and want to + enable force feedback for it. + +config USB_HIDDEV + bool "/dev/hiddev raw HID device support" + depends on USB_HID + help + Say Y here if you want to support HID devices (from the USB + specification standpoint) that aren't strictly user interface + devices, like monitor controls and Uninterruptable Power Supplies. + + This module supports these devices separately using a separate + event interface on /dev/usb/hiddevX (char 180:96 to 180:111). + + If unsure, say Y. + +menu "USB HID Boot Protocol drivers" + depends on USB!=n && USB_HID!=y + +config USB_KBD + tristate "USB HIDBP Keyboard (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB keyboard and prefer + to use the keyboard in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple keyboards. + + To compile this driver as a module, choose M here: the + module will be called usbkbd. + + If even remotely unsure, say N. + +config USB_MOUSE + tristate "USB HIDBP Mouse (simple Boot) support" + depends on USB && INPUT + ---help--- + Say Y here only if you are absolutely sure that you don't want + to use the generic HID driver for your USB mouse and prefer + to use the mouse in its limited Boot Protocol mode instead. + + This is almost certainly not what you want. This is mostly + useful for embedded applications or simple mice. + + To compile this driver as a module, choose M here: the + module will be called usbmouse. + + If even remotely unsure, say N. + +endmenu + + diff --git a/drivers/hid/usbhid/Makefile b/drivers/hid/usbhid/Makefile new file mode 100644 index 0000000..8e6ab5b1 --- /dev/null +++ b/drivers/hid/usbhid/Makefile @@ -0,0 +1,35 @@ +# +# Makefile for the USB input drivers +# + +# Multipart objects. +usbhid-objs := hid-core.o hid-quirks.o + +# Optional parts of multipart objects. + +ifeq ($(CONFIG_USB_HIDDEV),y) + usbhid-objs += hiddev.o +endif +ifeq ($(CONFIG_HID_PID),y) + usbhid-objs += hid-pidff.o +endif +ifeq ($(CONFIG_LOGITECH_FF),y) + usbhid-objs += hid-lgff.o +endif +ifeq ($(CONFIG_PANTHERLORD_FF),y) + usbhid-objs += hid-plff.o +endif +ifeq ($(CONFIG_THRUSTMASTER_FF),y) + usbhid-objs += hid-tmff.o +endif +ifeq ($(CONFIG_ZEROPLUS_FF),y) + usbhid-objs += hid-zpff.o +endif +ifeq ($(CONFIG_HID_FF),y) + usbhid-objs += hid-ff.o +endif + +obj-$(CONFIG_USB_HID) += usbhid.o +obj-$(CONFIG_USB_KBD) += usbkbd.o +obj-$(CONFIG_USB_MOUSE) += usbmouse.o + diff --git a/drivers/usb/input/hid-core.c b/drivers/hid/usbhid/hid-core.c index 4d8ed3d..91d6103 100644 --- a/drivers/usb/input/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -4,7 +4,7 @@ * Copyright (c) 1999 Andreas Gal * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc - * Copyright (c) 2006 Jiri Kosina + * Copyright (c) 2006-2007 Jiri Kosina */ /* @@ -27,9 +27,6 @@ #include <linux/input.h> #include <linux/wait.h> -#undef DEBUG -#undef DEBUG_DATA - #include <linux/usb.h> #include <linux/hid.h> @@ -42,7 +39,7 @@ */ #define DRIVER_VERSION "v2.6" -#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik" +#define DRIVER_AUTHOR "Andreas Gal, Vojtech Pavlik, Jiri Kosina" #define DRIVER_DESC "USB HID core driver" #define DRIVER_LICENSE "GPL" @@ -56,6 +53,13 @@ static unsigned int hid_mousepoll_interval; module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); +/* Quirks specified at module load time */ +static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; +module_param_array_named(quirks, quirks_param, charp, NULL, 0444); +MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " + " quirks=vendorID:productID:quirks" + " where vendorID, productID, and quirks are all in" + " 0x-prefixed hex"); /* * Input submission and I/O error handler. */ @@ -147,6 +151,11 @@ static void hid_io_error(struct hid_device *hid) if (usb_get_intfdata(usbhid->intf) == NULL) goto done; + /* If it has been a while since the last error, we'll assume + * this a brand new error and reset the retry timeout. */ + if (time_after(jiffies, usbhid->stop_retry + HZ/2)) + usbhid->retry_delay = 0; + /* When an error occurs, retry at increasing intervals */ if (usbhid->retry_delay == 0) { usbhid->retry_delay = 13; /* Then 26, 52, 104, 104, ... */ @@ -511,11 +520,6 @@ void usbhid_close(struct hid_device *hid) usb_kill_urb(usbhid->urbin); } -#define USB_VENDOR_ID_PANJIT 0x134c - -#define USB_VENDOR_ID_TURBOX 0x062a -#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 - /* * Initialize all reports */ @@ -547,414 +551,43 @@ void usbhid_init_reports(struct hid_device *hid) warn("timeout initializing reports"); } -#define USB_VENDOR_ID_GTCO 0x078c -#define USB_VENDOR_ID_GTCO_IPANEL_2 0x5543 -#define USB_DEVICE_ID_GTCO_90 0x0090 -#define USB_DEVICE_ID_GTCO_100 0x0100 -#define USB_DEVICE_ID_GTCO_101 0x0101 -#define USB_DEVICE_ID_GTCO_103 0x0103 -#define USB_DEVICE_ID_GTCO_104 0x0104 -#define USB_DEVICE_ID_GTCO_105 0x0105 -#define USB_DEVICE_ID_GTCO_106 0x0106 -#define USB_DEVICE_ID_GTCO_107 0x0107 -#define USB_DEVICE_ID_GTCO_108 0x0108 -#define USB_DEVICE_ID_GTCO_200 0x0200 -#define USB_DEVICE_ID_GTCO_201 0x0201 -#define USB_DEVICE_ID_GTCO_202 0x0202 -#define USB_DEVICE_ID_GTCO_203 0x0203 -#define USB_DEVICE_ID_GTCO_204 0x0204 -#define USB_DEVICE_ID_GTCO_205 0x0205 -#define USB_DEVICE_ID_GTCO_206 0x0206 -#define USB_DEVICE_ID_GTCO_207 0x0207 -#define USB_DEVICE_ID_GTCO_300 0x0300 -#define USB_DEVICE_ID_GTCO_301 0x0301 -#define USB_DEVICE_ID_GTCO_302 0x0302 -#define USB_DEVICE_ID_GTCO_303 0x0303 -#define USB_DEVICE_ID_GTCO_304 0x0304 -#define USB_DEVICE_ID_GTCO_305 0x0305 -#define USB_DEVICE_ID_GTCO_306 0x0306 -#define USB_DEVICE_ID_GTCO_307 0x0307 -#define USB_DEVICE_ID_GTCO_308 0x0308 -#define USB_DEVICE_ID_GTCO_309 0x0309 -#define USB_DEVICE_ID_GTCO_400 0x0400 -#define USB_DEVICE_ID_GTCO_401 0x0401 -#define USB_DEVICE_ID_GTCO_402 0x0402 -#define USB_DEVICE_ID_GTCO_403 0x0403 -#define USB_DEVICE_ID_GTCO_404 0x0404 -#define USB_DEVICE_ID_GTCO_405 0x0405 -#define USB_DEVICE_ID_GTCO_500 0x0500 -#define USB_DEVICE_ID_GTCO_501 0x0501 -#define USB_DEVICE_ID_GTCO_502 0x0502 -#define USB_DEVICE_ID_GTCO_503 0x0503 -#define USB_DEVICE_ID_GTCO_504 0x0504 -#define USB_DEVICE_ID_GTCO_1000 0x1000 -#define USB_DEVICE_ID_GTCO_1001 0x1001 -#define USB_DEVICE_ID_GTCO_1002 0x1002 -#define USB_DEVICE_ID_GTCO_1003 0x1003 -#define USB_DEVICE_ID_GTCO_1004 0x1004 -#define USB_DEVICE_ID_GTCO_1005 0x1005 -#define USB_DEVICE_ID_GTCO_1006 0x1006 -#define USB_DEVICE_ID_GTCO_8 0x0008 -#define USB_DEVICE_ID_GTCO_d 0x000d - -#define USB_VENDOR_ID_WACOM 0x056a - -#define USB_VENDOR_ID_ACECAD 0x0460 -#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 -#define USB_DEVICE_ID_ACECAD_302 0x0008 - -#define USB_VENDOR_ID_KBGEAR 0x084e -#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 - -#define USB_VENDOR_ID_AIPTEK 0x08ca -#define USB_DEVICE_ID_AIPTEK_01 0x0001 -#define USB_DEVICE_ID_AIPTEK_10 0x0010 -#define USB_DEVICE_ID_AIPTEK_20 0x0020 -#define USB_DEVICE_ID_AIPTEK_21 0x0021 -#define USB_DEVICE_ID_AIPTEK_22 0x0022 -#define USB_DEVICE_ID_AIPTEK_23 0x0023 -#define USB_DEVICE_ID_AIPTEK_24 0x0024 - -#define USB_VENDOR_ID_GRIFFIN 0x077d -#define USB_DEVICE_ID_POWERMATE 0x0410 -#define USB_DEVICE_ID_SOUNDKNOB 0x04AA - -#define USB_VENDOR_ID_ATEN 0x0557 -#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 -#define USB_DEVICE_ID_ATEN_CS124U 0x2202 -#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 -#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 -#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 - -#define USB_VENDOR_ID_TOPMAX 0x0663 -#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 - -#define USB_VENDOR_ID_HAPP 0x078b -#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 -#define USB_DEVICE_ID_UGCI_FLYING 0x0020 -#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 - -#define USB_VENDOR_ID_MGE 0x0463 -#define USB_DEVICE_ID_MGE_UPS 0xffff -#define USB_DEVICE_ID_MGE_UPS1 0x0001 - -#define USB_VENDOR_ID_ONTRAK 0x0a07 -#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 - -#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f -#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 - -#define USB_VENDOR_ID_A4TECH 0x09da -#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 - -#define USB_VENDOR_ID_AASHIMA 0x06d6 -#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 -#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 - -#define USB_VENDOR_ID_CYPRESS 0x04b4 -#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 -#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 -#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 - -#define USB_VENDOR_ID_BERKSHIRE 0x0c98 -#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 - -#define USB_VENDOR_ID_ALPS 0x0433 -#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 - -#define USB_VENDOR_ID_SAITEK 0x06a3 -#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 - -#define USB_VENDOR_ID_NEC 0x073e -#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 - -#define USB_VENDOR_ID_CHIC 0x05fe -#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 - -#define USB_VENDOR_ID_GLAB 0x06c2 -#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 -#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 -#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 -#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 -#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 -#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 -#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 -#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 - -#define USB_VENDOR_ID_WISEGROUP 0x0925 -#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 -#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 -#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 -#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 - -#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 -#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 - -#define USB_VENDOR_ID_CODEMERCS 0x07c0 -#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 -#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 -#define USB_DEVICE_ID_CODEMERCS_IOW48 0x1502 -#define USB_DEVICE_ID_CODEMERCS_IOW28 0x1503 - -#define USB_VENDOR_ID_DELORME 0x1163 -#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 -#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 - -#define USB_VENDOR_ID_MCC 0x09db -#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 -#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a - -#define USB_VENDOR_ID_VERNIER 0x08f7 -#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 -#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 -#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 -#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 - -#define USB_VENDOR_ID_LD 0x0f11 -#define USB_DEVICE_ID_LD_CASSY 0x1000 -#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 -#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 -#define USB_DEVICE_ID_LD_JWM 0x1080 -#define USB_DEVICE_ID_LD_DMMP 0x1081 -#define USB_DEVICE_ID_LD_UMIP 0x1090 -#define USB_DEVICE_ID_LD_XRAY1 0x1100 -#define USB_DEVICE_ID_LD_XRAY2 0x1101 -#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 -#define USB_DEVICE_ID_LD_COM3LAB 0x2000 -#define USB_DEVICE_ID_LD_TELEPORT 0x2010 -#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 -#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 -#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 - -#define USB_VENDOR_ID_APPLE 0x05ac -#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e -#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f -#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 -#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 -#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 -#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 -#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 -#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 -#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a -#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b -#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c -#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a -#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b -#define USB_DEVICE_ID_APPLE_IR 0x8240 - -#define USB_VENDOR_ID_CHERRY 0x046a -#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 - -#define USB_VENDOR_ID_YEALINK 0x6993 -#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 - -#define USB_VENDOR_ID_ALCOR 0x058f -#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 - -#define USB_VENDOR_ID_SUN 0x0430 -#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab - -#define USB_VENDOR_ID_AIRCABLE 0x16CA -#define USB_DEVICE_ID_AIRCABLE1 0x1502 - -#define USB_VENDOR_ID_LOGITECH 0x046d -#define USB_DEVICE_ID_LOGITECH_USB_RECEIVER 0xc101 - -#define USB_VENDOR_ID_IMATION 0x0718 -#define USB_DEVICE_ID_DISC_STAKKA 0xd000 - -#define USB_VENDOR_ID_PANTHERLORD 0x0810 -#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 - -#define USB_VENDOR_ID_SONY 0x054c -#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 - /* - * Alphabetically sorted blacklist by quirk type. + * Reset LEDs which BIOS might have left on. For now, just NumLock (0x01). */ +static int hid_find_field_early(struct hid_device *hid, unsigned int page, + unsigned int hid_code, struct hid_field **pfield) +{ + struct hid_report *report; + struct hid_field *field; + struct hid_usage *usage; + int i, j; + + list_for_each_entry(report, &hid->report_enum[HID_OUTPUT_REPORT].report_list, list) { + for (i = 0; i < report->maxfield; i++) { + field = report->field[i]; + for (j = 0; j < field->maxusage; j++) { + usage = &field->usage[j]; + if ((usage->hid & HID_USAGE_PAGE) == page && + (usage->hid & 0xFFFF) == hid_code) { + *pfield = field; + return j; + } + } + } + } + return -1; +} -static const struct hid_blacklist { - __u16 idVendor; - __u16 idProduct; - unsigned quirks; -} hid_blacklist[] = { - - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW48, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW28, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_8, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_GTCO_IPANEL_2, USB_DEVICE_ID_GTCO_d, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, - { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, - { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, - { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, - - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, - { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, - { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, - - { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, - - { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, - { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, - - { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, - - { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_USB_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, - - { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, - - { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, - - { 0, 0 } -}; +static void usbhid_set_leds(struct hid_device *hid) +{ + struct hid_field *field; + int offset; + + if ((offset = hid_find_field_early(hid, HID_UP_LED, 0x01, &field)) != -1) { + hid_set_field(field, offset, 0); + usbhid_submit_report(hid, field->report, USB_DIR_OUT); + } +} /* * Traverse the supplied list of reports and find the longest @@ -1043,25 +676,36 @@ static void hid_fixup_sony_ps3_controller(struct usb_device *dev, int ifnum) kfree(buf); } +/* + * Certain Logitech keyboards send in report #3 keys which are far + * above the logical maximum described in descriptor. This extends + * the original value of 0x28c of logical maximum to 0x104d + */ +static void hid_fixup_logitech_descriptor(unsigned char *rdesc, int rsize) +{ + if (rsize >= 90 && rdesc[83] == 0x26 + && rdesc[84] == 0x8c + && rdesc[85] == 0x02) { + info("Fixing up Logitech keyboard report descriptor"); + rdesc[84] = rdesc[89] = 0x4d; + rdesc[85] = rdesc[90] = 0x10; + } +} + static struct hid_device *usb_hid_configure(struct usb_interface *intf) { struct usb_host_interface *interface = intf->cur_altsetting; struct usb_device *dev = interface_to_usbdev (intf); struct hid_descriptor *hdesc; struct hid_device *hid; - unsigned quirks = 0, rsize = 0; + u32 quirks = 0; + unsigned rsize = 0; char *rdesc; int n, len, insize = 0; struct usbhid_device *usbhid; - /* Ignore all Wacom devices */ - if (le16_to_cpu(dev->descriptor.idVendor) == USB_VENDOR_ID_WACOM) - return NULL; - - for (n = 0; hid_blacklist[n].idVendor; n++) - if ((hid_blacklist[n].idVendor == le16_to_cpu(dev->descriptor.idVendor)) && - (hid_blacklist[n].idProduct == le16_to_cpu(dev->descriptor.idProduct))) - quirks = hid_blacklist[n].quirks; + quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), + le16_to_cpu(dev->descriptor.idProduct)); /* Many keyboards and mice don't like to be polled for reports, * so we will always set the HID_QUIRK_NOGET flag for them. */ @@ -1111,7 +755,10 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) if ((quirks & HID_QUIRK_CYMOTION)) hid_fixup_cymotion_descriptor(rdesc, rsize); -#ifdef DEBUG_DATA + if (quirks & HID_QUIRK_LOGITECH_DESCRIPTOR) + hid_fixup_logitech_descriptor(rdesc, rsize); + +#ifdef CONFIG_HID_DEBUG printk(KERN_DEBUG __FILE__ ": report descriptor (size %u, read %d) = ", rsize, n); for (n = 0; n < rsize; n++) printk(" %02x", (unsigned char) rdesc[n]); @@ -1227,8 +874,8 @@ static struct hid_device *usb_hid_configure(struct usb_interface *intf) le16_to_cpu(dev->descriptor.idProduct)); hid->bus = BUS_USB; - hid->vendor = dev->descriptor.idVendor; - hid->product = dev->descriptor.idProduct; + hid->vendor = le16_to_cpu(dev->descriptor.idVendor); + hid->product = le16_to_cpu(dev->descriptor.idProduct); usb_make_path(dev, hid->phys, sizeof(hid->phys)); strlcat(hid->phys, "/input", sizeof(hid->phys)); @@ -1316,6 +963,8 @@ static int hid_probe(struct usb_interface *intf, const struct usb_device_id *id) usbhid_init_reports(hid); hid_dump_device(hid); + if (hid->quirks & HID_QUIRK_RESET_LEDS) + usbhid_set_leds(hid); if (!hidinput_connect(hid)) hid->claimed |= HID_CLAIMED_INPUT; @@ -1430,6 +1079,9 @@ static struct usb_driver hid_driver = { static int __init hid_init(void) { int retval; + retval = usbhid_quirks_init(quirks_param); + if (retval) + goto usbhid_quirks_init_fail; retval = hiddev_init(); if (retval) goto hiddev_init_fail; @@ -1442,6 +1094,8 @@ static int __init hid_init(void) usb_register_fail: hiddev_exit(); hiddev_init_fail: + usbhid_quirks_exit(); +usbhid_quirks_init_fail: return retval; } @@ -1449,6 +1103,7 @@ static void __exit hid_exit(void) { usb_deregister(&hid_driver); hiddev_exit(); + usbhid_quirks_exit(); } module_init(hid_init); diff --git a/drivers/usb/input/hid-ff.c b/drivers/hid/usbhid/hid-ff.c index e431faa..23431fb 100644 --- a/drivers/usb/input/hid-ff.c +++ b/drivers/hid/usbhid/hid-ff.c @@ -56,6 +56,7 @@ static struct hid_ff_initializer inits[] = { { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */ { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */ { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */ + { 0x46d, 0xc286, hid_lgff_init }, /* Logitech Force 3D Pro Joystick */ { 0x46d, 0xc294, hid_lgff_init }, /* Logitech Formula Force EX */ { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */ { 0x46d, 0xca03, hid_lgff_init }, /* Logitech MOMO force wheel */ @@ -64,6 +65,7 @@ static struct hid_ff_initializer inits[] = { { 0x810, 0x0001, hid_plff_init }, #endif #ifdef CONFIG_THRUSTMASTER_FF + { 0x44f, 0xb300, hid_tmff_init }, { 0x44f, 0xb304, hid_tmff_init }, #endif #ifdef CONFIG_ZEROPLUS_FF diff --git a/drivers/usb/input/hid-lgff.c b/drivers/hid/usbhid/hid-lgff.c index e6f3af3..92d2553 100644 --- a/drivers/usb/input/hid-lgff.c +++ b/drivers/hid/usbhid/hid-lgff.c @@ -52,6 +52,7 @@ static const struct dev_type devices[] = { { 0x046d, 0xc211, ff_rumble }, { 0x046d, 0xc219, ff_rumble }, { 0x046d, 0xc283, ff_joystick }, + { 0x046d, 0xc286, ff_joystick }, { 0x046d, 0xc294, ff_joystick }, { 0x046d, 0xc295, ff_joystick }, { 0x046d, 0xca03, ff_joystick }, diff --git a/drivers/usb/input/hid-pidff.c b/drivers/hid/usbhid/hid-pidff.c index f5a90e9..f5a90e9 100644 --- a/drivers/usb/input/hid-pidff.c +++ b/drivers/hid/usbhid/hid-pidff.c diff --git a/drivers/usb/input/hid-plff.c b/drivers/hid/usbhid/hid-plff.c index 76d2e6e..76d2e6e 100644 --- a/drivers/usb/input/hid-plff.c +++ b/drivers/hid/usbhid/hid-plff.c diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c new file mode 100644 index 0000000..17a8755 --- /dev/null +++ b/drivers/hid/usbhid/hid-quirks.c @@ -0,0 +1,681 @@ +/* + * USB HID quirks support for Linux + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> + * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc + * Copyright (c) 2006-2007 Jiri Kosina + * Copyright (c) 2007 Paul Walmsley + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/hid.h> + +#define USB_VENDOR_ID_A4TECH 0x09da +#define USB_DEVICE_ID_A4TECH_WCP32PU 0x0006 + +#define USB_VENDOR_ID_AASHIMA 0x06d6 +#define USB_DEVICE_ID_AASHIMA_GAMEPAD 0x0025 +#define USB_DEVICE_ID_AASHIMA_PREDATOR 0x0026 + +#define USB_VENDOR_ID_ACECAD 0x0460 +#define USB_DEVICE_ID_ACECAD_FLAIR 0x0004 +#define USB_DEVICE_ID_ACECAD_302 0x0008 + +#define USB_VENDOR_ID_AIPTEK 0x08ca +#define USB_DEVICE_ID_AIPTEK_01 0x0001 +#define USB_DEVICE_ID_AIPTEK_10 0x0010 +#define USB_DEVICE_ID_AIPTEK_20 0x0020 +#define USB_DEVICE_ID_AIPTEK_21 0x0021 +#define USB_DEVICE_ID_AIPTEK_22 0x0022 +#define USB_DEVICE_ID_AIPTEK_23 0x0023 +#define USB_DEVICE_ID_AIPTEK_24 0x0024 + +#define USB_VENDOR_ID_AIRCABLE 0x16CA +#define USB_DEVICE_ID_AIRCABLE1 0x1502 + +#define USB_VENDOR_ID_ALCOR 0x058f +#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720 + +#define USB_VENDOR_ID_ALPS 0x0433 +#define USB_DEVICE_ID_IBM_GAMEPAD 0x1101 + +#define USB_VENDOR_ID_APPLE 0x05ac +#define USB_DEVICE_ID_APPLE_MIGHTYMOUSE 0x0304 +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI 0x020e +#define USB_DEVICE_ID_APPLE_FOUNTAIN_ISO 0x020f +#define USB_DEVICE_ID_APPLE_GEYSER_ANSI 0x0214 +#define USB_DEVICE_ID_APPLE_GEYSER_ISO 0x0215 +#define USB_DEVICE_ID_APPLE_GEYSER_JIS 0x0216 +#define USB_DEVICE_ID_APPLE_GEYSER3_ANSI 0x0217 +#define USB_DEVICE_ID_APPLE_GEYSER3_ISO 0x0218 +#define USB_DEVICE_ID_APPLE_GEYSER3_JIS 0x0219 +#define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a +#define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b +#define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c +#define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a +#define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b +#define USB_DEVICE_ID_APPLE_IR 0x8240 + +#define USB_VENDOR_ID_ATEN 0x0557 +#define USB_DEVICE_ID_ATEN_UC100KM 0x2004 +#define USB_DEVICE_ID_ATEN_CS124U 0x2202 +#define USB_DEVICE_ID_ATEN_2PORTKVM 0x2204 +#define USB_DEVICE_ID_ATEN_4PORTKVM 0x2205 +#define USB_DEVICE_ID_ATEN_4PORTKVMC 0x2208 + +#define USB_VENDOR_ID_BELKIN 0x050d +#define USB_DEVICE_ID_FLIP_KVM 0x3201 + +#define USB_VENDOR_ID_BERKSHIRE 0x0c98 +#define USB_DEVICE_ID_BERKSHIRE_PCWD 0x1140 + +#define USB_VENDOR_ID_CHERRY 0x046a +#define USB_DEVICE_ID_CHERRY_CYMOTION 0x0023 + +#define USB_VENDOR_ID_CHIC 0x05fe +#define USB_DEVICE_ID_CHIC_GAMEPAD 0x0014 + +#define USB_VENDOR_ID_CIDC 0x1677 + +#define USB_VENDOR_ID_CODEMERCS 0x07c0 +#define USB_DEVICE_ID_CODEMERCS_IOW_FIRST 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW_LAST 0x15ff + +#define USB_VENDOR_ID_CYPRESS 0x04b4 +#define USB_DEVICE_ID_CYPRESS_MOUSE 0x0001 +#define USB_DEVICE_ID_CYPRESS_HIDCOM 0x5500 +#define USB_DEVICE_ID_CYPRESS_ULTRAMOUSE 0x7417 + +#define USB_VENDOR_ID_DELL 0x413c +#define USB_DEVICE_ID_DELL_W7658 0x2005 + +#define USB_VENDOR_ID_DELORME 0x1163 +#define USB_DEVICE_ID_DELORME_EARTHMATE 0x0100 +#define USB_DEVICE_ID_DELORME_EM_LT20 0x0200 + +#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f +#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100 + +#define USB_VENDOR_ID_GLAB 0x06c2 +#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038 +#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039 +#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040 +#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044 +#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045 +#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051 +#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053 +#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058 + +#define USB_VENDOR_ID_GRIFFIN 0x077d +#define USB_DEVICE_ID_POWERMATE 0x0410 +#define USB_DEVICE_ID_SOUNDKNOB 0x04AA + +#define USB_VENDOR_ID_GTCO 0x078c +#define USB_DEVICE_ID_GTCO_90 0x0090 +#define USB_DEVICE_ID_GTCO_100 0x0100 +#define USB_DEVICE_ID_GTCO_101 0x0101 +#define USB_DEVICE_ID_GTCO_103 0x0103 +#define USB_DEVICE_ID_GTCO_104 0x0104 +#define USB_DEVICE_ID_GTCO_105 0x0105 +#define USB_DEVICE_ID_GTCO_106 0x0106 +#define USB_DEVICE_ID_GTCO_107 0x0107 +#define USB_DEVICE_ID_GTCO_108 0x0108 +#define USB_DEVICE_ID_GTCO_200 0x0200 +#define USB_DEVICE_ID_GTCO_201 0x0201 +#define USB_DEVICE_ID_GTCO_202 0x0202 +#define USB_DEVICE_ID_GTCO_203 0x0203 +#define USB_DEVICE_ID_GTCO_204 0x0204 +#define USB_DEVICE_ID_GTCO_205 0x0205 +#define USB_DEVICE_ID_GTCO_206 0x0206 +#define USB_DEVICE_ID_GTCO_207 0x0207 +#define USB_DEVICE_ID_GTCO_300 0x0300 +#define USB_DEVICE_ID_GTCO_301 0x0301 +#define USB_DEVICE_ID_GTCO_302 0x0302 +#define USB_DEVICE_ID_GTCO_303 0x0303 +#define USB_DEVICE_ID_GTCO_304 0x0304 +#define USB_DEVICE_ID_GTCO_305 0x0305 +#define USB_DEVICE_ID_GTCO_306 0x0306 +#define USB_DEVICE_ID_GTCO_307 0x0307 +#define USB_DEVICE_ID_GTCO_308 0x0308 +#define USB_DEVICE_ID_GTCO_309 0x0309 +#define USB_DEVICE_ID_GTCO_400 0x0400 +#define USB_DEVICE_ID_GTCO_401 0x0401 +#define USB_DEVICE_ID_GTCO_402 0x0402 +#define USB_DEVICE_ID_GTCO_403 0x0403 +#define USB_DEVICE_ID_GTCO_404 0x0404 +#define USB_DEVICE_ID_GTCO_405 0x0405 +#define USB_DEVICE_ID_GTCO_500 0x0500 +#define USB_DEVICE_ID_GTCO_501 0x0501 +#define USB_DEVICE_ID_GTCO_502 0x0502 +#define USB_DEVICE_ID_GTCO_503 0x0503 +#define USB_DEVICE_ID_GTCO_504 0x0504 +#define USB_DEVICE_ID_GTCO_1000 0x1000 +#define USB_DEVICE_ID_GTCO_1001 0x1001 +#define USB_DEVICE_ID_GTCO_1002 0x1002 +#define USB_DEVICE_ID_GTCO_1003 0x1003 +#define USB_DEVICE_ID_GTCO_1004 0x1004 +#define USB_DEVICE_ID_GTCO_1005 0x1005 +#define USB_DEVICE_ID_GTCO_1006 0x1006 + +#define USB_VENDOR_ID_HAPP 0x078b +#define USB_DEVICE_ID_UGCI_DRIVING 0x0010 +#define USB_DEVICE_ID_UGCI_FLYING 0x0020 +#define USB_DEVICE_ID_UGCI_FIGHTING 0x0030 + +#define USB_VENDOR_ID_IMATION 0x0718 +#define USB_DEVICE_ID_DISC_STAKKA 0xd000 + +#define USB_VENDOR_ID_KBGEAR 0x084e +#define USB_DEVICE_ID_KBGEAR_JAMSTUDIO 0x1001 + +#define USB_VENDOR_ID_LD 0x0f11 +#define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_JWM 0x1080 +#define USB_DEVICE_ID_LD_DMMP 0x1081 +#define USB_DEVICE_ID_LD_UMIP 0x1090 +#define USB_DEVICE_ID_LD_XRAY1 0x1100 +#define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_COM3LAB 0x2000 +#define USB_DEVICE_ID_LD_TELEPORT 0x2010 +#define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 +#define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 +#define USB_DEVICE_ID_LD_MACHINETEST 0x2040 + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 +#define USB_DEVICE_ID_S510_RECEIVER 0xc50c +#define USB_DEVICE_ID_S510_RECEIVER_2 0xc517 +#define USB_DEVICE_ID_MX3000_RECEIVER 0xc513 +#define USB_DEVICE_ID_DINOVO_EDGE 0xc714 + +#define USB_VENDOR_ID_MCC 0x09db +#define USB_DEVICE_ID_MCC_PMD1024LS 0x0076 +#define USB_DEVICE_ID_MCC_PMD1208LS 0x007a + +#define USB_VENDOR_ID_MGE 0x0463 +#define USB_DEVICE_ID_MGE_UPS 0xffff +#define USB_DEVICE_ID_MGE_UPS1 0x0001 + +#define USB_VENDOR_ID_NEC 0x073e +#define USB_DEVICE_ID_NEC_USB_GAME_PAD 0x0301 + +#define USB_VENDOR_ID_ONTRAK 0x0a07 +#define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 + +#define USB_VENDOR_ID_PANJIT 0x134c + +#define USB_VENDOR_ID_PANTHERLORD 0x0810 +#define USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK 0x0001 + +#define USB_VENDOR_ID_PLAYDOTCOM 0x0b43 +#define USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII 0x0003 + +#define USB_VENDOR_ID_SAITEK 0x06a3 +#define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 + +#define USB_VENDOR_ID_SONY 0x054c +#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268 + +#define USB_VENDOR_ID_SUN 0x0430 +#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab + +#define USB_VENDOR_ID_TOPMAX 0x0663 +#define USB_DEVICE_ID_TOPMAX_COBRAPAD 0x0103 + +#define USB_VENDOR_ID_TURBOX 0x062a +#define USB_DEVICE_ID_TURBOX_KEYBOARD 0x0201 + +#define USB_VENDOR_ID_VERNIER 0x08f7 +#define USB_DEVICE_ID_VERNIER_LABPRO 0x0001 +#define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 +#define USB_DEVICE_ID_VERNIER_SKIP 0x0003 +#define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 + +#define USB_VENDOR_ID_WACOM 0x056a + +#define USB_VENDOR_ID_WISEGROUP 0x0925 +#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101 +#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104 +#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201 +#define USB_DEVICE_ID_QUAD_USB_JOYPAD 0x8800 +#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866 + +#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677 +#define USB_DEVICE_ID_SMARTJOY_DUAL_PLUS 0x8802 + +#define USB_VENDOR_ID_YEALINK 0x6993 +#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001 + +/* + * Alphabetically sorted blacklist by quirk type. + */ + +static const struct hid_blacklist { + __u16 idVendor; + __u16 idProduct; + __u32 quirks; +} hid_blacklist[] = { + + { USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU, HID_QUIRK_2WHEEL_MOUSE_HACK_7 }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE, HID_QUIRK_2WHEEL_MOUSE_HACK_5 }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER, HID_QUIRK_BAD_RELATIVE_KEYS }, + + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_NEC, USB_DEVICE_ID_NEC_USB_GAME_PAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_RUMBLEPAD, HID_QUIRK_BADPAD }, + { USB_VENDOR_ID_TOPMAX, USB_DEVICE_ID_TOPMAX_COBRAPAD, HID_QUIRK_BADPAD }, + + { USB_VENDOR_ID_CHERRY, USB_DEVICE_ID_CHERRY_CYMOTION, HID_QUIRK_CYMOTION }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE, HID_QUIRK_DUPLICATE_USAGES }, + + { USB_VENDOR_ID_BELKIN, USB_DEVICE_ID_FLIP_KVM, HID_QUIRK_HIDDEV }, + + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_01, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_10, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_21, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CIDC, 0x0103, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_HIDCOM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_ULTRAMOUSE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EARTHMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_101, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_103, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_104, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_105, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_106, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_107, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_108, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_200, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_201, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_202, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_203, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_204, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_205, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_206, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_207, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_300, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_301, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_302, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_303, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_304, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_305, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_306, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_307, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_308, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_309, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_400, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_401, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_402, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_403, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_404, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_405, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_500, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_501, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_502, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_503, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_504, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1000, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1001, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1002, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1003, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1004, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1005, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_1006, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_IMATION, USB_DEVICE_ID_DISC_STAKKA, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_KBGEAR, USB_DEVICE_ID_KBGEAR_JAMSTUDIO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 500, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0001, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0002, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0003, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_PANJIT, 0x0004, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_LABPRO, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE }, + { USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_302, HID_QUIRK_IGNORE }, + + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER, HID_QUIRK_LOGITECH_DESCRIPTOR }, + { USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2, HID_QUIRK_LOGITECH_DESCRIPTOR }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE, HID_QUIRK_MIGHTYMOUSE | HID_QUIRK_INVERT_HWHEEL }, + + { USB_VENDOR_ID_PANTHERLORD, USB_DEVICE_ID_PANTHERLORD_TWIN_USB_JOYSTICK, HID_QUIRK_MULTI_INPUT | HID_QUIRK_SKIP_OUTPUT_REPORTS }, + { USB_VENDOR_ID_PLAYDOTCOM, USB_DEVICE_ID_PLAYDOTCOM_EMS_USBII, HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER, HID_QUIRK_SONY_PS3_CONTROLLER }, + + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_UC100KM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_CS124U, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_TURBOX, USB_DEVICE_ID_TURBOX_KEYBOARD, HID_QUIRK_NOGET }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_QUAD_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT }, + + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER3_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE | HID_QUIRK_POWERBOOK_ISO_KEYBOARD}, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_POWERBOOK_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, + + { USB_VENDOR_ID_DELL, USB_DEVICE_ID_DELL_W7658, HID_QUIRK_RESET_LEDS }, + + { 0, 0 } +}; + +/* Dynamic HID quirks list - specified at runtime */ +struct quirks_list_struct { + struct hid_blacklist hid_bl_item; + struct list_head node; +}; + +static LIST_HEAD(dquirks_list); +static DECLARE_RWSEM(dquirks_rwsem); + +/* Runtime ("dynamic") quirks manipulation functions */ + +/** + * usbhid_exists_dquirk: find any dynamic quirks for a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Scans dquirks_list for a matching dynamic quirk and returns + * the pointer to the relevant struct hid_blacklist if found. + * Must be called with a read lock held on dquirks_rwsem. + * + * Returns: NULL if no quirk found, struct hid_blacklist * if found. + */ +static struct hid_blacklist *usbhid_exists_dquirk(const u16 idVendor, + const u16 idProduct) +{ + struct quirks_list_struct *q; + struct hid_blacklist *bl_entry = NULL; + + list_for_each_entry(q, &dquirks_list, node) { + if (q->hid_bl_item.idVendor == idVendor && + q->hid_bl_item.idProduct == idProduct) { + bl_entry = &q->hid_bl_item; + break; + } + } + + if (bl_entry != NULL) + dbg("Found dynamic quirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + bl_entry->quirks, bl_entry->idVendor, + bl_entry->idProduct); + + return bl_entry; +} + + +/** + * usbhid_modify_dquirk: add/replace a HID quirk + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * @quirks: the u32 quirks value to add/replace + * + * Description: + * If an dynamic quirk exists in memory for this (idVendor, + * idProduct) pair, replace its quirks value with what was + * provided. Otherwise, add the quirk to the dynamic quirks list. + * + * Returns: 0 OK, -error on failure. + */ +int usbhid_modify_dquirk(const u16 idVendor, const u16 idProduct, + const u32 quirks) +{ + struct quirks_list_struct *q_new, *q; + int list_edited = 0; + + if (!idVendor) { + dbg("Cannot add a quirk with idVendor = 0"); + return -EINVAL; + } + + q_new = kmalloc(sizeof(struct quirks_list_struct), GFP_KERNEL); + if (!q_new) { + dbg("Could not allocate quirks_list_struct"); + return -ENOMEM; + } + + q_new->hid_bl_item.idVendor = idVendor; + q_new->hid_bl_item.idProduct = idProduct; + q_new->hid_bl_item.quirks = quirks; + + down_write(&dquirks_rwsem); + + list_for_each_entry(q, &dquirks_list, node) { + + if (q->hid_bl_item.idVendor == idVendor && + q->hid_bl_item.idProduct == idProduct) { + + list_replace(&q->node, &q_new->node); + kfree(q); + list_edited = 1; + break; + + } + + } + + if (!list_edited) + list_add_tail(&q_new->node, &dquirks_list); + + up_write(&dquirks_rwsem); + + return 0; +} + + +/** + * usbhid_remove_all_dquirks: remove all runtime HID quirks from memory + * + * Description: + * Free all memory associated with dynamic quirks - called before + * module unload. + * + */ +static void usbhid_remove_all_dquirks(void) +{ + struct quirks_list_struct *q, *temp; + + down_write(&dquirks_rwsem); + list_for_each_entry_safe(q, temp, &dquirks_list, node) { + list_del(&q->node); + kfree(q); + } + up_write(&dquirks_rwsem); + +} + +/** + * usbhid_quirks_init: apply USB HID quirks specified at module load time + */ +int usbhid_quirks_init(char **quirks_param) +{ + u16 idVendor, idProduct; + u32 quirks; + int n = 0, m; + + for (; quirks_param[n] && n < MAX_USBHID_BOOT_QUIRKS; n++) { + + m = sscanf(quirks_param[n], "0x%hx:0x%hx:0x%x", + &idVendor, &idProduct, &quirks); + + if (m != 3 || + usbhid_modify_dquirk(idVendor, idProduct, quirks) != 0) { + printk(KERN_WARNING + "Could not parse HID quirk module param %s\n", + quirks_param[n]); + } + } + + return 0; +} + +/** + * usbhid_quirks_exit: release memory associated with dynamic_quirks + * + * Description: + * Release all memory associated with dynamic quirks. Called upon + * module unload. + * + * Returns: nothing + */ +void usbhid_quirks_exit(void) +{ + usbhid_remove_all_dquirks(); +} + +/** + * usbhid_exists_squirk: return any static quirks for a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Given a USB vendor ID and product ID, return a pointer to + * the hid_blacklist entry associated with that device. + * + * Returns: pointer if quirk found, or NULL if no quirks found. + */ +static const struct hid_blacklist *usbhid_exists_squirk(const u16 idVendor, + const u16 idProduct) +{ + const struct hid_blacklist *bl_entry = NULL; + int n = 0; + + for (; hid_blacklist[n].idVendor; n++) + if (hid_blacklist[n].idVendor == idVendor && + hid_blacklist[n].idProduct == idProduct) + bl_entry = &hid_blacklist[n]; + + if (bl_entry != NULL) + dbg("Found squirk 0x%x for USB HID vendor 0x%hx prod 0x%hx\n", + bl_entry->quirks, bl_entry->idVendor, + bl_entry->idProduct); + return bl_entry; +} + +/** + * usbhid_lookup_quirk: return any quirks associated with a USB HID device + * @idVendor: the 16-bit USB vendor ID, in native byteorder + * @idProduct: the 16-bit USB product ID, in native byteorder + * + * Description: + * Given a USB vendor ID and product ID, return any quirks associated + * with that device. + * + * Returns: a u32 quirks value. + */ +u32 usbhid_lookup_quirk(const u16 idVendor, const u16 idProduct) +{ + u32 quirks = 0; + const struct hid_blacklist *bl_entry = NULL; + + /* Ignore all Wacom devices */ + if (idVendor == USB_VENDOR_ID_WACOM) + return HID_QUIRK_IGNORE; + + /* ignore all Code Mercenaries IOWarrior devices */ + if (idVendor == USB_VENDOR_ID_CODEMERCS) + if (idProduct >= USB_DEVICE_ID_CODEMERCS_IOW_FIRST && + idProduct <= USB_DEVICE_ID_CODEMERCS_IOW_LAST) + return HID_QUIRK_IGNORE; + + down_read(&dquirks_rwsem); + bl_entry = usbhid_exists_dquirk(idVendor, idProduct); + if (!bl_entry) + bl_entry = usbhid_exists_squirk(idVendor, idProduct); + if (bl_entry) + quirks = bl_entry->quirks; + up_read(&dquirks_rwsem); + + return quirks; +} + diff --git a/drivers/usb/input/hid-tmff.c b/drivers/hid/usbhid/hid-tmff.c index ab67331..ab67331 100644 --- a/drivers/usb/input/hid-tmff.c +++ b/drivers/hid/usbhid/hid-tmff.c diff --git a/drivers/usb/input/hid-zpff.c b/drivers/hid/usbhid/hid-zpff.c index 7bd8238..7bd8238 100644 --- a/drivers/usb/input/hid-zpff.c +++ b/drivers/hid/usbhid/hid-zpff.c diff --git a/drivers/usb/input/hiddev.c b/drivers/hid/usbhid/hiddev.c index a8b3d66..a8b3d66 100644 --- a/drivers/usb/input/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c diff --git a/drivers/usb/input/usbhid.h b/drivers/hid/usbhid/usbhid.h index 0023f96..0023f96 100644 --- a/drivers/usb/input/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h diff --git a/drivers/usb/input/usbkbd.c b/drivers/hid/usbhid/usbkbd.c index 8505824..65aa12e8 100644 --- a/drivers/usb/input/usbkbd.c +++ b/drivers/hid/usbhid/usbkbd.c @@ -31,6 +31,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/usb/input.h> +#include <linux/hid.h> /* * Version Information @@ -227,6 +228,7 @@ static int usb_kbd_probe(struct usb_interface *iface, struct usb_kbd *kbd; struct input_dev *input_dev; int i, pipe, maxp; + int error = -ENOMEM; interface = iface->cur_altsetting; @@ -305,15 +307,19 @@ static int usb_kbd_probe(struct usb_interface *iface, kbd->led->transfer_dma = kbd->leds_dma; kbd->led->transfer_flags |= (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP); - input_register_device(kbd->dev); + error = input_register_device(kbd->dev); + if (error) + goto fail2; usb_set_intfdata(iface, kbd); return 0; -fail2: usb_kbd_free_mem(dev, kbd); -fail1: input_free_device(input_dev); +fail2: + usb_kbd_free_mem(dev, kbd); +fail1: + input_free_device(input_dev); kfree(kbd); - return -ENOMEM; + return error; } static void usb_kbd_disconnect(struct usb_interface *intf) @@ -330,7 +336,8 @@ static void usb_kbd_disconnect(struct usb_interface *intf) } static struct usb_device_id usb_kbd_id_table [] = { - { USB_INTERFACE_INFO(3, 1, 1) }, + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_KEYBOARD) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/input/usbmouse.c b/drivers/hid/usbhid/usbmouse.c index 64a33e4..573776d 100644 --- a/drivers/usb/input/usbmouse.c +++ b/drivers/hid/usbhid/usbmouse.c @@ -31,6 +31,7 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/usb/input.h> +#include <linux/hid.h> /* * Version Information @@ -119,6 +120,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i struct usb_mouse *mouse; struct input_dev *input_dev; int pipe, maxp; + int error = -ENOMEM; interface = intf->cur_altsetting; @@ -187,15 +189,21 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i mouse->irq->transfer_dma = mouse->data_dma; mouse->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; - input_register_device(mouse->dev); + error = input_register_device(mouse->dev); + if (error) + goto fail3; usb_set_intfdata(intf, mouse); return 0; -fail2: usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); -fail1: input_free_device(input_dev); +fail3: + usb_free_urb(mouse->irq); +fail2: + usb_buffer_free(dev, 8, mouse->data, mouse->data_dma); +fail1: + input_free_device(input_dev); kfree(mouse); - return -ENOMEM; + return error; } static void usb_mouse_disconnect(struct usb_interface *intf) @@ -213,7 +221,8 @@ static void usb_mouse_disconnect(struct usb_interface *intf) } static struct usb_device_id usb_mouse_id_table [] = { - { USB_INTERFACE_INFO(3, 1, 2) }, + { USB_INTERFACE_INFO(USB_INTERFACE_CLASS_HID, USB_INTERFACE_SUBCLASS_BOOT, + USB_INTERFACE_PROTOCOL_MOUSE) }, { } /* Terminating entry */ }; diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 891ef6d..6d105a1 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -73,6 +73,17 @@ config SENSORS_ADM1026 This driver can also be built as a module. If so, the module will be called adm1026. +config SENSORS_ADM1029 + tristate "Analog Devices ADM1029" + depends on HWMON && I2C && EXPERIMENTAL + help + If you say yes here you get support for Analog Devices ADM1029 + sensor chip. + Very rare chip, please let us know you use it. + + This driver can also be built as a module. If so, the module + will be called adm1029. + config SENSORS_ADM1031 tristate "Analog Devices ADM1031 and compatibles" depends on HWMON && I2C && EXPERIMENTAL @@ -516,6 +527,7 @@ config SENSORS_W83792D config SENSORS_W83793 tristate "Winbond W83793" depends on HWMON && I2C && EXPERIMENTAL + select HWMON_VID help If you say yes here you get support for the Winbond W83793 hardware monitoring chip. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 3166112..4165c27 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_SENSORS_ABITUGURU) += abituguru.o obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o +obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_AMS) += ams/ diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c index b1dc63e..bede4d9 100644 --- a/drivers/hwmon/abituguru.c +++ b/drivers/hwmon/abituguru.c @@ -1267,30 +1267,42 @@ static int __devinit abituguru_probe(struct platform_device *pdev) printk(KERN_INFO ABIT_UGURU_NAME ": found Abit uGuru\n"); /* Register sysfs hooks */ - data->class_dev = hwmon_device_register(&pdev->dev); - if (IS_ERR(data->class_dev)) { - res = PTR_ERR(data->class_dev); - goto abituguru_probe_error; - } for (i = 0; i < sysfs_attr_i; i++) - device_create_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); + if (device_create_file(&pdev->dev, + &data->sysfs_attr[i].dev_attr)) + goto abituguru_probe_error; for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) - device_create_file(&pdev->dev, - &abituguru_sysfs_attr[i].dev_attr); + if (device_create_file(&pdev->dev, + &abituguru_sysfs_attr[i].dev_attr)) + goto abituguru_probe_error; - return 0; + data->class_dev = hwmon_device_register(&pdev->dev); + if (!IS_ERR(data->class_dev)) + return 0; /* success */ + res = PTR_ERR(data->class_dev); abituguru_probe_error: + for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++) + device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) + device_remove_file(&pdev->dev, + &abituguru_sysfs_attr[i].dev_attr); kfree(data); return res; } static int __devexit abituguru_remove(struct platform_device *pdev) { + int i; struct abituguru_data *data = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->class_dev); + for (i = 0; data->sysfs_attr[i].dev_attr.attr.name; i++) + device_remove_file(&pdev->dev, &data->sysfs_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++) + device_remove_file(&pdev->dev, + &abituguru_sysfs_attr[i].dev_attr); kfree(data); return 0; diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index b4618b2..ba80cd3 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -261,7 +261,6 @@ struct pwm_data { struct adm1026_data { struct i2c_client client; struct class_device *class_dev; - struct mutex lock; enum chips type; struct mutex update_lock; diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c new file mode 100644 index 0000000..73ce31b --- /dev/null +++ b/drivers/hwmon/adm1029.c @@ -0,0 +1,508 @@ +/* + * adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring + * + * Copyright (C) 2006 Corentin LABBE <corentin.labbe@geomatys.fr> + * + * Based on LM83 Driver by Jean Delvare <khali@linux-fr.org> + * + * Give only processor, motherboard temperatures and fan tachs + * Very rare chip please let me know if you use it + * + * http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon-sysfs.h> +#include <linux/hwmon.h> +#include <linux/err.h> +#include <linux/mutex.h> + +/* + * Addresses to scan + */ + +static unsigned short normal_i2c[] = { + 0x28, 0x29, 0x2a, + 0x2b, 0x2c, 0x2d, + 0x2e, 0x2f, I2C_CLIENT_END +}; + +/* + * Insmod parameters + */ + +I2C_CLIENT_INSMOD_1(adm1029); + +/* + * The ADM1029 registers + * Manufacturer ID is 0x41 for Analog Devices + */ + +#define ADM1029_REG_MAN_ID 0x0D +#define ADM1029_REG_CHIP_ID 0x0E +#define ADM1029_REG_CONFIG 0x01 +#define ADM1029_REG_NB_FAN_SUPPORT 0x02 + +#define ADM1029_REG_TEMP_DEVICES_INSTALLED 0x06 + +#define ADM1029_REG_LOCAL_TEMP 0xA0 +#define ADM1029_REG_REMOTE1_TEMP 0xA1 +#define ADM1029_REG_REMOTE2_TEMP 0xA2 + +#define ADM1029_REG_LOCAL_TEMP_HIGH 0x90 +#define ADM1029_REG_REMOTE1_TEMP_HIGH 0x91 +#define ADM1029_REG_REMOTE2_TEMP_HIGH 0x92 + +#define ADM1029_REG_LOCAL_TEMP_LOW 0x98 +#define ADM1029_REG_REMOTE1_TEMP_LOW 0x99 +#define ADM1029_REG_REMOTE2_TEMP_LOW 0x9A + +#define ADM1029_REG_FAN1 0x70 +#define ADM1029_REG_FAN2 0x71 + +#define ADM1029_REG_FAN1_MIN 0x78 +#define ADM1029_REG_FAN2_MIN 0x79 + +#define ADM1029_REG_FAN1_CONFIG 0x68 +#define ADM1029_REG_FAN2_CONFIG 0x69 + +#define TEMP_FROM_REG(val) ((val) * 1000) + +#define DIV_FROM_REG(val) ( 1 << (((val) >> 6) - 1)) + +/* Registers to be checked by adm1029_update_device() */ +static const u8 ADM1029_REG_TEMP[] = { + ADM1029_REG_LOCAL_TEMP, + ADM1029_REG_REMOTE1_TEMP, + ADM1029_REG_REMOTE2_TEMP, + ADM1029_REG_LOCAL_TEMP_HIGH, + ADM1029_REG_REMOTE1_TEMP_HIGH, + ADM1029_REG_REMOTE2_TEMP_HIGH, + ADM1029_REG_LOCAL_TEMP_LOW, + ADM1029_REG_REMOTE1_TEMP_LOW, + ADM1029_REG_REMOTE2_TEMP_LOW, +}; + +static const u8 ADM1029_REG_FAN[] = { + ADM1029_REG_FAN1, + ADM1029_REG_FAN2, + ADM1029_REG_FAN1_MIN, + ADM1029_REG_FAN2_MIN, +}; + +static const u8 ADM1029_REG_FAN_DIV[] = { + ADM1029_REG_FAN1_CONFIG, + ADM1029_REG_FAN2_CONFIG, +}; + +/* + * Functions declaration + */ + +static int adm1029_attach_adapter(struct i2c_adapter *adapter); +static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind); +static int adm1029_detach_client(struct i2c_client *client); +static struct adm1029_data *adm1029_update_device(struct device *dev); +static int adm1029_init_client(struct i2c_client *client); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver adm1029_driver = { + .driver = { + .name = "adm1029", + }, + .attach_adapter = adm1029_attach_adapter, + .detach_client = adm1029_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct adm1029_data { + struct i2c_client client; + struct class_device *class_dev; + struct mutex update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values, signed for temperature, unsigned for other stuff */ + s8 temp[ARRAY_SIZE(ADM1029_REG_TEMP)]; + u8 fan[ARRAY_SIZE(ADM1029_REG_FAN)]; + u8 fan_div[ARRAY_SIZE(ADM1029_REG_FAN_DIV)]; +}; + +/* + * Sysfs stuff + */ + +static ssize_t +show_temp(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adm1029_data *data = adm1029_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); +} + +static ssize_t +show_fan(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adm1029_data *data = adm1029_update_device(dev); + u16 val; + if (data->fan[attr->index] == 0 || data->fan_div[attr->index] == 0 + || data->fan[attr->index] == 255) { + return sprintf(buf, "0\n"); + } + + val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index]) + / data->fan[attr->index]; + return sprintf(buf, "%d\n", val); +} + +static ssize_t +show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adm1029_data *data = adm1029_update_device(dev); + if (data->fan_div[attr->index] == 0) + return sprintf(buf, "0\n"); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[attr->index])); +} + +static ssize_t set_fan_div(struct device *dev, + struct device_attribute *devattr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1029_data *data = i2c_get_clientdata(client); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + long val = simple_strtol(buf, NULL, 10); + u8 reg; + + mutex_lock(&data->update_lock); + + /*Read actual config */ + reg = i2c_smbus_read_byte_data(client, + ADM1029_REG_FAN_DIV[attr->index]); + + switch (val) { + case 1: + val = 1; + break; + case 2: + val = 2; + break; + case 4: + val = 3; + break; + default: + mutex_unlock(&data->update_lock); + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 1, 2 or 4!\n", val); + return -EINVAL; + } + /* Update the value */ + reg = (reg & 0x3F) | (val << 6); + + /* Write value */ + i2c_smbus_write_byte_data(client, + ADM1029_REG_FAN_DIV[attr->index], reg); + mutex_unlock(&data->update_lock); + + return count; +} + +/* +Access rights on sysfs, S_IRUGO stand for Is Readable by User, Group and Others + S_IWUSR stand for Is Writable by User +*/ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); + +static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp, NULL, 4); +static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp, NULL, 5); + +static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, NULL, 6); +static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp, NULL, 7); +static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp, NULL, 8); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); + +static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan, NULL, 3); + +static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, + show_fan_div, set_fan_div, 0); +static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, + show_fan_div, set_fan_div, 1); + +static struct attribute *adm1029_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan1_div.dev_attr.attr, + &sensor_dev_attr_fan2_div.dev_attr.attr, + NULL +}; + +static const struct attribute_group adm1029_group = { + .attrs = adm1029_attributes, +}; + +/* + * Real code + */ + +static int adm1029_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_probe(adapter, &addr_data, adm1029_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ + +static int adm1029_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct adm1029_data *data; + int err = 0; + const char *name = ""; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kzalloc(sizeof(struct adm1029_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + client->adapter = adapter; + client->driver = &adm1029_driver; + + /* Now we do the detection and identification. A negative kind + * means that the driver was loaded with no force parameter + * (default), so we must both detect and identify the chip + * (actually there is only one possible kind of chip for now, adm1029). + * A zero kind means that the driver was loaded with the force + * parameter, the detection step shall be skipped. A positive kind + * means that the driver was loaded with the force parameter and a + * given kind of chip is requested, so both the detection and the + * identification steps are skipped. */ + + /* Default to an adm1029 if forced */ + if (kind == 0) + kind = adm1029; + + /* ADM1029 doesn't have CHIP ID, check just MAN ID + * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED, + * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values + * documented + */ + + if (kind <= 0) { /* identification */ + u8 man_id, chip_id, temp_devices_installed, nb_fan_support; + + man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); + temp_devices_installed = i2c_smbus_read_byte_data(client, + ADM1029_REG_TEMP_DEVICES_INSTALLED); + nb_fan_support = i2c_smbus_read_byte_data(client, + ADM1029_REG_NB_FAN_SUPPORT); + /* 0x41 is Analog Devices */ + if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01 + && nb_fan_support == 0x03) { + if ((chip_id & 0xF0) == 0x00) { + kind = adm1029; + } else { + /* There are no "official" CHIP ID, so actually + * we use Major/Minor revision for that */ + printk(KERN_INFO + "adm1029: Unknown major revision %x, " + "please let us know\n", chip_id); + } + } + + if (kind <= 0) { /* identification failed */ + pr_debug("adm1029: Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X)\n", man_id, chip_id); + goto exit_free; + } + } + + if (kind == adm1029) { + name = "adm1029"; + } + + /* We can fill in the remaining client fields */ + strlcpy(client->name, name, I2C_NAME_SIZE); + mutex_init(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(client))) + goto exit_free; + + /* + * Initialize the ADM1029 chip + * Check config register + */ + if (adm1029_init_client(client) == 0) + goto exit_detach; + + /* Register sysfs hooks */ + if ((err = sysfs_create_group(&client->dev.kobj, &adm1029_group))) + goto exit_detach; + + data->class_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + goto exit_remove_files; + } + + return 0; + + exit_remove_files: + sysfs_remove_group(&client->dev.kobj, &adm1029_group); + exit_detach: + i2c_detach_client(client); + exit_free: + kfree(data); + exit: + return err; +} + +static int adm1029_init_client(struct i2c_client *client) +{ + u8 config; + config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG); + if ((config & 0x10) == 0) { + i2c_smbus_write_byte_data(client, ADM1029_REG_CONFIG, + config | 0x10); + } + /* recheck config */ + config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG); + if ((config & 0x10) == 0) { + dev_err(&client->dev, "Initialization failed!\n"); + return 0; + } + return 1; +} + +static int adm1029_detach_client(struct i2c_client *client) +{ + struct adm1029_data *data = i2c_get_clientdata(client); + int err; + + hwmon_device_unregister(data->class_dev); + sysfs_remove_group(&client->dev.kobj, &adm1029_group); + + if ((err = i2c_detach_client(client))) + return err; + + kfree(data); + return 0; +} + +/* +function that update the status of the chips (temperature for exemple) +*/ +static struct adm1029_data *adm1029_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1029_data *data = i2c_get_clientdata(client); + + mutex_lock(&data->update_lock); + /* + * Use the "cache" Luke, don't recheck values + * if there are already checked not a long time later + */ + if (time_after(jiffies, data->last_updated + HZ * 2) + || !data->valid) { + int nr; + + dev_dbg(&client->dev, "Updating adm1029 data\n"); + + for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_TEMP); nr++) { + data->temp[nr] = + i2c_smbus_read_byte_data(client, + ADM1029_REG_TEMP[nr]); + } + for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN); nr++) { + data->fan[nr] = + i2c_smbus_read_byte_data(client, + ADM1029_REG_FAN[nr]); + } + for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN_DIV); nr++) { + data->fan_div[nr] = + i2c_smbus_read_byte_data(client, + ADM1029_REG_FAN_DIV[nr]); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + mutex_unlock(&data->update_lock); + + return data; +} + +/* + Common module stuff +*/ +static int __init sensors_adm1029_init(void) +{ + + return i2c_add_driver(&adm1029_driver); +} + +static void __exit sensors_adm1029_exit(void) +{ + + i2c_del_driver(&adm1029_driver); +} + +MODULE_AUTHOR("Corentin LABBE <corentin.labbe@geomatys.fr>"); +MODULE_DESCRIPTION("adm1029 driver"); +MODULE_LICENSE("GPL v2"); + +module_init(sensors_adm1029_init); +module_exit(sensors_adm1029_exit); diff --git a/drivers/hwmon/ams/ams-core.c b/drivers/hwmon/ams/ams-core.c index f1f0f5d..f5ebad5 100644 --- a/drivers/hwmon/ams/ams-core.c +++ b/drivers/hwmon/ams/ams-core.c @@ -141,10 +141,10 @@ static void ams_worker(struct work_struct *work) int ams_sensor_attach(void) { int result; - u32 *prop; + const u32 *prop; /* Get orientation */ - prop = (u32*)get_property(ams_info.of_node, "orientation", NULL); + prop = get_property(ams_info.of_node, "orientation", NULL); if (!prop) return -ENODEV; ams_info.orient1 = *prop; diff --git a/drivers/hwmon/ams/ams-i2c.c b/drivers/hwmon/ams/ams-i2c.c index 0d24bdf..485d333 100644 --- a/drivers/hwmon/ams/ams-i2c.c +++ b/drivers/hwmon/ams/ams-i2c.c @@ -263,7 +263,7 @@ int __init ams_i2c_init(struct device_node *np) { char *tmp_bus; int result; - u32 *prop; + const u32 *prop; mutex_lock(&ams_info.lock); @@ -276,7 +276,7 @@ int __init ams_i2c_init(struct device_node *np) ams_info.bustype = BUS_I2C; /* look for bus either using "reg" or by path */ - prop = (u32*)get_property(ams_info.of_node, "reg", NULL); + prop = get_property(ams_info.of_node, "reg", NULL); if (!prop) { result = -ENODEV; diff --git a/drivers/hwmon/ams/ams-pmu.c b/drivers/hwmon/ams/ams-pmu.c index 4636ae0..1b01c21 100644 --- a/drivers/hwmon/ams/ams-pmu.c +++ b/drivers/hwmon/ams/ams-pmu.c @@ -146,7 +146,7 @@ static void ams_pmu_exit(void) int __init ams_pmu_init(struct device_node *np) { - u32 *prop; + const u32 *prop; int result; mutex_lock(&ams_info.lock); @@ -160,7 +160,7 @@ int __init ams_pmu_init(struct device_node *np) ams_info.bustype = BUS_HOST; /* Get PMU command, should be 0x4e, but we can never know */ - prop = (u32*)get_property(ams_info.of_node, "reg", NULL); + prop = get_property(ams_info.of_node, "reg", NULL); if (!prop) { result = -ENODEV; goto exit; diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index a272cae..7c29734 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -146,7 +146,6 @@ superio_exit(int base) struct f71805f_data { unsigned short addr; const char *name; - struct mutex lock; struct class_device *class_dev; struct mutex update_lock; @@ -271,50 +270,42 @@ static inline u8 temp_to_reg(long val) * Device I/O access */ +/* Must be called with data->update_lock held, except during initialization */ static u8 f71805f_read8(struct f71805f_data *data, u8 reg) { - u8 val; - - mutex_lock(&data->lock); outb(reg, data->addr + ADDR_REG_OFFSET); - val = inb(data->addr + DATA_REG_OFFSET); - mutex_unlock(&data->lock); - - return val; + return inb(data->addr + DATA_REG_OFFSET); } +/* Must be called with data->update_lock held, except during initialization */ static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val) { - mutex_lock(&data->lock); outb(reg, data->addr + ADDR_REG_OFFSET); outb(val, data->addr + DATA_REG_OFFSET); - mutex_unlock(&data->lock); } /* It is important to read the MSB first, because doing so latches the - value of the LSB, so we are sure both bytes belong to the same value. */ + value of the LSB, so we are sure both bytes belong to the same value. + Must be called with data->update_lock held, except during initialization */ static u16 f71805f_read16(struct f71805f_data *data, u8 reg) { u16 val; - mutex_lock(&data->lock); outb(reg, data->addr + ADDR_REG_OFFSET); val = inb(data->addr + DATA_REG_OFFSET) << 8; outb(++reg, data->addr + ADDR_REG_OFFSET); val |= inb(data->addr + DATA_REG_OFFSET); - mutex_unlock(&data->lock); return val; } +/* Must be called with data->update_lock held, except during initialization */ static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val) { - mutex_lock(&data->lock); outb(reg, data->addr + ADDR_REG_OFFSET); outb(val >> 8, data->addr + DATA_REG_OFFSET); outb(++reg, data->addr + ADDR_REG_OFFSET); outb(val & 0xff, data->addr + DATA_REG_OFFSET); - mutex_unlock(&data->lock); } static struct f71805f_data *f71805f_update_device(struct device *dev) @@ -1150,7 +1141,6 @@ static int __devinit f71805f_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IO, 0); data->addr = res->start; - mutex_init(&data->lock); data->name = names[sio_data->kind]; mutex_init(&data->update_lock); @@ -1300,14 +1290,11 @@ static int __init f71805f_device_add(unsigned short address, if (err) { printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", err); - goto exit_kfree_data; + goto exit_device_put; } return 0; -exit_kfree_data: - kfree(pdev->dev.platform_data); - pdev->dev.platform_data = NULL; exit_device_put: platform_device_put(pdev); exit: @@ -1400,10 +1387,7 @@ exit: static void __exit f71805f_exit(void) { - kfree(pdev->dev.platform_data); - pdev->dev.platform_data = NULL; platform_device_unregister(pdev); - platform_driver_unregister(&f71805f_driver); } diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 106fa01..affcc00 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -101,7 +101,7 @@ static void __exit hwmon_exit(void) class_destroy(hwmon_class); } -module_init(hwmon_init); +subsys_initcall(hwmon_init); module_exit(hwmon_exit); EXPORT_SYMBOL_GPL(hwmon_device_register); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 1ed8b7e..62afc63 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -202,15 +202,23 @@ static int DIV_TO_REG(int val) } #define DIV_FROM_REG(val) (1 << (val)) +static const unsigned int pwm_freq[8] = { + 48000000 / 128, + 24000000 / 128, + 12000000 / 128, + 8000000 / 128, + 6000000 / 128, + 3000000 / 128, + 1500000 / 128, + 750000 / 128, +}; + -/* For each registered IT87, we need to keep some data in memory. That - data is pointed to by it87_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new it87 client is - allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct it87_data { struct i2c_client client; struct class_device *class_dev; - struct mutex lock; enum chips type; struct mutex update_lock; @@ -232,6 +240,7 @@ struct it87_data { u8 vrm; u32 alarms; /* Register encoding, combined */ u8 fan_main_ctrl; /* Register value */ + u8 fan_ctl; /* Register value */ u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ }; @@ -519,6 +528,14 @@ static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, struct it87_data *data = it87_update_device(dev); return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); } +static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct it87_data *data = it87_update_device(dev); + int index = (data->fan_ctl >> 4) & 0x07; + + return sprintf(buf, "%u\n", pwm_freq[index]); +} static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -528,9 +545,10 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, struct i2c_client *client = to_i2c_client(dev); struct it87_data *data = i2c_get_clientdata(client); int val = simple_strtol(buf, NULL, 10); - u8 reg = it87_read_value(client, IT87_REG_FAN_DIV); + u8 reg; mutex_lock(&data->update_lock); + reg = it87_read_value(client, IT87_REG_FAN_DIV); switch (nr) { case 0: data->fan_div[nr] = reg & 0x07; break; case 1: data->fan_div[nr] = (reg >> 3) & 0x07; break; @@ -639,6 +657,28 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, mutex_unlock(&data->update_lock); return count; } +static ssize_t set_pwm_freq(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + int i; + + /* Search for the nearest available frequency */ + for (i = 0; i < 7; i++) { + if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) + break; + } + + mutex_lock(&data->update_lock); + data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL) & 0x8f; + data->fan_ctl |= i << 4; + it87_write_value(client, IT87_REG_FAN_CTL, data->fan_ctl); + mutex_unlock(&data->update_lock); + + return count; +} #define show_fan_offset(offset) \ static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ @@ -656,7 +696,10 @@ show_fan_offset(3); static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ show_pwm_enable, set_pwm_enable, offset - 1); \ static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm, set_pwm, offset - 1); + show_pwm, set_pwm, offset - 1); \ +static DEVICE_ATTR(pwm##offset##_freq, \ + (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ + show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); show_pwm_offset(1); show_pwm_offset(2); @@ -904,7 +947,6 @@ static int it87_detect(struct i2c_adapter *adapter) } new_client = &data->client; - mutex_init(&data->lock); i2c_set_clientdata(new_client, data); new_client->addr = isa_address; new_client->adapter = adapter; @@ -1021,7 +1063,13 @@ static int it87_detect(struct i2c_adapter *adapter) || (err = device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr)) || (err = device_create_file(&new_client->dev, - &sensor_dev_attr_pwm3.dev_attr))) + &sensor_dev_attr_pwm3.dev_attr)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm1_freq)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm2_freq)) + || (err = device_create_file(&new_client->dev, + &dev_attr_pwm3_freq))) goto ERROR4; } @@ -1076,33 +1124,22 @@ static int it87_detach_client(struct i2c_client *client) return 0; } -/* ISA access must be locked explicitly! +/* Must be called with data->update_lock held, except during initialization. We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, would slow down the IT87 access and should not be necessary. */ static int it87_read_value(struct i2c_client *client, u8 reg) { - struct it87_data *data = i2c_get_clientdata(client); - int res; - - mutex_lock(&data->lock); outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); - res = inb_p(client->addr + IT87_DATA_REG_OFFSET); - mutex_unlock(&data->lock); - - return res; + return inb_p(client->addr + IT87_DATA_REG_OFFSET); } -/* ISA access must be locked explicitly! +/* Must be called with data->update_lock held, except during initialization. We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, would slow down the IT87 access and should not be necessary. */ static void it87_write_value(struct i2c_client *client, u8 reg, u8 value) { - struct it87_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->lock); outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); outb_p(value, client->addr + IT87_DATA_REG_OFFSET); - mutex_unlock(&data->lock); } /* Return 1 if and only if the PWM interface is safe to use */ @@ -1316,6 +1353,7 @@ static struct it87_data *it87_update_device(struct device *dev) (it87_read_value(client, IT87_REG_ALARM2) << 8) | (it87_read_value(client, IT87_REG_ALARM3) << 16); data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); + data->fan_ctl = it87_read_value(client, IT87_REG_FAN_CTL); data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); /* The 8705 does not have VID capability */ diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c index 6ba8473..7eaae38 100644 --- a/drivers/hwmon/lm70.c +++ b/drivers/hwmon/lm70.c @@ -126,7 +126,7 @@ out_dev_reg_failed: return status; } -static int __exit lm70_remove(struct spi_device *spi) +static int __devexit lm70_remove(struct spi_device *spi) { struct lm70 *p_lm70 = dev_get_drvdata(&spi->dev); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index 73bc2ff..886786c 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -125,10 +125,8 @@ static inline int TEMP_FROM_REG(s8 val) bad. Quite a lot of bookkeeping is done. A real driver can often cut some corners. */ -/* For each registered LM78, we need to keep some data in memory. That - data is pointed to by lm78_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new lm78 client is - allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct lm78_data { struct i2c_client client; struct class_device *class_dev; diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 2c3293c..20a8c64 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -298,11 +298,6 @@ static int ZONE_TO_REG( int zone ) #define LM85_DATA_INTERVAL (HZ + HZ / 2) #define LM85_CONFIG_INTERVAL (1 * 60 * HZ) -/* For each registered LM85, we need to keep some data in memory. That - data is pointed to by lm85_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new lm85 client is - allocated. */ - /* LM85 can automatically adjust fan speeds based on temperature * This structure encapsulates an entire Zone config. There are * three zones (one for each temperature input) on the lm85 @@ -329,10 +324,11 @@ struct lm85_autofan { u8 min_off; /* Min PWM or OFF below "limit", flag */ }; +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct lm85_data { struct i2c_client client; struct class_device *class_dev; - struct mutex lock; enum chips type; struct mutex update_lock; diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c index 95a4b5d..3f40026 100644 --- a/drivers/hwmon/sis5595.c +++ b/drivers/hwmon/sis5595.c @@ -162,10 +162,8 @@ static inline u8 DIV_TO_REG(int val) } #define DIV_FROM_REG(val) (1 << (val)) -/* For the SIS5595, we need to keep some data in memory. That - data is pointed to by sis5595_list[NR]->data. The structure itself is - dynamically allocated, at the time when the new sis5595 client is - allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct sis5595_data { struct i2c_client client; struct class_device *class_dev; diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c index f8acada..9a440c8 100644 --- a/drivers/hwmon/via686a.c +++ b/drivers/hwmon/via686a.c @@ -292,9 +292,8 @@ static inline long TEMP_FROM_REG10(u16 val) #define DIV_FROM_REG(val) (1 << (val)) #define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) -/* For the VIA686A, we need to keep some data in memory. - The structure is dynamically allocated, at the same time when a new - via686a client is allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct via686a_data { struct i2c_client client; struct class_device *class_dev; diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c index 25cc560..89c23d6 100644 --- a/drivers/hwmon/vt1211.c +++ b/drivers/hwmon/vt1211.c @@ -178,9 +178,10 @@ struct vt1211_data { * Super-I/O constants and functions * --------------------------------------------------------------------- */ -/* Configuration & data index port registers */ -#define SIO_REG_CIP 0x2e -#define SIO_REG_DIP 0x2f +/* Configuration index port registers + * The vt1211 can live at 2 different addresses so we need to probe both */ +#define SIO_REG_CIP1 0x2e +#define SIO_REG_CIP2 0x4e /* Configuration registers */ #define SIO_VT1211_LDN 0x07 /* logical device number */ @@ -193,33 +194,33 @@ struct vt1211_data { /* VT1211 logical device numbers */ #define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */ -static inline void superio_outb(int reg, int val) +static inline void superio_outb(int sio_cip, int reg, int val) { - outb(reg, SIO_REG_CIP); - outb(val, SIO_REG_DIP); + outb(reg, sio_cip); + outb(val, sio_cip + 1); } -static inline int superio_inb(int reg) +static inline int superio_inb(int sio_cip, int reg) { - outb(reg, SIO_REG_CIP); - return inb(SIO_REG_DIP); + outb(reg, sio_cip); + return inb(sio_cip + 1); } -static inline void superio_select(int ldn) +static inline void superio_select(int sio_cip, int ldn) { - outb(SIO_VT1211_LDN, SIO_REG_CIP); - outb(ldn, SIO_REG_DIP); + outb(SIO_VT1211_LDN, sio_cip); + outb(ldn, sio_cip + 1); } -static inline void superio_enter(void) +static inline void superio_enter(int sio_cip) { - outb(0x87, SIO_REG_CIP); - outb(0x87, SIO_REG_CIP); + outb(0x87, sio_cip); + outb(0x87, sio_cip); } -static inline void superio_exit(void) +static inline void superio_exit(int sio_cip) { - outb(0xaa, SIO_REG_CIP); + outb(0xaa, sio_cip); } /* --------------------------------------------------------------------- @@ -1263,26 +1264,26 @@ EXIT: return err; } -static int __init vt1211_find(unsigned short *address) +static int __init vt1211_find(int sio_cip, unsigned short *address) { int err = -ENODEV; - superio_enter(); + superio_enter(sio_cip); - if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) { + if (superio_inb(sio_cip, SIO_VT1211_DEVID) != SIO_VT1211_ID) { goto EXIT; } - superio_select(SIO_VT1211_LDN_HWMON); + superio_select(sio_cip, SIO_VT1211_LDN_HWMON); - if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) { + if ((superio_inb(sio_cip, SIO_VT1211_ACTIVE) & 1) == 0) { printk(KERN_WARNING DRVNAME ": HW monitor is disabled, " "skipping\n"); goto EXIT; } - *address = ((superio_inb(SIO_VT1211_BADDR) << 8) | - (superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00; + *address = ((superio_inb(sio_cip, SIO_VT1211_BADDR) << 8) | + (superio_inb(sio_cip, SIO_VT1211_BADDR + 1))) & 0xff00; if (*address == 0) { printk(KERN_WARNING DRVNAME ": Base address is not set, " "skipping\n"); @@ -1291,10 +1292,11 @@ static int __init vt1211_find(unsigned short *address) err = 0; printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, " - "revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV)); + "revision %u\n", *address, + superio_inb(sio_cip, SIO_VT1211_DEVREV)); EXIT: - superio_exit(); + superio_exit(sio_cip); return err; } @@ -1303,8 +1305,8 @@ static int __init vt1211_init(void) int err; unsigned short address = 0; - err = vt1211_find(&address); - if (err) { + if ((err = vt1211_find(SIO_REG_CIP1, &address)) && + (err = vt1211_find(SIO_REG_CIP2, &address))) { goto EXIT; } diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 212a155..30a7640 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -32,8 +32,10 @@ Supports the following chips: - Chip #vin #fan #pwm #temp chip_id man_id - w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3 + Chip #vin #fan #pwm #temp chip IDs man ID + w83627ehf 10 5 4 3 0x8850 0x88 0x5ca3 + 0x8860 0xa1 + w83627dhg 9 5 4 3 0xa020 0xc1 0x5ca3 */ #include <linux/module.h> @@ -55,8 +57,18 @@ static unsigned short address; * Super-I/O constants and functions */ +/* + * The three following globals are initialized in w83627ehf_find(), before + * the i2c-isa device is created. Otherwise, they could be stored in + * w83627ehf_data. This is ugly, but necessary, and when the driver is next + * updated to become a platform driver, the globals will disappear. + */ static int REG; /* The register to read/write */ static int VAL; /* The value to read/write */ +/* The w83627ehf/ehg have 10 voltage inputs, but the w83627dhg has 9. This + * value is also used in w83627ehf_detect() to export a device name in sysfs + * (e.g. w83627ehf or w83627dhg) */ +static int w83627ehf_num_in; #define W83627EHF_LD_HWM 0x0b @@ -65,8 +77,10 @@ static int VAL; /* The value to read/write */ #define SIO_REG_ENABLE 0x30 /* Logical device enable */ #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ -#define SIO_W83627EHF_ID 0x8840 -#define SIO_ID_MASK 0xFFC0 +#define SIO_W83627EHF_ID 0x8850 +#define SIO_W83627EHG_ID 0x8860 +#define SIO_W83627DHG_ID 0xa020 +#define SIO_ID_MASK 0xFFF0 static inline void superio_outb(int reg, int val) @@ -107,16 +121,20 @@ superio_exit(void) * ISA constants */ -#define REGION_ALIGNMENT ~7 -#define REGION_OFFSET 5 -#define REGION_LENGTH 2 +#define IOREGION_ALIGNMENT ~7 +#define IOREGION_OFFSET 5 +#define IOREGION_LENGTH 2 #define ADDR_REG_OFFSET 5 #define DATA_REG_OFFSET 6 #define W83627EHF_REG_BANK 0x4E #define W83627EHF_REG_CONFIG 0x40 -#define W83627EHF_REG_CHIP_ID 0x49 -#define W83627EHF_REG_MAN_ID 0x4F + +/* Not currently used: + * REG_MAN_ID has the value 0x5ca3 for all supported chips. + * REG_CHIP_ID == 0x88/0xa1/0xc1 depending on chip model. + * REG_MAN_ID is at port 0x4f + * REG_CHIP_ID is at port 0x58 */ static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; @@ -389,7 +407,7 @@ static void w83627ehf_write_fan_div(struct i2c_client *client, int nr) break; case 4: reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73) - | ((data->fan_div[4] & 0x03) << 3) + | ((data->fan_div[4] & 0x03) << 2) | ((data->fan_div[4] & 0x04) << 5); w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg); break; @@ -429,7 +447,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) } /* Measured voltages and limits */ - for (i = 0; i < 10; i++) { + for (i = 0; i < w83627ehf_num_in; i++) { data->in[i] = w83627ehf_read_value(client, W83627EHF_REG_IN(i)); data->in_min[i] = w83627ehf_read_value(client, @@ -453,9 +471,9 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) time */ if (data->fan[i] == 0xff && data->fan_div[i] < 0x07) { - dev_dbg(&client->dev, "Increasing fan %d " + dev_dbg(&client->dev, "Increasing fan%d " "clock divider from %u to %u\n", - i, div_from_reg(data->fan_div[i]), + i + 1, div_from_reg(data->fan_div[i]), div_from_reg(data->fan_div[i] + 1)); data->fan_div[i]++; w83627ehf_write_fan_div(client, i); @@ -1121,7 +1139,7 @@ static void w83627ehf_device_remove_files(struct device *dev) device_remove_file(dev, &sda_sf3_arrays[i].dev_attr); for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr); - for (i = 0; i < 10; i++) { + for (i = 0; i < w83627ehf_num_in; i++) { device_remove_file(dev, &sda_in_input[i].dev_attr); device_remove_file(dev, &sda_in_alarm[i].dev_attr); device_remove_file(dev, &sda_in_min[i].dev_attr); @@ -1176,7 +1194,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) u8 fan4pin, fan5pin; int i, err = 0; - if (!request_region(address + REGION_OFFSET, REGION_LENGTH, + if (!request_region(address + IOREGION_OFFSET, IOREGION_LENGTH, w83627ehf_driver.driver.name)) { err = -EBUSY; goto exit; @@ -1196,7 +1214,11 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) client->flags = 0; dev = &client->dev; - strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); + if (w83627ehf_num_in == 9) + strlcpy(client->name, "w83627dhg", I2C_NAME_SIZE); + else /* just say ehf. 627EHG is 627EHF in lead-free packaging. */ + strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); + data->valid = 0; mutex_init(&data->update_lock); @@ -1246,7 +1268,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter) goto exit_remove; } - for (i = 0; i < 10; i++) + for (i = 0; i < w83627ehf_num_in; i++) if ((err = device_create_file(dev, &sda_in_input[i].dev_attr)) || (err = device_create_file(dev, &sda_in_alarm[i].dev_attr)) @@ -1300,7 +1322,7 @@ exit_remove: exit_free: kfree(data); exit_release: - release_region(address + REGION_OFFSET, REGION_LENGTH); + release_region(address + IOREGION_OFFSET, IOREGION_LENGTH); exit: return err; } @@ -1315,7 +1337,7 @@ static int w83627ehf_detach_client(struct i2c_client *client) if ((err = i2c_detach_client(client))) return err; - release_region(client->addr + REGION_OFFSET, REGION_LENGTH); + release_region(client->addr + IOREGION_OFFSET, IOREGION_LENGTH); kfree(data); return 0; @@ -1340,7 +1362,17 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr) val = (superio_inb(SIO_REG_DEVID) << 8) | superio_inb(SIO_REG_DEVID + 1); - if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) { + switch (val & SIO_ID_MASK) { + case SIO_W83627DHG_ID: + w83627ehf_num_in = 9; + break; + case SIO_W83627EHF_ID: + case SIO_W83627EHG_ID: + w83627ehf_num_in = 10; + break; + default: + printk(KERN_WARNING "w83627ehf: unsupported chip ID: 0x%04x\n", + val); superio_exit(); return -ENODEV; } @@ -1348,7 +1380,7 @@ static int __init w83627ehf_find(int sioaddr, unsigned short *addr) superio_select(W83627EHF_LD_HWM); val = (superio_inb(SIO_REG_ADDR) << 8) | superio_inb(SIO_REG_ADDR + 1); - *addr = val & REGION_ALIGNMENT; + *addr = val & IOREGION_ALIGNMENT; if (*addr == 0) { superio_exit(); return -ENODEV; diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index dfdc29c7..d7e2406 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -286,9 +286,8 @@ static inline u8 DIV_TO_REG(long val) return ((u8) i); } -/* For each registered chip, we need to keep some data in memory. That - data is pointed to by w83627hf_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new client is allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct w83627hf_data { struct i2c_client client; struct class_device *class_dev; diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index 1232171..a47da3e 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -221,14 +221,8 @@ DIV_TO_REG(long val, enum chips type) a bit - except if there could be more than one SMBus. Groan. No solution for this yet. */ -/* This module may seem overly long and complicated. In fact, it is not so - bad. Quite a lot of bookkeeping is done. A real driver can often cut - some corners. */ - -/* For each registered W83781D, we need to keep some data in memory. That - data is pointed to by w83781d_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new w83781d client is - allocated. */ +/* For each registered chip, we need to keep some data in memory. + The structure is dynamically allocated. */ struct w83781d_data { struct i2c_client client; struct class_device *class_dev; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 4d44a2d..ece31d2 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -344,8 +344,7 @@ config I2C_PARPORT_LIGHT config I2C_PASEMI tristate "PA Semi SMBus interface" -# depends on PPC_PASEMI && I2C && PCI - depends on I2C && PCI + depends on PPC_PASEMI && I2C && PCI help Supports the PA Semi PWRficient on-chip SMBus interfaces. @@ -495,6 +494,16 @@ config I2C_VERSATILE This driver can also be built as a module. If so, the module will be called i2c-versatile. +config I2C_ACORN + bool "Acorn IOC/IOMD I2C bus support" + depends on I2C && ARCH_ACORN + default y + select I2C_ALGOBIT + help + Say yes if you want to support the I2C bus on Acorn platforms. + + If you don't know, say Y. + config I2C_VIA tristate "VIA 82C586B" depends on I2C && PCI && EXPERIMENTAL diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 03505aa..290b540 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -42,6 +42,7 @@ obj-$(CONFIG_I2C_SIS630) += i2c-sis630.o obj-$(CONFIG_I2C_SIS96X) += i2c-sis96x.o obj-$(CONFIG_I2C_STUB) += i2c-stub.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o +obj-$(CONFIG_I2C_ACORN) += i2c-acorn.o obj-$(CONFIG_I2C_VIA) += i2c-via.o obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o diff --git a/drivers/i2c/busses/i2c-acorn.c b/drivers/i2c/busses/i2c-acorn.c new file mode 100644 index 0000000..09bd7f4 --- /dev/null +++ b/drivers/i2c/busses/i2c-acorn.c @@ -0,0 +1,97 @@ +/* + * linux/drivers/acorn/char/i2c.c + * + * Copyright (C) 2000 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ARM IOC/IOMD i2c driver. + * + * On Acorn machines, the following i2c devices are on the bus: + * - PCF8583 real time clock & static RAM + */ +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> + +#include <asm/hardware.h> +#include <asm/io.h> +#include <asm/hardware/ioc.h> +#include <asm/system.h> + +#define FORCE_ONES 0xdc +#define SCL 0x02 +#define SDA 0x01 + +/* + * We must preserve all non-i2c output bits in IOC_CONTROL. + * Note also that we need to preserve the value of SCL and + * SDA outputs as well (which may be different from the + * values read back from IOC_CONTROL). + */ +static u_int force_ones; + +static void ioc_setscl(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SCL; + else + ones &= ~SCL; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static void ioc_setsda(void *data, int state) +{ + u_int ioc_control = ioc_readb(IOC_CONTROL) & ~(SCL | SDA); + u_int ones = force_ones; + + if (state) + ones |= SDA; + else + ones &= ~SDA; + + force_ones = ones; + + ioc_writeb(ioc_control | ones, IOC_CONTROL); +} + +static int ioc_getscl(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SCL) != 0; +} + +static int ioc_getsda(void *data) +{ + return (ioc_readb(IOC_CONTROL) & SDA) != 0; +} + +static struct i2c_algo_bit_data ioc_data = { + .setsda = ioc_setsda, + .setscl = ioc_setscl, + .getsda = ioc_getsda, + .getscl = ioc_getscl, + .udelay = 80, + .timeout = 100 +}; + +static struct i2c_adapter ioc_ops = { + .id = I2C_HW_B_IOC, + .algo_data = &ioc_data, +}; + +static int __init i2c_ioc_init(void) +{ + force_ones = FORCE_ONES | SCL | SDA; + + return i2c_bit_add_bus(&ioc_ops); +} + +__initcall(i2c_ioc_init); diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index 6fd8ad7..1e277ba 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -494,7 +494,7 @@ static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_ return -ENODEV; } - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ ali1535_adapter.dev.parent = &dev->dev; snprintf(ali1535_adapter.name, I2C_NAME_SIZE, diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index e4e0df1..e47fe01 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -489,7 +489,7 @@ static int __devinit ali15x3_probe(struct pci_dev *dev, const struct pci_device_ return -ENODEV; } - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ ali15x3_adapter.dev.parent = &dev->dev; snprintf(ali15x3_adapter.name, I2C_NAME_SIZE, diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index fa6155a..7490dc1 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -374,7 +374,7 @@ static int __devinit amd756_probe(struct pci_dev *pdev, dev_dbg(&pdev->dev, "SMBREV = 0x%X\n", temp); dev_dbg(&pdev->dev, "AMD756_smba = 0x%X\n", amd756_ioport); - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ amd756_smbus.dev.parent = &pdev->dev; sprintf(amd756_smbus.name, "SMBus %s adapter at %04x", diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index 5bba3fb..0c70f82 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -254,7 +254,8 @@ static s32 amd8111_access(struct i2c_adapter * adap, u16 addr, break; case I2C_SMBUS_BLOCK_PROC_CALL: - len = min_t(u8, data->block[0], 31); + len = min_t(u8, data->block[0], + I2C_SMBUS_BLOCK_MAX - 1); amd_ec_write(smbus, AMD_SMB_CMD, command); amd_ec_write(smbus, AMD_SMB_BCNT, len); for (i = 0; i < len; i++) @@ -371,7 +372,7 @@ static int __devinit amd8111_probe(struct pci_dev *dev, smbus->adapter.algo = &smbus_algorithm; smbus->adapter.algo_data = smbus; - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ smbus->adapter.dev.parent = &dev->dev; pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 21f2671..a320e7d 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -97,6 +97,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, int command, int hwpec); static unsigned long i801_smba; +static unsigned char i801_original_hstcfg; static struct pci_driver i801_driver; static struct pci_dev *I801_dev; static int isich4; @@ -510,6 +511,7 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id } pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); + i801_original_hstcfg = temp; temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ if (!(temp & SMBHSTCFG_HST_EN)) { dev_info(&dev->dev, "Enabling SMBus device\n"); @@ -522,7 +524,7 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id else dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n"); - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ i801_adapter.dev.parent = &dev->dev; snprintf(i801_adapter.name, I2C_NAME_SIZE, @@ -543,6 +545,7 @@ exit: static void __devexit i801_remove(struct pci_dev *dev) { i2c_del_adapter(&i801_adapter); + pci_write_config_byte(I801_dev, SMBHSTCFG, i801_original_hstcfg); pci_release_region(dev, SMBBAR); /* * do not call pci_disable_device(dev) since it can cause hard hangs on @@ -550,11 +553,33 @@ static void __devexit i801_remove(struct pci_dev *dev) */ } +#ifdef CONFIG_PM +static int i801_suspend(struct pci_dev *dev, pm_message_t mesg) +{ + pci_save_state(dev); + pci_write_config_byte(dev, SMBHSTCFG, i801_original_hstcfg); + pci_set_power_state(dev, pci_choose_state(dev, mesg)); + return 0; +} + +static int i801_resume(struct pci_dev *dev) +{ + pci_set_power_state(dev, PCI_D0); + pci_restore_state(dev); + return pci_enable_device(dev); +} +#else +#define i801_suspend NULL +#define i801_resume NULL +#endif + static struct pci_driver i801_driver = { .name = "i801_smbus", .id_table = i801_ids, .probe = i801_probe, .remove = __devexit_p(i801_remove), + .suspend = i801_suspend, + .resume = i801_resume, }; static int __init i2c_i801_init(void) diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c index 20ee4f7..90e2d93 100644 --- a/drivers/i2c/busses/i2c-iop3xx.c +++ b/drivers/i2c/busses/i2c-iop3xx.c @@ -83,7 +83,7 @@ iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap) * Every time unit enable is asserted, GPOD needs to be cleared * on IOP3XX to avoid data corruption on the bus. */ -#ifdef CONFIG_PLAT_IOP +#if defined(CONFIG_ARCH_IOP32X) || defined(CONFIG_ARCH_IOP33X) if (iop3xx_adap->id == 0) { gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW); gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW); diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c index f54fb5d..bf89eee 100644 --- a/drivers/i2c/busses/i2c-pasemi.c +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -141,7 +141,7 @@ static int pasemi_i2c_xfer_msg(struct i2c_adapter *adapter, for (i = 0; i < msg->len - 1; i++) TXFIFO_WR(smbus, msg->buf[i]); - TXFIFO_WR(smbus, msg->buf[msg->len] | + TXFIFO_WR(smbus, msg->buf[msg->len-1] | (stop ? MTXFIFO_STOP : 0)); } @@ -226,7 +226,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter, rd = RXFIFO_RD(smbus); len = min_t(u8, (rd & MRXFIFO_DATA_M), I2C_SMBUS_BLOCK_MAX); - TXFIFO_WR(smbus, (len + 1) | MTXFIFO_READ | + TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); } else { len = min_t(u8, data->block[0], I2C_SMBUS_BLOCK_MAX); @@ -258,7 +258,7 @@ static int pasemi_smb_xfer(struct i2c_adapter *adapter, rd = RXFIFO_RD(smbus); len = min_t(u8, (rd & MRXFIFO_DATA_M), I2C_SMBUS_BLOCK_MAX - len); - TXFIFO_WR(smbus, (len + 1) | MTXFIFO_READ | MTXFIFO_STOP); + TXFIFO_WR(smbus, len | MTXFIFO_READ | MTXFIFO_STOP); break; default: diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index d888293..21b1809 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -425,7 +425,7 @@ static int __devinit piix4_probe(struct pci_dev *dev, if (retval) return retval; - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ piix4_adapter.dev.parent = &dev->dev; snprintf(piix4_adapter.name, I2C_NAME_SIZE, diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index c3b1567..14e83d0 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -34,6 +34,7 @@ #include <asm/hardware.h> #include <asm/irq.h> +#include <asm/io.h> #include <asm/arch/i2c.h> #include <asm/arch/pxa-regs.h> @@ -54,8 +55,21 @@ struct pxa_i2c { unsigned int irqlogidx; u32 isrlog[32]; u32 icrlog[32]; + + void __iomem *reg_base; + + unsigned long iobase; + unsigned long iosize; + + int irq; }; +#define _IBMR(i2c) ((i2c)->reg_base + 0) +#define _IDBR(i2c) ((i2c)->reg_base + 8) +#define _ICR(i2c) ((i2c)->reg_base + 0x10) +#define _ISR(i2c) ((i2c)->reg_base + 0x18) +#define _ISAR(i2c) ((i2c)->reg_base + 0x20) + /* * I2C Slave mode address */ @@ -130,7 +144,8 @@ static unsigned int i2c_debug = DEBUG; static void i2c_pxa_show_state(struct pxa_i2c *i2c, int lno, const char *fname) { - dev_dbg(&i2c->adap.dev, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno, ISR, ICR, IBMR); + dev_dbg(&i2c->adap.dev, "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno, + readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); } #define show_state(i2c) i2c_pxa_show_state(i2c, __LINE__, __FUNCTION__) @@ -153,7 +168,7 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) printk("i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n", i2c->msg_num, i2c->msg_idx, i2c->msg_ptr); printk("i2c: ICR: %08x ISR: %08x\n" - "i2c: log: ", ICR, ISR); + "i2c: log: ", readl(_ICR(i2c)), readl(_ISR(i2c))); for (i = 0; i < i2c->irqlogidx; i++) printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]); printk("\n"); @@ -161,7 +176,7 @@ static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) static inline int i2c_pxa_is_slavemode(struct pxa_i2c *i2c) { - return !(ICR & ICR_SCLE); + return !(readl(_ICR(i2c)) & ICR_SCLE); } static void i2c_pxa_abort(struct pxa_i2c *i2c) @@ -173,28 +188,29 @@ static void i2c_pxa_abort(struct pxa_i2c *i2c) return; } - while (time_before(jiffies, timeout) && (IBMR & 0x1) == 0) { - unsigned long icr = ICR; + while (time_before(jiffies, timeout) && (readl(_IBMR(i2c)) & 0x1) == 0) { + unsigned long icr = readl(_ICR(i2c)); icr &= ~ICR_START; icr |= ICR_ACKNAK | ICR_STOP | ICR_TB; - ICR = icr; + writel(icr, _ICR(i2c)); show_state(i2c); msleep(1); } - ICR &= ~(ICR_MA | ICR_START | ICR_STOP); + writel(readl(_ICR(i2c)) & ~(ICR_MA | ICR_START | ICR_STOP), + _ICR(i2c)); } static int i2c_pxa_wait_bus_not_busy(struct pxa_i2c *i2c) { int timeout = DEF_TIMEOUT; - while (timeout-- && ISR & (ISR_IBB | ISR_UB)) { - if ((ISR & ISR_SAD) != 0) + while (timeout-- && readl(_ISR(i2c)) & (ISR_IBB | ISR_UB)) { + if ((readl(_ISR(i2c)) & ISR_SAD) != 0) timeout += 4; msleep(2); @@ -214,9 +230,9 @@ static int i2c_pxa_wait_master(struct pxa_i2c *i2c) while (time_before(jiffies, timeout)) { if (i2c_debug > 1) dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", - __func__, (long)jiffies, ISR, ICR, IBMR); + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); - if (ISR & ISR_SAD) { + if (readl(_ISR(i2c)) & ISR_SAD) { if (i2c_debug > 0) dev_dbg(&i2c->adap.dev, "%s: Slave detected\n", __func__); goto out; @@ -226,7 +242,7 @@ static int i2c_pxa_wait_master(struct pxa_i2c *i2c) * quick check of the i2c lines themselves to ensure they've * gone high... */ - if ((ISR & (ISR_UB | ISR_IBB)) == 0 && IBMR == 3) { + if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) == 0 && readl(_IBMR(i2c)) == 3) { if (i2c_debug > 0) dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); return 1; @@ -246,7 +262,7 @@ static int i2c_pxa_set_master(struct pxa_i2c *i2c) if (i2c_debug) dev_dbg(&i2c->adap.dev, "setting to bus master\n"); - if ((ISR & (ISR_UB | ISR_IBB)) != 0) { + if ((readl(_ISR(i2c)) & (ISR_UB | ISR_IBB)) != 0) { dev_dbg(&i2c->adap.dev, "%s: unit is busy\n", __func__); if (!i2c_pxa_wait_master(i2c)) { dev_dbg(&i2c->adap.dev, "%s: error: unit busy\n", __func__); @@ -254,7 +270,7 @@ static int i2c_pxa_set_master(struct pxa_i2c *i2c) } } - ICR |= ICR_SCLE; + writel(readl(_ICR(i2c)) | ICR_SCLE, _ICR(i2c)); return 0; } @@ -270,11 +286,11 @@ static int i2c_pxa_wait_slave(struct pxa_i2c *i2c) while (time_before(jiffies, timeout)) { if (i2c_debug > 1) dev_dbg(&i2c->adap.dev, "%s: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", - __func__, (long)jiffies, ISR, ICR, IBMR); + __func__, (long)jiffies, readl(_ISR(i2c)), readl(_ICR(i2c)), readl(_IBMR(i2c))); - if ((ISR & (ISR_UB|ISR_IBB)) == 0 || - (ISR & ISR_SAD) != 0 || - (ICR & ICR_SCLE) == 0) { + if ((readl(_ISR(i2c)) & (ISR_UB|ISR_IBB)) == 0 || + (readl(_ISR(i2c)) & ISR_SAD) != 0 || + (readl(_ICR(i2c)) & ICR_SCLE) == 0) { if (i2c_debug > 1) dev_dbg(&i2c->adap.dev, "%s: done\n", __func__); return 1; @@ -302,9 +318,9 @@ static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode) /* we need to wait for the stop condition to end */ /* if we where in stop, then clear... */ - if (ICR & ICR_STOP) { + if (readl(_ICR(i2c)) & ICR_STOP) { udelay(100); - ICR &= ~ICR_STOP; + writel(readl(_ICR(i2c)) & ~ICR_STOP, _ICR(i2c)); } if (!i2c_pxa_wait_slave(i2c)) { @@ -314,12 +330,12 @@ static void i2c_pxa_set_slave(struct pxa_i2c *i2c, int errcode) } } - ICR &= ~(ICR_STOP|ICR_ACKNAK|ICR_MA); - ICR &= ~ICR_SCLE; + writel(readl(_ICR(i2c)) & ~(ICR_STOP|ICR_ACKNAK|ICR_MA), _ICR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); if (i2c_debug) { - dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", ICR, ISR); - decode_ICR(ICR); + dev_dbg(&i2c->adap.dev, "ICR now %08x, ISR %08x\n", readl(_ICR(i2c)), readl(_ISR(i2c))); + decode_ICR(readl(_ICR(i2c))); } } #else @@ -334,24 +350,24 @@ static void i2c_pxa_reset(struct pxa_i2c *i2c) i2c_pxa_abort(i2c); /* reset according to 9.8 */ - ICR = ICR_UR; - ISR = I2C_ISR_INIT; - ICR &= ~ICR_UR; + writel(ICR_UR, _ICR(i2c)); + writel(I2C_ISR_INIT, _ISR(i2c)); + writel(readl(_ICR(i2c)) & ~ICR_UR, _ICR(i2c)); - ISAR = i2c->slave_addr; + writel(i2c->slave_addr, _ISAR(i2c)); /* set control register values */ - ICR = I2C_ICR_INIT; + writel(I2C_ICR_INIT, _ICR(i2c)); #ifdef CONFIG_I2C_PXA_SLAVE dev_info(&i2c->adap.dev, "Enabling slave mode\n"); - ICR |= ICR_SADIE | ICR_ALDIE | ICR_SSDIE; + writel(readl(_ICR(i2c)) | ICR_SADIE | ICR_ALDIE | ICR_SSDIE, _ICR(i2c)); #endif i2c_pxa_set_slave(i2c, 0); /* enable unit */ - ICR |= ICR_IUE; + writel(readl(_ICR(i2c)) | ICR_IUE, _ICR(i2c)); udelay(100); } @@ -371,19 +387,19 @@ static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) if (i2c->slave != NULL) ret = i2c->slave->read(i2c->slave->data); - IDBR = ret; - ICR |= ICR_TB; /* allow next byte */ + writel(ret, _IDBR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); /* allow next byte */ } } static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) { - unsigned int byte = IDBR; + unsigned int byte = readl(_IDBR(i2c)); if (i2c->slave != NULL) i2c->slave->write(i2c->slave->data, byte); - ICR |= ICR_TB; + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); } static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) @@ -403,13 +419,13 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) * start condition... if this happens, we'd better back off * and stop holding the poor thing up */ - ICR &= ~(ICR_START|ICR_STOP); - ICR |= ICR_TB; + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); timeout = 0x10000; while (1) { - if ((IBMR & 2) == 2) + if ((readl(_IBMR(i2c)) & 2) == 2) break; timeout--; @@ -420,7 +436,7 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) } } - ICR &= ~ICR_SCLE; + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); } static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) @@ -447,14 +463,14 @@ static void i2c_pxa_slave_txempty(struct pxa_i2c *i2c, u32 isr) if (isr & ISR_BED) { /* what should we do here? */ } else { - IDBR = 0; - ICR |= ICR_TB; + writel(0, _IDBR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB, _ICR(i2c)); } } static void i2c_pxa_slave_rxfull(struct pxa_i2c *i2c, u32 isr) { - ICR |= ICR_TB | ICR_ACKNAK; + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); } static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) @@ -466,13 +482,13 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) * start condition... if this happens, we'd better back off * and stop holding the poor thing up */ - ICR &= ~(ICR_START|ICR_STOP); - ICR |= ICR_TB | ICR_ACKNAK; + writel(readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP), _ICR(i2c)); + writel(readl(_ICR(i2c)) | ICR_TB | ICR_ACKNAK, _ICR(i2c)); timeout = 0x10000; while (1) { - if ((IBMR & 2) == 2) + if ((readl(_IBMR(i2c)) & 2) == 2) break; timeout--; @@ -483,7 +499,7 @@ static void i2c_pxa_slave_start(struct pxa_i2c *i2c, u32 isr) } } - ICR &= ~ICR_SCLE; + writel(readl(_ICR(i2c)) & ~ICR_SCLE, _ICR(i2c)); } static void i2c_pxa_slave_stop(struct pxa_i2c *i2c) @@ -514,13 +530,13 @@ static inline void i2c_pxa_start_message(struct pxa_i2c *i2c) /* * Step 1: target slave address into IDBR */ - IDBR = i2c_pxa_addr_byte(i2c->msg); + writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); /* * Step 2: initiate the write. */ - icr = ICR & ~(ICR_STOP | ICR_ALDIE); - ICR = icr | ICR_START | ICR_TB; + icr = readl(_ICR(i2c)) & ~(ICR_STOP | ICR_ALDIE); + writel(icr | ICR_START | ICR_TB, _ICR(i2c)); } /* @@ -594,7 +610,7 @@ static void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret) static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) { - u32 icr = ICR & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); again: /* @@ -645,7 +661,7 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) /* * Write mode. Write the next data byte. */ - IDBR = i2c->msg->buf[i2c->msg_ptr++]; + writel(i2c->msg->buf[i2c->msg_ptr++], _IDBR(i2c)); icr |= ICR_ALDIE | ICR_TB; @@ -675,7 +691,7 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) /* * Write the next address. */ - IDBR = i2c_pxa_addr_byte(i2c->msg); + writel(i2c_pxa_addr_byte(i2c->msg), _IDBR(i2c)); /* * And trigger a repeated start, and send the byte. @@ -696,18 +712,18 @@ static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) i2c->icrlog[i2c->irqlogidx-1] = icr; - ICR = icr; + writel(icr, _ICR(i2c)); show_state(i2c); } static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) { - u32 icr = ICR & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + u32 icr = readl(_ICR(i2c)) & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); /* * Read the byte. */ - i2c->msg->buf[i2c->msg_ptr++] = IDBR; + i2c->msg->buf[i2c->msg_ptr++] = readl(_IDBR(i2c)); if (i2c->msg_ptr < i2c->msg->len) { /* @@ -724,17 +740,17 @@ static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) i2c->icrlog[i2c->irqlogidx-1] = icr; - ICR = icr; + writel(icr, _ICR(i2c)); } static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) { struct pxa_i2c *i2c = dev_id; - u32 isr = ISR; + u32 isr = readl(_ISR(i2c)); if (i2c_debug > 2 && 0) { dev_dbg(&i2c->adap.dev, "%s: ISR=%08x, ICR=%08x, IBMR=%02x\n", - __func__, isr, ICR, IBMR); + __func__, isr, readl(_ICR(i2c)), readl(_IBMR(i2c))); decode_ISR(isr); } @@ -746,7 +762,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id) /* * Always clear all pending IRQs. */ - ISR = isr & (ISR_SSD|ISR_ALD|ISR_ITE|ISR_IRF|ISR_SAD|ISR_BED); + writel(isr & (ISR_SSD|ISR_ALD|ISR_ITE|ISR_IRF|ISR_SAD|ISR_BED), _ISR(i2c)); if (isr & ISR_SAD) i2c_pxa_slave_start(i2c, isr); @@ -779,7 +795,7 @@ static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num /* If the I2C controller is disabled we need to reset it (probably due to a suspend/resume destroying state). We do this here as we can then avoid worrying about resuming the controller before its users. */ - if (!(ICR & ICR_IUE)) + if (!(readl(_ICR(i2c)) & ICR_IUE)) i2c_pxa_reset(i2c); for (i = adap->retries; i >= 0; i--) { @@ -810,28 +826,53 @@ static const struct i2c_algorithm i2c_pxa_algorithm = { static struct pxa_i2c i2c_pxa = { .lock = SPIN_LOCK_UNLOCKED, - .wait = __WAIT_QUEUE_HEAD_INITIALIZER(i2c_pxa.wait), .adap = { .owner = THIS_MODULE, .algo = &i2c_pxa_algorithm, - .name = "pxa2xx-i2c", + .name = "pxa2xx-i2c.0", .retries = 5, }, }; +#define res_len(r) ((r)->end - (r)->start + 1) static int i2c_pxa_probe(struct platform_device *dev) { struct pxa_i2c *i2c = &i2c_pxa; + struct resource *res; #ifdef CONFIG_I2C_PXA_SLAVE struct i2c_pxa_platform_data *plat = dev->dev.platform_data; #endif int ret; + int irq; -#ifdef CONFIG_PXA27x - pxa_gpio_mode(GPIO117_I2CSCL_MD); - pxa_gpio_mode(GPIO118_I2CSDA_MD); - udelay(100); -#endif + res = platform_get_resource(dev, IORESOURCE_MEM, 0); + irq = platform_get_irq(dev, 0); + if (res == NULL || irq < 0) + return -ENODEV; + + if (!request_mem_region(res->start, res_len(res), res->name)) + return -ENOMEM; + + i2c = kmalloc(sizeof(struct pxa_i2c), GFP_KERNEL); + if (!i2c) { + ret = -ENOMEM; + goto emalloc; + } + + memcpy(i2c, &i2c_pxa, sizeof(struct pxa_i2c)); + init_waitqueue_head(&i2c->wait); + i2c->adap.name[strlen(i2c->adap.name) - 1] = '0' + dev->id % 10; + + i2c->reg_base = ioremap(res->start, res_len(res)); + if (!i2c->reg_base) { + ret = -EIO; + goto eremap; + } + + i2c->iobase = res->start; + i2c->iosize = res_len(res); + + i2c->irq = irq; i2c->slave_addr = I2C_PXA_SLAVE_ADDR; @@ -842,11 +883,28 @@ static int i2c_pxa_probe(struct platform_device *dev) } #endif - pxa_set_cken(CKEN14_I2C, 1); - ret = request_irq(IRQ_I2C, i2c_pxa_handler, IRQF_DISABLED, - "pxa2xx-i2c", i2c); + switch (dev->id) { + case 0: +#ifdef CONFIG_PXA27x + pxa_gpio_mode(GPIO117_I2CSCL_MD); + pxa_gpio_mode(GPIO118_I2CSDA_MD); +#endif + pxa_set_cken(CKEN14_I2C, 1); + break; +#ifdef CONFIG_PXA27x + case 1: + local_irq_disable(); + PCFR |= PCFR_PI2CEN; + local_irq_enable(); + pxa_set_cken(CKEN15_PWRI2C, 1); +#endif + } + + ret = request_irq(irq, i2c_pxa_handler, IRQF_DISABLED, + i2c->adap.name, i2c); if (ret) - goto out; + goto ereqirq; + i2c_pxa_reset(i2c); @@ -856,7 +914,7 @@ static int i2c_pxa_probe(struct platform_device *dev) ret = i2c_add_adapter(&i2c->adap); if (ret < 0) { printk(KERN_INFO "I2C: Failed to add bus\n"); - goto err_irq; + goto eadapt; } platform_set_drvdata(dev, i2c); @@ -870,9 +928,25 @@ static int i2c_pxa_probe(struct platform_device *dev) #endif return 0; - err_irq: - free_irq(IRQ_I2C, i2c); - out: +eadapt: + free_irq(irq, i2c); +ereqirq: + switch (dev->id) { + case 0: + pxa_set_cken(CKEN14_I2C, 0); + break; +#ifdef CONFIG_PXA27x + case 1: + pxa_set_cken(CKEN15_PWRI2C, 0); + local_irq_disable(); + PCFR &= ~PCFR_PI2CEN; + local_irq_enable(); +#endif + } +eremap: + kfree(i2c); +emalloc: + release_mem_region(res->start, res_len(res)); return ret; } @@ -883,8 +957,21 @@ static int i2c_pxa_remove(struct platform_device *dev) platform_set_drvdata(dev, NULL); i2c_del_adapter(&i2c->adap); - free_irq(IRQ_I2C, i2c); - pxa_set_cken(CKEN14_I2C, 0); + free_irq(i2c->irq, i2c); + switch (dev->id) { + case 0: + pxa_set_cken(CKEN14_I2C, 0); + break; +#ifdef CONFIG_PXA27x + case 1: + pxa_set_cken(CKEN15_PWRI2C, 0); + local_irq_disable(); + PCFR &= ~PCFR_PI2CEN; + local_irq_enable(); +#endif + } + release_mem_region(i2c->iobase, i2c->iosize); + kfree(i2c); return 0; } diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index d333bab..a6feed4 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -384,7 +384,7 @@ static int __devinit sis5595_probe(struct pci_dev *dev, const struct pci_device_ return -ENODEV; } - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ sis5595_adapter.dev.parent = &dev->dev; sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 172bacf..5fd734f 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -477,7 +477,7 @@ static int __devinit sis630_probe(struct pci_dev *dev, const struct pci_device_i return -ENODEV; } - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ sis630_adapter.dev.parent = &dev->dev; sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x", diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index 73dae44..4157b0c 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -297,7 +297,7 @@ static int __devinit sis96x_probe(struct pci_dev *dev, return -EINVAL; } - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ sis96x_adapter.dev.parent = &dev->dev; snprintf(sis96x_adapter.name, I2C_NAME_SIZE, diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index bbcc621..8152086 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -138,7 +138,7 @@ static int __devinit vt586b_probe(struct pci_dev *dev, const struct pci_device_i outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); - /* set up the driverfs linkage to our parent device */ + /* set up the sysfs linkage to our parent device */ vt586b_adapter.dev.parent = &dev->dev; res = i2c_bit_add_bus(&vt586b_adapter); diff --git a/drivers/i2c/chips/ds1374.c b/drivers/i2c/chips/ds1374.c index 15edf40..8a2ff0c 100644 --- a/drivers/i2c/chips/ds1374.c +++ b/drivers/i2c/chips/ds1374.c @@ -207,6 +207,10 @@ static int ds1374_probe(struct i2c_adapter *adap, int addr, int kind) client->driver = &ds1374_driver; ds1374_workqueue = create_singlethread_workqueue("ds1374"); + if (!ds1374_workqueue) { + kfree(client); + return -ENOMEM; /* most expected reason */ + } if ((rc = i2c_attach_client(client)) != 0) { kfree(client); diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig index ec03341..5bdf64b 100644 --- a/drivers/ide/Kconfig +++ b/drivers/ide/Kconfig @@ -57,6 +57,7 @@ if IDE config IDE_MAX_HWIFS int "Max IDE interfaces" depends on ALPHA || SUPERH || IA64 || EMBEDDED + range 1 10 default 4 help This is the maximum number of IDE hardware interfaces that will @@ -103,8 +104,10 @@ config BLK_DEV_IDE_SATA ---help--- There are two drivers for Serial ATA controllers. - The main driver, "libata", exists inside the SCSI subsystem - and supports most modern SATA controllers. + The main driver, "libata", uses the SCSI subsystem + and supports most modern SATA controllers. In order to use it + you may take a look at "Serial ATA (prod) and Parallel ATA + (experimental) drivers". The IDE driver (which you are currently configuring) supports a few first-generation SATA controllers. @@ -383,6 +386,9 @@ config BLK_DEV_OFFBOARD config BLK_DEV_GENERIC tristate "Generic PCI IDE Chipset Support" depends on BLK_DEV_IDEPCI + help + This option provides generic support for various PCI IDE Chipsets + which otherwise might not be supported. config BLK_DEV_OPTI621 tristate "OPTi 82C621 chipset enhanced support (EXPERIMENTAL)" @@ -431,24 +437,8 @@ config BLK_DEV_IDEDMA_FORCED Generally say N here. -config IDEDMA_PCI_AUTO - bool "Use PCI DMA by default when available" - ---help--- - Prior to kernel version 2.1.112, Linux used to automatically use - DMA for IDE drives and chipsets which support it. Due to concerns - about a couple of cases where buggy hardware may have caused damage, - the default is now to NOT use DMA automatically. To revert to the - previous behaviour, say Y to this question. - - If you suspect your hardware is at all flakey, say N here. - Do NOT email the IDE kernel people regarding this issue! - - It is normally safe to answer Y to this question unless your - motherboard uses a VIA VP2 chipset, in which case you should say N. - config IDEDMA_ONLYDISK bool "Enable DMA only for disks " - depends on IDEDMA_PCI_AUTO help This is used if you know your ATAPI Devices are going to fail DMA Transfers. @@ -766,6 +756,14 @@ config BLK_DEV_TC86C001 help This driver adds support for Toshiba TC86C001 GOKU-S chip. +config BLK_DEV_CELLEB + tristate "Toshiba's Cell Reference Set IDE support" + depends on PPC_CELLEB + help + This driver provides support for the built-in IDE controller on + Toshiba Cell Reference Board. + If unsure, say Y. + endif config BLK_DEV_IDE_PMAC @@ -840,19 +838,6 @@ config BLK_DEV_IDEDMA_ICS Say Y here if you want to add DMA (Direct Memory Access) support to the ICS IDE driver. -config IDEDMA_ICS_AUTO - bool "Use ICS DMA by default" - depends on BLK_DEV_IDEDMA_ICS - help - Prior to kernel version 2.1.112, Linux used to automatically use - DMA for IDE drives and chipsets which support it. Due to concerns - about a couple of cases where buggy hardware may have caused damage, - the default is now to NOT use DMA automatically. To revert to the - previous behaviour, say Y to this question. - - If you suspect your hardware is at all flakey, say N here. - Do NOT email the IDE kernel people regarding this issue! - config BLK_DEV_IDE_RAPIDE tristate "RapIDE interface support" depends on ARM && ARCH_ACORN @@ -1012,7 +997,7 @@ config BLK_DEV_4DRIVES config BLK_DEV_ALI14XX tristate "ALI M14xx support" help - This driver is enabled at runtime using the "ide0=ali14xx" kernel + This driver is enabled at runtime using the "ali14xx.probe" kernel boot parameter. It enables support for the secondary IDE interface of the ALI M1439/1443/1445/1487/1489 chipsets, and permits faster I/O speeds to be set as well. See the files @@ -1022,7 +1007,7 @@ config BLK_DEV_ALI14XX config BLK_DEV_DTC2278 tristate "DTC-2278 support" help - This driver is enabled at runtime using the "ide0=dtc2278" kernel + This driver is enabled at runtime using the "dtc2278.probe" kernel boot parameter. It enables support for the secondary IDE interface of the DTC-2278 card, and permits faster I/O speeds to be set as well. See the <file:Documentation/ide.txt> and @@ -1031,7 +1016,7 @@ config BLK_DEV_DTC2278 config BLK_DEV_HT6560B tristate "Holtek HT6560B support" help - This driver is enabled at runtime using the "ide0=ht6560b" kernel + This driver is enabled at runtime using the "ht6560b.probe" kernel boot parameter. It enables support for the secondary IDE interface of the Holtek card, and permits faster I/O speeds to be set as well. See the <file:Documentation/ide.txt> and @@ -1040,7 +1025,7 @@ config BLK_DEV_HT6560B config BLK_DEV_QD65XX tristate "QDI QD65xx support" help - This driver is enabled at runtime using the "ide0=qd65xx" kernel + This driver is enabled at runtime using the "qd65xx.probe" kernel boot parameter. It permits faster I/O speeds to be set. See the <file:Documentation/ide.txt> and <file:drivers/ide/legacy/qd65xx.c> for more info. @@ -1048,7 +1033,7 @@ config BLK_DEV_QD65XX config BLK_DEV_UMC8672 tristate "UMC-8672 support" help - This driver is enabled at runtime using the "ide0=umc8672" kernel + This driver is enabled at runtime using the "umc8672.probe" kernel boot parameter. It enables support for the secondary IDE interface of the UMC-8672, and permits faster I/O speeds to be set as well. See the files <file:Documentation/ide.txt> and @@ -1075,9 +1060,6 @@ config IDEDMA_IVB It is normally safe to answer Y; however, the default is N. -config IDEDMA_AUTO - def_bool IDEDMA_PCI_AUTO || IDEDMA_ICS_AUTO - endif config BLK_DEV_HD_ONLY diff --git a/drivers/ide/arm/icside.c b/drivers/ide/arm/icside.c index 8a1c27f..e2953fc 100644 --- a/drivers/ide/arm/icside.c +++ b/drivers/ide/arm/icside.c @@ -196,11 +196,6 @@ static void icside_maskproc(ide_drive_t *drive, int mask) } #ifdef CONFIG_BLK_DEV_IDEDMA_ICS - -#ifndef CONFIG_IDEDMA_ICS_AUTO -#warning CONFIG_IDEDMA_ICS_AUTO=n support is obsolete, and will be removed soon. -#endif - /* * SG-DMA support. * @@ -307,26 +302,24 @@ static int icside_set_speed(ide_drive_t *drive, u8 xfer_mode) return on; } -static int icside_dma_host_off(ide_drive_t *drive) +static void icside_dma_host_off(ide_drive_t *drive) { - return 0; } -static int icside_dma_off_quietly(ide_drive_t *drive) +static void icside_dma_off_quietly(ide_drive_t *drive) { drive->using_dma = 0; - return icside_dma_host_off(drive); } -static int icside_dma_host_on(ide_drive_t *drive) +static void icside_dma_host_on(ide_drive_t *drive) { - return 0; } static int icside_dma_on(ide_drive_t *drive) { drive->using_dma = 1; - return icside_dma_host_on(drive); + + return 0; } static int icside_dma_check(ide_drive_t *drive) @@ -365,10 +358,7 @@ static int icside_dma_check(ide_drive_t *drive) out: on = icside_set_speed(drive, xfer_mode); - if (on) - return icside_dma_on(drive); - else - return icside_dma_off_quietly(drive); + return on ? 0 : -1; } static int icside_dma_end(ide_drive_t *drive) @@ -479,12 +469,6 @@ static int icside_dma_lostirq(ide_drive_t *drive) static void icside_dma_init(ide_hwif_t *hwif) { - int autodma = 0; - -#ifdef CONFIG_IDEDMA_ICS_AUTO - autodma = 1; -#endif - printk(" %s: SG-DMA", hwif->name); hwif->atapi_dma = 1; @@ -494,12 +478,12 @@ static void icside_dma_init(ide_hwif_t *hwif) hwif->dmatable_cpu = NULL; hwif->dmatable_dma = 0; hwif->speedproc = icside_set_speed; - hwif->autodma = autodma; + hwif->autodma = 1; hwif->ide_dma_check = icside_dma_check; - hwif->ide_dma_host_off = icside_dma_host_off; - hwif->ide_dma_off_quietly = icside_dma_off_quietly; - hwif->ide_dma_host_on = icside_dma_host_on; + hwif->dma_host_off = icside_dma_host_off; + hwif->dma_off_quietly = icside_dma_off_quietly; + hwif->dma_host_on = icside_dma_host_on; hwif->ide_dma_on = icside_dma_on; hwif->dma_setup = icside_dma_setup; hwif->dma_exec_cmd = icside_dma_exec_cmd; @@ -556,7 +540,7 @@ icside_setup(void __iomem *base, struct cardinfo *info, struct expansion_card *e * Ensure we're using MMIO */ default_hwif_mmiops(hwif); - hwif->mmio = 2; + hwif->mmio = 1; for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { hwif->hw.io_ports[i] = port; diff --git a/drivers/ide/arm/rapide.c b/drivers/ide/arm/rapide.c index 3058217..9c6c49f 100644 --- a/drivers/ide/arm/rapide.c +++ b/drivers/ide/arm/rapide.c @@ -46,7 +46,7 @@ rapide_locate_hwif(void __iomem *base, void __iomem *ctrl, unsigned int sz, int hwif->hw.io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; hwif->io_ports[IDE_CONTROL_OFFSET] = (unsigned long)ctrl; hwif->hw.irq = hwif->irq = irq; - hwif->mmio = 2; + hwif->mmio = 1; default_hwif_mmiops(hwif); return hwif; diff --git a/drivers/ide/cris/ide-cris.c b/drivers/ide/cris/ide-cris.c index 5797e0b..556455f 100644 --- a/drivers/ide/cris/ide-cris.c +++ b/drivers/ide/cris/ide-cris.c @@ -17,8 +17,6 @@ * device can't do DMA handshaking for some stupid reason. We don't need to do that. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/types.h> #include <linux/kernel.h> #include <linux/timer.h> @@ -682,9 +680,12 @@ static void cris_ide_input_data (ide_drive_t *drive, void *, unsigned int); static void cris_ide_output_data (ide_drive_t *drive, void *, unsigned int); static void cris_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int); static void cris_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int); -static int cris_dma_off (ide_drive_t *drive); static int cris_dma_on (ide_drive_t *drive); +static void cris_dma_off(ide_drive_t *drive) +{ +} + static void tune_cris_ide(ide_drive_t *drive, u8 pio) { int setup, strobe, hold; @@ -795,7 +796,7 @@ init_e100_ide (void) 0, 0, cris_ide_ack_intr, ide_default_irq(0)); ide_register_hw(&hw, &hwif); - hwif->mmio = 2; + hwif->mmio = 1; hwif->chipset = ide_etrax100; hwif->tuneproc = &tune_cris_ide; hwif->speedproc = &speed_cris_ide; @@ -814,13 +815,16 @@ init_e100_ide (void) hwif->OUTBSYNC = &cris_ide_outbsync; hwif->INB = &cris_ide_inb; hwif->INW = &cris_ide_inw; - hwif->ide_dma_host_off = &cris_dma_off; - hwif->ide_dma_host_on = &cris_dma_on; - hwif->ide_dma_off_quietly = &cris_dma_off; + hwif->dma_host_off = &cris_dma_off; + hwif->dma_host_on = &cris_dma_on; + hwif->dma_off_quietly = &cris_dma_off; hwif->udma_four = 0; hwif->ultra_mask = cris_ultra_mask; hwif->mwdma_mask = 0x07; /* Multiword DMA 0-2 */ hwif->swdma_mask = 0x07; /* Singleword DMA 0-2 */ + hwif->autodma = 1; + hwif->drives[0].autodma = 1; + hwif->drives[1].autodma = 1; } /* Reset pulse */ @@ -835,11 +839,6 @@ init_e100_ide (void) cris_ide_set_speed(TYPE_UDMA, ATA_UDMA2_CYC, ATA_UDMA2_DVS, 0); } -static int cris_dma_off (ide_drive_t *drive) -{ - return 0; -} - static int cris_dma_on (ide_drive_t *drive) { return 0; @@ -1045,17 +1044,10 @@ static ide_startstop_t cris_dma_intr (ide_drive_t *drive) static int cris_dma_check(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; - struct hd_driveid* id = drive->id; - - if (id && (id->capability & 1)) { - if (ide_use_dma(drive)) { - if (cris_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - } + if (ide_use_dma(drive) && cris_config_drive_for_dma(drive)) + return 0; - return hwif->ide_dma_off_quietly(drive); + return -1; } static int cris_dma_end(ide_drive_t *drive) diff --git a/drivers/ide/h8300/ide-h8300.c b/drivers/ide/h8300/ide-h8300.c index 608ca87..88750a30 100644 --- a/drivers/ide/h8300/ide-h8300.c +++ b/drivers/ide/h8300/ide-h8300.c @@ -76,13 +76,11 @@ static inline void hwif_setup(ide_hwif_t *hwif) { default_hwif_iops(hwif); - hwif->mmio = 2; + hwif->mmio = 1; hwif->OUTW = mm_outw; hwif->OUTSW = mm_outsw; hwif->INW = mm_inw; hwif->INSW = mm_insw; - hwif->OUTL = NULL; - hwif->INL = NULL; hwif->OUTSL = NULL; hwif->INSL = NULL; } diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c index 5969cec..638becd 100644 --- a/drivers/ide/ide-cd.c +++ b/drivers/ide/ide-cd.c @@ -687,15 +687,8 @@ static void ide_dump_status_no_sense(ide_drive_t *drive, const char *msg, u8 sta static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) { struct request *rq = HWGROUP(drive)->rq; - ide_hwif_t *hwif = HWIF(drive); int stat, err, sense_key; - /* We may have bogus DMA interrupts in PIO state here */ - if (HWIF(drive)->dma_status && hwif->atapi_irq_bogon) { - stat = hwif->INB(hwif->dma_status); - /* Should we force the bit as well ? */ - hwif->OUTB(stat, hwif->dma_status); - } /* Check for errors. */ stat = HWIF(drive)->INB(IDE_STATUS_REG); if (stat_ret) @@ -742,6 +735,15 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret) cdrom_saw_media_change (drive); /*printk("%s: media changed\n",drive->name);*/ return 0; + } else if ((sense_key == ILLEGAL_REQUEST) && + (rq->cmd[0] == GPCMD_START_STOP_UNIT)) { + /* + * Don't print error message for this condition-- + * SFF8090i indicates that 5/24/00 is the correct + * response to a request to close the tray if the + * drive doesn't have that capability. + * cdrom_log_sense() knows this! + */ } else if (!(rq->cmd_flags & REQ_QUIET)) { /* Otherwise, print an error. */ ide_dump_status(drive, "packet command error", stat); @@ -930,6 +932,10 @@ static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG); if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + /* waiting for CDB interrupt, not DMA yet. */ + if (info->dma) + drive->waiting_for_dma = 0; + /* packet command */ ide_execute_command(drive, WIN_PACKETCMD, handler, ATAPI_WAIT_PC, cdrom_timer_expiry); return ide_started; @@ -972,6 +978,10 @@ static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, /* Check for errors. */ if (cdrom_decode_status(drive, DRQ_STAT, NULL)) return ide_stopped; + + /* Ok, next interrupt will be DMA interrupt. */ + if (info->dma) + drive->waiting_for_dma = 1; } else { /* Otherwise, we must wait for DRQ to get set. */ if (ide_wait_stat(&startstop, drive, DRQ_STAT, @@ -1103,7 +1113,7 @@ static ide_startstop_t cdrom_read_intr (ide_drive_t *drive) if (dma) { info->dma = 0; if ((dma_error = HWIF(drive)->ide_dma_end(drive))) - __ide_dma_off(drive); + ide_dma_off(drive); } if (cdrom_decode_status(drive, 0, &stat)) @@ -1699,7 +1709,7 @@ static ide_startstop_t cdrom_newpc_intr(ide_drive_t *drive) if (dma) { if (dma_error) { printk(KERN_ERR "ide-cd: dma error\n"); - __ide_dma_off(drive); + ide_dma_off(drive); return ide_error(drive, "dma error", stat); } @@ -1825,7 +1835,7 @@ static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) info->dma = 0; if ((dma_error = HWIF(drive)->ide_dma_end(drive))) { printk(KERN_ERR "ide-cd: write dma error\n"); - __ide_dma_off(drive); + ide_dma_off(drive); } } @@ -3254,14 +3264,6 @@ int ide_cdrom_setup (ide_drive_t *drive) if (drive->autotune == IDE_TUNE_DEFAULT || drive->autotune == IDE_TUNE_AUTO) drive->dsc_overlap = (drive->next != drive); -#if 0 - drive->dsc_overlap = (HWIF(drive)->no_dsc) ? 0 : 1; - if (HWIF(drive)->no_dsc) { - printk(KERN_INFO "ide-cd: %s: disabling DSC overlap\n", - drive->name); - drive->dsc_overlap = 0; - } -#endif if (ide_cdrom_register(drive, nslots)) { printk (KERN_ERR "%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name); @@ -3360,21 +3362,16 @@ static int idecd_open(struct inode * inode, struct file * file) { struct gendisk *disk = inode->i_bdev->bd_disk; struct cdrom_info *info; - ide_drive_t *drive; int rc = -ENOMEM; if (!(info = ide_cd_get(disk))) return -ENXIO; - drive = info->drive; - - drive->usage++; - if (!info->buffer) - info->buffer = kmalloc(SECTOR_BUFFER_SIZE, - GFP_KERNEL|__GFP_REPEAT); - if (!info->buffer || (rc = cdrom_open(&info->devinfo, inode, file))) - drive->usage--; + info->buffer = kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL|__GFP_REPEAT); + + if (info->buffer) + rc = cdrom_open(&info->devinfo, inode, file); if (rc < 0) ide_cd_put(info); @@ -3386,10 +3383,8 @@ static int idecd_release(struct inode * inode, struct file * file) { struct gendisk *disk = inode->i_bdev->bd_disk; struct cdrom_info *info = ide_cd_g(disk); - ide_drive_t *drive = info->drive; cdrom_release (&info->devinfo, file); - drive->usage--; ide_cd_put(info); diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c index 0a05a37..37aa6dd 100644 --- a/drivers/ide/ide-disk.c +++ b/drivers/ide/ide-disk.c @@ -43,8 +43,6 @@ #define IDEDISK_VERSION "1.18" -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - //#define DEBUG #include <linux/module.h> @@ -77,6 +75,7 @@ struct ide_disk_obj { ide_driver_t *driver; struct gendisk *disk; struct kref kref; + unsigned int openers; /* protected by BKL for now */ }; static DEFINE_MUTEX(idedisk_ref_mutex); @@ -1081,8 +1080,9 @@ static int idedisk_open(struct inode *inode, struct file *filp) drive = idkp->drive; - drive->usage++; - if (drive->removable && drive->usage == 1) { + idkp->openers++; + + if (drive->removable && idkp->openers == 1) { ide_task_t args; memset(&args, 0, sizeof(ide_task_t)); args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORLOCK; @@ -1106,9 +1106,10 @@ static int idedisk_release(struct inode *inode, struct file *filp) struct ide_disk_obj *idkp = ide_disk_g(disk); ide_drive_t *drive = idkp->drive; - if (drive->usage == 1) + if (idkp->openers == 1) ide_cacheflush_p(drive); - if (drive->removable && drive->usage == 1) { + + if (drive->removable && idkp->openers == 1) { ide_task_t args; memset(&args, 0, sizeof(ide_task_t)); args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORUNLOCK; @@ -1117,7 +1118,8 @@ static int idedisk_release(struct inode *inode, struct file *filp) if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL)) drive->doorlocking = 0; } - drive->usage--; + + idkp->openers--; ide_disk_put(idkp); diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c index 56efed6..fd21308 100644 --- a/drivers/ide/ide-dma.c +++ b/drivers/ide/ide-dma.c @@ -348,15 +348,14 @@ EXPORT_SYMBOL_GPL(ide_destroy_dmatable); static int config_drive_for_dma (ide_drive_t *drive) { struct hd_driveid *id = drive->id; - ide_hwif_t *hwif = HWIF(drive); - if ((id->capability & 1) && hwif->autodma) { + if ((id->capability & 1) && drive->hwif->autodma) { /* * Enable DMA on any drive that has * UltraDMA (mode 0/1/2/3/4/5/6) enabled */ if ((id->field_valid & 4) && ((id->dma_ultra >> 8) & 0x7f)) - return hwif->ide_dma_on(drive); + return 0; /* * Enable DMA on any drive that has mode2 DMA * (multi or single) enabled @@ -364,14 +363,14 @@ static int config_drive_for_dma (ide_drive_t *drive) if (id->field_valid & 2) /* regular DMA */ if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) - return hwif->ide_dma_on(drive); + return 0; /* Consult the list of known "good" drives */ if (__ide_dma_good_drive(drive)) - return hwif->ide_dma_on(drive); + return 0; } -// if (hwif->tuneproc != NULL) hwif->tuneproc(drive, 255); - return hwif->ide_dma_off_quietly(drive); + + return -1; } /** @@ -415,72 +414,68 @@ static int dma_timer_expiry (ide_drive_t *drive) } /** - * __ide_dma_host_off - Generic DMA kill + * ide_dma_host_off - Generic DMA kill * @drive: drive to control * * Perform the generic IDE controller DMA off operation. This * works for most IDE bus mastering controllers */ -int __ide_dma_host_off (ide_drive_t *drive) +void ide_dma_host_off(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); u8 unit = (drive->select.b.unit & 0x01); u8 dma_stat = hwif->INB(hwif->dma_status); hwif->OUTB((dma_stat & ~(1<<(5+unit))), hwif->dma_status); - return 0; } -EXPORT_SYMBOL(__ide_dma_host_off); +EXPORT_SYMBOL(ide_dma_host_off); /** - * __ide_dma_host_off_quietly - Generic DMA kill + * ide_dma_off_quietly - Generic DMA kill * @drive: drive to control * * Turn off the current DMA on this IDE controller. */ -int __ide_dma_off_quietly (ide_drive_t *drive) +void ide_dma_off_quietly(ide_drive_t *drive) { drive->using_dma = 0; ide_toggle_bounce(drive, 0); - if (HWIF(drive)->ide_dma_host_off(drive)) - return 1; - - return 0; + drive->hwif->dma_host_off(drive); } -EXPORT_SYMBOL(__ide_dma_off_quietly); +EXPORT_SYMBOL(ide_dma_off_quietly); #endif /* CONFIG_BLK_DEV_IDEDMA_PCI */ /** - * __ide_dma_off - disable DMA on a device + * ide_dma_off - disable DMA on a device * @drive: drive to disable DMA on * * Disable IDE DMA for a device on this IDE controller. * Inform the user that DMA has been disabled. */ -int __ide_dma_off (ide_drive_t *drive) +void ide_dma_off(ide_drive_t *drive) { printk(KERN_INFO "%s: DMA disabled\n", drive->name); - return HWIF(drive)->ide_dma_off_quietly(drive); + drive->hwif->dma_off_quietly(drive); } -EXPORT_SYMBOL(__ide_dma_off); +EXPORT_SYMBOL(ide_dma_off); #ifdef CONFIG_BLK_DEV_IDEDMA_PCI /** - * __ide_dma_host_on - Enable DMA on a host + * ide_dma_host_on - Enable DMA on a host * @drive: drive to enable for DMA * * Enable DMA on an IDE controller following generic bus mastering * IDE controller behaviour */ - -int __ide_dma_host_on (ide_drive_t *drive) + +void ide_dma_host_on(ide_drive_t *drive) { if (drive->using_dma) { ide_hwif_t *hwif = HWIF(drive); @@ -488,12 +483,10 @@ int __ide_dma_host_on (ide_drive_t *drive) u8 dma_stat = hwif->INB(hwif->dma_status); hwif->OUTB((dma_stat|(1<<(5+unit))), hwif->dma_status); - return 0; } - return 1; } -EXPORT_SYMBOL(__ide_dma_host_on); +EXPORT_SYMBOL(ide_dma_host_on); /** * __ide_dma_on - Enable DMA on a device @@ -511,8 +504,7 @@ int __ide_dma_on (ide_drive_t *drive) drive->using_dma = 1; ide_toggle_bounce(drive, 1); - if (HWIF(drive)->ide_dma_host_on(drive)) - return 1; + drive->hwif->dma_host_on(drive); return 0; } @@ -565,7 +557,10 @@ int ide_dma_setup(ide_drive_t *drive) } /* PRD table */ - hwif->OUTL(hwif->dmatable_dma, hwif->dma_prdtable); + if (hwif->mmio) + writel(hwif->dmatable_dma, (void __iomem *)hwif->dma_prdtable); + else + outl(hwif->dmatable_dma, hwif->dma_prdtable); /* specify r/w */ hwif->OUTB(reading, hwif->dma_command); @@ -680,6 +675,9 @@ int ide_use_dma(ide_drive_t *drive) struct hd_driveid *id = drive->id; ide_hwif_t *hwif = drive->hwif; + if ((id->capability & 1) == 0 || drive->autodma == 0) + return 0; + /* consult the list of known "bad" drives */ if (__ide_dma_bad_drive(drive)) return 0; @@ -753,12 +751,37 @@ void ide_dma_verbose(ide_drive_t *drive) return; bug_dma_off: printk(", BUG DMA OFF"); - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); return; } EXPORT_SYMBOL(ide_dma_verbose); +int ide_set_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + int rc; + + rc = hwif->ide_dma_check(drive); + + switch(rc) { + case -1: /* DMA needs to be disabled */ + hwif->dma_off_quietly(drive); + return -1; + case 0: /* DMA needs to be enabled */ + return hwif->ide_dma_on(drive); + case 1: /* DMA setting cannot be changed */ + break; + default: + BUG(); + break; + } + + return rc; +} + +EXPORT_SYMBOL_GPL(ide_set_dma); + #ifdef CONFIG_BLK_DEV_IDEDMA_PCI int __ide_dma_lostirq (ide_drive_t *drive) { @@ -809,7 +832,7 @@ int ide_release_dma(ide_hwif_t *hwif) { ide_release_dma_engine(hwif); - if (hwif->mmio == 2) + if (hwif->mmio) return 1; else return ide_release_iomio_dma(hwif); @@ -878,9 +901,9 @@ static int ide_iomio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int port static int ide_dma_iobase(ide_hwif_t *hwif, unsigned long base, unsigned int ports) { - if (hwif->mmio == 2) + if (hwif->mmio) return ide_mapped_mmio_dma(hwif, base,ports); - BUG_ON(hwif->mmio == 1); + return ide_iomio_dma(hwif, base, ports); } @@ -908,14 +931,14 @@ void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_p if (!(hwif->dma_prdtable)) hwif->dma_prdtable = (hwif->dma_base + 4); - if (!hwif->ide_dma_off_quietly) - hwif->ide_dma_off_quietly = &__ide_dma_off_quietly; - if (!hwif->ide_dma_host_off) - hwif->ide_dma_host_off = &__ide_dma_host_off; + if (!hwif->dma_off_quietly) + hwif->dma_off_quietly = &ide_dma_off_quietly; + if (!hwif->dma_host_off) + hwif->dma_host_off = &ide_dma_host_off; if (!hwif->ide_dma_on) hwif->ide_dma_on = &__ide_dma_on; - if (!hwif->ide_dma_host_on) - hwif->ide_dma_host_on = &__ide_dma_host_on; + if (!hwif->dma_host_on) + hwif->dma_host_on = &ide_dma_host_on; if (!hwif->ide_dma_check) hwif->ide_dma_check = &__ide_dma_check; if (!hwif->dma_setup) diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c index d33717c..57cd21c 100644 --- a/drivers/ide/ide-floppy.c +++ b/drivers/ide/ide-floppy.c @@ -279,6 +279,7 @@ typedef struct ide_floppy_obj { ide_driver_t *driver; struct gendisk *disk; struct kref kref; + unsigned int openers; /* protected by BKL for now */ /* Current packet command */ idefloppy_pc_t *pc; @@ -866,7 +867,7 @@ static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive) if (test_and_clear_bit(PC_DMA_IN_PROGRESS, &pc->flags)) { printk(KERN_ERR "ide-floppy: The floppy wants to issue " "more interrupts in DMA mode\n"); - (void)__ide_dma_off(drive); + ide_dma_off(drive); return ide_do_reset(drive); } @@ -1096,9 +1097,9 @@ static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *p pc->current_position = pc->buffer; bcount.all = min(pc->request_transfer, 63 * 1024); - if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) { - (void)__ide_dma_off(drive); - } + if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) + ide_dma_off(drive); + feature.all = 0; if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) @@ -1433,7 +1434,8 @@ static int idefloppy_get_capacity (ide_drive_t *drive) drive->bios_cyl = 0; drive->bios_head = drive->bios_sect = 0; - floppy->blocks = floppy->bs_factor = 0; + floppy->blocks = 0; + floppy->bs_factor = 1; set_capacity(floppy->disk, 0); idefloppy_create_read_capacity_cmd(&pc); @@ -1949,9 +1951,9 @@ static int idefloppy_open(struct inode *inode, struct file *filp) drive = floppy->drive; - drive->usage++; + floppy->openers++; - if (drive->usage == 1) { + if (floppy->openers == 1) { clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); /* Just in case */ @@ -1969,13 +1971,11 @@ static int idefloppy_open(struct inode *inode, struct file *filp) ** capacity of the drive or begin the format - Sam */ ) { - drive->usage--; ret = -EIO; goto out_put_floppy; } if (floppy->wp && (filp->f_mode & 2)) { - drive->usage--; ret = -EROFS; goto out_put_floppy; } @@ -1987,13 +1987,13 @@ static int idefloppy_open(struct inode *inode, struct file *filp) } check_disk_change(inode->i_bdev); } else if (test_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags)) { - drive->usage--; ret = -EBUSY; goto out_put_floppy; } return 0; out_put_floppy: + floppy->openers--; ide_floppy_put(floppy); return ret; } @@ -2007,7 +2007,7 @@ static int idefloppy_release(struct inode *inode, struct file *filp) debug_log(KERN_INFO "Reached idefloppy_release\n"); - if (drive->usage == 1) { + if (floppy->openers == 1) { /* IOMEGA Clik! drives do not support lock/unlock commands */ if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { idefloppy_create_prevent_cmd(&pc, 0); @@ -2016,7 +2016,8 @@ static int idefloppy_release(struct inode *inode, struct file *filp) clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); } - drive->usage--; + + floppy->openers--; ide_floppy_put(floppy); @@ -2050,7 +2051,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, prevent = 0; /* fall through */ case CDROM_LOCKDOOR: - if (drive->usage > 1) + if (floppy->openers > 1) return -EBUSY; /* The IOMEGA Clik! Drive doesn't support this command - no room for an eject mechanism */ @@ -2072,7 +2073,7 @@ static int idefloppy_ioctl(struct inode *inode, struct file *file, if (!(file->f_mode & 2)) return -EPERM; - if (drive->usage > 1) { + if (floppy->openers > 1) { /* Don't format if someone is using the disk */ clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c index 2614f41..8670112 100644 --- a/drivers/ide/ide-io.c +++ b/drivers/ide/ide-io.c @@ -226,7 +226,7 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request * break; if (drive->hwif->ide_dma_check == NULL) break; - drive->hwif->ide_dma_check(drive); + ide_set_dma(drive); break; } pm->pm_step = ide_pm_state_completed; @@ -519,21 +519,24 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8 if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ && hwif->err_stops_fifo == 0) try_to_flush_leftover_data(drive); + if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) { + ide_kill_rq(drive, rq); + return ide_stopped; + } + if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT)) - /* force an abort */ - hwif->OUTB(WIN_IDLEIMMEDIATE, IDE_COMMAND_REG); + rq->errors |= ERROR_RESET; - if (rq->errors >= ERROR_MAX || blk_noretry_request(rq)) - ide_kill_rq(drive, rq); - else { - if ((rq->errors & ERROR_RESET) == ERROR_RESET) { - ++rq->errors; - return ide_do_reset(drive); - } - if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) - drive->special.b.recalibrate = 1; + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { ++rq->errors; + return ide_do_reset(drive); } + + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + drive->special.b.recalibrate = 1; + + ++rq->errors; + return ide_stopped; } @@ -1025,6 +1028,13 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) if (!drive->special.all) { ide_driver_t *drv; + /* + * We reset the drive so we need to issue a SETFEATURES. + * Do it _after_ do_special() restored device parameters. + */ + if (drive->current_speed == 0xff) + ide_config_drive_speed(drive, drive->desired_speed); + if (rq->cmd_type == REQ_TYPE_ATA_CMD || rq->cmd_type == REQ_TYPE_ATA_TASK || rq->cmd_type == REQ_TYPE_ATA_TASKFILE) @@ -1216,6 +1226,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) #endif /* so that ide_timer_expiry knows what to do */ hwgroup->sleeping = 1; + hwgroup->req_gen_timer = hwgroup->req_gen; mod_timer(&hwgroup->timer, sleep); /* we purposely leave hwgroup->busy==1 * while sleeping */ @@ -1351,7 +1362,7 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error) */ drive->retry_pio++; drive->state = DMA_PIO_RETRY; - (void) hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); /* * un-busy drive etc (hwgroup->busy is cleared on return) and @@ -1401,7 +1412,8 @@ void ide_timer_expiry (unsigned long data) spin_lock_irqsave(&ide_lock, flags); - if ((handler = hwgroup->handler) == NULL) { + if (((handler = hwgroup->handler) == NULL) || + (hwgroup->req_gen != hwgroup->req_gen_timer)) { /* * Either a marginal timeout occurred * (got the interrupt just as timer expired), @@ -1429,6 +1441,7 @@ void ide_timer_expiry (unsigned long data) if ((wait = expiry(drive)) > 0) { /* reset timer */ hwgroup->timer.expires = jiffies + wait; + hwgroup->req_gen_timer = hwgroup->req_gen; add_timer(&hwgroup->timer); spin_unlock_irqrestore(&ide_lock, flags); return; @@ -1643,9 +1656,21 @@ irqreturn_t ide_intr (int irq, void *dev_id) printk(KERN_ERR "%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); } hwgroup->handler = NULL; + hwgroup->req_gen++; del_timer(&hwgroup->timer); spin_unlock(&ide_lock); + /* Some controllers might set DMA INTR no matter DMA or PIO; + * bmdma status might need to be cleared even for + * PIO interrupts to prevent spurious/lost irq. + */ + if (hwif->ide_dma_clear_irq && !(drive->waiting_for_dma)) + /* ide_dma_end() needs bmdma status for error checking. + * So, skip clearing bmdma status here and leave it + * to ide_dma_end() if this is dma interrupt. + */ + hwif->ide_dma_clear_irq(drive); + if (drive->unmask) local_irq_enable_in_hardirq(); /* service this interrupt, may set handler for next interrupt */ diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c index badde63..3caa176 100644 --- a/drivers/ide/ide-iops.c +++ b/drivers/ide/ide-iops.c @@ -49,11 +49,6 @@ static void ide_insw (unsigned long port, void *addr, u32 count) insw(port, addr, count); } -static u32 ide_inl (unsigned long port) -{ - return (u32) inl(port); -} - static void ide_insl (unsigned long port, void *addr, u32 count) { insl(port, addr, count); @@ -79,11 +74,6 @@ static void ide_outsw (unsigned long port, void *addr, u32 count) outsw(port, addr, count); } -static void ide_outl (u32 val, unsigned long port) -{ - outl(val, port); -} - static void ide_outsl (unsigned long port, void *addr, u32 count) { outsl(port, addr, count); @@ -94,12 +84,10 @@ void default_hwif_iops (ide_hwif_t *hwif) hwif->OUTB = ide_outb; hwif->OUTBSYNC = ide_outbsync; hwif->OUTW = ide_outw; - hwif->OUTL = ide_outl; hwif->OUTSW = ide_outsw; hwif->OUTSL = ide_outsl; hwif->INB = ide_inb; hwif->INW = ide_inw; - hwif->INL = ide_inl; hwif->INSW = ide_insw; hwif->INSL = ide_insl; } @@ -123,11 +111,6 @@ static void ide_mm_insw (unsigned long port, void *addr, u32 count) __ide_mm_insw((void __iomem *) port, addr, count); } -static u32 ide_mm_inl (unsigned long port) -{ - return (u32) readl((void __iomem *) port); -} - static void ide_mm_insl (unsigned long port, void *addr, u32 count) { __ide_mm_insl((void __iomem *) port, addr, count); @@ -153,11 +136,6 @@ static void ide_mm_outsw (unsigned long port, void *addr, u32 count) __ide_mm_outsw((void __iomem *) port, addr, count); } -static void ide_mm_outl (u32 value, unsigned long port) -{ - writel(value, (void __iomem *) port); -} - static void ide_mm_outsl (unsigned long port, void *addr, u32 count) { __ide_mm_outsl((void __iomem *) port, addr, count); @@ -170,12 +148,10 @@ void default_hwif_mmiops (ide_hwif_t *hwif) this one is controller specific! */ hwif->OUTBSYNC = ide_mm_outbsync; hwif->OUTW = ide_mm_outw; - hwif->OUTL = ide_mm_outl; hwif->OUTSW = ide_mm_outsw; hwif->OUTSL = ide_mm_outsl; hwif->INB = ide_mm_inb; hwif->INW = ide_mm_inw; - hwif->INL = ide_mm_inl; hwif->INSW = ide_mm_insw; hwif->INSL = ide_mm_insl; } @@ -607,6 +583,12 @@ u8 eighty_ninty_three (ide_drive_t *drive) if(!(drive->id->hw_config & 0x4000)) return 0; #endif /* CONFIG_IDEDMA_IVB */ + /* + * FIXME: + * - change master/slave IDENTIFY order + * - force bit13 (80c cable present) check + * (unless the slave device is pre-ATA3) + */ return 1; } @@ -777,7 +759,7 @@ int ide_config_drive_speed (ide_drive_t *drive, u8 speed) #ifdef CONFIG_BLK_DEV_IDEDMA if (hwif->ide_dma_check) /* check if host supports DMA */ - hwif->ide_dma_host_off(drive); + hwif->dma_host_off(drive); #endif /* @@ -854,9 +836,9 @@ int ide_config_drive_speed (ide_drive_t *drive, u8 speed) #ifdef CONFIG_BLK_DEV_IDEDMA if (speed >= XFER_SW_DMA_0) - hwif->ide_dma_host_on(drive); + hwif->dma_host_on(drive); else if (hwif->ide_dma_check) /* check if host supports DMA */ - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); #endif switch(speed) { @@ -907,6 +889,7 @@ static void __ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, hwgroup->handler = handler; hwgroup->expiry = expiry; hwgroup->timer.expires = jiffies + timeout; + hwgroup->req_gen_timer = hwgroup->req_gen; add_timer(&hwgroup->timer); } @@ -947,6 +930,7 @@ void ide_execute_command(ide_drive_t *drive, task_ioreg_t cmd, ide_handler_t *ha hwgroup->handler = handler; hwgroup->expiry = expiry; hwgroup->timer.expires = jiffies + timeout; + hwgroup->req_gen_timer = hwgroup->req_gen; add_timer(&hwgroup->timer); hwif->OUTBSYNC(drive, cmd, IDE_COMMAND_REG); /* Drive takes 400nS to respond, we must avoid the IRQ being @@ -1066,12 +1050,12 @@ static void check_dma_crc(ide_drive_t *drive) { #ifdef CONFIG_BLK_DEV_IDEDMA if (drive->crc_count) { - (void) HWIF(drive)->ide_dma_off_quietly(drive); + drive->hwif->dma_off_quietly(drive); ide_set_xfer_rate(drive, ide_auto_reduce_xfer(drive)); if (drive->current_speed >= XFER_SW_DMA_0) (void) HWIF(drive)->ide_dma_on(drive); } else - (void)__ide_dma_off(drive); + ide_dma_off(drive); #endif } @@ -1112,6 +1096,9 @@ static void pre_reset(ide_drive_t *drive) if (HWIF(drive)->pre_reset != NULL) HWIF(drive)->pre_reset(drive); + if (drive->current_speed != 0xff) + drive->desired_speed = drive->current_speed; + drive->current_speed = 0xff; } /* diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c index 8237d89..6871931 100644 --- a/drivers/ide/ide-lib.c +++ b/drivers/ide/ide-lib.c @@ -205,6 +205,21 @@ int ide_dma_enable (ide_drive_t *drive) EXPORT_SYMBOL(ide_dma_enable); +int ide_use_fast_pio(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + if ((id->capability & 1) && drive->autodma) + return 1; + + if ((id->capability & 8) || (id->field_valid & 2)) + return 1; + + return 0; +} + +EXPORT_SYMBOL_GPL(ide_use_fast_pio); + /* * Standard (generic) timings for PIO modes, from ATA2 specification. * These timings are for access to the IDE data port register *only*. @@ -330,16 +345,16 @@ static int ide_scan_pio_blacklist (char *model) /** * ide_get_best_pio_mode - get PIO mode from drive - * @driver: drive to consider + * @drive: drive to consider * @mode_wanted: preferred mode - * @max_mode: highest allowed - * @d: pio data + * @max_mode: highest allowed mode + * @d: PIO data * * This routine returns the recommended PIO settings for a given drive, * based on the drive->id information and the ide_pio_blacklist[]. - * This is used by most chipset support modules when "auto-tuning". * - * Drive PIO mode auto selection + * Drive PIO mode is auto-selected if 255 is passed as mode_wanted. + * This is used by most chipset support modules when "auto-tuning". */ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode, ide_pio_data_t *d) @@ -349,15 +364,14 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode, ide_p int use_iordy = 0; struct hd_driveid* id = drive->id; int overridden = 0; - int blacklisted = 0; if (mode_wanted != 255) { pio_mode = mode_wanted; + use_iordy = (pio_mode > 2); } else if (!drive->id) { pio_mode = 0; } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) { overridden = 1; - blacklisted = 1; use_iordy = (pio_mode > 2); } else { pio_mode = id->tPIO; @@ -383,19 +397,12 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode, ide_p } } -#if 0 - if (drive->id->major_rev_num & 0x0004) printk("ATA-2 "); -#endif - /* * Conservative "downgrade" for all pre-ATA2 drives */ if (pio_mode && pio_mode < 4) { pio_mode--; overridden = 1; -#if 0 - use_iordy = (pio_mode > 2); -#endif if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time) cycle_time = 0; /* use standard timing */ } @@ -409,7 +416,6 @@ u8 ide_get_best_pio_mode (ide_drive_t *drive, u8 mode_wanted, u8 max_mode, ide_p d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time; d->use_iordy = use_iordy; d->overridden = overridden; - d->blacklisted = blacklisted; } return pio_mode; } @@ -462,8 +468,6 @@ int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) return -1; } -EXPORT_SYMBOL_GPL(ide_set_xfer_rate); - static void ide_dump_opcode(ide_drive_t *drive) { struct request *rq; diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c index 176bbc8..8f15c23 100644 --- a/drivers/ide/ide-probe.c +++ b/drivers/ide/ide-probe.c @@ -31,8 +31,6 @@ * valid after probe time even with noprobe */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/module.h> #include <linux/types.h> #include <linux/string.h> @@ -853,11 +851,11 @@ static void probe_hwif(ide_hwif_t *hwif) * things, if not checked and cleared. * PARANOIA!!! */ - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); #ifdef CONFIG_IDEDMA_ONLYDISK if (drive->media == ide_disk) #endif - hwif->ide_dma_check(drive); + ide_set_dma(drive); } } } diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c index afb71c6..a9e0b30 100644 --- a/drivers/ide/ide-proc.c +++ b/drivers/ide/ide-proc.c @@ -310,14 +310,12 @@ static int proc_ide_read_driver ide_driver_t *ide_drv; int len; - down_read(&dev->bus->subsys.rwsem); if (dev->driver) { ide_drv = container_of(dev->driver, ide_driver_t, gen_driver); len = sprintf(page, "%s version %s\n", dev->driver->name, ide_drv->version); } else len = sprintf(page, "ide-default version 0.9.newide\n"); - up_read(&dev->bus->subsys.rwsem); PROC_IDE_READ_RETURN(page,start,off,count,eof,len); } @@ -327,7 +325,6 @@ static int ide_replace_subdriver(ide_drive_t *drive, const char *driver) int ret = 1; int err; - down_write(&dev->bus->subsys.rwsem); device_release_driver(dev); /* FIXME: device can still be in use by previous driver */ strlcpy(drive->driver_req, driver, sizeof(drive->driver_req)); @@ -345,7 +342,6 @@ static int ide_replace_subdriver(ide_drive_t *drive, const char *driver) } if (dev->driver && !strcmp(dev->driver->name, driver)) ret = 0; - up_write(&dev->bus->subsys.rwsem); return ret; } diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c index c6eec04..4e59239 100644 --- a/drivers/ide/ide-tape.c +++ b/drivers/ide/ide-tape.c @@ -1970,7 +1970,7 @@ static ide_startstop_t idetape_pc_intr (ide_drive_t *drive) printk(KERN_ERR "ide-tape: The tape wants to issue more " "interrupts in DMA mode\n"); printk(KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n"); - (void)__ide_dma_off(drive); + ide_dma_off(drive); return ide_do_reset(drive); } /* Get the number of bytes to transfer on this interrupt. */ @@ -2176,7 +2176,7 @@ static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) { printk(KERN_WARNING "ide-tape: DMA disabled, " "reverting to PIO\n"); - (void)__ide_dma_off(drive); + ide_dma_off(drive); } if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) dma_ok = !hwif->dma_setup(drive); @@ -4792,15 +4792,10 @@ static int idetape_open(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; struct ide_tape_obj *tape; - ide_drive_t *drive; if (!(tape = ide_tape_get(disk))) return -ENXIO; - drive = tape->drive; - - drive->usage++; - return 0; } @@ -4808,9 +4803,6 @@ static int idetape_release(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; struct ide_tape_obj *tape = ide_tape_g(disk); - ide_drive_t *drive = tape->drive; - - drive->usage--; ide_tape_put(tape); diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c index c750f6c..ae5bf2b 100644 --- a/drivers/ide/ide.c +++ b/drivers/ide/ide.c @@ -126,8 +126,6 @@ #define REVISION "Revision: 7.00alpha2" #define VERSION "Id: ide.c 7.00a2 20020906" -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #define _IDE_C /* Tell ide.h it's really us */ #include <linux/module.h> @@ -179,11 +177,7 @@ DECLARE_MUTEX(ide_cfg_sem); static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ #endif -#ifdef CONFIG_IDEDMA_AUTO int noautodma = 0; -#else -int noautodma = 1; -#endif EXPORT_SYMBOL(noautodma); @@ -389,9 +383,8 @@ int ide_hwif_request_regions(ide_hwif_t *hwif) unsigned long addr; unsigned int i; - if (hwif->mmio == 2) + if (hwif->mmio) return 0; - BUG_ON(hwif->mmio == 1); addr = hwif->io_ports[IDE_CONTROL_OFFSET]; if (addr && !hwif_request_region(hwif, addr, 1)) goto control_region_busy; @@ -438,7 +431,7 @@ void ide_hwif_release_regions(ide_hwif_t *hwif) { u32 i = 0; - if (hwif->mmio == 2) + if (hwif->mmio) return; if (hwif->io_ports[IDE_CONTROL_OFFSET]) release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1); @@ -507,23 +500,22 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) hwif->ide_dma_end = tmp_hwif->ide_dma_end; hwif->ide_dma_check = tmp_hwif->ide_dma_check; hwif->ide_dma_on = tmp_hwif->ide_dma_on; - hwif->ide_dma_off_quietly = tmp_hwif->ide_dma_off_quietly; + hwif->dma_off_quietly = tmp_hwif->dma_off_quietly; hwif->ide_dma_test_irq = tmp_hwif->ide_dma_test_irq; - hwif->ide_dma_host_on = tmp_hwif->ide_dma_host_on; - hwif->ide_dma_host_off = tmp_hwif->ide_dma_host_off; + hwif->ide_dma_clear_irq = tmp_hwif->ide_dma_clear_irq; + hwif->dma_host_on = tmp_hwif->dma_host_on; + hwif->dma_host_off = tmp_hwif->dma_host_off; hwif->ide_dma_lostirq = tmp_hwif->ide_dma_lostirq; hwif->ide_dma_timeout = tmp_hwif->ide_dma_timeout; hwif->OUTB = tmp_hwif->OUTB; hwif->OUTBSYNC = tmp_hwif->OUTBSYNC; hwif->OUTW = tmp_hwif->OUTW; - hwif->OUTL = tmp_hwif->OUTL; hwif->OUTSW = tmp_hwif->OUTSW; hwif->OUTSL = tmp_hwif->OUTSL; hwif->INB = tmp_hwif->INB; hwif->INW = tmp_hwif->INW; - hwif->INL = tmp_hwif->INL; hwif->INSW = tmp_hwif->INSW; hwif->INSL = tmp_hwif->INSL; @@ -551,7 +543,6 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif) hwif->extra_ports = tmp_hwif->extra_ports; hwif->autodma = tmp_hwif->autodma; hwif->udma_four = tmp_hwif->udma_four; - hwif->no_dsc = tmp_hwif->no_dsc; hwif->hwif_data = tmp_hwif->hwif_data; } @@ -1133,18 +1124,40 @@ static int set_io_32bit(ide_drive_t *drive, int arg) static int set_using_dma (ide_drive_t *drive, int arg) { #ifdef CONFIG_BLK_DEV_IDEDMA + ide_hwif_t *hwif = drive->hwif; + int err = -EPERM; + if (!drive->id || !(drive->id->capability & 1)) - return -EPERM; - if (HWIF(drive)->ide_dma_check == NULL) - return -EPERM; + goto out; + + if (hwif->ide_dma_check == NULL) + goto out; + + err = -EBUSY; + if (ide_spin_wait_hwgroup(drive)) + goto out; + /* + * set ->busy flag, unlock and let it ride + */ + hwif->hwgroup->busy = 1; + spin_unlock_irq(&ide_lock); + + err = 0; + if (arg) { - if (HWIF(drive)->ide_dma_check(drive)) return -EIO; - if (HWIF(drive)->ide_dma_on(drive)) return -EIO; - } else { - if (__ide_dma_off(drive)) - return -EIO; - } - return 0; + if (ide_set_dma(drive) || hwif->ide_dma_on(drive)) + err = -EIO; + } else + ide_dma_off(drive); + + /* + * lock, clear ->busy flag and unlock before leaving + */ + spin_lock_irq(&ide_lock); + hwif->hwgroup->busy = 0; + spin_unlock_irq(&ide_lock); +out: + return err; #else return -EPERM; #endif @@ -1490,23 +1503,23 @@ static int __init match_parm (char *s, const char *keywords[], int vals[], int m } #ifdef CONFIG_BLK_DEV_ALI14XX -static int __initdata probe_ali14xx; +extern int probe_ali14xx; extern int ali14xx_init(void); #endif #ifdef CONFIG_BLK_DEV_UMC8672 -static int __initdata probe_umc8672; +extern int probe_umc8672; extern int umc8672_init(void); #endif #ifdef CONFIG_BLK_DEV_DTC2278 -static int __initdata probe_dtc2278; +extern int probe_dtc2278; extern int dtc2278_init(void); #endif #ifdef CONFIG_BLK_DEV_HT6560B -static int __initdata probe_ht6560b; +extern int probe_ht6560b; extern int ht6560b_init(void); #endif #ifdef CONFIG_BLK_DEV_QD65XX -static int __initdata probe_qd65xx; +extern int probe_qd65xx; extern int qd65xx_init(void); #endif @@ -1584,7 +1597,7 @@ static int __init ide_setup(char *s) */ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { const char *hd_words[] = { - "none", "noprobe", "nowerr", "cdrom", "serialize", + "none", "noprobe", "nowerr", "cdrom", "minus5", "autotune", "noautotune", "minus8", "swapdata", "bswap", "noflush", "remap", "remap63", "scsi", NULL }; unit = s[2] - 'a'; @@ -1612,9 +1625,6 @@ static int __init ide_setup(char *s) drive->ready_stat = 0; hwif->noprobe = 0; goto done; - case -5: /* "serialize" */ - printk(" -- USE \"ide%d=serialize\" INSTEAD", hw); - goto do_serialize; case -6: /* "autotune" */ drive->autotune = IDE_TUNE_AUTO; goto obsolete_option; @@ -1675,7 +1685,7 @@ static int __init ide_setup(char *s) * (-8, -9, -10) are reserved to ease the hardcoding. */ static const char *ide_words[] = { - "noprobe", "serialize", "autotune", "noautotune", + "noprobe", "serialize", "minus3", "minus4", "reset", "dma", "ata66", "minus8", "minus9", "minus10", "four", "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL }; @@ -1746,12 +1756,17 @@ static int __init ide_setup(char *s) hwif->chipset = mate->chipset = ide_4drives; mate->irq = hwif->irq; memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); - goto do_serialize; + hwif->mate = mate; + mate->mate = hwif; + hwif->serialized = mate->serialized = 1; + goto obsolete_option; } #endif /* CONFIG_BLK_DEV_4DRIVES */ case -10: /* minus10 */ case -9: /* minus9 */ case -8: /* minus8 */ + case -4: + case -3: goto bad_option; case -7: /* ata66 */ #ifdef CONFIG_BLK_DEV_IDEPCI @@ -1766,16 +1781,7 @@ static int __init ide_setup(char *s) case -5: /* "reset" */ hwif->reset = 1; goto obsolete_option; - case -4: /* "noautotune" */ - hwif->drives[0].autotune = IDE_TUNE_NOAUTO; - hwif->drives[1].autotune = IDE_TUNE_NOAUTO; - goto obsolete_option; - case -3: /* "autotune" */ - hwif->drives[0].autotune = IDE_TUNE_AUTO; - hwif->drives[1].autotune = IDE_TUNE_AUTO; - goto obsolete_option; case -2: /* "serialize" */ - do_serialize: hwif->mate = &ide_hwifs[hw^1]; hwif->mate->mate = hwif; hwif->serialized = hwif->mate->serialized = 1; @@ -1844,8 +1850,8 @@ static void __init probe_for_hwifs (void) #endif /* CONFIG_BLK_DEV_CMD640 */ #ifdef CONFIG_BLK_DEV_IDE_PMAC { - extern void pmac_ide_probe(void); - pmac_ide_probe(); + extern int pmac_ide_probe(void); + (void)pmac_ide_probe(); } #endif /* CONFIG_BLK_DEV_IDE_PMAC */ #ifdef CONFIG_BLK_DEV_GAYLE @@ -1956,6 +1962,8 @@ static char *media_string(ide_drive_t *drive) return "tape"; case ide_floppy: return "floppy"; + case ide_optical: + return "optical"; default: return "UNKNOWN"; } diff --git a/drivers/ide/legacy/ali14xx.c b/drivers/ide/legacy/ali14xx.c index 9c54446..91961aa 100644 --- a/drivers/ide/legacy/ali14xx.c +++ b/drivers/ide/legacy/ali14xx.c @@ -37,8 +37,6 @@ * mode 4 for a while now with no trouble.) -Derek */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -230,9 +228,17 @@ static int __init ali14xx_probe(void) return 0; } +int probe_ali14xx = 0; + +module_param_named(probe, probe_ali14xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for ALI M14xx chipsets"); + /* Can be called directly from ide.c. */ int __init ali14xx_init(void) { + if (probe_ali14xx == 0) + goto out; + /* auto-detect IDE controller port */ if (findPort()) { if (ali14xx_probe()) @@ -240,6 +246,7 @@ int __init ali14xx_init(void) return 0; } printk(KERN_ERR "ali14xx: not found.\n"); +out: return -ENODEV; } diff --git a/drivers/ide/legacy/buddha.c b/drivers/ide/legacy/buddha.c index 0391a31..1ed224a 100644 --- a/drivers/ide/legacy/buddha.c +++ b/drivers/ide/legacy/buddha.c @@ -215,7 +215,7 @@ fail_base2: index = ide_register_hw(&hw, &hwif); if (index != -1) { - hwif->mmio = 2; + hwif->mmio = 1; printk("ide%d: ", index); switch(type) { case BOARD_BUDDHA: diff --git a/drivers/ide/legacy/dtc2278.c b/drivers/ide/legacy/dtc2278.c index 3b1d33b..0219ffa 100644 --- a/drivers/ide/legacy/dtc2278.c +++ b/drivers/ide/legacy/dtc2278.c @@ -4,8 +4,6 @@ * Copyright (C) 1996 Linus Torvalds & author (see below) */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -94,7 +92,7 @@ static void tune_dtc2278 (ide_drive_t *drive, u8 pio) HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1; } -static int __init probe_dtc2278(void) +static int __init dtc2278_probe(void) { unsigned long flags; ide_hwif_t *hwif, *mate; @@ -145,10 +143,18 @@ static int __init probe_dtc2278(void) return 0; } +int probe_dtc2278 = 0; + +module_param_named(probe, probe_dtc2278, bool, 0); +MODULE_PARM_DESC(probe, "probe for DTC2278xx chipsets"); + /* Can be called directly from ide.c. */ int __init dtc2278_init(void) { - if (probe_dtc2278()) { + if (probe_dtc2278 == 0) + return -ENODEV; + + if (dtc2278_probe()) { printk(KERN_ERR "dtc2278: ide interfaces already in use!\n"); return -EBUSY; } diff --git a/drivers/ide/legacy/gayle.c b/drivers/ide/legacy/gayle.c index 64d4261..dcfadbb 100644 --- a/drivers/ide/legacy/gayle.c +++ b/drivers/ide/legacy/gayle.c @@ -167,7 +167,7 @@ found: index = ide_register_hw(&hw, &hwif); if (index != -1) { - hwif->mmio = 2; + hwif->mmio = 1; switch (i) { case 0: printk("ide%d: Gayle IDE interface (A%d style)\n", index, diff --git a/drivers/ide/legacy/ht6560b.c b/drivers/ide/legacy/ht6560b.c index c48e87e..a283264 100644 --- a/drivers/ide/legacy/ht6560b.c +++ b/drivers/ide/legacy/ht6560b.c @@ -36,8 +36,6 @@ #define HT6560B_VERSION "v0.07" -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -143,16 +141,16 @@ static void ht6560b_selectproc (ide_drive_t *drive) current_timing = timing; if (drive->media != ide_disk || !drive->present) select |= HT_PREFETCH_MODE; - (void) HWIF(drive)->INB(HT_CONFIG_PORT); - (void) HWIF(drive)->INB(HT_CONFIG_PORT); - (void) HWIF(drive)->INB(HT_CONFIG_PORT); - (void) HWIF(drive)->INB(HT_CONFIG_PORT); - HWIF(drive)->OUTB(select, HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + (void)inb(HT_CONFIG_PORT); + outb(select, HT_CONFIG_PORT); /* * Set timing for this drive: */ - HWIF(drive)->OUTB(timing, IDE_SELECT_REG); - (void) HWIF(drive)->INB(IDE_STATUS_REG); + outb(timing, IDE_SELECT_REG); + (void)inb(IDE_STATUS_REG); #ifdef DEBUG printk("ht6560b: %s: select=%#x timing=%#x\n", drive->name, select, timing); @@ -303,12 +301,20 @@ static void tune_ht6560b (ide_drive_t *drive, u8 pio) #endif } +int probe_ht6560b = 0; + +module_param_named(probe, probe_ht6560b, bool, 0); +MODULE_PARM_DESC(probe, "probe for HT6560B chipset"); + /* Can be called directly from ide.c. */ int __init ht6560b_init(void) { ide_hwif_t *hwif, *mate; int t; + if (probe_ht6560b == 0) + return -ENODEV; + hwif = &ide_hwifs[0]; mate = &ide_hwifs[1]; diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c index a5023cd..b08c37c 100644 --- a/drivers/ide/legacy/ide-cs.c +++ b/drivers/ide/legacy/ide-cs.c @@ -359,14 +359,17 @@ void ide_release(struct pcmcia_device *link) static struct pcmcia_device_id ide_ids[] = { PCMCIA_DEVICE_FUNC_ID(4), PCMCIA_DEVICE_MANF_CARD(0x0007, 0x0000), /* Hitachi */ + PCMCIA_DEVICE_MANF_CARD(0x000a, 0x0000), /* I-O Data CFA */ + PCMCIA_DEVICE_MANF_CARD(0x001c, 0x0001), /* Mitsubishi CFA */ PCMCIA_DEVICE_MANF_CARD(0x0032, 0x0704), - PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), + PCMCIA_DEVICE_MANF_CARD(0x0045, 0x0401), /* SanDisk CFA */ PCMCIA_DEVICE_MANF_CARD(0x0098, 0x0000), /* Toshiba */ PCMCIA_DEVICE_MANF_CARD(0x00a4, 0x002d), PCMCIA_DEVICE_MANF_CARD(0x00ce, 0x0000), /* Samsung */ PCMCIA_DEVICE_MANF_CARD(0x0319, 0x0000), /* Hitachi */ PCMCIA_DEVICE_MANF_CARD(0x2080, 0x0001), - PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar */ + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0100), /* Viking CFA */ + PCMCIA_DEVICE_MANF_CARD(0x4e01, 0x0200), /* Lexar, Viking CFA */ PCMCIA_DEVICE_PROD_ID123("Caravelle", "PSC-IDE ", "PSC000", 0x8c36137c, 0xd0693ab8, 0x2768a9f0), PCMCIA_DEVICE_PROD_ID123("CDROM", "IDE", "MCD-601p", 0x1b9179ca, 0xede88951, 0x0d902f74), PCMCIA_DEVICE_PROD_ID123("PCMCIA", "IDE CARD", "F1", 0x281f1c5d, 0x1907960c, 0xf7fde8b9), diff --git a/drivers/ide/legacy/macide.c b/drivers/ide/legacy/macide.c index b1730d7..4c0079a 100644 --- a/drivers/ide/legacy/macide.c +++ b/drivers/ide/legacy/macide.c @@ -141,7 +141,7 @@ void macide_init(void) } if (index != -1) { - hwif->mmio = 2; + hwif->mmio = 1; if (macintosh_config->ide_type == MAC_IDE_QUADRA) printk(KERN_INFO "ide%d: Macintosh Quadra IDE interface\n", index); else if (macintosh_config->ide_type == MAC_IDE_PB) diff --git a/drivers/ide/legacy/q40ide.c b/drivers/ide/legacy/q40ide.c index 434a94f..74f0812 100644 --- a/drivers/ide/legacy/q40ide.c +++ b/drivers/ide/legacy/q40ide.c @@ -145,7 +145,7 @@ void q40ide_init(void) index = ide_register_hw(&hw, &hwif); // **FIXME** if (index != -1) - hwif->mmio = 2; + hwif->mmio = 1; } } diff --git a/drivers/ide/legacy/qd65xx.c b/drivers/ide/legacy/qd65xx.c index d3c3bc2..2fb8f50 100644 --- a/drivers/ide/legacy/qd65xx.c +++ b/drivers/ide/legacy/qd65xx.c @@ -16,8 +16,8 @@ * Please set local bus speed using kernel parameter idebus * for example, "idebus=33" stands for 33Mhz VLbus * To activate controller support, use "ide0=qd65xx" - * To enable tuning, use "ide0=autotune" - * To enable second channel tuning (qd6580 only), use "ide1=autotune" + * To enable tuning, use "hda=autotune hdb=autotune" + * To enable 2nd channel tuning (qd6580 only), use "hdc=autotune hdd=autotune" */ /* @@ -25,8 +25,6 @@ * Samuel Thibault <samuel.thibault@fnac.net> */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -490,9 +488,17 @@ static int __init qd_probe(int base) return 1; } +int probe_qd65xx = 0; + +module_param_named(probe, probe_qd65xx, bool, 0); +MODULE_PARM_DESC(probe, "probe for QD65xx chipsets"); + /* Can be called directly from ide.c. */ int __init qd65xx_init(void) { + if (probe_qd65xx == 0) + return -ENODEV; + if (qd_probe(0x30)) qd_probe(0xb0); if (ide_hwifs[0].chipset != ide_qd65xx && diff --git a/drivers/ide/legacy/umc8672.c b/drivers/ide/legacy/umc8672.c index 6e2c58c..ca79744 100644 --- a/drivers/ide/legacy/umc8672.c +++ b/drivers/ide/legacy/umc8672.c @@ -165,12 +165,21 @@ static int __init umc8672_probe(void) return 0; } +int probe_umc8672 = 0; + +module_param_named(probe, probe_umc8672, bool, 0); +MODULE_PARM_DESC(probe, "probe for UMC8672 chipset"); + /* Can be called directly from ide.c. */ int __init umc8672_init(void) { - if (umc8672_probe()) - return -ENODEV; - return 0; + if (probe_umc8672 == 0) + goto out; + + if (umc8672_probe() == 0) + return 0;; +out: + return -ENODEV;; } #ifdef MODULE diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c index c7854ea..d54d9fe 100644 --- a/drivers/ide/mips/au1xxx-ide.c +++ b/drivers/ide/mips/au1xxx-ide.c @@ -29,8 +29,6 @@ * Note: for more information, please refer "AMD Alchemy Au1200/Au1550 IDE * Interface and Linux Device Driver" Application Note. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/types.h> #include <linux/module.h> #include <linux/kernel.h> @@ -181,12 +179,6 @@ static int auide_tune_chipset (ide_drive_t *drive, u8 speed) { int mem_sttime; int mem_stcfg; - unsigned long mode; - -#ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - if (ide_use_dma(drive)) - mode = ide_dma_speed(drive, 0); -#endif mem_sttime = 0; mem_stcfg = au_readl(MEM_STCFG2); @@ -195,7 +187,7 @@ static int auide_tune_chipset (ide_drive_t *drive, u8 speed) auide_tune_drive(drive, speed - XFER_PIO_0); return 0; } - + switch(speed) { #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA case XFER_MW_DMA_2: @@ -207,7 +199,6 @@ static int auide_tune_chipset (ide_drive_t *drive, u8 speed) mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA2_TCSOE | SBC_IDE_MDMA2_TOECS; - mode = XFER_MW_DMA_2; break; case XFER_MW_DMA_1: mem_sttime = SBC_IDE_TIMING(MDMA1); @@ -218,7 +209,6 @@ static int auide_tune_chipset (ide_drive_t *drive, u8 speed) mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA1_TCSOE | SBC_IDE_MDMA1_TOECS; - mode = XFER_MW_DMA_1; break; case XFER_MW_DMA_0: mem_sttime = SBC_IDE_TIMING(MDMA0); @@ -229,14 +219,13 @@ static int auide_tune_chipset (ide_drive_t *drive, u8 speed) mem_stcfg &= ~TOECS_MASK; mem_stcfg |= SBC_IDE_MDMA0_TCSOE | SBC_IDE_MDMA0_TOECS; - mode = XFER_MW_DMA_0; break; #endif default: return 1; } - - if (ide_config_drive_speed(drive, mode)) + + if (ide_config_drive_speed(drive, speed)) return 1; au_writel(mem_sttime,MEM_STTIME2); @@ -423,9 +412,9 @@ static int auide_dma_check(ide_drive_t *drive) speed = ide_find_best_mode(drive, XFER_PIO | XFER_MWDMA); if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) - return HWIF(drive)->ide_dma_on(drive); + return 0; - return HWIF(drive)->ide_dma_off_quietly(drive); + return -1; } static int auide_dma_test_irq(ide_drive_t *drive) @@ -447,27 +436,24 @@ static int auide_dma_test_irq(ide_drive_t *drive) return 0; } -static int auide_dma_host_on(ide_drive_t *drive) +static void auide_dma_host_on(ide_drive_t *drive) { - return 0; } static int auide_dma_on(ide_drive_t *drive) { drive->using_dma = 1; - return auide_dma_host_on(drive); -} + return 0; +} -static int auide_dma_host_off(ide_drive_t *drive) +static void auide_dma_host_off(ide_drive_t *drive) { - return 0; } -static int auide_dma_off_quietly(ide_drive_t *drive) +static void auide_dma_off_quietly(ide_drive_t *drive) { drive->using_dma = 0; - return auide_dma_host_off(drive); } static int auide_dma_lostirq(ide_drive_t *drive) @@ -653,6 +639,7 @@ static int au_ide_probe(struct device *dev) _auide_hwif *ahwif = &auide_hwif; ide_hwif_t *hwif; struct resource *res; + hw_regs_t *hw; int ret = 0; #if defined(CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA) @@ -695,7 +682,7 @@ static int au_ide_probe(struct device *dev) /* FIXME: This might possibly break PCMCIA IDE devices */ hwif = &ide_hwifs[pdev->id]; - hw_regs_t *hw = &hwif->hw; + hw = &hwif->hw; hwif->irq = hw->irq = ahwif->irq; hwif->chipset = ide_au1xxx; @@ -717,7 +704,8 @@ static int au_ide_probe(struct device *dev) /* hold should be on in all cases */ hwif->hold = 1; - hwif->mmio = 2; + + hwif->mmio = 1; /* If the user has selected DDMA assisted copies, then set up a few local I/O function entry points @@ -732,7 +720,7 @@ static int au_ide_probe(struct device *dev) hwif->speedproc = &auide_tune_chipset; #ifdef CONFIG_BLK_DEV_IDE_AU1XXX_MDMA2_DBDMA - hwif->ide_dma_off_quietly = &auide_dma_off_quietly; + hwif->dma_off_quietly = &auide_dma_off_quietly; hwif->ide_dma_timeout = &auide_dma_timeout; hwif->ide_dma_check = &auide_dma_check; @@ -741,8 +729,8 @@ static int au_ide_probe(struct device *dev) hwif->ide_dma_end = &auide_dma_end; hwif->dma_setup = &auide_dma_setup; hwif->ide_dma_test_irq = &auide_dma_test_irq; - hwif->ide_dma_host_off = &auide_dma_host_off; - hwif->ide_dma_host_on = &auide_dma_host_on; + hwif->dma_host_off = &auide_dma_host_off; + hwif->dma_host_on = &auide_dma_host_on; hwif->ide_dma_lostirq = &auide_dma_lostirq; hwif->ide_dma_on = &auide_dma_on; diff --git a/drivers/ide/mips/swarm.c b/drivers/ide/mips/swarm.c index 09c9e79..81fa068 100644 --- a/drivers/ide/mips/swarm.c +++ b/drivers/ide/mips/swarm.c @@ -115,7 +115,7 @@ static int __devinit swarm_ide_probe(struct device *dev) /* Setup MMIO ops. */ default_hwif_mmiops(hwif); /* Prevent resource map manipulation. */ - hwif->mmio = 2; + hwif->mmio = 1; hwif->noprobe = 0; for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile index 6591ff4..95d1ea8 100644 --- a/drivers/ide/pci/Makefile +++ b/drivers/ide/pci/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o obj-$(CONFIG_BLK_DEV_ATIIXP) += atiixp.o +obj-$(CONFIG_BLK_DEV_CELLEB) += scc_pata.o obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o obj-$(CONFIG_BLK_DEV_CS5520) += cs5520.o obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c index d261bfb..990eafe 100644 --- a/drivers/ide/pci/aec62xx.c +++ b/drivers/ide/pci/aec62xx.c @@ -94,9 +94,9 @@ static u8 aec62xx_ratemask (ide_drive_t *drive) switch(hwif->pci_dev->device) { case PCI_DEVICE_ID_ARTOP_ATP865: case PCI_DEVICE_ID_ARTOP_ATP865R: - mode = (hwif->INB(((hwif->channel) ? - hwif->mate->dma_status : - hwif->dma_status)) & 0x10) ? 4 : 3; + mode = (inb(hwif->channel ? + hwif->mate->dma_status : + hwif->dma_status) & 0x10) ? 4 : 3; break; case PCI_DEVICE_ID_ARTOP_ATP860: case PCI_DEVICE_ID_ARTOP_ATP860R: @@ -209,25 +209,13 @@ static void aec62xx_tune_drive (ide_drive_t *drive, u8 pio) static int aec62xx_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) aec62xx_tune_drive(drive, 5); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + + return -1; } static int aec62xx_irq_timeout (ide_drive_t *drive) @@ -286,10 +274,8 @@ static void __devinit init_hwif_aec62xx(ide_hwif_t *hwif) hwif->tuneproc = &aec62xx_tune_drive; hwif->speedproc = &aec62xx_tune_chipset; - if (hwif->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + if (hwif->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) hwif->serialized = hwif->channel; - hwif->no_dsc = 1; - } if (hwif->mate) hwif->mate->serialized = hwif->serialized; diff --git a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c index 68df77e..83e0aa6 100644 --- a/drivers/ide/pci/alim15x3.c +++ b/drivers/ide/pci/alim15x3.c @@ -1,5 +1,5 @@ /* - * linux/drivers/ide/pci/alim15x3.c Version 0.17 2003/01/02 + * linux/drivers/ide/pci/alim15x3.c Version 0.21 2007/02/03 * * Copyright (C) 1998-2000 Michel Aubry, Maintainer * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer @@ -9,6 +9,7 @@ * May be copied or modified under the terms of the GNU General Public License * Copyright (C) 2002 Alan Cox <alan@redhat.com> * ALi (now ULi M5228) support by Clear Zhang <Clear.Zhang@ali.com.tw> + * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com> * * (U)DMA capable version of ali 1533/1543(C), 1535(D) * @@ -280,15 +281,17 @@ static int ali_get_info (char *buffer, char **addr, off_t offset, int count) #endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ /** - * ali15x3_tune_drive - set up a drive + * ali15x3_tune_pio - set up chipset for PIO mode * @drive: drive to tune - * @pio: unused + * @pio: desired mode * - * Select the best PIO timing for the drive in question. Then - * program the controller for this drive set up + * Select the best PIO mode for the drive in question. + * Then program the controller for this mode. + * + * Returns the PIO mode programmed. */ -static void ali15x3_tune_drive (ide_drive_t *drive, u8 pio) +static u8 ali15x3_tune_pio (ide_drive_t *drive, u8 pio) { ide_pio_data_t d; ide_hwif_t *hwif = HWIF(drive); @@ -356,6 +359,22 @@ static void ali15x3_tune_drive (ide_drive_t *drive, u8 pio) * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard) */ + return pio; +} + +/** + * ali15x3_tune_drive - set up drive for PIO mode + * @drive: drive to tune + * @pio: desired mode + * + * Program the controller with the best PIO timing for the given drive. + * Then set up the drive itself. + */ + +static void ali15x3_tune_drive (ide_drive_t *drive, u8 pio) +{ + pio = ali15x3_tune_pio(drive, pio); + (void) ide_config_drive_speed(drive, XFER_PIO_0 + pio); } /** @@ -430,7 +449,7 @@ static u8 ali15x3_ratemask (ide_drive_t *drive) } /** - * ali15x3_tune_chipset - set up chiset for new speed + * ali15x3_tune_chipset - set up chipset/drive for new speed * @drive: drive to configure for * @xferspeed: desired speed * @@ -461,7 +480,7 @@ static int ali15x3_tune_chipset (ide_drive_t *drive, u8 xferspeed) pci_write_config_byte(dev, m5229_udma, tmpbyte); if (speed < XFER_SW_DMA_0) - ali15x3_tune_drive(drive, speed); + (void) ali15x3_tune_pio(drive, speed - XFER_PIO_0); } else { pci_read_config_byte(dev, m5229_udma, &tmpbyte); tmpbyte &= (0x0f << ((1-unit) << 2)); @@ -507,17 +526,15 @@ static int config_chipset_for_dma (ide_drive_t *drive) * * Configure a drive for DMA operation. If DMA is not possible we * drop the drive into PIO mode instead. - * - * FIXME: exactly what are we trying to return here */ - + static int ali15x3_config_drive_for_dma(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); struct hd_driveid *id = drive->id; if ((m5229_revision<=0x20) && (drive->media!=ide_disk)) - return hwif->ide_dma_off_quietly(drive); + goto no_dma_set; drive->init_speed = 0; @@ -552,9 +569,10 @@ try_dma_modes: ata_pio: hwif->tuneproc(drive, 255); no_dma_set: - return hwif->ide_dma_off_quietly(drive); + return -1; } - return hwif->ide_dma_on(drive); + + return 0; } /** @@ -852,8 +870,8 @@ static void __devinit init_dma_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) { if (m5229_revision < 0x20) return; - if (!(hwif->channel)) - hwif->OUTB(hwif->INB(dmabase+2) & 0x60, dmabase+2); + if (!hwif->channel) + outb(inb(dmabase + 2) & 0x60, dmabase + 2); ide_setup_dma(hwif, dmabase, 8); } diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c index a433699..7989bdd8 100644 --- a/drivers/ide/pci/amd74xx.c +++ b/drivers/ide/pci/amd74xx.c @@ -304,8 +304,9 @@ static int amd74xx_ide_dma_check(ide_drive_t *drive) amd_set_drive(drive, speed); if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) - return HWIF(drive)->ide_dma_on(drive); - return HWIF(drive)->ide_dma_off_quietly(drive); + return 0; + + return -1; } /* diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c index 982ac31..2d48af3 100644 --- a/drivers/ide/pci/atiixp.c +++ b/drivers/ide/pci/atiixp.c @@ -101,7 +101,7 @@ static u8 atiixp_dma_2_pio(u8 xfer_rate) { } } -static int atiixp_ide_dma_host_on(ide_drive_t *drive) +static void atiixp_dma_host_on(ide_drive_t *drive) { struct pci_dev *dev = drive->hwif->pci_dev; unsigned long flags; @@ -118,10 +118,10 @@ static int atiixp_ide_dma_host_on(ide_drive_t *drive) spin_unlock_irqrestore(&atiixp_lock, flags); - return __ide_dma_host_on(drive); + ide_dma_host_on(drive); } -static int atiixp_ide_dma_host_off(ide_drive_t *drive) +static void atiixp_dma_host_off(ide_drive_t *drive) { struct pci_dev *dev = drive->hwif->pci_dev; unsigned long flags; @@ -135,7 +135,7 @@ static int atiixp_ide_dma_host_off(ide_drive_t *drive) spin_unlock_irqrestore(&atiixp_lock, flags); - return __ide_dma_host_off(drive); + ide_dma_host_off(drive); } /** @@ -235,11 +235,8 @@ static int atiixp_config_drive_for_dma(ide_drive_t *drive) { u8 speed = ide_dma_speed(drive, atiixp_ratemask(drive)); - /* If no DMA speed was available then disable DMA and use PIO. */ - if (!speed) { - u8 tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); - speed = atiixp_dma_2_pio(XFER_PIO_0 + tspeed) + XFER_PIO_0; - } + if (!speed) + return 0; (void) atiixp_speedproc(drive, speed); return ide_dma_enable(drive); @@ -255,30 +252,20 @@ static int atiixp_config_drive_for_dma(ide_drive_t *drive) static int atiixp_dma_check(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; u8 tspeed, speed; drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (atiixp_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && atiixp_config_drive_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) { tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); speed = atiixp_dma_2_pio(XFER_PIO_0 + tspeed) + XFER_PIO_0; - hwif->speedproc(drive, speed); - return hwif->ide_dma_off_quietly(drive); + atiixp_speedproc(drive, speed); } - /* IORDY not supported */ - return 0; + + return -1; } /** @@ -318,8 +305,8 @@ static void __devinit init_hwif_atiixp(ide_hwif_t *hwif) else hwif->udma_four = 0; - hwif->ide_dma_host_on = &atiixp_ide_dma_host_on; - hwif->ide_dma_host_off = &atiixp_ide_dma_host_off; + hwif->dma_host_on = &atiixp_dma_host_on; + hwif->dma_host_off = &atiixp_dma_host_off; hwif->ide_dma_check = &atiixp_dma_check; if (!noautodma) hwif->autodma = 1; diff --git a/drivers/ide/pci/cmd640.c b/drivers/ide/pci/cmd640.c index 61b5f9c..dc43f00 100644 --- a/drivers/ide/pci/cmd640.c +++ b/drivers/ide/pci/cmd640.c @@ -98,7 +98,6 @@ * (patch courtesy of Zoltan Hidvegi) */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #define CMD640_PREFETCH_MASKS 1 //#define CMD640_DUMP_REGS diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c index aee947e..561197f 100644 --- a/drivers/ide/pci/cmd64x.c +++ b/drivers/ide/pci/cmd64x.c @@ -1,6 +1,6 @@ /* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16 * - * linux/drivers/ide/pci/cmd64x.c Version 1.30 Sept 10, 2002 + * linux/drivers/ide/pci/cmd64x.c Version 1.42 Feb 8, 2007 * * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. * Note, this driver is not used at all on other systems because @@ -12,6 +12,7 @@ * Copyright (C) 1998 David S. Miller (davem@redhat.com) * * Copyright (C) 1999-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com> */ #include <linux/module.h> @@ -188,6 +189,11 @@ static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) #endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */ +static u8 quantize_timing(int timing, int quant) +{ + return (timing + quant - 1) / quant; +} + /* * This routine writes the prepared setup/active/recovery counts * for a drive into the cmd646 chipset registers to active them. @@ -262,80 +268,63 @@ static void program_drive_counts (ide_drive_t *drive, int setup_count, int activ } /* - * Attempts to set the interface PIO mode. - * The preferred method of selecting PIO modes (e.g. mode 4) is - * "echo 'piomode:4' > /proc/ide/hdx/settings". Special cases are - * 8: prefetch off, 9: prefetch on, 255: auto-select best mode. - * Called with 255 at boot time. + * This routine selects drive's best PIO mode, calculates setup/active/recovery + * counts, and then writes them into the chipset registers. */ - -static void cmd64x_tuneproc (ide_drive_t *drive, u8 mode_wanted) +static u8 cmd64x_tune_pio (ide_drive_t *drive, u8 mode_wanted) { - int setup_time, active_time, recovery_time; - int clock_time, pio_mode, cycle_time; - u8 recovery_count2, cycle_count; - int setup_count, active_count, recovery_count; - int bus_speed = system_bus_clock(); - /*byte b;*/ - ide_pio_data_t d; - - switch (mode_wanted) { - case 8: /* set prefetch off */ - case 9: /* set prefetch on */ - mode_wanted &= 1; - /*set_prefetch_mode(index, mode_wanted);*/ - cmdprintk("%s: %sabled cmd640 prefetch\n", - drive->name, mode_wanted ? "en" : "dis"); - return; - } + int setup_time, active_time, cycle_time; + u8 cycle_count, setup_count, active_count, recovery_count; + u8 pio_mode; + int clock_time = 1000 / system_bus_clock(); + ide_pio_data_t pio; - mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d); - pio_mode = d.pio_mode; - cycle_time = d.cycle_time; + pio_mode = ide_get_best_pio_mode(drive, mode_wanted, 5, &pio); + cycle_time = pio.cycle_time; - /* - * I copied all this complicated stuff from cmd640.c and made a few - * minor changes. For now I am just going to pray that it is correct. - */ - if (pio_mode > 5) - pio_mode = 5; setup_time = ide_pio_timings[pio_mode].setup_time; active_time = ide_pio_timings[pio_mode].active_time; - recovery_time = cycle_time - (setup_time + active_time); - clock_time = 1000 / bus_speed; - cycle_count = (cycle_time + clock_time - 1) / clock_time; - - setup_count = (setup_time + clock_time - 1) / clock_time; - active_count = (active_time + clock_time - 1) / clock_time; + setup_count = quantize_timing( setup_time, clock_time); + cycle_count = quantize_timing( cycle_time, clock_time); + active_count = quantize_timing(active_time, clock_time); - recovery_count = (recovery_time + clock_time - 1) / clock_time; - recovery_count2 = cycle_count - (setup_count + active_count); - if (recovery_count2 > recovery_count) - recovery_count = recovery_count2; + recovery_count = cycle_count - active_count; + /* program_drive_counts() takes care of zero recovery cycles */ if (recovery_count > 16) { active_count += recovery_count - 16; recovery_count = 16; } if (active_count > 16) - active_count = 16; /* maximum allowed by cmd646 */ + active_count = 16; /* maximum allowed by cmd64x */ - /* - * In a perfect world, we might set the drive pio mode here - * (using WIN_SETFEATURE) before continuing. - * - * But we do not, because: - * 1) this is the wrong place to do it - * (proper is do_special() in ide.c) - * 2) in practice this is rarely, if ever, necessary - */ program_drive_counts (drive, setup_count, active_count, recovery_count); - cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, " + cmdprintk("%s: PIO mode wanted %d, selected %d (%dns)%s, " "clocks=%d/%d/%d\n", - drive->name, pio_mode, mode_wanted, cycle_time, - d.overridden ? " (overriding vendor mode)" : "", + drive->name, mode_wanted, pio_mode, cycle_time, + pio.overridden ? " (overriding vendor mode)" : "", setup_count, active_count, recovery_count); + + return pio_mode; +} + +/* + * Attempts to set drive's PIO mode. + * Special cases are 8: prefetch off, 9: prefetch on (both never worked), + * and 255: auto-select best mode (used at boot time). + */ +static void cmd64x_tune_drive (ide_drive_t *drive, u8 pio) +{ + /* + * Filter out the prefetch control values + * to prevent PIO5 from being programmed + */ + if (pio == 8 || pio == 9) + return; + + pio = cmd64x_tune_pio(drive, pio); + (void) ide_config_drive_speed(drive, XFER_PIO_0 + pio); } static u8 cmd64x_ratemask (ide_drive_t *drive) @@ -387,22 +376,6 @@ static u8 cmd64x_ratemask (ide_drive_t *drive) return mode; } -static void config_cmd64x_chipset_for_pio (ide_drive_t *drive, u8 set_speed) -{ - u8 speed = 0x00; - u8 set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); - - cmd64x_tuneproc(drive, set_pio); - speed = XFER_PIO_0 + set_pio; - if (set_speed) - (void) ide_config_drive_speed(drive, speed); -} - -static void config_chipset_for_pio (ide_drive_t *drive, u8 set_speed) -{ - config_cmd64x_chipset_for_pio(drive, set_speed); -} - static int cmd64x_tune_chipset (ide_drive_t *drive, u8 xferspeed) { ide_hwif_t *hwif = HWIF(drive); @@ -414,7 +387,7 @@ static int cmd64x_tune_chipset (ide_drive_t *drive, u8 xferspeed) u8 speed = ide_rate_filter(cmd64x_ratemask(drive), xferspeed); - if (speed > XFER_PIO_4) { + if (speed >= XFER_SW_DMA_0) { (void) pci_read_config_byte(dev, pciD, ®D); (void) pci_read_config_byte(dev, pciU, ®U); regD &= ~(unit ? 0x40 : 0x20); @@ -438,17 +411,20 @@ static int cmd64x_tune_chipset (ide_drive_t *drive, u8 xferspeed) case XFER_SW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; case XFER_SW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; case XFER_SW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; - case XFER_PIO_4: cmd64x_tuneproc(drive, 4); break; - case XFER_PIO_3: cmd64x_tuneproc(drive, 3); break; - case XFER_PIO_2: cmd64x_tuneproc(drive, 2); break; - case XFER_PIO_1: cmd64x_tuneproc(drive, 1); break; - case XFER_PIO_0: cmd64x_tuneproc(drive, 0); break; + case XFER_PIO_5: + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + (void) cmd64x_tune_pio(drive, speed - XFER_PIO_0); + break; default: return 1; } - if (speed > XFER_PIO_4) { + if (speed >= XFER_SW_DMA_0) { (void) pci_write_config_byte(dev, pciU, regU); regD |= (unit ? 0x40 : 0x20); (void) pci_write_config_byte(dev, pciD, regD); @@ -461,41 +437,24 @@ static int config_chipset_for_dma (ide_drive_t *drive) { u8 speed = ide_dma_speed(drive, cmd64x_ratemask(drive)); - config_chipset_for_pio(drive, !speed); - if (!speed) return 0; - if(ide_set_xfer_rate(drive, speed)) - return 0; - - if (!drive->init_speed) - drive->init_speed = speed; + if (cmd64x_tune_chipset(drive, speed)) + return 0; return ide_dma_enable(drive); } static int cmd64x_config_drive_for_dma (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - - if ((id != NULL) && ((id->capability & 1) != 0) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - goto fast_ata_pio; + if (ide_use_fast_pio(drive)) + cmd64x_tune_drive(drive, 255); - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - config_chipset_for_pio(drive, 1); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + return -1; } static int cmd64x_alt_dma_status (struct pci_dev *dev) @@ -518,13 +477,13 @@ static int cmd64x_ide_dma_end (ide_drive_t *drive) drive->waiting_for_dma = 0; /* read DMA command state */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); /* stop DMA */ - hwif->OUTB((dma_cmd & ~1), hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_command); /* get DMA status */ - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = inb(hwif->dma_status); /* clear the INTR & ERROR bits */ - hwif->OUTB(dma_stat|6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_status); if (cmd64x_alt_dma_status(dev)) { u8 dma_intr = 0; u8 dma_mask = (hwif->channel) ? ARTTIM23_INTR_CH1 : @@ -546,7 +505,7 @@ static int cmd64x_ide_dma_test_irq (ide_drive_t *drive) struct pci_dev *dev = hwif->pci_dev; u8 dma_alt_stat = 0, mask = (hwif->channel) ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_status); (void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat); #ifdef DEBUG @@ -576,13 +535,13 @@ static int cmd646_1_ide_dma_end (ide_drive_t *drive) drive->waiting_for_dma = 0; /* get DMA status */ - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = inb(hwif->dma_status); /* read DMA command state */ - dma_cmd = hwif->INB(hwif->dma_command); + dma_cmd = inb(hwif->dma_command); /* stop DMA */ - hwif->OUTB((dma_cmd & ~1), hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_command); /* clear the INTR & ERROR bits */ - hwif->OUTB(dma_stat|6, hwif->dma_status); + outb(dma_stat | 6, hwif->dma_status); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -694,14 +653,13 @@ static void __devinit init_hwif_cmd64x(ide_hwif_t *hwif) pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); class_rev &= 0xff; - hwif->tuneproc = &cmd64x_tuneproc; + hwif->tuneproc = &cmd64x_tune_drive; hwif->speedproc = &cmd64x_tune_chipset; - if (!hwif->dma_base) { - hwif->drives[0].autotune = 1; - hwif->drives[1].autotune = 1; + hwif->drives[0].autotune = hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) return; - } hwif->atapi_dma = 1; diff --git a/drivers/ide/pci/cs5520.c b/drivers/ide/pci/cs5520.c index ba6786a..400859a 100644 --- a/drivers/ide/pci/cs5520.c +++ b/drivers/ide/pci/cs5520.c @@ -132,12 +132,11 @@ static void cs5520_tune_drive(ide_drive_t *drive, u8 pio) static int cs5520_config_drive_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - /* Tune the drive for PIO modes up to PIO 4 */ cs5520_tune_drive(drive, 4); + /* Then tell the core to use DMA operations */ - return hwif->ide_dma_on(drive); + return 0; } /* diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c index 9bf5fdf..b2d7c13 100644 --- a/drivers/ide/pci/cs5530.c +++ b/drivers/ide/pci/cs5530.c @@ -81,8 +81,8 @@ static void cs5530_tuneproc (ide_drive_t *drive, u8 pio) /* pio=255 means "autot pio = ide_get_best_pio_mode(drive, pio, 4, NULL); if (!cs5530_set_xfer_mode(drive, modes[pio])) { - format = (hwif->INL(basereg+4) >> 31) & 1; - hwif->OUTL(cs5530_pio_timings[format][pio], + format = (inl(basereg + 4) >> 31) & 1; + outl(cs5530_pio_timings[format][pio], basereg+(drive->select.b.unit<<3)); } } @@ -103,16 +103,13 @@ static int cs5530_config_dma (ide_drive_t *drive) int unit = drive->select.b.unit; ide_drive_t *mate = &hwif->drives[unit^1]; struct hd_driveid *id = drive->id; - unsigned int reg, timings; + unsigned int reg, timings = 0; unsigned long basereg; /* * Default to DMA-off in case we run into trouble here. */ - hwif->ide_dma_off_quietly(drive); - /* turn off DMA while we fiddle */ - hwif->ide_dma_host_off(drive); - /* clear DMA_capable bit */ + hwif->dma_off_quietly(drive); /* * The CS5530 specifies that two drives sharing a cable cannot @@ -182,30 +179,24 @@ static int cs5530_config_dma (ide_drive_t *drive) case XFER_MW_DMA_1: timings = 0x00012121; break; case XFER_MW_DMA_2: timings = 0x00002020; break; default: - printk(KERN_ERR "%s: cs5530_config_dma: huh? mode=%02x\n", - drive->name, mode); - return 1; /* failure */ + BUG(); + break; } basereg = CS5530_BASEREG(hwif); - reg = hwif->INL(basereg+4); /* get drive0 config register */ + reg = inl(basereg + 4); /* get drive0 config register */ timings |= reg & 0x80000000; /* preserve PIO format bit */ if (unit == 0) { /* are we configuring drive0? */ - hwif->OUTL(timings, basereg+4); /* write drive0 config register */ + outl(timings, basereg + 4); /* write drive0 config register */ } else { if (timings & 0x00100000) reg |= 0x00100000; /* enable UDMA timings for both drives */ else reg &= ~0x00100000; /* disable UDMA timings for both drives */ - hwif->OUTL(reg, basereg+4); /* write drive0 config register */ - hwif->OUTL(timings, basereg+12); /* write drive1 config register */ + outl(reg, basereg + 4); /* write drive0 config register */ + outl(timings, basereg + 12); /* write drive1 config register */ } - (void) hwif->ide_dma_host_on(drive); - /* set DMA_capable bit */ - /* - * Finally, turn DMA on in software, and exit. - */ - return hwif->ide_dma_on(drive); /* success */ + return 0; /* success */ } /** @@ -321,17 +312,17 @@ static void __devinit init_hwif_cs5530 (ide_hwif_t *hwif) hwif->tuneproc = &cs5530_tuneproc; basereg = CS5530_BASEREG(hwif); - d0_timings = hwif->INL(basereg+0); + d0_timings = inl(basereg + 0); if (CS5530_BAD_PIO(d0_timings)) { /* PIO timings not initialized? */ - hwif->OUTL(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0); + outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 0); if (!hwif->drives[0].autotune) hwif->drives[0].autotune = 1; /* needs autotuning later */ } - if (CS5530_BAD_PIO(hwif->INL(basereg+8))) { - /* PIO timings not initialized? */ - hwif->OUTL(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8); + if (CS5530_BAD_PIO(inl(basereg + 8))) { + /* PIO timings not initialized? */ + outl(cs5530_pio_timings[(d0_timings >> 31) & 1][0], basereg + 8); if (!hwif->drives[1].autotune) hwif->drives[1].autotune = 1; /* needs autotuning later */ diff --git a/drivers/ide/pci/cs5535.c b/drivers/ide/pci/cs5535.c index 5c5aec2..45f43ef 100644 --- a/drivers/ide/pci/cs5535.c +++ b/drivers/ide/pci/cs5535.c @@ -195,28 +195,19 @@ static int cs5535_config_drive_for_dma(ide_drive_t *drive) static int cs5535_dma_check(ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; - struct hd_driveid *id = drive->id; u8 speed; drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - if (ide_use_dma(drive)) { - if (cs5535_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && cs5535_config_drive_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) { speed = ide_get_best_pio_mode(drive, 255, 4, NULL); cs5535_set_drive(drive, speed); - return hwif->ide_dma_off_quietly(drive); } - /* IORDY not supported */ - return 0; + + return -1; } static u8 __devinit cs5535_cable_detect(struct pci_dev *dev) diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c index 9eafcbf..103b9db 100644 --- a/drivers/ide/pci/cy82c693.c +++ b/drivers/ide/pci/cy82c693.c @@ -197,8 +197,8 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) #if CY82C693_DEBUG_LOGS /* for debug let's show the previous values */ - HWIF(drive)->OUTB(index, CY82_INDEX_PORT); - data = HWIF(drive)->INB(CY82_DATA_PORT); + outb(index, CY82_INDEX_PORT); + data = inb(CY82_DATA_PORT); printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", drive->name, HWIF(drive)->channel, drive->select.b.unit, @@ -207,8 +207,8 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) data = (u8)mode|(u8)(single<<2); - HWIF(drive)->OUTB(index, CY82_INDEX_PORT); - HWIF(drive)->OUTB(data, CY82_DATA_PORT); + outb(index, CY82_INDEX_PORT); + outb(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", @@ -227,8 +227,8 @@ static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) */ data = BUSMASTER_TIMEOUT; - HWIF(drive)->OUTB(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); - HWIF(drive)->OUTB(data, CY82_DATA_PORT); + outb(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); + outb(data, CY82_DATA_PORT); #if CY82C693_DEBUG_INFO printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", @@ -478,21 +478,18 @@ static void __devinit init_iops_cy82c693(ide_hwif_t *hwif) } } -static ide_pci_device_t cy82c693_chipsets[] __devinitdata = { - { /* 0 */ - .name = "CY82C693", - .init_chipset = init_chipset_cy82c693, - .init_iops = init_iops_cy82c693, - .init_hwif = init_hwif_cy82c693, - .channels = 1, - .autodma = AUTODMA, - .bootable = ON_BOARD, - } +static ide_pci_device_t cy82c693_chipset __devinitdata = { + .name = "CY82C693", + .init_chipset = init_chipset_cy82c693, + .init_iops = init_iops_cy82c693, + .init_hwif = init_hwif_cy82c693, + .channels = 1, + .autodma = AUTODMA, + .bootable = ON_BOARD, }; static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_device_id *id) { - ide_pci_device_t *d = &cy82c693_chipsets[id->driver_data]; struct pci_dev *dev2; int ret = -ENODEV; @@ -501,7 +498,7 @@ static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_dev if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && PCI_FUNC(dev->devfn) == 1) { dev2 = pci_get_slot(dev->bus, dev->devfn + 1); - ret = ide_setup_pci_devices(dev, dev2, d); + ret = ide_setup_pci_devices(dev, dev2, &cy82c693_chipset); /* We leak pci refs here but thats ok - we can't be unloaded */ } return ret; diff --git a/drivers/ide/pci/delkin_cb.c b/drivers/ide/pci/delkin_cb.c index e2672fc..dd7ec37 100644 --- a/drivers/ide/pci/delkin_cb.c +++ b/drivers/ide/pci/delkin_cb.c @@ -108,6 +108,7 @@ delkin_cb_remove (struct pci_dev *dev) static struct pci_device_id delkin_cb_pci_tbl[] __devinitdata = { { 0x1145, 0xf021, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0x1145, 0xf024, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, { 0, }, }; MODULE_DEVICE_TABLE(pci, delkin_cb_pci_tbl); @@ -122,7 +123,7 @@ static struct pci_driver driver = { static int delkin_cb_init (void) { - return pci_module_init(&driver); + return pci_register_driver(&driver); } static void diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c index b408c6c..f2c5a14 100644 --- a/drivers/ide/pci/generic.c +++ b/drivers/ide/pci/generic.c @@ -21,8 +21,6 @@ * are deemed to be part of the source code. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/types.h> #include <linux/module.h> #include <linux/kernel.h> diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c index ce7b08f..924eaa3 100644 --- a/drivers/ide/pci/hpt34x.c +++ b/drivers/ide/pci/hpt34x.c @@ -48,19 +48,6 @@ static u8 hpt34x_ratemask (ide_drive_t *drive) return 1; } -static void hpt34x_clear_chipset (ide_drive_t *drive) -{ - struct pci_dev *dev = HWIF(drive)->pci_dev; - u32 reg1 = 0, tmp1 = 0, reg2 = 0, tmp2 = 0; - - pci_read_config_dword(dev, 0x44, ®1); - pci_read_config_dword(dev, 0x48, ®2); - tmp1 = ((0x00 << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); - tmp2 = (reg2 & ~(0x11 << drive->dn)); - pci_write_config_dword(dev, 0x44, tmp1); - pci_write_config_dword(dev, 0x48, tmp2); -} - static int hpt34x_tune_chipset (ide_drive_t *drive, u8 xferspeed) { struct pci_dev *dev = HWIF(drive)->pci_dev; @@ -81,7 +68,7 @@ static int hpt34x_tune_chipset (ide_drive_t *drive, u8 xferspeed) pci_read_config_dword(dev, 0x44, ®1); pci_read_config_dword(dev, 0x48, ®2); tmp1 = ((lo_speed << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); - tmp2 = ((hi_speed << drive->dn) | reg2); + tmp2 = ((hi_speed << drive->dn) | (reg2 & ~(0x11 << drive->dn))); pci_write_config_dword(dev, 0x44, tmp1); pci_write_config_dword(dev, 0x48, tmp2); @@ -99,7 +86,6 @@ static int hpt34x_tune_chipset (ide_drive_t *drive, u8 xferspeed) static void hpt34x_tune_drive (ide_drive_t *drive, u8 pio) { pio = ide_get_best_pio_mode(drive, pio, 5, NULL); - hpt34x_clear_chipset(drive); (void) hpt34x_tune_chipset(drive, (XFER_PIO_0 + pio)); } @@ -117,38 +103,25 @@ static int config_chipset_for_dma (ide_drive_t *drive) if (!(speed)) return 0; - hpt34x_clear_chipset(drive); (void) hpt34x_tune_chipset(drive, speed); return ide_dma_enable(drive); } static int hpt34x_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if (id && (id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) #ifndef CONFIG_HPT34X_AUTODMA - return hwif->ide_dma_off_quietly(drive); + return -1; #else - return hwif->ide_dma_on(drive); + return 0; #endif - } - - goto fast_ata_pio; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) hpt34x_tune_drive(drive, 255); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + + return -1; } /* @@ -209,7 +182,6 @@ static void __devinit init_hwif_hpt34x(ide_hwif_t *hwif) hwif->tuneproc = &hpt34x_tune_drive; hwif->speedproc = &hpt34x_tune_chipset; - hwif->no_dsc = 1; hwif->drives[0].autotune = 1; hwif->drives[1].autotune = 1; diff --git a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c index 05be8fa..ab6fa27 100644 --- a/drivers/ide/pci/hpt366.c +++ b/drivers/ide/pci/hpt366.c @@ -1,10 +1,10 @@ /* - * linux/drivers/ide/pci/hpt366.c Version 1.01 Dec 23, 2006 + * linux/drivers/ide/pci/hpt366.c Version 1.02 Apr 18, 2007 * * Copyright (C) 1999-2003 Andre Hedrick <andre@linux-ide.org> * Portions Copyright (C) 2001 Sun Microsystems, Inc. * Portions Copyright (C) 2003 Red Hat Inc - * Portions Copyright (C) 2005-2006 MontaVista Software, Inc. + * Portions Copyright (C) 2005-2007 MontaVista Software, Inc. * * Thanks to HighPoint Technologies for their assistance, and hardware. * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his @@ -494,6 +494,7 @@ static struct hpt_info hpt302n __devinitdata = { .chip_type = HPT302N, .max_mode = HPT302_ALLOW_ATA133_6 ? 4 : 3, .dpll_clk = 77, + .settings = hpt37x_settings }; static struct hpt_info hpt371n __devinitdata = { @@ -736,24 +737,15 @@ static void hpt3xx_maskproc(ide_drive_t *drive, int mask) static int hpt366_config_drive_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - if (ide_use_dma(drive) && config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) hpt3xx_tune_drive(drive, 255); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + + return -1; } /* @@ -841,7 +833,7 @@ static int hpt374_ide_dma_test_irq(ide_drive_t *drive) return 0; } - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = inb(hwif->dma_status); /* return 1 if INTR asserted */ if (dma_stat & 4) return 1; @@ -1391,9 +1383,6 @@ static void __devinit init_dma_hpt366(ide_hwif_t *hwif, unsigned long dmabase) u8 dma_new = 0, dma_old = 0; unsigned long flags; - if (!dmabase) - return; - dma_old = hwif->INB(dmabase + 2); local_irq_save(flags); diff --git a/drivers/ide/pci/it8213.c b/drivers/ide/pci/it8213.c index 63248b6..424f00b 100644 --- a/drivers/ide/pci/it8213.c +++ b/drivers/ide/pci/it8213.c @@ -244,17 +244,15 @@ static int config_chipset_for_dma (ide_drive_t *drive) static int it8213_config_drive_for_dma (ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; + u8 pio; - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - hwif->speedproc(drive, XFER_PIO_0 - + ide_get_best_pio_mode(drive, 255, 4, NULL)); + pio = ide_get_best_pio_mode(drive, 255, 4, NULL); + it8213_tune_chipset(drive, XFER_PIO_0 + pio); - return hwif->ide_dma_off_quietly(drive); + return -1; } /** diff --git a/drivers/ide/pci/it821x.c b/drivers/ide/pci/it821x.c index e9bad18..a132767 100644 --- a/drivers/ide/pci/it821x.c +++ b/drivers/ide/pci/it821x.c @@ -520,14 +520,12 @@ static int config_chipset_for_dma (ide_drive_t *drive) static int it821x_config_drive_for_dma (ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } config_it821x_chipset_for_pio(drive, 1); - return hwif->ide_dma_off_quietly(drive); + + return -1; } /** @@ -608,11 +606,11 @@ static void __devinit it821x_fixups(ide_hwif_t *hwif) printk(".\n"); /* Now the core code will have wrongly decided no DMA so we need to fix this */ - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); #ifdef CONFIG_IDEDMA_ONLYDISK if (drive->media == ide_disk) #endif - hwif->ide_dma_check(drive); + ide_set_dma(drive); } else { /* Non RAID volume. Fixups to stop the core code doing unsupported things */ diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c index f07bbbe..be4fc96 100644 --- a/drivers/ide/pci/jmicron.c +++ b/drivers/ide/pci/jmicron.c @@ -147,7 +147,9 @@ static int config_chipset_for_dma (ide_drive_t *drive) { u8 speed = ide_dma_speed(drive, jmicron_ratemask(drive)); - config_jmicron_chipset_for_pio(drive, !speed); + if (!speed) + return 0; + jmicron_tune_chipset(drive, speed); return ide_dma_enable(drive); } @@ -162,14 +164,12 @@ static int config_chipset_for_dma (ide_drive_t *drive) static int jmicron_config_drive_for_dma (ide_drive_t *drive) { - ide_hwif_t *hwif = drive->hwif; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } config_jmicron_chipset_for_pio(drive, 1); - return hwif->ide_dma_off_quietly(drive); + + return -1; } /** @@ -240,12 +240,31 @@ static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_devi return 0; } +/* If libata is configured, jmicron PCI quirk will configure it such + * that the SATA ports are in AHCI function while the PATA ports are + * in a separate IDE function. In such cases, match device class and + * attach only to IDE. If libata isn't configured, keep the old + * behavior for backward compatibility. + */ +#if defined(CONFIG_ATA) || defined(CONFIG_ATA_MODULE) +#define JMB_CLASS PCI_CLASS_STORAGE_IDE << 8 +#define JMB_CLASS_MASK 0xffff00 +#else +#define JMB_CLASS 0 +#define JMB_CLASS_MASK 0 +#endif + static struct pci_device_id jmicron_pci_tbl[] = { - { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, - { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1}, - { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2}, - { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3}, - { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, + PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 0}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, + PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 1}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365, + PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 2}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366, + PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 3}, + { PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368, + PCI_ANY_ID, PCI_ANY_ID, JMB_CLASS, JMB_CLASS_MASK, 4}, { 0, }, }; diff --git a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c index 8aaea4e..b310c4f 100644 --- a/drivers/ide/pci/ns87415.c +++ b/drivers/ide/pci/ns87415.c @@ -166,10 +166,10 @@ static int ns87415_ide_dma_end (ide_drive_t *drive) /* get dma command mode */ dma_cmd = hwif->INB(hwif->dma_command); /* stop DMA */ - hwif->OUTB(dma_cmd & ~1, hwif->dma_command); + outb(dma_cmd & ~1, hwif->dma_command); /* from ERRATA: clear the INTR & ERROR bits */ dma_cmd = hwif->INB(hwif->dma_command); - hwif->OUTB(dma_cmd|6, hwif->dma_command); + outb(dma_cmd | 6, hwif->dma_command); /* and free any DMA resources */ ide_destroy_dmatable(drive); /* verify good DMA status */ @@ -190,7 +190,8 @@ static int ns87415_ide_dma_setup(ide_drive_t *drive) static int ns87415_ide_dma_check (ide_drive_t *drive) { if (drive->media != ide_disk) - return HWIF(drive)->ide_dma_off_quietly(drive); + return -1; + return __ide_dma_check(drive); } @@ -243,9 +244,9 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) * to SELECT_DRIVE() properly during first probe_hwif(). */ timeout = 10000; - hwif->OUTB(12, hwif->io_ports[IDE_CONTROL_OFFSET]); + outb(12, hwif->io_ports[IDE_CONTROL_OFFSET]); udelay(10); - hwif->OUTB(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + outb(8, hwif->io_ports[IDE_CONTROL_OFFSET]); do { udelay(50); stat = hwif->INB(hwif->io_ports[IDE_STATUS_OFFSET]); @@ -263,7 +264,7 @@ static void __devinit init_hwif_ns87415 (ide_hwif_t *hwif) if (!hwif->dma_base) return; - hwif->OUTB(0x60, hwif->dma_status); + outb(0x60, hwif->dma_status); hwif->dma_setup = &ns87415_ide_dma_setup; hwif->ide_dma_check = &ns87415_ide_dma_check; hwif->ide_dma_end = &ns87415_ide_dma_end; diff --git a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c index 22bbf61..aede7ee 100644 --- a/drivers/ide/pci/opti621.c +++ b/drivers/ide/pci/opti621.c @@ -57,7 +57,7 @@ * There is a 25/33MHz switch in configuration * register, but driver is written for use at any frequency which get * (use idebus=xx to select PCI bus speed). - * Use ide0=autotune for automatical tune of the PIO modes. + * Use hda=autotune and hdb=autotune for automatical tune of the PIO modes. * If you get strange results, do not use this and set PIO manually * by hdparm. * @@ -87,7 +87,6 @@ * 0.5 doesn't work. */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #define OPTI621_DEBUG /* define for debug messages */ #include <linux/types.h> @@ -176,34 +175,35 @@ static int cmpt_clk(int time, int bus_speed) return ((time*bus_speed+999)/1000); } -static void write_reg(ide_hwif_t *hwif, u8 value, int reg) /* Write value to register reg, base of register * is at reg_base (0x1f0 primary, 0x170 secondary, * if not changed by PCI configuration). * This is from setupvic.exe program. */ +static void write_reg(u8 value, int reg) { - hwif->INW(reg_base+1); - hwif->INW(reg_base+1); - hwif->OUTB(3, reg_base+2); - hwif->OUTB(value, reg_base+reg); - hwif->OUTB(0x83, reg_base+2); + inw(reg_base + 1); + inw(reg_base + 1); + outb(3, reg_base + 2); + outb(value, reg_base + reg); + outb(0x83, reg_base + 2); } -static u8 read_reg(ide_hwif_t *hwif, int reg) /* Read value from register reg, base of register * is at reg_base (0x1f0 primary, 0x170 secondary, * if not changed by PCI configuration). * This is from setupvic.exe program. */ +static u8 read_reg(int reg) { u8 ret = 0; - hwif->INW(reg_base+1); - hwif->INW(reg_base+1); - hwif->OUTB(3, reg_base+2); - ret = hwif->INB(reg_base+reg); - hwif->OUTB(0x83, reg_base+2); + inw(reg_base + 1); + inw(reg_base + 1); + outb(3, reg_base + 2); + ret = inb(reg_base + reg); + outb(0x83, reg_base + 2); + return ret; } @@ -286,39 +286,39 @@ static void opti621_tune_drive (ide_drive_t *drive, u8 pio) reg_base = hwif->io_ports[IDE_DATA_OFFSET]; /* allow Register-B */ - hwif->OUTB(0xc0, reg_base+CNTRL_REG); + outb(0xc0, reg_base + CNTRL_REG); /* hmm, setupvic.exe does this ;-) */ - hwif->OUTB(0xff, reg_base+5); + outb(0xff, reg_base + 5); /* if reads 0xff, adapter not exist? */ - (void) hwif->INB(reg_base+CNTRL_REG); + (void)inb(reg_base + CNTRL_REG); /* if reads 0xc0, no interface exist? */ - read_reg(hwif, CNTRL_REG); + read_reg(CNTRL_REG); /* read version, probably 0 */ - read_reg(hwif, STRAP_REG); + read_reg(STRAP_REG); /* program primary drive */ - /* select Index-0 for Register-A */ - write_reg(hwif, 0, MISC_REG); - /* set read cycle timings */ - write_reg(hwif, cycle1, READ_REG); - /* set write cycle timings */ - write_reg(hwif, cycle1, WRITE_REG); + /* select Index-0 for Register-A */ + write_reg(0, MISC_REG); + /* set read cycle timings */ + write_reg(cycle1, READ_REG); + /* set write cycle timings */ + write_reg(cycle1, WRITE_REG); /* program secondary drive */ - /* select Index-1 for Register-B */ - write_reg(hwif, 1, MISC_REG); - /* set read cycle timings */ - write_reg(hwif, cycle2, READ_REG); - /* set write cycle timings */ - write_reg(hwif, cycle2, WRITE_REG); + /* select Index-1 for Register-B */ + write_reg(1, MISC_REG); + /* set read cycle timings */ + write_reg(cycle2, READ_REG); + /* set write cycle timings */ + write_reg(cycle2, WRITE_REG); /* use Register-A for drive 0 */ /* use Register-B for drive 1 */ - write_reg(hwif, 0x85, CNTRL_REG); + write_reg(0x85, CNTRL_REG); /* set address setup, DRDY timings, */ /* and read prefetch for both drives */ - write_reg(hwif, misc, MISC_REG); + write_reg(misc, MISC_REG); spin_unlock_irqrestore(&ide_lock, flags); } diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c index 236a031..ace9892 100644 --- a/drivers/ide/pci/pdc202xx_new.c +++ b/drivers/ide/pci/pdc202xx_new.c @@ -101,8 +101,8 @@ static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index) { u8 value; - hwif->OUTB(index, hwif->dma_vendor1); - value = hwif->INB(hwif->dma_vendor3); + outb(index, hwif->dma_vendor1); + value = inb(hwif->dma_vendor3); DBG("index[%02X] value[%02X]\n", index, value); return value; @@ -115,8 +115,8 @@ static u8 get_indexed_reg(ide_hwif_t *hwif, u8 index) */ static void set_indexed_reg(ide_hwif_t *hwif, u8 index, u8 value) { - hwif->OUTB(index, hwif->dma_vendor1); - hwif->OUTB(value, hwif->dma_vendor3); + outb(index, hwif->dma_vendor1); + outb(value, hwif->dma_vendor3); DBG("index[%02X] value[%02X]\n", index, value); } @@ -255,7 +255,7 @@ static int config_chipset_for_dma(ide_drive_t *drive) printk(KERN_WARNING "%s reduced to Ultra33 mode.\n", drive->name); } - if (drive->media != ide_disk) + if (drive->media != ide_disk && drive->media != ide_cdrom) return 0; if (id->capability & 4) { @@ -281,25 +281,15 @@ static int config_chipset_for_dma(ide_drive_t *drive) static int pdcnew_config_drive_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive) && config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - goto fast_ata_pio; + if (ide_use_fast_pio(drive)) + pdcnew_tune_drive(drive, 255); - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - hwif->tuneproc(drive, 255); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + return -1; } static int pdcnew_quirkproc(ide_drive_t *drive) @@ -555,6 +545,7 @@ static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif) hwif->drives[0].autotune = hwif->drives[1].autotune = 1; + hwif->atapi_dma = 1; hwif->ultra_mask = 0x7f; hwif->mwdma_mask = 0x07; diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c index 730e8d1..a7a639f 100644 --- a/drivers/ide/pci/pdc202xx_old.c +++ b/drivers/ide/pci/pdc202xx_old.c @@ -2,6 +2,7 @@ * linux/drivers/ide/pci/pdc202xx_old.c Version 0.36 Sept 11, 2002 * * Copyright (C) 1998-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2006-2007 MontaVista Software, Inc. * * Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this * compiled into the kernel if you have more than one card installed. @@ -216,21 +217,10 @@ static int pdc202xx_tune_chipset (ide_drive_t *drive, u8 xferspeed) } -/* 0 1 2 3 4 5 6 7 8 - * 960, 480, 390, 300, 240, 180, 120, 90, 60 - * 180, 150, 120, 90, 60 - * DMA_Speed - * 180, 120, 90, 90, 90, 60, 30 - * 11, 5, 4, 3, 2, 1, 0 - */ -static void config_chipset_for_pio (ide_drive_t *drive, u8 pio) +static void pdc202xx_tune_drive(ide_drive_t *drive, u8 pio) { - u8 speed = 0; - - if (pio == 5) pio = 4; - speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, pio, NULL); - - pdc202xx_tune_chipset(drive, speed); + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + pdc202xx_tune_chipset(drive, XFER_PIO_0 + pio); } static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif) @@ -250,17 +240,17 @@ static u8 pdc202xx_old_cable_detect (ide_hwif_t *hwif) static void pdc_old_enable_66MHz_clock(ide_hwif_t *hwif) { unsigned long clock_reg = hwif->dma_master + 0x11; - u8 clock = hwif->INB(clock_reg); + u8 clock = inb(clock_reg); - hwif->OUTB(clock | (hwif->channel ? 0x08 : 0x02), clock_reg); + outb(clock | (hwif->channel ? 0x08 : 0x02), clock_reg); } static void pdc_old_disable_66MHz_clock(ide_hwif_t *hwif) { unsigned long clock_reg = hwif->dma_master + 0x11; - u8 clock = hwif->INB(clock_reg); + u8 clock = inb(clock_reg); - hwif->OUTB(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg); + outb(clock & ~(hwif->channel ? 0x08 : 0x02), clock_reg); } static int config_chipset_for_dma (ide_drive_t *drive) @@ -332,27 +322,15 @@ chipset_is_set: static int pdc202xx_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if (id && (id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - goto fast_ata_pio; + if (ide_use_fast_pio(drive)) + pdc202xx_tune_drive(drive, 255); - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - hwif->tuneproc(drive, 5); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + return -1; } static int pdc202xx_quirkproc (ide_drive_t *drive) @@ -375,14 +353,14 @@ static void pdc202xx_old_ide_dma_start(ide_drive_t *drive) unsigned long high_16 = hwif->dma_master; unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); u32 word_count = 0; - u8 clock = hwif->INB(high_16 + 0x11); + u8 clock = inb(high_16 + 0x11); - hwif->OUTB(clock|(hwif->channel ? 0x08 : 0x02), high_16+0x11); + outb(clock | (hwif->channel ? 0x08 : 0x02), high_16 + 0x11); word_count = (rq->nr_sectors << 8); word_count = (rq_data_dir(rq) == READ) ? word_count | 0x05000000 : word_count | 0x06000000; - hwif->OUTL(word_count, atapi_reg); + outl(word_count, atapi_reg); } ide_dma_start(drive); } @@ -395,9 +373,9 @@ static int pdc202xx_old_ide_dma_end(ide_drive_t *drive) unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); u8 clock = 0; - hwif->OUTL(0, atapi_reg); /* zero out extra */ - clock = hwif->INB(high_16 + 0x11); - hwif->OUTB(clock & ~(hwif->channel ? 0x08:0x02), high_16+0x11); + outl(0, atapi_reg); /* zero out extra */ + clock = inb(high_16 + 0x11); + outb(clock & ~(hwif->channel ? 0x08:0x02), high_16 + 0x11); } if (drive->current_speed > XFER_UDMA_2) pdc_old_disable_66MHz_clock(drive->hwif); @@ -408,8 +386,8 @@ static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); unsigned long high_16 = hwif->dma_master; - u8 dma_stat = hwif->INB(hwif->dma_status); - u8 sc1d = hwif->INB((high_16 + 0x001d)); + u8 dma_stat = inb(hwif->dma_status); + u8 sc1d = inb(high_16 + 0x001d); if (hwif->channel) { /* bit7: Error, bit6: Interrupting, bit5: FIFO Full, bit4: FIFO Empty */ @@ -445,11 +423,11 @@ static int pdc202xx_ide_dma_timeout(ide_drive_t *drive) static void pdc202xx_reset_host (ide_hwif_t *hwif) { unsigned long high_16 = hwif->dma_master; - u8 udma_speed_flag = hwif->INB(high_16|0x001f); + u8 udma_speed_flag = inb(high_16 | 0x001f); - hwif->OUTB((udma_speed_flag | 0x10), (high_16|0x001f)); + outb(udma_speed_flag | 0x10, high_16 | 0x001f); mdelay(100); - hwif->OUTB((udma_speed_flag & ~0x10), (high_16|0x001f)); + outb(udma_speed_flag & ~0x10, high_16 | 0x001f); mdelay(2000); /* 2 seconds ?! */ printk(KERN_WARNING "PDC202XX: %s channel reset.\n", @@ -463,7 +441,7 @@ static void pdc202xx_reset (ide_drive_t *drive) pdc202xx_reset_host(hwif); pdc202xx_reset_host(mate); - hwif->tuneproc(drive, 5); + pdc202xx_tune_drive(drive, 255); } static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev, @@ -490,7 +468,7 @@ static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif) hwif->rqsize = 256; hwif->autodma = 0; - hwif->tuneproc = &config_chipset_for_pio; + hwif->tuneproc = &pdc202xx_tune_drive; hwif->quirkproc = &pdc202xx_quirkproc; if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246) @@ -537,9 +515,9 @@ static void __devinit init_dma_pdc202xx(ide_hwif_t *hwif, unsigned long dmabase) return; } - udma_speed_flag = hwif->INB((dmabase|0x1f)); - primary_mode = hwif->INB((dmabase|0x1a)); - secondary_mode = hwif->INB((dmabase|0x1b)); + udma_speed_flag = inb(dmabase | 0x1f); + primary_mode = inb(dmabase | 0x1a); + secondary_mode = inb(dmabase | 0x1b); printk(KERN_INFO "%s: (U)DMA Burst Bit %sABLED " \ "Primary %s Mode " \ "Secondary %s Mode.\n", hwif->cds->name, @@ -552,30 +530,10 @@ static void __devinit init_dma_pdc202xx(ide_hwif_t *hwif, unsigned long dmabase) printk(KERN_INFO "%s: FORCING BURST BIT 0x%02x->0x%02x ", hwif->cds->name, udma_speed_flag, (udma_speed_flag|1)); - hwif->OUTB(udma_speed_flag|1,(dmabase|0x1f)); - printk("%sACTIVE\n", - (hwif->INB(dmabase|0x1f)&1) ? "":"IN"); + outb(udma_speed_flag | 1, dmabase | 0x1f); + printk("%sACTIVE\n", (inb(dmabase | 0x1f) & 1) ? "" : "IN"); } #endif /* CONFIG_PDC202XX_BURST */ -#ifdef CONFIG_PDC202XX_MASTER - if (!(primary_mode & 1)) { - printk(KERN_INFO "%s: FORCING PRIMARY MODE BIT " - "0x%02x -> 0x%02x ", hwif->cds->name, - primary_mode, (primary_mode|1)); - hwif->OUTB(primary_mode|1, (dmabase|0x1a)); - printk("%s\n", - (hwif->INB((dmabase|0x1a)) & 1) ? "MASTER" : "PCI"); - } - - if (!(secondary_mode & 1)) { - printk(KERN_INFO "%s: FORCING SECONDARY MODE BIT " - "0x%02x -> 0x%02x ", hwif->cds->name, - secondary_mode, (secondary_mode|1)); - hwif->OUTB(secondary_mode|1, (dmabase|0x1b)); - printk("%s\n", - (hwif->INB((dmabase|0x1b)) & 1) ? "MASTER" : "PCI"); - } -#endif /* CONFIG_PDC202XX_MASTER */ ide_setup_dma(hwif, dmabase, 8); } diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c index 52cfc2a..061d300 100644 --- a/drivers/ide/pci/piix.c +++ b/drivers/ide/pci/piix.c @@ -1,10 +1,10 @@ /* - * linux/drivers/ide/pci/piix.c Version 0.46 December 3, 2006 + * linux/drivers/ide/pci/piix.c Version 0.47 February 8, 2007 * * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer * Copyright (C) 1998-2000 Andre Hedrick <andre@linux-ide.org> * Copyright (C) 2003 Red Hat Inc <alan@redhat.com> - * Copyright (C) 2006 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com> * * May be copied or modified under the terms of the GNU General Public License * @@ -205,14 +205,13 @@ static u8 piix_dma_2_pio (u8 xfer_rate) { } /** - * piix_tune_drive - tune a drive attached to a PIIX + * piix_tune_pio - tune PIIX for PIO mode * @drive: drive to tune * @pio: desired PIO mode * - * Set the interface PIO mode based upon the settings done by AMI BIOS - * (might be useful if drive is not registered in CMOS for any reason). + * Set the interface PIO mode based upon the settings done by AMI BIOS. */ -static void piix_tune_drive (ide_drive_t *drive, u8 pio) +static void piix_tune_pio (ide_drive_t *drive, u8 pio) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; @@ -233,8 +232,6 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio) { 2, 1 }, { 2, 3 }, }; - pio = ide_get_best_pio_mode(drive, pio, 4, NULL); - /* * Master vs slave is synchronized above us but the slave register is * shared by the two hwifs so the corner case of two slave timeouts in @@ -253,19 +250,20 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio) master_data |= 0x4000; master_data &= ~0x0070; if (pio > 1) { - /* enable PPE, IE and TIME */ - master_data = master_data | (control << 4); + /* Set PPE, IE and TIME */ + master_data |= control << 4; } pci_read_config_byte(dev, slave_port, &slave_data); - slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); - slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 4 : 0)); + slave_data &= hwif->channel ? 0x0f : 0xf0; + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << + (hwif->channel ? 4 : 0); } else { master_data &= ~0x3307; if (pio > 1) { /* enable PPE, IE and TIME */ - master_data = master_data | control; + master_data |= control; } - master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8); + master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8); } pci_write_config_word(dev, master_port, master_data); if (is_slave) @@ -274,6 +272,21 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio) } /** + * piix_tune_drive - tune a drive attached to PIIX + * @drive: drive to tune + * @pio: desired PIO mode + * + * Set the drive's PIO mode (might be useful if drive is not registered + * in CMOS for any reason). + */ +static void piix_tune_drive (ide_drive_t *drive, u8 pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + piix_tune_pio(drive, pio); + (void) ide_config_drive_speed(drive, XFER_PIO_0 + pio); +} + +/** * piix_tune_chipset - tune a PIIX interface * @drive: IDE drive to tune * @xferspeed: speed to configure @@ -348,8 +361,8 @@ static int piix_tune_chipset (ide_drive_t *drive, u8 xferspeed) pci_write_config_byte(dev, 0x55, (u8) reg55 & ~w_flag); } - piix_tune_drive(drive, piix_dma_2_pio(speed)); - return (ide_config_drive_speed(drive, speed)); + piix_tune_pio(drive, piix_dma_2_pio(speed)); + return ide_config_drive_speed(drive, speed); } /** @@ -369,7 +382,7 @@ static int piix_config_drive_for_dma (ide_drive_t *drive) * If no DMA speed was available or the chipset has DMA bugs * then disable DMA and use PIO */ - if (!speed || no_piix_dma) + if (!speed) return 0; (void) piix_tune_chipset(drive, speed); @@ -386,41 +399,26 @@ static int piix_config_drive_for_dma (ide_drive_t *drive) static int piix_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive) && piix_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); + if (ide_use_dma(drive) && piix_config_drive_for_dma(drive)) + return 0; - goto fast_ata_pio; + if (ide_use_fast_pio(drive)) + piix_tune_drive(drive, 255); - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - /* Find best PIO mode. */ - (void) hwif->speedproc(drive, XFER_PIO_0 + - ide_get_best_pio_mode(drive, 255, 4, NULL)); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + return -1; } /** - * init_chipset_piix - set up the PIIX chipset - * @dev: PCI device to set up - * @name: Name of the device + * piix_is_ichx - check if ICHx + * @dev: PCI device to check * - * Initialize the PCI device as required. For the PIIX this turns - * out to be nice and simple + * returns 1 if ICHx, 0 otherwise. */ - -static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name) +static int piix_is_ichx(struct pci_dev *dev) { - switch(dev->device) { + switch (dev->device) { case PCI_DEVICE_ID_INTEL_82801EB_1: case PCI_DEVICE_ID_INTEL_82801AA_1: case PCI_DEVICE_ID_INTEL_82801AB_1: @@ -438,19 +436,61 @@ static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char case PCI_DEVICE_ID_INTEL_ICH7_21: case PCI_DEVICE_ID_INTEL_ESB2_18: case PCI_DEVICE_ID_INTEL_ICH8_6: - { - unsigned int extra = 0; - pci_read_config_dword(dev, 0x54, &extra); - pci_write_config_dword(dev, 0x54, extra|0x400); - } - default: - break; + return 1; } return 0; } /** + * init_chipset_piix - set up the PIIX chipset + * @dev: PCI device to set up + * @name: Name of the device + * + * Initialize the PCI device as required. For the PIIX this turns + * out to be nice and simple + */ + +static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name) +{ + if (piix_is_ichx(dev)) { + unsigned int extra = 0; + pci_read_config_dword(dev, 0x54, &extra); + pci_write_config_dword(dev, 0x54, extra|0x400); + } + + return 0; +} + +/** + * piix_dma_clear_irq - clear BMDMA status + * @drive: IDE drive to clear + * + * Called from ide_intr() for PIO interrupts + * to clear BMDMA status as needed by ICHx + */ +static void piix_dma_clear_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat; + + /* clear the INTR & ERROR bits */ + dma_stat = hwif->INB(hwif->dma_status); + /* Should we force the bit as well ? */ + hwif->OUTB(dma_stat, hwif->dma_status); +} + +static int __devinit piix_cable_detect(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + u8 reg54h = 0, mask = hwif->channel ? 0xc0 : 0x30; + + pci_read_config_byte(dev, 0x54, ®54h); + + return (reg54h & mask) ? 1 : 0; +} + +/** * init_hwif_piix - fill in the hwif for the PIIX * @hwif: IDE interface * @@ -460,9 +500,6 @@ static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char static void __devinit init_hwif_piix(ide_hwif_t *hwif) { - u8 reg54h = 0, reg55h = 0, ata66 = 0; - u8 mask = hwif->channel ? 0xc0 : 0x30; - #ifndef CONFIG_IA64 if (!hwif->irq) hwif->irq = hwif->channel ? 15 : 14; @@ -472,10 +509,6 @@ static void __devinit init_hwif_piix(ide_hwif_t *hwif) /* This is a painful system best to let it self tune for now */ return; } - /* ESB2 appears to generate spurious DMA interrupts in PIO mode - when in native mode */ - if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_ESB2_18) - hwif->atapi_irq_bogon = 1; hwif->autodma = 0; hwif->tuneproc = &piix_tune_drive; @@ -486,15 +519,16 @@ static void __devinit init_hwif_piix(ide_hwif_t *hwif) if (!hwif->dma_base) return; + /* ICHx need to clear the bmdma status for all interrupts */ + if (piix_is_ichx(hwif->pci_dev)) + hwif->ide_dma_clear_irq = &piix_dma_clear_irq; + hwif->atapi_dma = 1; hwif->ultra_mask = 0x3f; hwif->mwdma_mask = 0x06; hwif->swdma_mask = 0x04; switch(hwif->pci_dev->device) { - case PCI_DEVICE_ID_INTEL_82371MX: - hwif->mwdma_mask = 0x80; - hwif->swdma_mask = 0x80; case PCI_DEVICE_ID_INTEL_82371FB_0: case PCI_DEVICE_ID_INTEL_82371FB_1: case PCI_DEVICE_ID_INTEL_82371SB_1: @@ -507,14 +541,14 @@ static void __devinit init_hwif_piix(ide_hwif_t *hwif) hwif->ultra_mask = 0x07; break; default: - pci_read_config_byte(hwif->pci_dev, 0x54, ®54h); - pci_read_config_byte(hwif->pci_dev, 0x55, ®55h); - ata66 = (reg54h & mask) ? 1 : 0; + if (!hwif->udma_four) + hwif->udma_four = piix_cable_detect(hwif); break; } - if (!(hwif->udma_four)) - hwif->udma_four = ata66; + if (no_piix_dma) + hwif->ultra_mask = hwif->mwdma_mask = hwif->swdma_mask = 0; + hwif->ide_dma_check = &piix_config_drive_xfer_rate; if (!noautodma) hwif->autodma = 1; diff --git a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c index c185531..f8c9546 100644 --- a/drivers/ide/pci/rz1000.c +++ b/drivers/ide/pci/rz1000.c @@ -15,8 +15,6 @@ * Dunno if this fixes both ports, or only the primary port (?). */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ - #include <linux/types.h> #include <linux/module.h> #include <linux/kernel.h> diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c index 8d762d3..b5ae0c5 100644 --- a/drivers/ide/pci/sc1200.c +++ b/drivers/ide/pci/sc1200.c @@ -161,7 +161,7 @@ static int sc1200_config_dma2 (ide_drive_t *drive, int mode) /* * Default to DMA-off in case we run into trouble here. */ - hwif->ide_dma_off_quietly(drive); /* turn off DMA while we fiddle */ + hwif->dma_off_quietly(drive); /* turn off DMA while we fiddle */ outb(inb(hwif->dma_base+2)&~(unit?0x40:0x20), hwif->dma_base+2); /* clear DMA_capable bit */ /* @@ -241,10 +241,7 @@ static int sc1200_config_dma2 (ide_drive_t *drive, int mode) outb(inb(hwif->dma_base+2)|(unit?0x40:0x20), hwif->dma_base+2); /* set DMA_capable bit */ - /* - * Finally, turn DMA on in software, and exit. - */ - return hwif->ide_dma_on(drive); /* success */ + return 0; /* success */ } /* @@ -442,10 +439,10 @@ static int sc1200_resume (struct pci_dev *dev) ide_drive_t *drive = &(hwif->drives[d]); if (drive->present && !__ide_dma_bad_drive(drive)) { int was_using_dma = drive->using_dma; - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); sc1200_config_dma(drive); if (!was_using_dma && drive->using_dma) { - hwif->ide_dma_off_quietly(drive); + hwif->dma_off_quietly(drive); } } } diff --git a/drivers/ide/pci/scc_pata.c b/drivers/ide/pci/scc_pata.c new file mode 100644 index 0000000..f84bf79 --- /dev/null +++ b/drivers/ide/pci/scc_pata.c @@ -0,0 +1,858 @@ +/* + * Support for IDE interfaces on Celleb platform + * + * (C) Copyright 2006 TOSHIBA CORPORATION + * + * This code is based on drivers/ide/pci/siimage.c: + * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> + * Copyright (C) 2003 Red Hat <alan@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/hdreg.h> +#include <linux/ide.h> +#include <linux/init.h> + +#define PCI_DEVICE_ID_TOSHIBA_SCC_ATA 0x01b4 + +#define SCC_PATA_NAME "scc IDE" + +#define TDVHSEL_MASTER 0x00000001 +#define TDVHSEL_SLAVE 0x00000004 + +#define MODE_JCUSFEN 0x00000080 + +#define CCKCTRL_ATARESET 0x00040000 +#define CCKCTRL_BUFCNT 0x00020000 +#define CCKCTRL_CRST 0x00010000 +#define CCKCTRL_OCLKEN 0x00000100 +#define CCKCTRL_ATACLKOEN 0x00000002 +#define CCKCTRL_LCLKEN 0x00000001 + +#define QCHCD_IOS_SS 0x00000001 + +#define QCHSD_STPDIAG 0x00020000 + +#define INTMASK_MSK 0xD1000012 +#define INTSTS_SERROR 0x80000000 +#define INTSTS_PRERR 0x40000000 +#define INTSTS_RERR 0x10000000 +#define INTSTS_ICERR 0x01000000 +#define INTSTS_BMSINT 0x00000010 +#define INTSTS_BMHE 0x00000008 +#define INTSTS_IOIRQS 0x00000004 +#define INTSTS_INTRQ 0x00000002 +#define INTSTS_ACTEINT 0x00000001 + +#define ECMODE_VALUE 0x01 + +static struct scc_ports { + unsigned long ctl, dma; + unsigned char hwif_id; /* for removing hwif from system */ +} scc_ports[MAX_HWIFS]; + +/* PIO transfer mode table */ +/* JCHST */ +static unsigned long JCHSTtbl[2][7] = { + {0x0E, 0x05, 0x02, 0x03, 0x02, 0x00, 0x00}, /* 100MHz */ + {0x13, 0x07, 0x04, 0x04, 0x03, 0x00, 0x00} /* 133MHz */ +}; + +/* JCHHT */ +static unsigned long JCHHTtbl[2][7] = { + {0x0E, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00}, /* 100MHz */ + {0x13, 0x03, 0x03, 0x03, 0x03, 0x00, 0x00} /* 133MHz */ +}; + +/* JCHCT */ +static unsigned long JCHCTtbl[2][7] = { + {0x1D, 0x1D, 0x1C, 0x0B, 0x06, 0x00, 0x00}, /* 100MHz */ + {0x27, 0x26, 0x26, 0x0E, 0x09, 0x00, 0x00} /* 133MHz */ +}; + + +/* DMA transfer mode table */ +/* JCHDCTM/JCHDCTS */ +static unsigned long JCHDCTxtbl[2][7] = { + {0x0A, 0x06, 0x04, 0x03, 0x01, 0x00, 0x00}, /* 100MHz */ + {0x0E, 0x09, 0x06, 0x04, 0x02, 0x01, 0x00} /* 133MHz */ +}; + +/* JCSTWTM/JCSTWTS */ +static unsigned long JCSTWTxtbl[2][7] = { + {0x06, 0x04, 0x03, 0x02, 0x02, 0x02, 0x00}, /* 100MHz */ + {0x09, 0x06, 0x04, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ +}; + +/* JCTSS */ +static unsigned long JCTSStbl[2][7] = { + {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x00}, /* 100MHz */ + {0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05} /* 133MHz */ +}; + +/* JCENVT */ +static unsigned long JCENVTtbl[2][7] = { + {0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00}, /* 100MHz */ + {0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02} /* 133MHz */ +}; + +/* JCACTSELS/JCACTSELM */ +static unsigned long JCACTSELtbl[2][7] = { + {0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00}, /* 100MHz */ + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01} /* 133MHz */ +}; + + +static u8 scc_ide_inb(unsigned long port) +{ + u32 data = in_be32((void*)port); + return (u8)data; +} + +static u16 scc_ide_inw(unsigned long port) +{ + u32 data = in_be32((void*)port); + return (u16)data; +} + +static void scc_ide_insw(unsigned long port, void *addr, u32 count) +{ + u16 *ptr = (u16 *)addr; + while (count--) { + *ptr++ = le16_to_cpu(in_be32((void*)port)); + } +} + +static void scc_ide_insl(unsigned long port, void *addr, u32 count) +{ + u16 *ptr = (u16 *)addr; + while (count--) { + *ptr++ = le16_to_cpu(in_be32((void*)port)); + *ptr++ = le16_to_cpu(in_be32((void*)port)); + } +} + +static void scc_ide_outb(u8 addr, unsigned long port) +{ + out_be32((void*)port, addr); +} + +static void scc_ide_outw(u16 addr, unsigned long port) +{ + out_be32((void*)port, addr); +} + +static void +scc_ide_outbsync(ide_drive_t * drive, u8 addr, unsigned long port) +{ + ide_hwif_t *hwif = HWIF(drive); + + out_be32((void*)port, addr); + __asm__ __volatile__("eieio":::"memory"); + in_be32((void*)(hwif->dma_base + 0x01c)); + __asm__ __volatile__("eieio":::"memory"); +} + +static void +scc_ide_outsw(unsigned long port, void *addr, u32 count) +{ + u16 *ptr = (u16 *)addr; + while (count--) { + out_be32((void*)port, cpu_to_le16(*ptr++)); + } +} + +static void +scc_ide_outsl(unsigned long port, void *addr, u32 count) +{ + u16 *ptr = (u16 *)addr; + while (count--) { + out_be32((void*)port, cpu_to_le16(*ptr++)); + out_be32((void*)port, cpu_to_le16(*ptr++)); + } +} + +/** + * scc_ratemask - Compute available modes + * @drive: IDE drive + * + * Compute the available speeds for the devices on the interface. + * Enforce UDMA33 as a limit if there is no 80pin cable present. + */ + +static u8 scc_ratemask(ide_drive_t *drive) +{ + u8 mode = 4; + + if (!eighty_ninty_three(drive)) + mode = min(mode, (u8)1); + return mode; +} + +/** + * scc_tuneproc - tune a drive PIO mode + * @drive: drive to tune + * @mode_wanted: the target operating mode + * + * Load the timing settings for this device mode into the + * controller. + */ + +static void scc_tuneproc(ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scc_ports *ports = ide_get_hwifdata(hwif); + unsigned long ctl_base = ports->ctl; + unsigned long cckctrl_port = ctl_base + 0xff0; + unsigned long piosht_port = ctl_base + 0x000; + unsigned long pioct_port = ctl_base + 0x004; + unsigned long reg; + unsigned char speed = XFER_PIO_0; + int offset; + + mode_wanted = ide_get_best_pio_mode(drive, mode_wanted, 4, NULL); + switch (mode_wanted) { + case 4: + speed = XFER_PIO_4; + break; + case 3: + speed = XFER_PIO_3; + break; + case 2: + speed = XFER_PIO_2; + break; + case 1: + speed = XFER_PIO_1; + break; + case 0: + default: + speed = XFER_PIO_0; + break; + } + + reg = in_be32((void __iomem *)cckctrl_port); + if (reg & CCKCTRL_ATACLKOEN) { + offset = 1; /* 133MHz */ + } else { + offset = 0; /* 100MHz */ + } + reg = JCHSTtbl[offset][mode_wanted] << 16 | JCHHTtbl[offset][mode_wanted]; + out_be32((void __iomem *)piosht_port, reg); + reg = JCHCTtbl[offset][mode_wanted]; + out_be32((void __iomem *)pioct_port, reg); + + ide_config_drive_speed(drive, speed); +} + +/** + * scc_tune_chipset - tune a drive DMA mode + * @drive: Drive to set up + * @xferspeed: speed we want to achieve + * + * Load the timing settings for this device mode into the + * controller. + */ + +static int scc_tune_chipset(ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 speed = ide_rate_filter(scc_ratemask(drive), xferspeed); + struct scc_ports *ports = ide_get_hwifdata(hwif); + unsigned long ctl_base = ports->ctl; + unsigned long cckctrl_port = ctl_base + 0xff0; + unsigned long mdmact_port = ctl_base + 0x008; + unsigned long mcrcst_port = ctl_base + 0x00c; + unsigned long sdmact_port = ctl_base + 0x010; + unsigned long scrcst_port = ctl_base + 0x014; + unsigned long udenvt_port = ctl_base + 0x018; + unsigned long tdvhsel_port = ctl_base + 0x020; + int is_slave = (&hwif->drives[1] == drive); + int offset, idx; + unsigned long reg; + unsigned long jcactsel; + + reg = in_be32((void __iomem *)cckctrl_port); + if (reg & CCKCTRL_ATACLKOEN) { + offset = 1; /* 133MHz */ + } else { + offset = 0; /* 100MHz */ + } + + switch (speed) { + case XFER_UDMA_6: + idx = 6; + break; + case XFER_UDMA_5: + idx = 5; + break; + case XFER_UDMA_4: + idx = 4; + break; + case XFER_UDMA_3: + idx = 3; + break; + case XFER_UDMA_2: + idx = 2; + break; + case XFER_UDMA_1: + idx = 1; + break; + case XFER_UDMA_0: + idx = 0; + break; + default: + return 1; + } + + jcactsel = JCACTSELtbl[offset][idx]; + if (is_slave) { + out_be32((void __iomem *)sdmact_port, JCHDCTxtbl[offset][idx]); + out_be32((void __iomem *)scrcst_port, JCSTWTxtbl[offset][idx]); + jcactsel = jcactsel << 2; + out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_SLAVE) | jcactsel); + } else { + out_be32((void __iomem *)mdmact_port, JCHDCTxtbl[offset][idx]); + out_be32((void __iomem *)mcrcst_port, JCSTWTxtbl[offset][idx]); + out_be32((void __iomem *)tdvhsel_port, (in_be32((void __iomem *)tdvhsel_port) & ~TDVHSEL_MASTER) | jcactsel); + } + reg = JCTSStbl[offset][idx] << 16 | JCENVTtbl[offset][idx]; + out_be32((void __iomem *)udenvt_port, reg); + + return ide_config_drive_speed(drive, speed); +} + +/** + * scc_config_chipset_for_dma - configure for DMA + * @drive: drive to configure + * + * Called by scc_config_drive_for_dma(). + */ + +static int scc_config_chipset_for_dma(ide_drive_t *drive) +{ + u8 speed = ide_dma_speed(drive, scc_ratemask(drive)); + + if (!speed) + return 0; + + if (scc_tune_chipset(drive, speed)) + return 0; + + return ide_dma_enable(drive); +} + +/** + * scc_configure_drive_for_dma - set up for DMA transfers + * @drive: drive we are going to set up + * + * Set up the drive for DMA, tune the controller and drive as + * required. + * If the drive isn't suitable for DMA or we hit other problems + * then we will drop down to PIO and set up PIO appropriately. + * (return 1) + */ + +static int scc_config_drive_for_dma(ide_drive_t *drive) +{ + if (ide_use_dma(drive) && scc_config_chipset_for_dma(drive)) + return 0; + + if (ide_use_fast_pio(drive)) + scc_tuneproc(drive, 4); + + return -1; +} + +/** + * scc_ide_dma_setup - begin a DMA phase + * @drive: target device + * + * Build an IDE DMA PRD (IDE speak for scatter gather table) + * and then set up the DMA transfer registers. + * + * Returns 0 on success. If a PIO fallback is required then 1 + * is returned. + */ + +static int scc_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = drive->hwif; + struct request *rq = HWGROUP(drive)->rq; + unsigned int reading; + u8 dma_stat; + + if (rq_data_dir(rq)) + reading = 0; + else + reading = 1 << 3; + + /* fall back to pio! */ + if (!ide_build_dmatable(drive, rq)) { + ide_map_sg(drive, rq); + return 1; + } + + /* PRD table */ + out_be32((void __iomem *)hwif->dma_prdtable, hwif->dmatable_dma); + + /* specify r/w */ + out_be32((void __iomem *)hwif->dma_command, reading); + + /* read dma_status for INTR & ERROR flags */ + dma_stat = in_be32((void __iomem *)hwif->dma_status); + + /* clear INTR & ERROR flags */ + out_be32((void __iomem *)hwif->dma_status, dma_stat|6); + drive->waiting_for_dma = 1; + return 0; +} + + +/** + * scc_ide_dma_end - Stop DMA + * @drive: IDE drive + * + * Check and clear INT Status register. + * Then call __ide_dma_end(). + */ + +static int scc_ide_dma_end(ide_drive_t * drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long intsts_port = hwif->dma_base + 0x014; + u32 reg; + + while (1) { + reg = in_be32((void __iomem *)intsts_port); + + if (reg & INTSTS_SERROR) { + printk(KERN_WARNING "%s: SERROR\n", SCC_PATA_NAME); + out_be32((void __iomem *)intsts_port, INTSTS_SERROR|INTSTS_BMSINT); + + out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + continue; + } + + if (reg & INTSTS_PRERR) { + u32 maea0, maec0; + unsigned long ctl_base = hwif->config_data; + + maea0 = in_be32((void __iomem *)(ctl_base + 0xF50)); + maec0 = in_be32((void __iomem *)(ctl_base + 0xF54)); + + printk(KERN_WARNING "%s: PRERR [addr:%x cmd:%x]\n", SCC_PATA_NAME, maea0, maec0); + + out_be32((void __iomem *)intsts_port, INTSTS_PRERR|INTSTS_BMSINT); + + out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + continue; + } + + if (reg & INTSTS_RERR) { + printk(KERN_WARNING "%s: Response Error\n", SCC_PATA_NAME); + out_be32((void __iomem *)intsts_port, INTSTS_RERR|INTSTS_BMSINT); + + out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + continue; + } + + if (reg & INTSTS_ICERR) { + out_be32((void __iomem *)hwif->dma_command, in_be32((void __iomem *)hwif->dma_command) & ~QCHCD_IOS_SS); + + printk(KERN_WARNING "%s: Illegal Configuration\n", SCC_PATA_NAME); + out_be32((void __iomem *)intsts_port, INTSTS_ICERR|INTSTS_BMSINT); + continue; + } + + if (reg & INTSTS_BMSINT) { + printk(KERN_WARNING "%s: Internal Bus Error\n", SCC_PATA_NAME); + out_be32((void __iomem *)intsts_port, INTSTS_BMSINT); + + ide_do_reset(drive); + continue; + } + + if (reg & INTSTS_BMHE) { + out_be32((void __iomem *)intsts_port, INTSTS_BMHE); + continue; + } + + if (reg & INTSTS_ACTEINT) { + out_be32((void __iomem *)intsts_port, INTSTS_ACTEINT); + continue; + } + + if (reg & INTSTS_IOIRQS) { + out_be32((void __iomem *)intsts_port, INTSTS_IOIRQS); + continue; + } + break; + } + + return __ide_dma_end(drive); +} + +/* returns 1 if dma irq issued, 0 otherwise */ +static int scc_dma_test_irq(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + u8 dma_stat = hwif->INB(hwif->dma_status); + + /* return 1 if INTR asserted */ + if ((dma_stat & 4) == 4) + return 1; + + /* Workaround for PTERADD: emulate DMA_INTR when + * - IDE_STATUS[ERR] = 1 + * - INT_STATUS[INTRQ] = 1 + * - DMA_STATUS[IORACTA] = 1 + */ + if (in_be32((void __iomem *)IDE_ALTSTATUS_REG) & ERR_STAT && + in_be32((void __iomem *)(hwif->dma_base + 0x014)) & INTSTS_INTRQ && + dma_stat & 1) + return 1; + + if (!drive->waiting_for_dma) + printk(KERN_WARNING "%s: (%s) called while not waiting\n", + drive->name, __FUNCTION__); + return 0; +} + +/** + * setup_mmio_scc - map CTRL/BMID region + * @dev: PCI device we are configuring + * @name: device name + * + */ + +static int setup_mmio_scc (struct pci_dev *dev, const char *name) +{ + unsigned long ctl_base = pci_resource_start(dev, 0); + unsigned long dma_base = pci_resource_start(dev, 1); + unsigned long ctl_size = pci_resource_len(dev, 0); + unsigned long dma_size = pci_resource_len(dev, 1); + void *ctl_addr; + void *dma_addr; + int i; + + for (i = 0; i < MAX_HWIFS; i++) { + if (scc_ports[i].ctl == 0) + break; + } + if (i >= MAX_HWIFS) + return -ENOMEM; + + if (!request_mem_region(ctl_base, ctl_size, name)) { + printk(KERN_WARNING "%s: IDE controller MMIO ports not available.\n", SCC_PATA_NAME); + goto fail_0; + } + + if (!request_mem_region(dma_base, dma_size, name)) { + printk(KERN_WARNING "%s: IDE controller MMIO ports not available.\n", SCC_PATA_NAME); + goto fail_1; + } + + if ((ctl_addr = ioremap(ctl_base, ctl_size)) == NULL) + goto fail_2; + + if ((dma_addr = ioremap(dma_base, dma_size)) == NULL) + goto fail_3; + + pci_set_master(dev); + scc_ports[i].ctl = (unsigned long)ctl_addr; + scc_ports[i].dma = (unsigned long)dma_addr; + pci_set_drvdata(dev, (void *) &scc_ports[i]); + + return 1; + + fail_3: + iounmap(ctl_addr); + fail_2: + release_mem_region(dma_base, dma_size); + fail_1: + release_mem_region(ctl_base, ctl_size); + fail_0: + return -ENOMEM; +} + +/** + * init_setup_scc - set up an SCC PATA Controller + * @dev: PCI device + * @d: IDE PCI device + * + * Perform the initial set up for this device. + */ + +static int __devinit init_setup_scc(struct pci_dev *dev, ide_pci_device_t *d) +{ + unsigned long ctl_base; + unsigned long dma_base; + unsigned long cckctrl_port; + unsigned long intmask_port; + unsigned long mode_port; + unsigned long ecmode_port; + unsigned long dma_status_port; + u32 reg = 0; + struct scc_ports *ports; + int rc; + + rc = setup_mmio_scc(dev, d->name); + if (rc < 0) { + return rc; + } + + ports = pci_get_drvdata(dev); + ctl_base = ports->ctl; + dma_base = ports->dma; + cckctrl_port = ctl_base + 0xff0; + intmask_port = dma_base + 0x010; + mode_port = ctl_base + 0x024; + ecmode_port = ctl_base + 0xf00; + dma_status_port = dma_base + 0x004; + + /* controller initialization */ + reg = 0; + out_be32((void*)cckctrl_port, reg); + reg |= CCKCTRL_ATACLKOEN; + out_be32((void*)cckctrl_port, reg); + reg |= CCKCTRL_LCLKEN | CCKCTRL_OCLKEN; + out_be32((void*)cckctrl_port, reg); + reg |= CCKCTRL_CRST; + out_be32((void*)cckctrl_port, reg); + + for (;;) { + reg = in_be32((void*)cckctrl_port); + if (reg & CCKCTRL_CRST) + break; + udelay(5000); + } + + reg |= CCKCTRL_ATARESET; + out_be32((void*)cckctrl_port, reg); + + out_be32((void*)ecmode_port, ECMODE_VALUE); + out_be32((void*)mode_port, MODE_JCUSFEN); + out_be32((void*)intmask_port, INTMASK_MSK); + + return ide_setup_pci_device(dev, d); +} + +/** + * init_mmio_iops_scc - set up the iops for MMIO + * @hwif: interface to set up + * + */ + +static void __devinit init_mmio_iops_scc(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + struct scc_ports *ports = pci_get_drvdata(dev); + unsigned long dma_base = ports->dma; + + ide_set_hwifdata(hwif, ports); + + hwif->INB = scc_ide_inb; + hwif->INW = scc_ide_inw; + hwif->INSW = scc_ide_insw; + hwif->INSL = scc_ide_insl; + hwif->OUTB = scc_ide_outb; + hwif->OUTBSYNC = scc_ide_outbsync; + hwif->OUTW = scc_ide_outw; + hwif->OUTSW = scc_ide_outsw; + hwif->OUTSL = scc_ide_outsl; + + hwif->io_ports[IDE_DATA_OFFSET] = dma_base + 0x20; + hwif->io_ports[IDE_ERROR_OFFSET] = dma_base + 0x24; + hwif->io_ports[IDE_NSECTOR_OFFSET] = dma_base + 0x28; + hwif->io_ports[IDE_SECTOR_OFFSET] = dma_base + 0x2c; + hwif->io_ports[IDE_LCYL_OFFSET] = dma_base + 0x30; + hwif->io_ports[IDE_HCYL_OFFSET] = dma_base + 0x34; + hwif->io_ports[IDE_SELECT_OFFSET] = dma_base + 0x38; + hwif->io_ports[IDE_STATUS_OFFSET] = dma_base + 0x3c; + hwif->io_ports[IDE_CONTROL_OFFSET] = dma_base + 0x40; + + hwif->irq = hwif->pci_dev->irq; + hwif->dma_base = dma_base; + hwif->config_data = ports->ctl; + hwif->mmio = 1; +} + +/** + * init_iops_scc - set up iops + * @hwif: interface to set up + * + * Do the basic setup for the SCC hardware interface + * and then do the MMIO setup. + */ + +static void __devinit init_iops_scc(ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + hwif->hwif_data = NULL; + if (pci_get_drvdata(dev) == NULL) + return; + init_mmio_iops_scc(hwif); +} + +/** + * init_hwif_scc - set up hwif + * @hwif: interface to set up + * + * We do the basic set up of the interface structure. The SCC + * requires several custom handlers so we override the default + * ide DMA handlers appropriately. + */ + +static void __devinit init_hwif_scc(ide_hwif_t *hwif) +{ + struct scc_ports *ports = ide_get_hwifdata(hwif); + + ports->hwif_id = hwif->index; + + hwif->dma_command = hwif->dma_base; + hwif->dma_status = hwif->dma_base + 0x04; + hwif->dma_prdtable = hwif->dma_base + 0x08; + + /* PTERADD */ + out_be32((void __iomem *)(hwif->dma_base + 0x018), hwif->dmatable_dma); + + hwif->dma_setup = scc_dma_setup; + hwif->ide_dma_end = scc_ide_dma_end; + hwif->speedproc = scc_tune_chipset; + hwif->tuneproc = scc_tuneproc; + hwif->ide_dma_check = scc_config_drive_for_dma; + hwif->ide_dma_test_irq = scc_dma_test_irq; + + hwif->drives[0].autotune = IDE_TUNE_AUTO; + hwif->drives[1].autotune = IDE_TUNE_AUTO; + + if (in_be32((void __iomem *)(hwif->config_data + 0xff0)) & CCKCTRL_ATACLKOEN) { + hwif->ultra_mask = 0x7f; /* 133MHz */ + } else { + hwif->ultra_mask = 0x3f; /* 100MHz */ + } + hwif->mwdma_mask = 0x00; + hwif->swdma_mask = 0x00; + hwif->atapi_dma = 1; + + /* we support 80c cable only. */ + hwif->udma_four = 1; + + hwif->autodma = 0; + if (!noautodma) + hwif->autodma = 1; + hwif->drives[0].autodma = hwif->autodma; + hwif->drives[1].autodma = hwif->autodma; +} + +#define DECLARE_SCC_DEV(name_str) \ + { \ + .name = name_str, \ + .init_setup = init_setup_scc, \ + .init_iops = init_iops_scc, \ + .init_hwif = init_hwif_scc, \ + .channels = 1, \ + .autodma = AUTODMA, \ + .bootable = ON_BOARD, \ + } + +static ide_pci_device_t scc_chipsets[] __devinitdata = { + /* 0 */ DECLARE_SCC_DEV("sccIDE"), +}; + +/** + * scc_init_one - pci layer discovery entry + * @dev: PCI device + * @id: ident table entry + * + * Called by the PCI code when it finds an SCC PATA controller. + * We then use the IDE PCI generic helper to do most of the work. + */ + +static int __devinit scc_init_one(struct pci_dev *dev, const struct pci_device_id *id) +{ + ide_pci_device_t *d = &scc_chipsets[id->driver_data]; + return d->init_setup(dev, d); +} + +/** + * scc_remove - pci layer remove entry + * @dev: PCI device + * + * Called by the PCI code when it removes an SCC PATA controller. + */ + +static void __devexit scc_remove(struct pci_dev *dev) +{ + struct scc_ports *ports = pci_get_drvdata(dev); + ide_hwif_t *hwif = &ide_hwifs[ports->hwif_id]; + unsigned long ctl_base = pci_resource_start(dev, 0); + unsigned long dma_base = pci_resource_start(dev, 1); + unsigned long ctl_size = pci_resource_len(dev, 0); + unsigned long dma_size = pci_resource_len(dev, 1); + + if (hwif->dmatable_cpu) { + pci_free_consistent(hwif->pci_dev, + PRD_ENTRIES * PRD_BYTES, + hwif->dmatable_cpu, + hwif->dmatable_dma); + hwif->dmatable_cpu = NULL; + } + + ide_unregister(hwif->index); + + hwif->chipset = ide_unknown; + iounmap((void*)ports->dma); + iounmap((void*)ports->ctl); + release_mem_region(dma_base, dma_size); + release_mem_region(ctl_base, ctl_size); + memset(ports, 0, sizeof(*ports)); +} + +static struct pci_device_id scc_pci_tbl[] = { + { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SCC_ATA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, scc_pci_tbl); + +static struct pci_driver driver = { + .name = "SCC IDE", + .id_table = scc_pci_tbl, + .probe = scc_init_one, + .remove = scc_remove, +}; + +static int scc_ide_init(void) +{ + return ide_pci_register_driver(&driver); +} + +module_init(scc_ide_init); +/* -- No exit code? +static void scc_ide_exit(void) +{ + ide_pci_unregister_driver(&driver); +} +module_exit(scc_ide_exit); + */ + + +MODULE_DESCRIPTION("PCI driver module for Toshiba SCC IDE"); +MODULE_LICENSE("GPL"); diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c index ea9a28a..dbcd37a 100644 --- a/drivers/ide/pci/serverworks.c +++ b/drivers/ide/pci/serverworks.c @@ -160,7 +160,7 @@ static int svwks_tune_chipset (ide_drive_t *drive, u8 xferspeed) if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) { if (!drive->init_speed) { - u8 dma_stat = hwif->INB(hwif->dma_status); + u8 dma_stat = inb(hwif->dma_status); dma_pio: if (((ultra_enable << (7-drive->dn) & 0x80) == 0x80) && @@ -315,35 +315,15 @@ static int config_chipset_for_dma (ide_drive_t *drive) static int svwks_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) config_chipset_for_pio(drive); - // hwif->tuneproc(drive, 5); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; -} - -/* This can go soon */ -static int svwks_ide_dma_end (ide_drive_t *drive) -{ - return __ide_dma_end(drive); + return -1; } static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const char *name) @@ -537,35 +517,20 @@ static void __devinit init_hwif_svwks (ide_hwif_t *hwif) } hwif->ide_dma_check = &svwks_config_drive_xfer_rate; - if (hwif->pci_dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) - hwif->ide_dma_end = &svwks_ide_dma_end; - else if (!(hwif->udma_four)) - hwif->udma_four = ata66_svwks(hwif); + if (hwif->pci_dev->device != PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + if (!hwif->udma_four) + hwif->udma_four = ata66_svwks(hwif); + } if (!noautodma) hwif->autodma = 1; - dma_stat = hwif->INB(hwif->dma_status); + dma_stat = inb(hwif->dma_status); hwif->drives[0].autodma = (dma_stat & 0x20); hwif->drives[1].autodma = (dma_stat & 0x40); hwif->drives[0].autotune = (!(dma_stat & 0x20)); hwif->drives[1].autotune = (!(dma_stat & 0x40)); } -/* - * We allow the BM-DMA driver to only work on enabled interfaces. - */ -static void __devinit init_dma_svwks (ide_hwif_t *hwif, unsigned long dmabase) -{ - struct pci_dev *dev = hwif->pci_dev; - - if (((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) || - (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2)) && - (!(PCI_FUNC(dev->devfn) & 1)) && (hwif->channel)) - return; - - ide_setup_dma(hwif, dmabase, 8); -} - static int __devinit init_setup_svwks (struct pci_dev *dev, ide_pci_device_t *d) { return ide_setup_pci_device(dev, d); @@ -600,7 +565,6 @@ static ide_pci_device_t serverworks_chipsets[] __devinitdata = { .init_setup = init_setup_svwks, .init_chipset = init_chipset_svwks, .init_hwif = init_hwif_svwks, - .init_dma = init_dma_svwks, .channels = 2, .autodma = AUTODMA, .bootable = ON_BOARD, @@ -609,7 +573,6 @@ static ide_pci_device_t serverworks_chipsets[] __devinitdata = { .init_setup = init_setup_csb6, .init_chipset = init_chipset_svwks, .init_hwif = init_hwif_svwks, - .init_dma = init_dma_svwks, .channels = 2, .autodma = AUTODMA, .bootable = ON_BOARD, @@ -618,7 +581,6 @@ static ide_pci_device_t serverworks_chipsets[] __devinitdata = { .init_setup = init_setup_csb6, .init_chipset = init_chipset_svwks, .init_hwif = init_hwif_svwks, - .init_dma = init_dma_svwks, .channels = 1, /* 2 */ .autodma = AUTODMA, .bootable = ON_BOARD, @@ -627,7 +589,6 @@ static ide_pci_device_t serverworks_chipsets[] __devinitdata = { .init_setup = init_setup_svwks, .init_chipset = init_chipset_svwks, .init_hwif = init_hwif_svwks, - .init_dma = init_dma_svwks, .channels = 1, /* 2 */ .autodma = AUTODMA, .bootable = ON_BOARD, diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c index b0bf018..fd09b29 100644 --- a/drivers/ide/pci/sgiioc4.c +++ b/drivers/ide/pci/sgiioc4.c @@ -110,24 +110,24 @@ sgiioc4_init_hwif_ports(hw_regs_t * hw, unsigned long data_port, static void sgiioc4_maskproc(ide_drive_t * drive, int mask) { - ide_hwif_t *hwif = HWIF(drive); - hwif->OUTB(mask ? (drive->ctl | 2) : (drive->ctl & ~2), - IDE_CONTROL_REG); + writeb(mask ? (drive->ctl | 2) : (drive->ctl & ~2), + (void __iomem *)IDE_CONTROL_REG); } static int sgiioc4_checkirq(ide_hwif_t * hwif) { - u8 intr_reg = - hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + IOC4_INTR_REG * 4); + unsigned long intr_addr = + hwif->io_ports[IDE_IRQ_OFFSET] + IOC4_INTR_REG * 4; - if (intr_reg & 0x03) + if ((u8)readl((void __iomem *)intr_addr) & 0x03) return 1; return 0; } +static u8 sgiioc4_INB(unsigned long); static int sgiioc4_clearirq(ide_drive_t * drive) @@ -138,21 +138,21 @@ sgiioc4_clearirq(ide_drive_t * drive) hwif->io_ports[IDE_IRQ_OFFSET] + (IOC4_INTR_REG << 2); /* Code to check for PCI error conditions */ - intr_reg = hwif->INL(other_ir); + intr_reg = readl((void __iomem *)other_ir); if (intr_reg & 0x03) { /* Valid IOC4-IDE interrupt */ /* - * Using hwif->INB to read the IDE_STATUS_REG has a side effect + * Using sgiioc4_INB to read the IDE_STATUS_REG has a side effect * of clearing the interrupt. The first read should clear it * if it is set. The second read should return a "clear" status * if it got cleared. If not, then spin for a bit trying to * clear it. */ - u8 stat = hwif->INB(IDE_STATUS_REG); + u8 stat = sgiioc4_INB(IDE_STATUS_REG); int count = 0; - stat = hwif->INB(IDE_STATUS_REG); + stat = sgiioc4_INB(IDE_STATUS_REG); while ((stat & 0x80) && (count++ < 100)) { udelay(1); - stat = hwif->INB(IDE_STATUS_REG); + stat = sgiioc4_INB(IDE_STATUS_REG); } if (intr_reg & 0x02) { @@ -161,9 +161,9 @@ sgiioc4_clearirq(ide_drive_t * drive) pci_stat_cmd_reg; pci_err_addr_low = - hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET]); + readl((void __iomem *)hwif->io_ports[IDE_IRQ_OFFSET]); pci_err_addr_high = - hwif->INL(hwif->io_ports[IDE_IRQ_OFFSET] + 4); + readl((void __iomem *)(hwif->io_ports[IDE_IRQ_OFFSET] + 4)); pci_read_config_dword(hwif->pci_dev, PCI_COMMAND, &pci_stat_cmd_reg); printk(KERN_ERR @@ -180,9 +180,9 @@ sgiioc4_clearirq(ide_drive_t * drive) } /* Clear the Interrupt, Error bits on the IOC4 */ - hwif->OUTL(0x03, other_ir); + writel(0x03, (void __iomem *)other_ir); - intr_reg = hwif->INL(other_ir); + intr_reg = readl((void __iomem *)other_ir); } return intr_reg & 3; @@ -191,23 +191,25 @@ sgiioc4_clearirq(ide_drive_t * drive) static void sgiioc4_ide_dma_start(ide_drive_t * drive) { ide_hwif_t *hwif = HWIF(drive); - unsigned int reg = hwif->INL(hwif->dma_base + IOC4_DMA_CTRL * 4); + unsigned long ioc4_dma_addr = hwif->dma_base + IOC4_DMA_CTRL * 4; + unsigned int reg = readl((void __iomem *)ioc4_dma_addr); unsigned int temp_reg = reg | IOC4_S_DMA_START; - hwif->OUTL(temp_reg, hwif->dma_base + IOC4_DMA_CTRL * 4); + writel(temp_reg, (void __iomem *)ioc4_dma_addr); } static u32 sgiioc4_ide_dma_stop(ide_hwif_t *hwif, u64 dma_base) { + unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4; u32 ioc4_dma; int count; count = 0; - ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = readl((void __iomem *)ioc4_dma_addr); while ((ioc4_dma & IOC4_S_DMA_STOP) && (count++ < 200)) { udelay(1); - ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = readl((void __iomem *)ioc4_dma_addr); } return ioc4_dma; } @@ -218,11 +220,11 @@ sgiioc4_ide_dma_end(ide_drive_t * drive) { u32 ioc4_dma, bc_dev, bc_mem, num, valid = 0, cnt = 0; ide_hwif_t *hwif = HWIF(drive); - u64 dma_base = hwif->dma_base; + unsigned long dma_base = hwif->dma_base; int dma_stat = 0; unsigned long *ending_dma = ide_get_hwifdata(hwif); - hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + writel(IOC4_S_DMA_STOP, (void __iomem *)(dma_base + IOC4_DMA_CTRL * 4)); ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); @@ -254,8 +256,8 @@ sgiioc4_ide_dma_end(ide_drive_t * drive) dma_stat = 1; } - bc_dev = hwif->INL(dma_base + IOC4_BC_DEV * 4); - bc_mem = hwif->INL(dma_base + IOC4_BC_MEM * 4); + bc_dev = readl((void __iomem *)(dma_base + IOC4_BC_DEV * 4)); + bc_mem = readl((void __iomem *)(dma_base + IOC4_BC_MEM * 4)); if ((bc_dev & 0x01FF) || (bc_mem & 0x1FF)) { if (bc_dev > bc_mem + 8) { @@ -273,34 +275,29 @@ sgiioc4_ide_dma_end(ide_drive_t * drive) } static int -sgiioc4_ide_dma_check(ide_drive_t * drive) +sgiioc4_ide_dma_on(ide_drive_t * drive) { - if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0) { - printk(KERN_INFO - "Couldnot set %s in Multimode-2 DMA mode | " - "Drive %s using PIO instead\n", - drive->name, drive->name); - drive->using_dma = 0; - } else - drive->using_dma = 1; + drive->using_dma = 1; return 0; } -static int -sgiioc4_ide_dma_on(ide_drive_t * drive) +static void sgiioc4_dma_off_quietly(ide_drive_t *drive) { - drive->using_dma = 1; + drive->using_dma = 0; - return HWIF(drive)->ide_dma_host_on(drive); + drive->hwif->dma_host_off(drive); } -static int -sgiioc4_ide_dma_off_quietly(ide_drive_t * drive) +static int sgiioc4_ide_dma_check(ide_drive_t *drive) { - drive->using_dma = 0; - - return HWIF(drive)->ide_dma_host_off(drive); + /* FIXME: check for available DMA modes */ + if (ide_config_drive_speed(drive, XFER_MW_DMA_2) != 0) { + printk(KERN_WARNING "%s: couldn't set MWDMA2 mode, " + "using PIO instead\n", drive->name); + return -1; + } else + return 0; } /* returns 1 if dma irq issued, 0 otherwise */ @@ -310,21 +307,13 @@ sgiioc4_ide_dma_test_irq(ide_drive_t * drive) return sgiioc4_checkirq(HWIF(drive)); } -static int -sgiioc4_ide_dma_host_on(ide_drive_t * drive) +static void sgiioc4_dma_host_on(ide_drive_t * drive) { - if (drive->using_dma) - return 0; - - return 1; } -static int -sgiioc4_ide_dma_host_off(ide_drive_t * drive) +static void sgiioc4_dma_host_off(ide_drive_t * drive) { sgiioc4_clearirq(drive); - - return 0; } static int @@ -436,16 +425,17 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) { u32 ioc4_dma; ide_hwif_t *hwif = HWIF(drive); - u64 dma_base = hwif->dma_base; + unsigned long dma_base = hwif->dma_base; + unsigned long ioc4_dma_addr = dma_base + IOC4_DMA_CTRL * 4; u32 dma_addr, ending_dma_addr; - ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = readl((void __iomem *)ioc4_dma_addr); if (ioc4_dma & IOC4_S_DMA_ACTIVE) { printk(KERN_WARNING "%s(%s):Warning!! DMA from previous transfer was still active\n", __FUNCTION__, drive->name); - hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr); ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); if (ioc4_dma & IOC4_S_DMA_STOP) @@ -454,13 +444,13 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) __FUNCTION__, drive->name); } - ioc4_dma = hwif->INL(dma_base + IOC4_DMA_CTRL * 4); + ioc4_dma = readl((void __iomem *)ioc4_dma_addr); if (ioc4_dma & IOC4_S_DMA_ERROR) { printk(KERN_WARNING "%s(%s) : Warning!! - DMA Error during Previous" " transfer | status 0x%x\n", __FUNCTION__, drive->name, ioc4_dma); - hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4); + writel(IOC4_S_DMA_STOP, (void __iomem *)ioc4_dma_addr); ioc4_dma = sgiioc4_ide_dma_stop(hwif, dma_base); if (ioc4_dma & IOC4_S_DMA_STOP) @@ -471,14 +461,14 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive) /* Address of the Scatter Gather List */ dma_addr = cpu_to_le32(hwif->dmatable_dma); - hwif->OUTL(dma_addr, dma_base + IOC4_DMA_PTR_L * 4); + writel(dma_addr, (void __iomem *)(dma_base + IOC4_DMA_PTR_L * 4)); /* Address of the Ending DMA */ memset(ide_get_hwifdata(hwif), 0, IOC4_IDE_CACHELINE_SIZE); ending_dma_addr = cpu_to_le32(hwif->dma_status); - hwif->OUTL(ending_dma_addr, dma_base + IOC4_DMA_END_ADDR * 4); + writel(ending_dma_addr, (void __iomem *)(dma_base + IOC4_DMA_END_ADDR * 4)); - hwif->OUTL(dma_direction, dma_base + IOC4_DMA_CTRL * 4); + writel(dma_direction, (void __iomem *)ioc4_dma_addr); drive->waiting_for_dma = 1; } @@ -590,7 +580,7 @@ static int sgiioc4_ide_dma_setup(ide_drive_t *drive) static void __devinit ide_init_sgiioc4(ide_hwif_t * hwif) { - hwif->mmio = 2; + hwif->mmio = 1; hwif->autodma = 1; hwif->atapi_dma = 1; hwif->ultra_mask = 0x0; /* Disable Ultra DMA */ @@ -613,10 +603,10 @@ ide_init_sgiioc4(ide_hwif_t * hwif) hwif->ide_dma_end = &sgiioc4_ide_dma_end; hwif->ide_dma_check = &sgiioc4_ide_dma_check; hwif->ide_dma_on = &sgiioc4_ide_dma_on; - hwif->ide_dma_off_quietly = &sgiioc4_ide_dma_off_quietly; + hwif->dma_off_quietly = &sgiioc4_dma_off_quietly; hwif->ide_dma_test_irq = &sgiioc4_ide_dma_test_irq; - hwif->ide_dma_host_on = &sgiioc4_ide_dma_host_on; - hwif->ide_dma_host_off = &sgiioc4_ide_dma_host_off; + hwif->dma_host_on = &sgiioc4_dma_host_on; + hwif->dma_host_off = &sgiioc4_dma_host_off; hwif->ide_dma_lostirq = &sgiioc4_ide_dma_lostirq; hwif->ide_dma_timeout = &__ide_dma_timeout; @@ -688,7 +678,7 @@ sgiioc4_ide_setup_pci_device(struct pci_dev *dev, ide_pci_device_t * d) default_hwif_mmiops(hwif); /* Initializing chipset IRQ Registers */ - hwif->OUTL(0x03, irqport + IOC4_INTR_SET * 4); + writel(0x03, (void __iomem *)(irqport + IOC4_INTR_SET * 4)); ide_init_sgiioc4(hwif); @@ -729,8 +719,7 @@ out: return ret; } -static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = { - { +static ide_pci_device_t sgiioc4_chipset __devinitdata = { /* Channel 0 */ .name = "SGIIOC4", .init_hwif = ide_init_sgiioc4, @@ -739,7 +728,6 @@ static ide_pci_device_t sgiioc4_chipsets[] __devinitdata = { .autodma = AUTODMA, /* SGI IOC4 doesn't have enablebits. */ .bootable = ON_BOARD, - } }; int @@ -751,8 +739,7 @@ ioc4_ide_attach_one(struct ioc4_driver_data *idd) if (idd->idd_variant == IOC4_VARIANT_PCI_RT) return 0; - return pci_init_sgiioc4(idd->idd_pdev, - &sgiioc4_chipsets[idd->idd_pci_id->driver_data]); + return pci_init_sgiioc4(idd->idd_pdev, &sgiioc4_chipset); } static struct ioc4_submodule ioc4_ide_submodule = { diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c index 4ff89c7..71eccdf 100644 --- a/drivers/ide/pci/siimage.c +++ b/drivers/ide/pci/siimage.c @@ -1,8 +1,9 @@ /* - * linux/drivers/ide/pci/siimage.c Version 1.07 Nov 30, 2003 + * linux/drivers/ide/pci/siimage.c Version 1.11 Jan 27, 2007 * * Copyright (C) 2001-2002 Andre Hedrick <andre@linux-ide.org> * Copyright (C) 2003 Red Hat <alan@redhat.com> + * Copyright (C) 2007 MontaVista Software, Inc. * * May be copied or modified under the terms of the GNU General Public License * @@ -25,6 +26,11 @@ * If you have strange problems with nVidia chipset systems please * see the SI support documentation and update your system BIOS * if neccessary + * + * The Dell DRAC4 has some interesting features including effectively hot + * unplugging/replugging the virtual CD interface when the DRAC is reset. + * This often causes drivers/ide/siimage to panic but is ok with the rather + * smarter code in libata. */ #include <linux/types.h> @@ -205,41 +211,39 @@ static void siimage_tuneproc (ide_drive_t *drive, byte mode_wanted) unsigned long tfaddr = siimage_selreg(hwif, 0x02); /* cheat for now and use the docs */ - switch(mode_wanted) { - case 4: - speedp = 0x10c1; - speedt = 0x10c1; - break; - case 3: - speedp = 0x10C3; - speedt = 0x10C3; - break; - case 2: - speedp = 0x1104; - speedt = 0x1281; - break; - case 1: - speedp = 0x2283; - speedt = 0x1281; - break; - case 0: - default: - speedp = 0x328A; - speedt = 0x328A; - break; + switch (mode_wanted) { + case 4: + speedp = 0x10c1; + speedt = 0x10c1; + break; + case 3: + speedp = 0x10c3; + speedt = 0x10c3; + break; + case 2: + speedp = 0x1104; + speedt = 0x1281; + break; + case 1: + speedp = 0x2283; + speedt = 0x2283; + break; + case 0: + default: + speedp = 0x328a; + speedt = 0x328a; + break; } - if (hwif->mmio) - { - hwif->OUTW(speedt, addr); - hwif->OUTW(speedp, tfaddr); + + if (hwif->mmio) { + hwif->OUTW(speedp, addr); + hwif->OUTW(speedt, tfaddr); /* Now set up IORDY */ if(mode_wanted == 3 || mode_wanted == 4) hwif->OUTW(hwif->INW(tfaddr-2)|0x200, tfaddr-2); else hwif->OUTW(hwif->INW(tfaddr-2)&~0x200, tfaddr-2); - } - else - { + } else { pci_write_config_word(hwif->pci_dev, addr, speedp); pci_write_config_word(hwif->pci_dev, tfaddr, speedt); pci_read_config_word(hwif->pci_dev, tfaddr-2, &speedp); @@ -397,12 +401,9 @@ static int config_chipset_for_dma (ide_drive_t *drive) if (!speed) return 0; - if (ide_set_xfer_rate(drive, speed)) + if (siimage_tune_chipset(drive, speed)) return 0; - if (!drive->init_speed) - drive->init_speed = speed; - return ide_dma_enable(drive); } @@ -418,25 +419,13 @@ static int config_chipset_for_dma (ide_drive_t *drive) static int siimage_config_drive_for_dma (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - - if ((id->capability & 1) != 0 && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) config_chipset_for_pio(drive, 1); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + + return -1; } /* returns 1 if dma irq issued, 0 otherwise */ @@ -472,11 +461,11 @@ static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive) unsigned long addr = siimage_selreg(hwif, 0x1); if (SATA_ERROR_REG) { - u32 ext_stat = hwif->INL(base + 0x10); + u32 ext_stat = readl((void __iomem *)(base + 0x10)); u8 watchdog = 0; if (ext_stat & ((hwif->channel) ? 0x40 : 0x10)) { - u32 sata_error = hwif->INL(SATA_ERROR_REG); - hwif->OUTL(sata_error, SATA_ERROR_REG); + u32 sata_error = readl((void __iomem *)SATA_ERROR_REG); + writel(sata_error, (void __iomem *)SATA_ERROR_REG); watchdog = (sata_error & 0x00680000) ? 1 : 0; printk(KERN_WARNING "%s: sata_error = 0x%08x, " "watchdog = %d, %s\n", @@ -493,11 +482,11 @@ static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive) } /* return 1 if INTR asserted */ - if ((hwif->INB(hwif->dma_status) & 0x04) == 0x04) + if ((readb((void __iomem *)hwif->dma_status) & 0x04) == 0x04) return 1; /* return 1 if Device INTR asserted */ - if ((hwif->INB(addr) & 8) == 8) + if ((readb((void __iomem *)addr) & 8) == 8) return 0; //return 1; return 0; @@ -519,9 +508,9 @@ static int siimage_busproc (ide_drive_t * drive, int state) u32 stat_config = 0; unsigned long addr = siimage_selreg(hwif, 0); - if (hwif->mmio) { - stat_config = hwif->INL(addr); - } else + if (hwif->mmio) + stat_config = readl((void __iomem *)addr); + else pci_read_config_dword(hwif->pci_dev, addr, &stat_config); switch (state) { @@ -557,9 +546,10 @@ static int siimage_reset_poll (ide_drive_t *drive) if (SATA_STATUS_REG) { ide_hwif_t *hwif = HWIF(drive); - if ((hwif->INL(SATA_STATUS_REG) & 0x03) != 0x03) { + /* SATA_STATUS_REG is valid only when in MMIO mode */ + if ((readl((void __iomem *)SATA_STATUS_REG) & 0x03) != 0x03) { printk(KERN_WARNING "%s: reset phy dead, status=0x%08x\n", - hwif->name, hwif->INL(SATA_STATUS_REG)); + hwif->name, readl((void __iomem *)SATA_STATUS_REG)); HWGROUP(drive)->polling = 0; return ide_started; } @@ -619,7 +609,8 @@ static void siimage_reset (ide_drive_t *drive) } if (SATA_STATUS_REG) { - u32 sata_stat = hwif->INL(SATA_STATUS_REG); + /* SATA_STATUS_REG is valid only when in MMIO mode */ + u32 sata_stat = readl((void __iomem *)SATA_STATUS_REG); printk(KERN_WARNING "%s: reset phy, status=0x%08x, %s\n", hwif->name, sata_stat, __FUNCTION__); if (!(sata_stat)) { @@ -898,7 +889,8 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif) base = (unsigned long) addr; hwif->dma_base = base + (ch ? 0x08 : 0x00); - hwif->mmio = 2; + + hwif->mmio = 1; } static int is_dev_seagate_sata(ide_drive_t *drive) diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c index 1afff65..2ba0669 100644 --- a/drivers/ide/pci/sis5513.c +++ b/drivers/ide/pci/sis5513.c @@ -667,67 +667,20 @@ static int config_chipset_for_dma (ide_drive_t *drive) return ide_dma_enable(drive); } -static int sis5513_config_drive_xfer_rate (ide_drive_t *drive) +static int sis5513_config_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; + config_art_rwp_pio(drive, 5); drive->init_speed = 0; - if (id && (id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive)) { - if (config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) sis5513_tune_drive(drive, 5); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; -} - -/* initiates/aborts (U)DMA read/write operations on a drive. */ -static int sis5513_config_xfer_rate (ide_drive_t *drive) -{ - config_drive_art_rwp(drive); - config_art_rwp_pio(drive, 5); - return sis5513_config_drive_xfer_rate(drive); -} - -/* - Future simpler config_xfer_rate : - When ide_find_best_mode is made bad-drive aware - - remove config_drive_xfer_rate and config_chipset_for_dma, - - replace config_xfer_rate with the following - -static int sis5513_config_xfer_rate (ide_drive_t *drive) -{ - u16 w80 = HWIF(drive)->udma_four; - u16 speed; - - config_drive_art_rwp(drive); - config_art_rwp_pio(drive, 5); - - speed = ide_find_best_mode(drive, - XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA | - (chipset_family >= ATA_33 ? XFER_UDMA : 0) | - (w80 && chipset_family >= ATA_66 ? XFER_UDMA_66 : 0) | - (w80 && chipset_family >= ATA_100a ? XFER_UDMA_100 : 0) | - (w80 && chipset_family >= ATA_133a ? XFER_UDMA_133 : 0)); - - sis5513_tune_chipset(drive, speed); - if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) - return HWIF(drive)->ide_dma_on(drive); - return HWIF(drive)->ide_dma_off_quietly(drive); + return -1; } -*/ /* Chip detection and general config */ static unsigned int __devinit init_chipset_sis5513 (struct pci_dev *dev, const char *name) diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c index 170a261..3a8a76f 100644 --- a/drivers/ide/pci/sl82c105.c +++ b/drivers/ide/pci/sl82c105.c @@ -161,14 +161,14 @@ static int sl82c105_check_drive (ide_drive_t *drive) if (id->field_valid & 2) { if ((id->dma_mword & hwif->mwdma_mask) || (id->dma_1word & hwif->swdma_mask)) - return hwif->ide_dma_on(drive); + return 0; } - if (__ide_dma_good_drive(drive)) - return hwif->ide_dma_on(drive); + if (__ide_dma_good_drive(drive) && id->eide_dma_time < 150) + return 0; } while (0); - return hwif->ide_dma_off_quietly(drive); + return -1; } /* @@ -215,7 +215,7 @@ static int sl82c105_ide_dma_lost_irq(ide_drive_t *drive) * Was DMA enabled? If so, disable it - we're resetting the * host. The IDE layer will be handling the drive for us. */ - val = hwif->INB(dma_base); + val = inb(dma_base); if (val & 1) { outb(val & ~1, dma_base); printk("sl82c105: DMA was enabled\n"); @@ -259,28 +259,22 @@ static int sl82c105_ide_dma_on (ide_drive_t *drive) { DBG(("sl82c105_ide_dma_on(drive:%s)\n", drive->name)); - if (config_for_dma(drive)) { - config_for_pio(drive, 4, 0, 0); - return HWIF(drive)->ide_dma_off_quietly(drive); - } + if (config_for_dma(drive)) + return 1; printk(KERN_INFO "%s: DMA enabled\n", drive->name); return __ide_dma_on(drive); } -static int sl82c105_ide_dma_off_quietly (ide_drive_t *drive) +static void sl82c105_dma_off_quietly(ide_drive_t *drive) { u8 speed = XFER_PIO_0; - int rc; - - DBG(("sl82c105_ide_dma_off_quietly(drive:%s)\n", drive->name)); - rc = __ide_dma_off_quietly(drive); + DBG(("sl82c105_dma_off_quietly(drive:%s)\n", drive->name)); + + ide_dma_off_quietly(drive); if (drive->pio_speed) speed = drive->pio_speed - XFER_PIO_0; config_for_pio(drive, speed, 0, 1); - drive->current_speed = drive->pio_speed; - - return rc; } /* @@ -401,11 +395,9 @@ static unsigned int __devinit init_chipset_sl82c105(struct pci_dev *dev, const c /* * Initialise the chip */ - static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) { unsigned int rev; - u8 dma_state; DBG(("init_hwif_sl82c105(hwif: ide%d)\n", hwif->index)); @@ -431,7 +423,6 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) if (!hwif->dma_base) return; - dma_state = hwif->INB(hwif->dma_base + 2) & ~0x60; rev = sl82c105_bridge_revision(hwif->pci_dev); if (rev <= 5) { /* @@ -441,15 +432,12 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n", hwif->name, rev); } else { - dma_state |= 0x60; - hwif->atapi_dma = 1; - hwif->mwdma_mask = 0x07; - hwif->swdma_mask = 0x07; + hwif->mwdma_mask = 0x04; hwif->ide_dma_check = &sl82c105_check_drive; hwif->ide_dma_on = &sl82c105_ide_dma_on; - hwif->ide_dma_off_quietly = &sl82c105_ide_dma_off_quietly; + hwif->dma_off_quietly = &sl82c105_dma_off_quietly; hwif->ide_dma_lostirq = &sl82c105_ide_dma_lost_irq; hwif->dma_start = &sl82c105_ide_dma_start; hwif->ide_dma_timeout = &sl82c105_ide_dma_timeout; @@ -462,7 +450,6 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) if (hwif->mate) hwif->serialized = hwif->mate->serialized = 1; } - hwif->OUTB(dma_state, hwif->dma_base + 2); } static ide_pci_device_t sl82c105_chipset __devinitdata = { diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c index 2663ddb..852ccb3 100644 --- a/drivers/ide/pci/slc90e66.c +++ b/drivers/ide/pci/slc90e66.c @@ -1,8 +1,8 @@ /* - * linux/drivers/ide/pci/slc90e66.c Version 0.13 December 30, 2006 + * linux/drivers/ide/pci/slc90e66.c Version 0.14 February 8, 2007 * * Copyright (C) 2000-2002 Andre Hedrick <andre@linux-ide.org> - * Copyright (C) 2006 MontaVista Software, Inc. <source@mvista.com> + * Copyright (C) 2006-2007 MontaVista Software, Inc. <source@mvista.com> * * This is a look-alike variation of the ICH0 PIIX4 Ultra-66, * but this keeps the ISA-Bridge and slots alive. @@ -57,11 +57,7 @@ static u8 slc90e66_dma_2_pio (u8 xfer_rate) { } } -/* - * Based on settings done by AMI BIOS - * (might be useful if drive is not registered in CMOS for any reason). - */ -static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) +static void slc90e66_tune_pio (ide_drive_t *drive, u8 pio) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; @@ -80,7 +76,6 @@ static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) { 2, 1 }, { 2, 3 }, }; - pio = ide_get_best_pio_mode(drive, pio, 4, NULL); spin_lock_irqsave(&ide_lock, flags); pci_read_config_word(dev, master_port, &master_data); @@ -94,19 +89,20 @@ static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) master_data |= 0x4000; master_data &= ~0x0070; if (pio > 1) { - /* enable PPE, IE and TIME */ - master_data = master_data | (control << 4); + /* Set PPE, IE and TIME */ + master_data |= control << 4; } pci_read_config_byte(dev, slave_port, &slave_data); - slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); - slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 4 : 0)); + slave_data &= hwif->channel ? 0x0f : 0xf0; + slave_data |= ((timings[pio][0] << 2) | timings[pio][1]) << + (hwif->channel ? 4 : 0); } else { master_data &= ~0x3307; if (pio > 1) { /* enable PPE, IE and TIME */ - master_data = master_data | control; + master_data |= control; } - master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8); + master_data |= (timings[pio][0] << 12) | (timings[pio][1] << 8); } pci_write_config_word(dev, master_port, master_data); if (is_slave) @@ -114,6 +110,13 @@ static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) spin_unlock_irqrestore(&ide_lock, flags); } +static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio) +{ + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + slc90e66_tune_pio(drive, pio); + (void) ide_config_drive_speed(drive, XFER_PIO_0 + pio); +} + static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed) { ide_hwif_t *hwif = HWIF(drive); @@ -162,8 +165,8 @@ static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed) pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); } - slc90e66_tune_drive(drive, slc90e66_dma_2_pio(speed)); - return (ide_config_drive_speed(drive, speed)); + slc90e66_tune_pio(drive, slc90e66_dma_2_pio(speed)); + return ide_config_drive_speed(drive, speed); } static int slc90e66_config_drive_for_dma (ide_drive_t *drive) @@ -179,26 +182,15 @@ static int slc90e66_config_drive_for_dma (ide_drive_t *drive) static int slc90e66_config_drive_xfer_rate (ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - drive->init_speed = 0; - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive) && slc90e66_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); + if (ide_use_dma(drive) && slc90e66_config_drive_for_dma(drive)) + return 0; - goto fast_ata_pio; + if (ide_use_fast_pio(drive)) + slc90e66_tune_drive(drive, 255); - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: - (void) hwif->speedproc(drive, XFER_PIO_0 + - ide_get_best_pio_mode(drive, 255, 4, NULL)); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + return -1; } static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif) diff --git a/drivers/ide/pci/tc86c001.c b/drivers/ide/pci/tc86c001.c index 2ad72bb..0b6d81d 100644 --- a/drivers/ide/pci/tc86c001.c +++ b/drivers/ide/pci/tc86c001.c @@ -45,7 +45,7 @@ static int tc86c001_tune_chipset(ide_drive_t *drive, u8 speed) scr &= (speed < XFER_MW_DMA_0) ? 0xf8ff : 0xff0f; scr |= mode; - hwif->OUTW(scr, scr_port); + outw(scr, scr_port); return ide_config_drive_speed(drive, speed); } @@ -89,15 +89,15 @@ static int tc86c001_timer_expiry(ide_drive_t *drive) "attempting recovery...\n", drive->name); /* Stop DMA */ - hwif->OUTB(dma_cmd & ~0x01, hwif->dma_command); + outb(dma_cmd & ~0x01, hwif->dma_command); /* Setup the dummy DMA transfer */ - hwif->OUTW(0, sc_base + 0x0a); /* Sector Count */ - hwif->OUTW(0, twcr_port); /* Transfer Word Count 1 or 2 */ + outw(0, sc_base + 0x0a); /* Sector Count */ + outw(0, twcr_port); /* Transfer Word Count 1 or 2 */ /* Start the dummy DMA transfer */ - hwif->OUTB(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ - hwif->OUTB(0x01, hwif->dma_command); /* set START_STOPBM */ + outb(0x00, hwif->dma_command); /* clear R_OR_WCTR for write */ + outb(0x01, hwif->dma_command); /* set START_STOPBM */ /* * If an interrupt was pending, it should come thru shortly. @@ -128,8 +128,8 @@ static void tc86c001_dma_start(ide_drive_t *drive) * the appropriate system control registers for DMA to work * with LBA48 and ATAPI devices... */ - hwif->OUTW(nsectors, sc_base + 0x0a); /* Sector Count */ - hwif->OUTW(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */ + outw(nsectors, sc_base + 0x0a); /* Sector Count */ + outw(SECTOR_SIZE / 2, twcr_port); /* Transfer Word Count 1/2 */ /* Install our timeout expiry hook, saving the current handler... */ ide_set_hwifdata(hwif, hwgroup->expiry); @@ -168,7 +168,7 @@ static int tc86c001_busproc(ide_drive_t *drive, int state) } /* System Control 1 Register bit 11 (ATA Hard Reset) write */ - hwif->OUTW(scr1, sc_base + 0x00); + outw(scr1, sc_base + 0x00); return 0; } @@ -185,23 +185,13 @@ static int config_chipset_for_dma(ide_drive_t *drive) static int tc86c001_config_drive_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - - if ((id->capability & 1) && drive->autodma) { - - if (ide_use_dma(drive) && config_chipset_for_dma(drive)) - return hwif->ide_dma_on(drive); - - goto fast_ata_pio; + if (ide_use_dma(drive) && config_chipset_for_dma(drive)) + return 0; - } else if ((id->capability & 8) || (id->field_valid & 2)) { -fast_ata_pio: + if (ide_use_fast_pio(drive)) tc86c001_tune_drive(drive, 255); - return hwif->ide_dma_off_quietly(drive); - } - /* IORDY not supported */ - return 0; + + return -1; } static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif) @@ -210,13 +200,13 @@ static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif) u16 scr1 = hwif->INW(sc_base + 0x00);; /* System Control 1 Register bit 15 (Soft Reset) set */ - hwif->OUTW(scr1 | 0x8000, sc_base + 0x00); + outw(scr1 | 0x8000, sc_base + 0x00); /* System Control 1 Register bit 14 (FIFO Reset) set */ - hwif->OUTW(scr1 | 0x4000, sc_base + 0x00); + outw(scr1 | 0x4000, sc_base + 0x00); /* System Control 1 Register: reset clear */ - hwif->OUTW(scr1 & ~0xc000, sc_base + 0x00); + outw(scr1 & ~0xc000, sc_base + 0x00); /* Store the system control register base for convenience... */ hwif->config_data = sc_base; @@ -234,7 +224,7 @@ static void __devinit init_hwif_tc86c001(ide_hwif_t *hwif) * Sector Count Control Register bits 0 and 1 set: * software sets Sector Count Register for master and slave device */ - hwif->OUTW(0x0003, sc_base + 0x0c); + outw(0x0003, sc_base + 0x0c); /* Sector Count Register limit */ hwif->rqsize = 0xffff; diff --git a/drivers/ide/pci/triflex.c b/drivers/ide/pci/triflex.c index b13cce1..5e06179 100644 --- a/drivers/ide/pci/triflex.c +++ b/drivers/ide/pci/triflex.c @@ -104,29 +104,21 @@ static int triflex_config_drive_for_dma(ide_drive_t *drive) { int speed = ide_dma_speed(drive, 0); /* No ultra speeds */ - if (!speed) { - u8 pspeed = ide_get_best_pio_mode(drive, 255, 4, NULL); - speed = XFER_PIO_0 + pspeed; - } - + if (!speed) + return 0; + (void) triflex_tune_chipset(drive, speed); return ide_dma_enable(drive); } static int triflex_config_drive_xfer_rate(ide_drive_t *drive) { - ide_hwif_t *hwif = HWIF(drive); - struct hd_driveid *id = drive->id; - - if ((id->capability & 1) && drive->autodma) { - if (ide_use_dma(drive)) { - if (triflex_config_drive_for_dma(drive)) - return hwif->ide_dma_on(drive); - } - } + if (ide_use_dma(drive) && triflex_config_drive_for_dma(drive)) + return 0; + + triflex_tune_drive(drive, 255); - hwif->tuneproc(drive, 255); - return hwif->ide_dma_off_quietly(drive); + return -1; } static void __devinit init_hwif_triflex(ide_hwif_t *hwif) diff --git a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c index 174b88c..cbb1b11 100644 --- a/drivers/ide/pci/trm290.c +++ b/drivers/ide/pci/trm290.c @@ -157,16 +157,16 @@ static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) if (reg != hwif->select_data) { hwif->select_data = reg; /* set PIO/DMA */ - hwif->OUTB(0x51|(hwif->channel<<3), hwif->config_data+1); - hwif->OUTW(reg & 0xff, hwif->config_data); + outb(0x51 | (hwif->channel << 3), hwif->config_data + 1); + outw(reg & 0xff, hwif->config_data); } /* enable IRQ if not probing */ if (drive->present) { - reg = hwif->INW(hwif->config_data + 3); + reg = inw(hwif->config_data + 3); reg &= 0x13; reg &= ~(1 << hwif->channel); - hwif->OUTW(reg, hwif->config_data+3); + outw(reg, hwif->config_data + 3); } local_irq_restore(flags); @@ -177,15 +177,12 @@ static void trm290_selectproc (ide_drive_t *drive) trm290_prepare_drive(drive, drive->using_dma); } -#ifdef CONFIG_BLK_DEV_IDEDMA static void trm290_ide_dma_exec_cmd(ide_drive_t *drive, u8 command) { - ide_hwif_t *hwif = HWIF(drive); - BUG_ON(HWGROUP(drive)->handler != NULL); /* paranoia check */ ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); /* issue cmd to drive */ - hwif->OUTB(command, IDE_COMMAND_REG); + outb(command, IDE_COMMAND_REG); } static int trm290_ide_dma_setup(ide_drive_t *drive) @@ -211,10 +208,10 @@ static int trm290_ide_dma_setup(ide_drive_t *drive) } /* select DMA xfer */ trm290_prepare_drive(drive, 1); - hwif->OUTL(hwif->dmatable_dma|rw, hwif->dma_command); + outl(hwif->dmatable_dma | rw, hwif->dma_command); drive->waiting_for_dma = 1; /* start DMA */ - hwif->OUTW((count * 2) - 1, hwif->dma_status); + outw((count * 2) - 1, hwif->dma_status); return 0; } @@ -230,7 +227,7 @@ static int trm290_ide_dma_end (ide_drive_t *drive) drive->waiting_for_dma = 0; /* purge DMA mappings */ ide_destroy_dmatable(drive); - status = hwif->INW(hwif->dma_status); + status = inw(hwif->dma_status); return (status != 0x00ff); } @@ -239,10 +236,9 @@ static int trm290_ide_dma_test_irq (ide_drive_t *drive) ide_hwif_t *hwif = HWIF(drive); u16 status = 0; - status = hwif->INW(hwif->dma_status); + status = inw(hwif->dma_status); return (status == 0x00ff); } -#endif /* CONFIG_BLK_DEV_IDEDMA */ /* * Invoked from ide-dma.c at boot time. @@ -269,15 +265,15 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) local_irq_save(flags); /* put config reg into first byte of hwif->select_data */ - hwif->OUTB(0x51|(hwif->channel<<3), hwif->config_data+1); + outb(0x51 | (hwif->channel << 3), hwif->config_data + 1); /* select PIO as default */ hwif->select_data = 0x21; - hwif->OUTB(hwif->select_data, hwif->config_data); + outb(hwif->select_data, hwif->config_data); /* get IRQ info */ - reg = hwif->INB(hwif->config_data+3); + reg = inb(hwif->config_data + 3); /* mask IRQs for both ports */ reg = (reg & 0x10) | 0x03; - hwif->OUTB(reg, hwif->config_data+3); + outb(reg, hwif->config_data + 3); local_irq_restore(flags); if ((reg & 0x10)) @@ -289,13 +285,11 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3); -#ifdef CONFIG_BLK_DEV_IDEDMA hwif->dma_setup = &trm290_ide_dma_setup; hwif->dma_exec_cmd = &trm290_ide_dma_exec_cmd; hwif->dma_start = &trm290_ide_dma_start; hwif->ide_dma_end = &trm290_ide_dma_end; hwif->ide_dma_test_irq = &trm290_ide_dma_test_irq; -#endif /* CONFIG_BLK_DEV_IDEDMA */ hwif->selectproc = &trm290_selectproc; hwif->autodma = 0; /* play it safe for now */ @@ -312,16 +306,16 @@ static void __devinit init_hwif_trm290(ide_hwif_t *hwif) static u16 next_offset = 0; u8 old_mask; - hwif->OUTB(0x54|(hwif->channel<<3), hwif->config_data+1); - old = hwif->INW(hwif->config_data); + outb(0x54 | (hwif->channel << 3), hwif->config_data + 1); + old = inw(hwif->config_data); old &= ~1; - old_mask = hwif->INB(old+2); + old_mask = inb(old + 2); if (old != compat && old_mask == 0xff) { /* leave lower 10 bits untouched */ compat += (next_offset += 0x400); hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2; - hwif->OUTW(compat|1, hwif->config_data); - new = hwif->INW(hwif->config_data); + outw(compat | 1, hwif->config_data); + new = inw(hwif->config_data); printk(KERN_INFO "%s: control basereg workaround: " "old=0x%04x, new=0x%04x\n", hwif->name, old, new & ~1); diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c index 6fb6e50..a508550 100644 --- a/drivers/ide/pci/via82cxxx.c +++ b/drivers/ide/pci/via82cxxx.c @@ -240,8 +240,9 @@ static int via82cxxx_ide_dma_check (ide_drive_t *drive) via_set_drive(drive, speed); if (drive->autodma && (speed & XFER_MODE) != XFER_PIO) - return hwif->ide_dma_on(drive); - return hwif->ide_dma_off_quietly(drive); + return 0; + + return -1; } static struct via_isa_bridge *via_config_find(struct pci_dev **isa) diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c index d8ea237..071a030 100644 --- a/drivers/ide/ppc/pmac.c +++ b/drivers/ide/ppc/pmac.c @@ -48,7 +48,7 @@ #include <asm/mediabay.h> #endif -#include "ide-timing.h" +#include "../ide-timing.h" #undef IDE_PMAC_DEBUG @@ -1237,7 +1237,7 @@ pmac_ide_setup_device(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) hwif->OUTBSYNC = pmac_outbsync; /* Tell common code _not_ to mess with resources */ - hwif->mmio = 2; + hwif->mmio = 1; hwif->hwif_data = pmif; pmac_ide_init_hwif_ports(&hwif->hw, pmif->regbase, 0, &hwif->irq); memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); @@ -1551,19 +1551,34 @@ static struct pci_driver pmac_ide_pci_driver = { }; MODULE_DEVICE_TABLE(pci, pmac_ide_pci_match); -void __init -pmac_ide_probe(void) +int __init pmac_ide_probe(void) { + int error; + if (!machine_is(powermac)) - return; + return -ENODEV; #ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST - pci_register_driver(&pmac_ide_pci_driver); - macio_register_driver(&pmac_ide_macio_driver); + error = pci_register_driver(&pmac_ide_pci_driver); + if (error) + goto out; + error = macio_register_driver(&pmac_ide_macio_driver); + if (error) { + pci_unregister_driver(&pmac_ide_pci_driver); + goto out; + } #else - macio_register_driver(&pmac_ide_macio_driver); - pci_register_driver(&pmac_ide_pci_driver); + error = macio_register_driver(&pmac_ide_macio_driver); + if (error) + goto out; + error = pci_register_driver(&pmac_ide_pci_driver); + if (error) { + macio_unregister_driver(&pmac_ide_macio_driver); + goto out; + } #endif +out: + return error; } #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC @@ -1979,16 +1994,12 @@ pmac_ide_dma_test_irq (ide_drive_t *drive) return 1; } -static int -pmac_ide_dma_host_off (ide_drive_t *drive) +static void pmac_ide_dma_host_off(ide_drive_t *drive) { - return 0; } -static int -pmac_ide_dma_host_on (ide_drive_t *drive) +static void pmac_ide_dma_host_on(ide_drive_t *drive) { - return 0; } static int @@ -2034,7 +2045,7 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) return; } - hwif->ide_dma_off_quietly = &__ide_dma_off_quietly; + hwif->dma_off_quietly = &ide_dma_off_quietly; hwif->ide_dma_on = &__ide_dma_on; hwif->ide_dma_check = &pmac_ide_dma_check; hwif->dma_setup = &pmac_ide_dma_setup; @@ -2042,8 +2053,8 @@ pmac_ide_setup_dma(pmac_ide_hwif_t *pmif, ide_hwif_t *hwif) hwif->dma_start = &pmac_ide_dma_start; hwif->ide_dma_end = &pmac_ide_dma_end; hwif->ide_dma_test_irq = &pmac_ide_dma_test_irq; - hwif->ide_dma_host_off = &pmac_ide_dma_host_off; - hwif->ide_dma_host_on = &pmac_ide_dma_host_on; + hwif->dma_host_off = &pmac_ide_dma_host_off; + hwif->dma_host_on = &pmac_ide_dma_host_on; hwif->ide_dma_timeout = &__ide_dma_timeout; hwif->ide_dma_lostirq = &pmac_ide_dma_lostirq; diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c index a52c80f..118fb32 100644 --- a/drivers/ide/setup-pci.c +++ b/drivers/ide/setup-pci.c @@ -505,11 +505,6 @@ static void ide_hwif_setup_dma(struct pci_dev *dev, ide_pci_device_t *d, ide_hwi } } } - -#ifndef CONFIG_IDEDMA_PCI_AUTO -#warning CONFIG_IDEDMA_PCI_AUTO=n support is obsolete, and will be removed soon. -#endif - #endif /* CONFIG_BLK_DEV_IDEDMA_PCI*/ /** diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index b8a4734..61d7809 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -1,11 +1,8 @@ -# -*- shell-script -*- - menu "IEEE 1394 (FireWire) support" config IEEE1394 tristate "IEEE 1394 (FireWire) support" depends on PCI || BROKEN - select NET help IEEE 1394 describes a high performance serial bus, which is also known as FireWire(tm) or i.Link(tm) and is used for connecting all @@ -35,24 +32,7 @@ config IEEE1394_VERBOSEDEBUG Say Y if you really want or need the debugging output, everyone else says N. -config IEEE1394_EXTRA_CONFIG_ROMS - bool "Build in extra config rom entries for certain functionality" - depends on IEEE1394 - help - Some IEEE1394 functionality depends on extra config rom entries - being available in the host adapters CSR. These options will - allow you to choose which ones. - -config IEEE1394_CONFIG_ROM_IP1394 - bool "IP-1394 Entry" - depends on IEEE1394_EXTRA_CONFIG_ROMS && IEEE1394 - help - Adds an entry for using IP-over-1394. If you want to use your - IEEE1394 bus as a network for IP systems (including interacting - with MacOSX and WinXP IP-over-1394), enable this option and the - eth1394 option below. - -comment "Device Drivers" +comment "Controllers" depends on IEEE1394 comment "Texas Instruments PCILynx requires I2C" @@ -70,6 +50,10 @@ config IEEE1394_PCILYNX To compile this driver as a module, say M here: the module will be called pcilynx. + Only some old and now very rare PCI and CardBus cards and + PowerMacs G3 B&W contain the PCILynx controller. Therefore + almost everybody can say N here. + config IEEE1394_OHCI1394 tristate "OHCI-1394 support" depends on PCI && IEEE1394 @@ -83,7 +67,7 @@ config IEEE1394_OHCI1394 To compile this driver as a module, say M here: the module will be called ohci1394. -comment "Protocol Drivers" +comment "Protocols" depends on IEEE1394 config IEEE1394_VIDEO1394 @@ -121,11 +105,15 @@ config IEEE1394_SBP2_PHYS_DMA This option is buggy and currently broken on some architectures. If unsure, say N. +config IEEE1394_ETH1394_ROM_ENTRY + depends on IEEE1394 + bool + default n + config IEEE1394_ETH1394 - tristate "Ethernet over 1394" + tristate "IP over 1394" depends on IEEE1394 && EXPERIMENTAL && INET - select IEEE1394_CONFIG_ROM_IP1394 - select IEEE1394_EXTRA_CONFIG_ROMS + select IEEE1394_ETH1394_ROM_ENTRY help This driver implements a functional majority of RFC 2734: IPv4 over 1394. It will provide IP connectivity with implementations of RFC @@ -134,24 +122,26 @@ config IEEE1394_ETH1394 This driver is still considered experimental. It does not yet support MCAP, therefore multicast support is significantly limited. + The module is called eth1394 although it does not emulate Ethernet. + config IEEE1394_DV1394 tristate "OHCI-DV I/O support (deprecated)" depends on IEEE1394 && IEEE1394_OHCI1394 help - The dv1394 driver will be removed from Linux in a future release. - Its functionality is now provided by raw1394 together with libraries - such as libiec61883. + The dv1394 driver is unsupported and may be removed from Linux in a + future release. Its functionality is now provided by raw1394 together + with libraries such as libiec61883. config IEEE1394_RAWIO tristate "Raw IEEE1394 I/O support" depends on IEEE1394 help - Say Y here if you want support for the raw device. This is generally - a good idea, so you should say Y here. The raw device enables - direct communication of user programs with the IEEE 1394 bus and - thus with the attached peripherals. + This option adds support for the raw1394 device file which enables + direct communication of user programs with the IEEE 1394 bus and thus + with the attached peripherals. Almost all application programs which + access FireWire require this option. - To compile this driver as a module, say M here: the - module will be called raw1394. + To compile this driver as a module, say M here: the module will be + called raw1394. endmenu diff --git a/drivers/ieee1394/config_roms.c b/drivers/ieee1394/config_roms.c index e2de6fa..1b98120 100644 --- a/drivers/ieee1394/config_roms.c +++ b/drivers/ieee1394/config_roms.c @@ -26,12 +26,6 @@ struct hpsb_config_rom_entry { /* Base initialization, called at module load */ int (*init)(void); - /* Add entry to specified host */ - int (*add)(struct hpsb_host *host); - - /* Remove entry from specified host */ - void (*remove)(struct hpsb_host *host); - /* Cleanup called at module exit */ void (*cleanup)(void); @@ -39,7 +33,7 @@ struct hpsb_config_rom_entry { unsigned int flag; }; - +/* The default host entry. This must succeed. */ int hpsb_default_host_entry(struct hpsb_host *host) { struct csr1212_keyval *root; @@ -63,9 +57,9 @@ int hpsb_default_host_entry(struct hpsb_host *host) return -ENOMEM; } - ret = csr1212_associate_keyval(vend_id, text); + csr1212_associate_keyval(vend_id, text); csr1212_release_keyval(text); - ret |= csr1212_attach_keyval_to_directory(root, vend_id); + ret = csr1212_attach_keyval_to_directory(root, vend_id); csr1212_release_keyval(vend_id); if (ret != CSR1212_SUCCESS) { csr1212_destroy_csr(host->csr.rom); @@ -78,7 +72,7 @@ int hpsb_default_host_entry(struct hpsb_host *host) } -#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY #include "eth1394.h" static struct csr1212_keyval *ip1394_ud; @@ -103,10 +97,12 @@ static int config_rom_ip1394_init(void) if (!ip1394_ud || !spec_id || !spec_desc || !ver || !ver_desc) goto ip1394_fail; - if (csr1212_associate_keyval(spec_id, spec_desc) == CSR1212_SUCCESS && - csr1212_associate_keyval(ver, ver_desc) == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) == CSR1212_SUCCESS && - csr1212_attach_keyval_to_directory(ip1394_ud, ver) == CSR1212_SUCCESS) + csr1212_associate_keyval(spec_id, spec_desc); + csr1212_associate_keyval(ver, ver_desc); + if (csr1212_attach_keyval_to_directory(ip1394_ud, spec_id) + == CSR1212_SUCCESS && + csr1212_attach_keyval_to_directory(ip1394_ud, ver) + == CSR1212_SUCCESS) ret = 0; ip1394_fail: @@ -135,7 +131,7 @@ static void config_rom_ip1394_cleanup(void) } } -static int config_rom_ip1394_add(struct hpsb_host *host) +int hpsb_config_rom_ip1394_add(struct hpsb_host *host) { if (!ip1394_ud) return -ENODEV; @@ -144,92 +140,55 @@ static int config_rom_ip1394_add(struct hpsb_host *host) ip1394_ud) != CSR1212_SUCCESS) return -ENOMEM; + host->config_roms |= HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; return 0; } +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_add); -static void config_rom_ip1394_remove(struct hpsb_host *host) +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host) { csr1212_detach_keyval_from_directory(host->csr.rom->root_kv, ip1394_ud); + host->config_roms &= ~HPSB_CONFIG_ROM_ENTRY_IP1394; + host->update_config_rom = 1; } +EXPORT_SYMBOL_GPL(hpsb_config_rom_ip1394_remove); static struct hpsb_config_rom_entry ip1394_entry = { .name = "ip1394", .init = config_rom_ip1394_init, - .add = config_rom_ip1394_add, - .remove = config_rom_ip1394_remove, .cleanup = config_rom_ip1394_cleanup, .flag = HPSB_CONFIG_ROM_ENTRY_IP1394, }; -#endif /* CONFIG_IEEE1394_CONFIG_ROM_IP1394 */ +#endif /* CONFIG_IEEE1394_ETH1394_ROM_ENTRY */ static struct hpsb_config_rom_entry *const config_rom_entries[] = { -#ifdef CONFIG_IEEE1394_CONFIG_ROM_IP1394 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY &ip1394_entry, #endif - NULL, }; - +/* Initialize all config roms */ int hpsb_init_config_roms(void) { int i, error = 0; - for (i = 0; config_rom_entries[i]; i++) { - if (!config_rom_entries[i]->init) - continue; - + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) if (config_rom_entries[i]->init()) { HPSB_ERR("Failed to initialize config rom entry `%s'", config_rom_entries[i]->name); error = -1; - } else - HPSB_DEBUG("Initialized config rom entry `%s'", - config_rom_entries[i]->name); - } - - return error; -} - -void hpsb_cleanup_config_roms(void) -{ - int i; - - for (i = 0; config_rom_entries[i]; i++) { - if (config_rom_entries[i]->cleanup) - config_rom_entries[i]->cleanup(); - } -} - -int hpsb_add_extra_config_roms(struct hpsb_host *host) -{ - int i, error = 0; - - for (i = 0; config_rom_entries[i]; i++) { - if (config_rom_entries[i]->add(host)) { - HPSB_ERR("fw-host%d: Failed to attach config rom entry `%s'", - host->id, config_rom_entries[i]->name); - error = -1; - } else { - host->config_roms |= config_rom_entries[i]->flag; - host->update_config_rom = 1; } - } return error; } -void hpsb_remove_extra_config_roms(struct hpsb_host *host) +/* Cleanup all config roms */ +void hpsb_cleanup_config_roms(void) { int i; - for (i = 0; config_rom_entries[i]; i++) { - if (!(host->config_roms & config_rom_entries[i]->flag)) - continue; - - config_rom_entries[i]->remove(host); - - host->config_roms &= ~config_rom_entries[i]->flag; - host->update_config_rom = 1; - } + for (i = 0; i < ARRAY_SIZE(config_rom_entries); i++) + config_rom_entries[i]->cleanup(); } diff --git a/drivers/ieee1394/config_roms.h b/drivers/ieee1394/config_roms.h index 0a70544..1f5cd1f 100644 --- a/drivers/ieee1394/config_roms.h +++ b/drivers/ieee1394/config_roms.h @@ -1,27 +1,19 @@ #ifndef _IEEE1394_CONFIG_ROMS_H #define _IEEE1394_CONFIG_ROMS_H -#include "ieee1394_types.h" -#include "hosts.h" +struct hpsb_host; -/* The default host entry. This must succeed. */ int hpsb_default_host_entry(struct hpsb_host *host); - -/* Initialize all config roms */ int hpsb_init_config_roms(void); - -/* Cleanup all config roms */ void hpsb_cleanup_config_roms(void); -/* Add extra config roms to specified host */ -int hpsb_add_extra_config_roms(struct hpsb_host *host); - -/* Remove extra config roms from specified host */ -void hpsb_remove_extra_config_roms(struct hpsb_host *host); - - /* List of flags to check if a host contains a certain extra config rom * entry. Available in the host->config_roms member. */ #define HPSB_CONFIG_ROM_ENTRY_IP1394 0x00000001 +#ifdef CONFIG_IEEE1394_ETH1394_ROM_ENTRY +int hpsb_config_rom_ip1394_add(struct hpsb_host *host); +void hpsb_config_rom_ip1394_remove(struct hpsb_host *host); +#endif + #endif /* _IEEE1394_CONFIG_ROMS_H */ diff --git a/drivers/ieee1394/csr1212.c b/drivers/ieee1394/csr1212.c index c28f639..d08166b 100644 --- a/drivers/ieee1394/csr1212.c +++ b/drivers/ieee1394/csr1212.c @@ -31,12 +31,13 @@ /* TODO List: * - Verify interface consistency: i.e., public functions that take a size * parameter expect size to be in bytes. - * - Convenience functions for reading a block of data from a given offset. */ -#ifndef __KERNEL__ -#include <string.h> -#endif +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <asm/bug.h> +#include <asm/byteorder.h> #include "csr1212.h" @@ -46,7 +47,7 @@ #define __C (1 << CSR1212_KV_TYPE_CSR_OFFSET) #define __D (1 << CSR1212_KV_TYPE_DIRECTORY) #define __L (1 << CSR1212_KV_TYPE_LEAF) -static const u_int8_t csr1212_key_id_type_map[0x30] = { +static const u8 csr1212_key_id_type_map[0x30] = { __C, /* used by Apple iSight */ __D | __L, /* Descriptor */ __I | __D | __L, /* Bus_Dependent_Info */ @@ -82,10 +83,10 @@ static const u_int8_t csr1212_key_id_type_map[0x30] = { #undef __L -#define quads_to_bytes(_q) ((_q) * sizeof(u_int32_t)) -#define bytes_to_quads(_b) (((_b) + sizeof(u_int32_t) - 1) / sizeof(u_int32_t)) +#define quads_to_bytes(_q) ((_q) * sizeof(u32)) +#define bytes_to_quads(_b) (((_b) + sizeof(u32) - 1) / sizeof(u32)) -static inline void free_keyval(struct csr1212_keyval *kv) +static void free_keyval(struct csr1212_keyval *kv) { if ((kv->key.type == CSR1212_KV_TYPE_LEAF) && (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM)) @@ -94,14 +95,14 @@ static inline void free_keyval(struct csr1212_keyval *kv) CSR1212_FREE(kv); } -static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) +static u16 csr1212_crc16(const u32 *buffer, size_t length) { int shift; - u_int32_t data; - u_int16_t sum, crc = 0; + u32 data; + u16 sum, crc = 0; for (; length; length--) { - data = CSR1212_BE32_TO_CPU(*buffer); + data = be32_to_cpu(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; @@ -110,21 +111,18 @@ static u_int16_t csr1212_crc16(const u_int32_t *buffer, size_t length) crc &= 0xffff; } - return CSR1212_CPU_TO_BE16(crc); + return cpu_to_be16(crc); } -#if 0 -/* Microsoft computes the CRC with the bytes in reverse order. Therefore we - * have a special version of the CRC algorithm to account for their buggy - * software. */ -static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) +/* Microsoft computes the CRC with the bytes in reverse order. */ +static u16 csr1212_msft_crc16(const u32 *buffer, size_t length) { int shift; - u_int32_t data; - u_int16_t sum, crc = 0; + u32 data; + u16 sum, crc = 0; for (; length; length--) { - data = CSR1212_LE32_TO_CPU(*buffer); + data = le32_to_cpu(*buffer); buffer++; for (shift = 28; shift >= 0; shift -= 4 ) { sum = ((crc >> 12) ^ (data >> shift)) & 0xf; @@ -133,38 +131,35 @@ static u_int16_t csr1212_msft_crc16(const u_int32_t *buffer, size_t length) crc &= 0xffff; } - return CSR1212_CPU_TO_BE16(crc); + return cpu_to_be16(crc); } -#endif -static inline struct csr1212_dentry *csr1212_find_keyval(struct csr1212_keyval *dir, - struct csr1212_keyval *kv) +static struct csr1212_dentry * +csr1212_find_keyval(struct csr1212_keyval *dir, struct csr1212_keyval *kv) { struct csr1212_dentry *pos; for (pos = dir->value.directory.dentries_head; - pos != NULL; pos = pos->next) { + pos != NULL; pos = pos->next) if (pos->kv == kv) return pos; - } return NULL; } - -static inline struct csr1212_keyval *csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, - u_int32_t offset) +static struct csr1212_keyval * +csr1212_find_keyval_offset(struct csr1212_keyval *kv_list, u32 offset) { struct csr1212_keyval *kv; - for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) { + for (kv = kv_list->next; kv && (kv != kv_list); kv = kv->next) if (kv->offset == offset) return kv; - } return NULL; } /* Creation Routines */ + struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, size_t bus_info_size, void *private) { @@ -202,27 +197,17 @@ struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, return csr; } - - void csr1212_init_local_csr(struct csr1212_csr *csr, - const u_int32_t *bus_info_data, int max_rom) + const u32 *bus_info_data, int max_rom) { static const int mr_map[] = { 4, 64, 1024, 0 }; -#ifdef __KERNEL__ BUG_ON(max_rom & ~0x3); csr->max_rom = mr_map[max_rom]; -#else - if (max_rom & ~0x3) /* caller supplied invalid argument */ - csr->max_rom = 0; - else - csr->max_rom = mr_map[max_rom]; -#endif memcpy(csr->bus_info_data, bus_info_data, csr->bus_info_len); } - -static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key) +static struct csr1212_keyval *csr1212_new_keyval(u8 type, u8 key) { struct csr1212_keyval *kv; @@ -246,10 +231,11 @@ static struct csr1212_keyval *csr1212_new_keyval(u_int8_t type, u_int8_t key) return kv; } -struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) +struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_IMMEDIATE, key); if (!kv) return NULL; @@ -258,10 +244,12 @@ struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value) return kv; } -struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t data_len) +static struct csr1212_keyval * +csr1212_new_leaf(u8 key, const void *data, size_t data_len) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, key); if (!kv) return NULL; @@ -285,10 +273,12 @@ struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, size_t d return kv; } -struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset) +static struct csr1212_keyval * +csr1212_new_csr_offset(u8 key, u32 csr_offset) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_CSR_OFFSET, key); if (!kv) return NULL; @@ -299,10 +289,11 @@ struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, u_int32_t csr_offset return kv; } -struct csr1212_keyval *csr1212_new_directory(u_int8_t key) +struct csr1212_keyval *csr1212_new_directory(u8 key) { - struct csr1212_keyval *kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); + struct csr1212_keyval *kv; + kv = csr1212_new_keyval(CSR1212_KV_TYPE_DIRECTORY, key); if (!kv) return NULL; @@ -314,43 +305,29 @@ struct csr1212_keyval *csr1212_new_directory(u_int8_t key) return kv; } -int csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate) +void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate) { - if (!kv || !associate) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_DESCRIPTOR || - (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && - associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && - associate->key.id < 0x30)) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && - associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) - return CSR1212_EINVAL; - - if (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && - associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) - return CSR1212_EINVAL; - - if (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) - return CSR1212_EINVAL; - - if (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && - kv->key.id != CSR1212_KV_ID_EXTENDED_KEY) - return CSR1212_EINVAL; + BUG_ON(!kv || !associate || kv->key.id == CSR1212_KV_ID_DESCRIPTOR || + (associate->key.id != CSR1212_KV_ID_DESCRIPTOR && + associate->key.id != CSR1212_KV_ID_DEPENDENT_INFO && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA && + associate->key.id < 0x30) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID && + associate->key.id != CSR1212_KV_ID_EXTENDED_KEY) || + (kv->key.id == CSR1212_KV_ID_EXTENDED_KEY && + associate->key.id != CSR1212_KV_ID_EXTENDED_DATA) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_KEY && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) || + (associate->key.id == CSR1212_KV_ID_EXTENDED_DATA && + kv->key.id != CSR1212_KV_ID_EXTENDED_KEY)); if (kv->associate) csr1212_release_keyval(kv->associate); associate->refcnt++; kv->associate = associate; - - return CSR1212_SUCCESS; } int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, @@ -358,12 +335,11 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, { struct csr1212_dentry *dentry; - if (!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY) - return CSR1212_EINVAL; + BUG_ON(!kv || !dir || dir->key.type != CSR1212_KV_TYPE_DIRECTORY); dentry = CSR1212_MALLOC(sizeof(*dentry)); if (!dentry) - return CSR1212_ENOMEM; + return -ENOMEM; dentry->kv = kv; @@ -382,66 +358,22 @@ int csr1212_attach_keyval_to_directory(struct csr1212_keyval *dir, return CSR1212_SUCCESS; } -struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, u_int32_t key, - u_int32_t value) -{ - struct csr1212_keyval *kvs, *kvk, *kvv; - - kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); - kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); - kvv = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_DATA, value); - - if (!kvs || !kvk || !kvv) { - if (kvs) - free_keyval(kvs); - if (kvk) - free_keyval(kvk); - if (kvv) - free_keyval(kvv); - return NULL; - } - - /* Don't keep a local reference to the extended key or value. */ - kvk->refcnt = 0; - kvv->refcnt = 0; - - csr1212_associate_keyval(kvk, kvv); - csr1212_associate_keyval(kvs, kvk); - - return kvs; -} - -struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, u_int32_t key, - const void *data, size_t data_len) -{ - struct csr1212_keyval *kvs, *kvk, *kvv; - - kvs = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID, spec); - kvk = csr1212_new_immediate(CSR1212_KV_ID_EXTENDED_KEY, key); - kvv = csr1212_new_leaf(CSR1212_KV_ID_EXTENDED_DATA, data, data_len); - - if (!kvs || !kvk || !kvv) { - if (kvs) - free_keyval(kvs); - if (kvk) - free_keyval(kvk); - if (kvv) - free_keyval(kvv); - return NULL; - } - - /* Don't keep a local reference to the extended key or value. */ - kvk->refcnt = 0; - kvv->refcnt = 0; - - csr1212_associate_keyval(kvk, kvv); - csr1212_associate_keyval(kvs, kvk); - - return kvs; -} - -struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t specifier_id, - const void *data, size_t data_len) +#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ + (&((kv)->value.leaf.data[1])) + +#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ + ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) +#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ + ((kv)->value.leaf.data[0] = \ + cpu_to_be32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ + ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + +static struct csr1212_keyval * +csr1212_new_descriptor_leaf(u8 dtype, u32 specifier_id, + const void *data, size_t data_len) { struct csr1212_keyval *kv; @@ -453,197 +385,72 @@ struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, u_int32_t spe CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, dtype); CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, specifier_id); - if (data) { + if (data) memcpy(CSR1212_DESCRIPTOR_LEAF_DATA(kv), data, data_len); - } - - return kv; -} - - -struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, - u_int16_t cset, - u_int16_t language, - const void *data, - size_t data_len) -{ - struct csr1212_keyval *kv; - char *lstr; - - kv = csr1212_new_descriptor_leaf(0, 0, NULL, data_len + - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); - if (!kv) - return NULL; - - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, cwidth); - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, cset); - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); - - lstr = (char*)CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); - - /* make sure last quadlet is zeroed out */ - *((u_int32_t*)&(lstr[(data_len - 1) & ~0x3])) = 0; - - /* don't copy the NUL terminator */ - memcpy(lstr, data, data_len); return kv; } +/* Check if string conforms to minimal ASCII as per IEEE 1212 clause 7.4 */ static int csr1212_check_minimal_ascii(const char *s) { static const char minimal_ascii_table[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x0a, 0x00, 0x0C, 0x0D, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x21, 0x22, 0x00, 0x00, 0x25, 0x26, 0x27, - 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, - 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, - 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, - 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, - 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, - 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + /* 1 2 4 8 16 32 64 128 */ + 128, /* --, --, --, --, --, --, --, 07, */ + 4 + 16 + 32, /* --, --, 0a, --, 0C, 0D, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 0, /* --, --, --, --, --, --, --, --, */ + 255 - 8 - 16, /* 20, 21, 22, --, --, 25, 26, 27, */ + 255, /* 28, 29, 2a, 2b, 2c, 2d, 2e, 2f, */ + 255, /* 30, 31, 32, 33, 34, 35, 36, 37, */ + 255, /* 38, 39, 3a, 3b, 3c, 3d, 3e, 3f, */ + 255, /* 40, 41, 42, 43, 44, 45, 46, 47, */ + 255, /* 48, 49, 4a, 4b, 4c, 4d, 4e, 4f, */ + 255, /* 50, 51, 52, 53, 54, 55, 56, 57, */ + 1 + 2 + 4 + 128, /* 58, 59, 5a, --, --, --, --, 5f, */ + 255 - 1, /* --, 61, 62, 63, 64, 65, 66, 67, */ + 255, /* 68, 69, 6a, 6b, 6c, 6d, 6e, 6f, */ + 255, /* 70, 71, 72, 73, 74, 75, 76, 77, */ + 1 + 2 + 4, /* 78, 79, 7a, --, --, --, --, --, */ }; + int i, j; + for (; *s; s++) { - if (minimal_ascii_table[*s & 0x7F] != *s) - return -1; /* failed */ + i = *s >> 3; /* i = *s / 8; */ + j = 1 << (*s & 3); /* j = 1 << (*s % 8); */ + + if (i >= ARRAY_SIZE(minimal_ascii_table) || + !(minimal_ascii_table[i] & j)) + return -EINVAL; } - /* String conforms to minimal-ascii, as specified by IEEE 1212, - * par. 7.4 */ return 0; } +/* IEEE 1212 clause 7.5.4.1 textual descriptors (English, minimal ASCII) */ struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s) { - /* Check if string conform to minimal_ascii format */ - if (csr1212_check_minimal_ascii(s)) - return NULL; - - /* IEEE 1212, par. 7.5.4.1 Textual descriptors (minimal ASCII) */ - return csr1212_new_textual_descriptor_leaf(0, 0, 0, s, strlen(s)); -} - -struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, - u_int8_t palette_depth, - u_int8_t color_space, - u_int16_t language, - u_int16_t hscan, - u_int16_t vscan, - u_int32_t *palette, - u_int32_t *pixels) -{ - static const int pd[4] = { 0, 4, 16, 256 }; - static const int cs[16] = { 4, 2 }; struct csr1212_keyval *kv; - int palette_size; - int pixel_size = (hscan * vscan + 3) & ~0x3; + u32 *text; + size_t str_len, quads; - if (!pixels || (!palette && palette_depth) || - (palette_depth & ~0x3) || (color_space & ~0xf)) + if (!s || !*s || csr1212_check_minimal_ascii(s)) return NULL; - palette_size = pd[palette_depth] * cs[color_space]; - - kv = csr1212_new_descriptor_leaf(1, 0, NULL, - palette_size + pixel_size + - CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD); + str_len = strlen(s); + quads = bytes_to_quads(str_len); + kv = csr1212_new_descriptor_leaf(0, 0, NULL, quads_to_bytes(quads) + + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD); if (!kv) return NULL; - CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan); - CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan); - - if (palette_size) - memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv), palette, - palette_size); - - memcpy(CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(kv), pixels, pixel_size); - - return kv; -} - -struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, - u_int64_t address) -{ - struct csr1212_keyval *kv; - - /* IEEE 1212, par. 7.5.4.3 Modifiable descriptors */ - kv = csr1212_new_leaf(CSR1212_KV_ID_MODIFIABLE_DESCRIPTOR, NULL, sizeof(u_int64_t)); - if(!kv) - return NULL; - - CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, max_size); - CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, address); - CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, address); + kv->value.leaf.data[1] = 0; /* width, character_set, language */ + text = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv); + text[quads - 1] = 0; /* padding */ + memcpy(text, s, str_len); return kv; } -static int csr1212_check_keyword(const char *s) -{ - for (; *s; s++) { - - if (('A' <= *s) && (*s <= 'Z')) - continue; - if (('0' <= *s) && (*s <= '9')) - continue; - if (*s == '-') - continue; - - return -1; /* failed */ - } - /* String conforms to keyword, as specified by IEEE 1212, - * par. 7.6.5 */ - return CSR1212_SUCCESS; -} - -struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, const char *strv[]) -{ - struct csr1212_keyval *kv; - char *buffer; - int i, data_len = 0; - - /* Check all keywords to see if they conform to restrictions: - * Only the following characters is allowed ['A'..'Z','0'..'9','-'] - * Each word is zero-terminated. - * Also calculate the total length of the keywords. - */ - for (i = 0; i < strc; i++) { - if (!strv[i] || csr1212_check_keyword(strv[i])) { - return NULL; - } - data_len += strlen(strv[i]) + 1; /* Add zero-termination char. */ - } - - /* IEEE 1212, par. 7.6.5 Keyword leaves */ - kv = csr1212_new_leaf(CSR1212_KV_ID_KEYWORD, NULL, data_len); - if (!kv) - return NULL; - - buffer = (char *)kv->value.leaf.data; - - /* make sure last quadlet is zeroed out */ - *((u_int32_t*)&(buffer[(data_len - 1) & ~0x3])) = 0; - - /* Copy keyword(s) into leaf data buffer */ - for (i = 0; i < strc; i++) { - int len = strlen(strv[i]) + 1; - memcpy(buffer, strv[i], len); - buffer += len; - } - return kv; -} - /* Destruction Routines */ @@ -674,23 +481,12 @@ void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, csr1212_release_keyval(kv); } - -void csr1212_disassociate_keyval(struct csr1212_keyval *kv) -{ - if (kv->associate) { - csr1212_release_keyval(kv->associate); - } - - kv->associate = NULL; -} - - /* This function is used to free the memory taken by a keyval. If the given * keyval is a directory type, then any keyvals contained in that directory * will be destroyed as well if their respective refcnts are 0. By means of * list manipulation, this routine will descend a directory structure in a * non-recursive manner. */ -void _csr1212_destroy_keyval(struct csr1212_keyval *kv) +static void csr1212_destroy_keyval(struct csr1212_keyval *kv) { struct csr1212_keyval *k, *a; struct csr1212_dentry dentry; @@ -715,11 +511,13 @@ void _csr1212_destroy_keyval(struct csr1212_keyval *kv) a = k->associate; if (k->key.type == CSR1212_KV_TYPE_DIRECTORY) { - /* If the current entry is a directory, then move all + /* If the current entry is a directory, move all * the entries to the destruction list. */ if (k->value.directory.dentries_head) { - tail->next = k->value.directory.dentries_head; - k->value.directory.dentries_head->prev = tail; + tail->next = + k->value.directory.dentries_head; + k->value.directory.dentries_head->prev = + tail; tail = k->value.directory.dentries_tail; } } @@ -729,15 +527,22 @@ void _csr1212_destroy_keyval(struct csr1212_keyval *kv) head = head->next; if (head) { - if (head->prev && head->prev != &dentry) { + if (head->prev && head->prev != &dentry) CSR1212_FREE(head->prev); - } head->prev = NULL; - } else if (tail != &dentry) + } else if (tail != &dentry) { CSR1212_FREE(tail); + } } } +void csr1212_release_keyval(struct csr1212_keyval *kv) +{ + if (kv->refcnt > 1) + kv->refcnt--; + else + csr1212_destroy_keyval(kv); +} void csr1212_destroy_csr(struct csr1212_csr *csr) { @@ -763,49 +568,51 @@ void csr1212_destroy_csr(struct csr1212_csr *csr) } - /* CSR Image Creation */ static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) { struct csr1212_csr_rom_cache *cache; - u_int64_t csr_addr; + u64 csr_addr; - if (!csr || !csr->ops || !csr->ops->allocate_addr_range || - !csr->ops->release_addr || csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(!csr || !csr->ops || !csr->ops->allocate_addr_range || + !csr->ops->release_addr || csr->max_rom < 1); /* ROM size must be a multiple of csr->max_rom */ romsize = (romsize + (csr->max_rom - 1)) & ~(csr->max_rom - 1); - csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, csr->private); - if (csr_addr == CSR1212_INVALID_ADDR_SPACE) { - return CSR1212_ENOMEM; - } + csr_addr = csr->ops->allocate_addr_range(romsize, csr->max_rom, + csr->private); + if (csr_addr == CSR1212_INVALID_ADDR_SPACE) + return -ENOMEM; + if (csr_addr < CSR1212_REGISTER_SPACE_BASE) { /* Invalid address returned from allocate_addr_range(). */ csr->ops->release_addr(csr_addr, csr->private); - return CSR1212_ENOMEM; + return -ENOMEM; } - cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, romsize); + cache = csr1212_rom_cache_malloc(csr_addr - CSR1212_REGISTER_SPACE_BASE, + romsize); if (!cache) { csr->ops->release_addr(csr_addr, csr->private); - return CSR1212_ENOMEM; + return -ENOMEM; } - cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, CSR1212_KV_ID_EXTENDED_ROM); + cache->ext_rom = csr1212_new_keyval(CSR1212_KV_TYPE_LEAF, + CSR1212_KV_ID_EXTENDED_ROM); if (!cache->ext_rom) { csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); - return CSR1212_ENOMEM; + return -ENOMEM; } - if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != CSR1212_SUCCESS) { + if (csr1212_attach_keyval_to_directory(csr->root_kv, cache->ext_rom) != + CSR1212_SUCCESS) { csr1212_release_keyval(cache->ext_rom); csr->ops->release_addr(csr_addr, csr->private); CSR1212_FREE(cache); - return CSR1212_ENOMEM; + return -ENOMEM; } cache->ext_rom->offset = csr_addr - CSR1212_REGISTER_SPACE_BASE; cache->ext_rom->value.leaf.len = -1; @@ -818,8 +625,8 @@ static int csr1212_append_new_cache(struct csr1212_csr *csr, size_t romsize) return CSR1212_SUCCESS; } -static inline void csr1212_remove_cache(struct csr1212_csr *csr, - struct csr1212_csr_rom_cache *cache) +static void csr1212_remove_cache(struct csr1212_csr *csr, + struct csr1212_csr_rom_cache *cache) { if (csr->cache_head == cache) csr->cache_head = cache->next; @@ -832,7 +639,8 @@ static inline void csr1212_remove_cache(struct csr1212_csr *csr, cache->next->prev = cache->prev; if (cache->ext_rom) { - csr1212_detach_keyval_from_directory(csr->root_kv, cache->ext_rom); + csr1212_detach_keyval_from_directory(csr->root_kv, + cache->ext_rom); csr1212_release_keyval(cache->ext_rom); } @@ -852,28 +660,29 @@ static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, dentry = dentry->next) { for (dkv = dentry->kv; dkv; dkv = dkv->associate) { /* Special Case: Extended Key Specifier_ID */ - if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) { + if (dkv->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) last_extkey_spec = dkv; - } else if (dkv->value.immediate != last_extkey_spec->value.immediate) { + else if (dkv->value.immediate != + last_extkey_spec->value.immediate) last_extkey_spec = dkv; - } else { + else continue; - } /* Special Case: Extended Key */ } else if (dkv->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) { + if (last_extkey == NULL) last_extkey = dkv; - } else if (dkv->value.immediate != last_extkey->value.immediate) { + else if (dkv->value.immediate != + last_extkey->value.immediate) last_extkey = dkv; - } else { + else continue; - } } num_entries += 1; - switch(dkv->key.type) { + switch (dkv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: @@ -891,8 +700,9 @@ static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, /* Special case: Extended ROM leafs */ if (dkv->key.id == CSR1212_KV_ID_EXTENDED_ROM) { dkv->value.leaf.len = -1; - /* Don't add Extended ROM leafs in the layout list, - * they are handled differently. */ + /* Don't add Extended ROM leafs in the + * layout list, they are handled + * differently. */ break; } @@ -908,20 +718,21 @@ static int csr1212_generate_layout_subdir(struct csr1212_keyval *dir, return num_entries; } -size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) +static size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) { struct csr1212_keyval *ltail = kv; size_t agg_size = 0; - while(kv) { - switch(kv->key.type) { + while (kv) { + switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: /* Add 1 quadlet for crc/len field */ agg_size += kv->value.leaf.len + 1; break; case CSR1212_KV_TYPE_DIRECTORY: - kv->value.directory.len = csr1212_generate_layout_subdir(kv, <ail); + kv->value.directory.len = + csr1212_generate_layout_subdir(kv, <ail); /* Add 1 quadlet for crc/len field */ agg_size += kv->value.directory.len + 1; break; @@ -931,9 +742,9 @@ size_t csr1212_generate_layout_order(struct csr1212_keyval *kv) return quads_to_bytes(agg_size); } -struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, - int start_pos) +static struct csr1212_keyval * +csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, + struct csr1212_keyval *start_kv, int start_pos) { struct csr1212_keyval *kv = start_kv; struct csr1212_keyval *okv = start_kv; @@ -942,13 +753,12 @@ struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache * cache->layout_head = kv; - while(kv && pos < cache->size) { + while (kv && pos < cache->size) { /* Special case: Extended ROM leafs */ - if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { + if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) kv->offset = cache->offset + pos; - } - switch(kv->key.type) { + switch (kv->key.type) { case CSR1212_KV_TYPE_LEAF: kv_len = kv->value.leaf.len; break; @@ -959,6 +769,7 @@ struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache * default: /* Should never get here */ + WARN_ON(1); break; } @@ -972,46 +783,55 @@ struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache * } cache->layout_tail = okv; - cache->len = (okv->offset - cache->offset) + quads_to_bytes(okv_len + 1); + cache->len = okv->offset - cache->offset + quads_to_bytes(okv_len + 1); return kv; } -static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, - u_int32_t *data_buffer) +#define CSR1212_KV_KEY_SHIFT 24 +#define CSR1212_KV_KEY_TYPE_SHIFT 6 +#define CSR1212_KV_KEY_ID_MASK 0x3f +#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* after shift */ + +static void +csr1212_generate_tree_subdir(struct csr1212_keyval *dir, u32 *data_buffer) { struct csr1212_dentry *dentry; struct csr1212_keyval *last_extkey_spec = NULL; struct csr1212_keyval *last_extkey = NULL; int index = 0; - for (dentry = dir->value.directory.dentries_head; dentry; dentry = dentry->next) { + for (dentry = dir->value.directory.dentries_head; + dentry; + dentry = dentry->next) { struct csr1212_keyval *a; for (a = dentry->kv; a; a = a->associate) { - u_int32_t value = 0; + u32 value = 0; /* Special Case: Extended Key Specifier_ID */ - if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { - if (last_extkey_spec == NULL) { + if (a->key.id == + CSR1212_KV_ID_EXTENDED_KEY_SPECIFIER_ID) { + if (last_extkey_spec == NULL) last_extkey_spec = a; - } else if (a->value.immediate != last_extkey_spec->value.immediate) { + else if (a->value.immediate != + last_extkey_spec->value.immediate) last_extkey_spec = a; - } else { + else continue; - } + /* Special Case: Extended Key */ } else if (a->key.id == CSR1212_KV_ID_EXTENDED_KEY) { - if (last_extkey == NULL) { + if (last_extkey == NULL) last_extkey = a; - } else if (a->value.immediate != last_extkey->value.immediate) { + else if (a->value.immediate != + last_extkey->value.immediate) last_extkey = a; - } else { + else continue; - } } - switch(a->key.type) { + switch (a->key.type) { case CSR1212_KV_TYPE_IMMEDIATE: value = a->value.immediate; break; @@ -1030,32 +850,46 @@ static void csr1212_generate_tree_subdir(struct csr1212_keyval *dir, break; default: /* Should never get here */ - break; /* GDB breakpoint */ + WARN_ON(1); + break; } - value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << CSR1212_KV_KEY_SHIFT; + value |= (a->key.id & CSR1212_KV_KEY_ID_MASK) << + CSR1212_KV_KEY_SHIFT; value |= (a->key.type & CSR1212_KV_KEY_TYPE_MASK) << - (CSR1212_KV_KEY_SHIFT + CSR1212_KV_KEY_TYPE_SHIFT); - data_buffer[index] = CSR1212_CPU_TO_BE32(value); + (CSR1212_KV_KEY_SHIFT + + CSR1212_KV_KEY_TYPE_SHIFT); + data_buffer[index] = cpu_to_be32(value); index++; } } } -void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) +struct csr1212_keyval_img { + u16 length; + u16 crc; + + /* Must be last */ + u32 data[0]; /* older gcc can't handle [] which is standard */ +}; + +static void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) { struct csr1212_keyval *kv, *nkv; struct csr1212_keyval_img *kvi; - for (kv = cache->layout_head; kv != cache->layout_tail->next; kv = nkv) { - kvi = (struct csr1212_keyval_img *) - (cache->data + bytes_to_quads(kv->offset - cache->offset)); - switch(kv->key.type) { + for (kv = cache->layout_head; + kv != cache->layout_tail->next; + kv = nkv) { + kvi = (struct csr1212_keyval_img *)(cache->data + + bytes_to_quads(kv->offset - cache->offset)); + switch (kv->key.type) { default: case CSR1212_KV_TYPE_IMMEDIATE: case CSR1212_KV_TYPE_CSR_OFFSET: /* Should never get here */ - break; /* GDB breakpoint */ + WARN_ON(1); + break; case CSR1212_KV_TYPE_LEAF: /* Don't copy over Extended ROM areas, they are @@ -1064,15 +898,16 @@ void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) memcpy(kvi->data, kv->value.leaf.data, quads_to_bytes(kv->value.leaf.len)); - kvi->length = CSR1212_CPU_TO_BE16(kv->value.leaf.len); + kvi->length = cpu_to_be16(kv->value.leaf.len); kvi->crc = csr1212_crc16(kvi->data, kv->value.leaf.len); break; case CSR1212_KV_TYPE_DIRECTORY: csr1212_generate_tree_subdir(kv, kvi->data); - kvi->length = CSR1212_CPU_TO_BE16(kv->value.directory.len); - kvi->crc = csr1212_crc16(kvi->data, kv->value.directory.len); + kvi->length = cpu_to_be16(kv->value.directory.len); + kvi->crc = csr1212_crc16(kvi->data, + kv->value.directory.len); break; } @@ -1086,6 +921,10 @@ void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache) } } +/* This size is arbitrarily chosen. + * The struct overhead is subtracted for more economic allocations. */ +#define CSR1212_EXTENDED_ROM_SIZE (2048 - sizeof(struct csr1212_csr_rom_cache)) + int csr1212_generate_csr_image(struct csr1212_csr *csr) { struct csr1212_bus_info_block_img *bi; @@ -1095,8 +934,7 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) int ret; int init_offset; - if (!csr) - return CSR1212_EINVAL; + BUG_ON(!csr); cache = csr->cache_head; @@ -1113,18 +951,21 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) init_offset = csr->bus_info_len; - for (kv = csr->root_kv, cache = csr->cache_head; kv; cache = cache->next) { + for (kv = csr->root_kv, cache = csr->cache_head; + kv; + cache = cache->next) { if (!cache) { /* Estimate approximate number of additional cache * regions needed (it assumes that the cache holding * the first 1K Config ROM space always exists). */ int est_c = agg_size / (CSR1212_EXTENDED_ROM_SIZE - - (2 * sizeof(u_int32_t))) + 1; + (2 * sizeof(u32))) + 1; /* Add additional cache regions, extras will be * removed later */ for (; est_c; est_c--) { - ret = csr1212_append_new_cache(csr, CSR1212_EXTENDED_ROM_SIZE); + ret = csr1212_append_new_cache(csr, + CSR1212_EXTENDED_ROM_SIZE); if (ret != CSR1212_SUCCESS) return ret; } @@ -1136,7 +977,7 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) } kv = csr1212_generate_positions(cache, kv, init_offset); agg_size -= cache->len; - init_offset = sizeof(u_int32_t); + init_offset = sizeof(u32); } /* Remove unused, excess cache regions */ @@ -1149,15 +990,14 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) /* Go through the list backward so that when done, the correct CRC * will be calculated for the Extended ROM areas. */ - for(cache = csr->cache_tail; cache; cache = cache->prev) { + for (cache = csr->cache_tail; cache; cache = cache->prev) { /* Only Extended ROM caches should have this set. */ if (cache->ext_rom) { int leaf_size; /* Make sure the Extended ROM leaf is a multiple of * max_rom in size. */ - if (csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(csr->max_rom < 1); leaf_size = (cache->len + (csr->max_rom - 1)) & ~(csr->max_rom - 1); @@ -1166,7 +1006,7 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) leaf_size - cache->len); /* Subtract leaf header */ - leaf_size -= sizeof(u_int32_t); + leaf_size -= sizeof(u32); /* Update the Extended ROM leaf length */ cache->ext_rom->value.leaf.len = @@ -1184,33 +1024,31 @@ int csr1212_generate_csr_image(struct csr1212_csr *csr) /* Set the length and CRC of the extended ROM. */ struct csr1212_keyval_img *kvi = (struct csr1212_keyval_img*)cache->data; + u16 len = bytes_to_quads(cache->len) - 1; - kvi->length = CSR1212_CPU_TO_BE16(bytes_to_quads(cache->len) - 1); - kvi->crc = csr1212_crc16(kvi->data, - bytes_to_quads(cache->len) - 1); - + kvi->length = cpu_to_be16(len); + kvi->crc = csr1212_crc16(kvi->data, len); } } return CSR1212_SUCCESS; } -int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, u_int32_t len) +int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, u32 len) { struct csr1212_csr_rom_cache *cache; - for (cache = csr->cache_head; cache; cache = cache->next) { + for (cache = csr->cache_head; cache; cache = cache->next) if (offset >= cache->offset && (offset + len) <= (cache->offset + cache->size)) { - memcpy(buffer, - &cache->data[bytes_to_quads(offset - cache->offset)], + memcpy(buffer, &cache->data[ + bytes_to_quads(offset - cache->offset)], len); return CSR1212_SUCCESS; } - } - return CSR1212_ENOENT; -} + return -ENOENT; +} /* Parse a chunk of data as a Config ROM */ @@ -1227,46 +1065,43 @@ static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) * Unfortunately, many IEEE 1394 devices do not abide by that, so the * bus info block will be read 1 quadlet at a time. The rest of the * ConfigROM will be read according to the max_rom field. */ - for (i = 0; i < csr->bus_info_len; i += sizeof(csr1212_quad_t)) { + for (i = 0; i < csr->bus_info_len; i += sizeof(u32)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - sizeof(csr1212_quad_t), - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); + sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)], + csr->private); if (ret != CSR1212_SUCCESS) return ret; /* check ROM header's info_length */ if (i == 0 && - CSR1212_BE32_TO_CPU(csr->cache_head->data[0]) >> 24 != + be32_to_cpu(csr->cache_head->data[0]) >> 24 != bytes_to_quads(csr->bus_info_len) - 1) - return CSR1212_EINVAL; + return -EINVAL; } bi = (struct csr1212_bus_info_block_img*)csr->cache_head->data; csr->crc_len = quads_to_bytes(bi->crc_length); - /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that is not - * always the case, so read the rest of the crc area 1 quadlet at a time. */ - for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(csr1212_quad_t)) { + /* IEEE 1212 recommends that crc_len be equal to bus_info_len, but that + * is not always the case, so read the rest of the crc area 1 quadlet at + * a time. */ + for (i = csr->bus_info_len; i <= csr->crc_len; i += sizeof(u32)) { ret = csr->ops->bus_read(csr, CSR1212_CONFIG_ROM_SPACE_BASE + i, - sizeof(csr1212_quad_t), - &csr->cache_head->data[bytes_to_quads(i)], - csr->private); + sizeof(u32), &csr->cache_head->data[bytes_to_quads(i)], + csr->private); if (ret != CSR1212_SUCCESS) return ret; } -#if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn. */ if ((csr1212_crc16(bi->data, bi->crc_length) != bi->crc) && (csr1212_msft_crc16(bi->data, bi->crc_length) != bi->crc)) - return CSR1212_EINVAL; -#endif + printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n"); cr = CSR1212_MALLOC(sizeof(*cr)); if (!cr) - return CSR1212_ENOMEM; + return -ENOMEM; cr->next = NULL; cr->prev = NULL; @@ -1279,21 +1114,26 @@ static int csr1212_parse_bus_info_block(struct csr1212_csr *csr) return CSR1212_SUCCESS; } -static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, - csr1212_quad_t ki, - u_int32_t kv_pos) +#define CSR1212_KV_KEY(q) (be32_to_cpu(q) >> CSR1212_KV_KEY_SHIFT) +#define CSR1212_KV_KEY_TYPE(q) (CSR1212_KV_KEY(q) >> CSR1212_KV_KEY_TYPE_SHIFT) +#define CSR1212_KV_KEY_ID(q) (CSR1212_KV_KEY(q) & CSR1212_KV_KEY_ID_MASK) +#define CSR1212_KV_VAL_MASK 0xffffff +#define CSR1212_KV_VAL(q) (be32_to_cpu(q) & CSR1212_KV_VAL_MASK) + +static int +csr1212_parse_dir_entry(struct csr1212_keyval *dir, u32 ki, u32 kv_pos) { int ret = CSR1212_SUCCESS; struct csr1212_keyval *k = NULL; - u_int32_t offset; + u32 offset; - switch(CSR1212_KV_KEY_TYPE(ki)) { + switch (CSR1212_KV_KEY_TYPE(ki)) { case CSR1212_KV_TYPE_IMMEDIATE: k = csr1212_new_immediate(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ @@ -1303,8 +1143,8 @@ static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, k = csr1212_new_csr_offset(CSR1212_KV_KEY_ID(ki), CSR1212_KV_VAL(ki)); if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ break; @@ -1316,8 +1156,8 @@ static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, /* Uh-oh. Can't have a relative offset of 0 for Leaves * or Directories. The Config ROM image is most likely * messed up, so we'll just abort here. */ - ret = CSR1212_EIO; - goto fail; + ret = -EIO; + goto out; } k = csr1212_find_keyval_offset(dir, offset); @@ -1325,14 +1165,14 @@ static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, if (k) break; /* Found it. */ - if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) { + if (CSR1212_KV_KEY_TYPE(ki) == CSR1212_KV_TYPE_DIRECTORY) k = csr1212_new_directory(CSR1212_KV_KEY_ID(ki)); - } else { + else k = csr1212_new_leaf(CSR1212_KV_KEY_ID(ki), NULL, 0); - } + if (!k) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } k->refcnt = 0; /* Don't keep local reference when parsing. */ k->valid = 0; /* Contents not read yet so it's not valid. */ @@ -1344,16 +1184,12 @@ static int csr1212_parse_dir_entry(struct csr1212_keyval *dir, dir->next = k; } ret = csr1212_attach_keyval_to_directory(dir, k); - -fail: - if (ret != CSR1212_SUCCESS) { - if (k) - free_keyval(k); - } +out: + if (ret != CSR1212_SUCCESS && k != NULL) + free_keyval(k); return ret; } - int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache) { @@ -1362,24 +1198,20 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv, int ret = CSR1212_SUCCESS; int kvi_len; - kvi = (struct csr1212_keyval_img*)&cache->data[bytes_to_quads(kv->offset - - cache->offset)]; - kvi_len = CSR1212_BE16_TO_CPU(kvi->length); + kvi = (struct csr1212_keyval_img*) + &cache->data[bytes_to_quads(kv->offset - cache->offset)]; + kvi_len = be16_to_cpu(kvi->length); -#if 0 - /* Apparently there are too many differnt wrong implementations of the - * CRC algorithm that verifying them is moot. */ + /* Apparently there are many different wrong implementations of the CRC + * algorithm. We don't fail, we just warn. */ if ((csr1212_crc16(kvi->data, kvi_len) != kvi->crc) && - (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) { - ret = CSR1212_EINVAL; - goto fail; - } -#endif + (csr1212_msft_crc16(kvi->data, kvi_len) != kvi->crc)) + printk(KERN_DEBUG "IEEE 1394 device has ROM CRC error\n"); - switch(kv->key.type) { + switch (kv->key.type) { case CSR1212_KV_TYPE_DIRECTORY: for (i = 0; i < kvi_len; i++) { - csr1212_quad_t ki = kvi->data[i]; + u32 ki = kvi->data[i]; /* Some devices put null entries in their unit * directories. If we come across such an entry, @@ -1387,76 +1219,72 @@ int csr1212_parse_keyval(struct csr1212_keyval *kv, if (ki == 0x0) continue; ret = csr1212_parse_dir_entry(kv, ki, - (kv->offset + - quads_to_bytes(i + 1))); + kv->offset + quads_to_bytes(i + 1)); } kv->value.directory.len = kvi_len; break; case CSR1212_KV_TYPE_LEAF: if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) { - kv->value.leaf.data = CSR1212_MALLOC(quads_to_bytes(kvi_len)); + size_t size = quads_to_bytes(kvi_len); + + kv->value.leaf.data = CSR1212_MALLOC(size); if (!kv->value.leaf.data) { - ret = CSR1212_ENOMEM; - goto fail; + ret = -ENOMEM; + goto out; } kv->value.leaf.len = kvi_len; - memcpy(kv->value.leaf.data, kvi->data, quads_to_bytes(kvi_len)); + memcpy(kv->value.leaf.data, kvi->data, size); } break; } kv->valid = 1; - -fail: +out: return ret; } - -int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +static int +csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) { struct csr1212_cache_region *cr, *ncr, *newcr = NULL; struct csr1212_keyval_img *kvi = NULL; struct csr1212_csr_rom_cache *cache; int cache_index; - u_int64_t addr; - u_int32_t *cache_ptr; - u_int16_t kv_len = 0; + u64 addr; + u32 *cache_ptr; + u16 kv_len = 0; - if (!csr || !kv || csr->max_rom < 1) - return CSR1212_EINVAL; + BUG_ON(!csr || !kv || csr->max_rom < 1); /* First find which cache the data should be in (or go in if not read * yet). */ - for (cache = csr->cache_head; cache; cache = cache->next) { + for (cache = csr->cache_head; cache; cache = cache->next) if (kv->offset >= cache->offset && kv->offset < (cache->offset + cache->size)) break; - } if (!cache) { - csr1212_quad_t q; - u_int32_t cache_size; + u32 q, cache_size; /* Only create a new cache for Extended ROM leaves. */ if (kv->key.id != CSR1212_KV_ID_EXTENDED_ROM) - return CSR1212_EINVAL; + return -EINVAL; if (csr->ops->bus_read(csr, CSR1212_REGISTER_SPACE_BASE + kv->offset, - sizeof(csr1212_quad_t), &q, csr->private)) { - return CSR1212_EIO; - } + sizeof(u32), &q, csr->private)) + return -EIO; - kv->value.leaf.len = CSR1212_BE32_TO_CPU(q) >> 16; + kv->value.leaf.len = be32_to_cpu(q) >> 16; cache_size = (quads_to_bytes(kv->value.leaf.len + 1) + (csr->max_rom - 1)) & ~(csr->max_rom - 1); cache = csr1212_rom_cache_malloc(kv->offset, cache_size); if (!cache) - return CSR1212_ENOMEM; + return -ENOMEM; kv->value.leaf.data = &cache->data[1]; csr->cache_tail->next = cache; @@ -1465,12 +1293,11 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) csr->cache_tail = cache; cache->filled_head = CSR1212_MALLOC(sizeof(*cache->filled_head)); - if (!cache->filled_head) { - return CSR1212_ENOMEM; - } + if (!cache->filled_head) + return -ENOMEM; cache->filled_head->offset_start = 0; - cache->filled_head->offset_end = sizeof(csr1212_quad_t); + cache->filled_head->offset_end = sizeof(u32); cache->filled_tail = cache->filled_head; cache->filled_head->next = NULL; cache->filled_head->prev = NULL; @@ -1488,7 +1315,7 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) if (cache_index < cr->offset_start) { newcr = CSR1212_MALLOC(sizeof(*newcr)); if (!newcr) - return CSR1212_ENOMEM; + return -ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; @@ -1501,18 +1328,18 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) (cache_index < cr->offset_end)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + - 1); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); break; - } else if (cache_index == cr->offset_end) + } else if (cache_index == cr->offset_end) { break; + } } if (!cr) { cr = cache->filled_tail; newcr = CSR1212_MALLOC(sizeof(*newcr)); if (!newcr) - return CSR1212_ENOMEM; + return -ENOMEM; newcr->offset_start = cache_index & ~(csr->max_rom - 1); newcr->offset_end = newcr->offset_start; @@ -1534,7 +1361,7 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) csr->private)) { if (csr->max_rom == 4) /* We've got problems! */ - return CSR1212_EIO; + return -EIO; /* Apperently the max_rom value was a lie, set it to * do quadlet reads and try again. */ @@ -1548,8 +1375,7 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) if (!kvi && (cr->offset_end > cache_index)) { kvi = (struct csr1212_keyval_img*) (&cache->data[bytes_to_quads(cache_index)]); - kv_len = quads_to_bytes(CSR1212_BE16_TO_CPU(kvi->length) + - 1); + kv_len = quads_to_bytes(be16_to_cpu(kvi->length) + 1); } if ((kv_len + (kv->offset - cache->offset)) > cache->size) { @@ -1557,7 +1383,7 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) * beyond the ConfigROM image region and thus beyond the * end of our cache region. Therefore, we abort now * rather than seg faulting later. */ - return CSR1212_EIO; + return -EIO; } ncr = cr->next; @@ -1579,7 +1405,16 @@ int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) return csr1212_parse_keyval(kv, cache); } - +struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv) +{ + if (!kv) + return NULL; + if (!kv->valid) + if (csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) + return NULL; + return kv; +} int csr1212_parse_csr(struct csr1212_csr *csr) { @@ -1587,20 +1422,19 @@ int csr1212_parse_csr(struct csr1212_csr *csr) struct csr1212_dentry *dentry; int ret; - if (!csr || !csr->ops || !csr->ops->bus_read) - return CSR1212_EINVAL; + BUG_ON(!csr || !csr->ops || !csr->ops->bus_read); ret = csr1212_parse_bus_info_block(csr); if (ret != CSR1212_SUCCESS) return ret; - if (!csr->ops->get_max_rom) + if (!csr->ops->get_max_rom) { csr->max_rom = mr_map[0]; /* default value */ - else { + } else { int i = csr->ops->get_max_rom(csr->bus_info_data, csr->private); if (i & ~0x3) - return CSR1212_EINVAL; + return -EINVAL; csr->max_rom = mr_map[i]; } @@ -1613,7 +1447,7 @@ int csr1212_parse_csr(struct csr1212_csr *csr) csr->root_kv->valid = 0; csr->root_kv->next = csr->root_kv; csr->root_kv->prev = csr->root_kv; - ret = _csr1212_read_keyval(csr, csr->root_kv); + ret = csr1212_read_keyval(csr, csr->root_kv); if (ret != CSR1212_SUCCESS) return ret; @@ -1623,7 +1457,7 @@ int csr1212_parse_csr(struct csr1212_csr *csr) dentry; dentry = dentry->next) { if (dentry->kv->key.id == CSR1212_KV_ID_EXTENDED_ROM && !dentry->kv->valid) { - ret = _csr1212_read_keyval(csr, dentry->kv); + ret = csr1212_read_keyval(csr, dentry->kv); if (ret != CSR1212_SUCCESS) return ret; } diff --git a/drivers/ieee1394/csr1212.h b/drivers/ieee1394/csr1212.h index 17ddd72..df909ce 100644 --- a/drivers/ieee1394/csr1212.h +++ b/drivers/ieee1394/csr1212.h @@ -30,94 +30,13 @@ #ifndef __CSR1212_H__ #define __CSR1212_H__ - -/* Compatibility layer */ -#ifdef __KERNEL__ - #include <linux/types.h> #include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/vmalloc.h> -#include <asm/pgalloc.h> - -#define CSR1212_MALLOC(size) vmalloc((size)) -#define CSR1212_FREE(ptr) vfree(ptr) -#define CSR1212_BE16_TO_CPU(quad) be16_to_cpu(quad) -#define CSR1212_CPU_TO_BE16(quad) cpu_to_be16(quad) -#define CSR1212_BE32_TO_CPU(quad) be32_to_cpu(quad) -#define CSR1212_CPU_TO_BE32(quad) cpu_to_be32(quad) -#define CSR1212_BE64_TO_CPU(quad) be64_to_cpu(quad) -#define CSR1212_CPU_TO_BE64(quad) cpu_to_be64(quad) - -#define CSR1212_LE16_TO_CPU(quad) le16_to_cpu(quad) -#define CSR1212_CPU_TO_LE16(quad) cpu_to_le16(quad) -#define CSR1212_LE32_TO_CPU(quad) le32_to_cpu(quad) -#define CSR1212_CPU_TO_LE32(quad) cpu_to_le32(quad) -#define CSR1212_LE64_TO_CPU(quad) le64_to_cpu(quad) -#define CSR1212_CPU_TO_LE64(quad) cpu_to_le64(quad) - -#include <linux/errno.h> -#define CSR1212_SUCCESS (0) -#define CSR1212_EINVAL (-EINVAL) -#define CSR1212_ENOMEM (-ENOMEM) -#define CSR1212_ENOENT (-ENOENT) -#define CSR1212_EIO (-EIO) -#define CSR1212_EBUSY (-EBUSY) - -#else /* Userspace */ - -#include <sys/types.h> -#include <malloc.h> -#define CSR1212_MALLOC(size) malloc(size) -#define CSR1212_FREE(ptr) free(ptr) -#include <endian.h> -#if __BYTE_ORDER == __LITTLE_ENDIAN -#include <byteswap.h> -#define CSR1212_BE16_TO_CPU(quad) bswap_16(quad) -#define CSR1212_CPU_TO_BE16(quad) bswap_16(quad) -#define CSR1212_BE32_TO_CPU(quad) bswap_32(quad) -#define CSR1212_CPU_TO_BE32(quad) bswap_32(quad) -#define CSR1212_BE64_TO_CPU(quad) bswap_64(quad) -#define CSR1212_CPU_TO_BE64(quad) bswap_64(quad) - -#define CSR1212_LE16_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE16(quad) (quad) -#define CSR1212_LE32_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE32(quad) (quad) -#define CSR1212_LE64_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_LE64(quad) (quad) -#else -#define CSR1212_BE16_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE16(quad) (quad) -#define CSR1212_BE32_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE32(quad) (quad) -#define CSR1212_BE64_TO_CPU(quad) (quad) -#define CSR1212_CPU_TO_BE64(quad) (quad) - -#define CSR1212_LE16_TO_CPU(quad) bswap_16(quad) -#define CSR1212_CPU_TO_LE16(quad) bswap_16(quad) -#define CSR1212_LE32_TO_CPU(quad) bswap_32(quad) -#define CSR1212_CPU_TO_LE32(quad) bswap_32(quad) -#define CSR1212_LE64_TO_CPU(quad) bswap_64(quad) -#define CSR1212_CPU_TO_LE64(quad) bswap_64(quad) -#endif - -#include <errno.h> -#define CSR1212_SUCCESS (0) -#define CSR1212_EINVAL (EINVAL) -#define CSR1212_ENOMEM (ENOMEM) -#define CSR1212_ENOENT (ENOENT) -#define CSR1212_EIO (EIO) -#define CSR1212_EBUSY (EBUSY) - -#endif +#define CSR1212_MALLOC(size) kmalloc((size), GFP_KERNEL) +#define CSR1212_FREE(ptr) kfree(ptr) -#define CSR1212_KV_VAL_MASK 0xffffff -#define CSR1212_KV_KEY_SHIFT 24 -#define CSR1212_KV_KEY_TYPE_SHIFT 6 -#define CSR1212_KV_KEY_ID_MASK 0x3f -#define CSR1212_KV_KEY_TYPE_MASK 0x3 /* After shift */ +#define CSR1212_SUCCESS (0) /* CSR 1212 key types */ @@ -190,48 +109,22 @@ #define CSR1212_UNITS_SPACE_END (CSR1212_UNITS_SPACE_BASE + CSR1212_UNITS_SPACE_SIZE) #define CSR1212_UNITS_SPACE_OFFSET (CSR1212_UNITS_SPACE_BASE - CSR1212_REGISTER_SPACE_BASE) -#define CSR1212_EXTENDED_ROM_SIZE (0x10000 * sizeof(u_int32_t)) - #define CSR1212_INVALID_ADDR_SPACE -1 + /* Config ROM image structures */ struct csr1212_bus_info_block_img { - u_int8_t length; - u_int8_t crc_length; - u_int16_t crc; + u8 length; + u8 crc_length; + u16 crc; /* Must be last */ - u_int32_t data[0]; /* older gcc can't handle [] which is standard */ -}; - -#define CSR1212_KV_KEY(quad) (CSR1212_BE32_TO_CPU(quad) >> CSR1212_KV_KEY_SHIFT) -#define CSR1212_KV_KEY_TYPE(quad) (CSR1212_KV_KEY(quad) >> CSR1212_KV_KEY_TYPE_SHIFT) -#define CSR1212_KV_KEY_ID(quad) (CSR1212_KV_KEY(quad) & CSR1212_KV_KEY_ID_MASK) -#define CSR1212_KV_VAL(quad) (CSR1212_BE32_TO_CPU(quad) & CSR1212_KV_VAL_MASK) - -#define CSR1212_SET_KV_KEY(quad, key) ((quad) = \ - CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | ((key) << CSR1212_KV_KEY_SHIFT))) -#define CSR1212_SET_KV_VAL(quad, val) ((quad) = \ - CSR1212_CPU_TO_BE32((CSR1212_KV_KEY(quad) << CSR1212_KV_KEY_SHIFT) | (val))) -#define CSR1212_SET_KV_TYPEID(quad, type, id) ((quad) = \ - CSR1212_CPU_TO_BE32(CSR1212_KV_VAL(quad) | \ - (((((type) & CSR1212_KV_KEY_TYPE_MASK) << CSR1212_KV_KEY_TYPE_SHIFT) | \ - ((id) & CSR1212_KV_KEY_ID_MASK)) << CSR1212_KV_KEY_SHIFT))) - -typedef u_int32_t csr1212_quad_t; - - -struct csr1212_keyval_img { - u_int16_t length; - u_int16_t crc; - - /* Must be last */ - csr1212_quad_t data[0]; /* older gcc can't handle [] which is standard */ + u32 data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_leaf { int len; - u_int32_t *data; + u32 *data; }; struct csr1212_dentry { @@ -246,12 +139,12 @@ struct csr1212_directory { struct csr1212_keyval { struct { - u_int8_t type; - u_int8_t id; + u8 type; + u8 id; } key; union { - u_int32_t immediate; - u_int32_t csr_offset; + u32 immediate; + u32 csr_offset; struct csr1212_leaf leaf; struct csr1212_directory directory; } value; @@ -260,15 +153,15 @@ struct csr1212_keyval { /* used in generating and/or parsing CSR image */ struct csr1212_keyval *next, *prev; /* flat list of CSR elements */ - u_int32_t offset; /* position in CSR from 0xffff f000 0000 */ - u_int8_t valid; /* flag indicating keyval has valid data*/ + u32 offset; /* position in CSR from 0xffff f000 0000 */ + u8 valid; /* flag indicating keyval has valid data*/ }; struct csr1212_cache_region { struct csr1212_cache_region *next, *prev; - u_int32_t offset_start; /* inclusive */ - u_int32_t offset_end; /* exclusive */ + u32 offset_start; /* inclusive */ + u32 offset_end; /* exclusive */ }; struct csr1212_csr_rom_cache { @@ -276,18 +169,18 @@ struct csr1212_csr_rom_cache { struct csr1212_cache_region *filled_head, *filled_tail; struct csr1212_keyval *layout_head, *layout_tail; size_t size; - u_int32_t offset; + u32 offset; struct csr1212_keyval *ext_rom; size_t len; /* Must be last */ - u_int32_t data[0]; /* older gcc can't handle [] which is standard */ + u32 data[0]; /* older gcc can't handle [] which is standard */ }; struct csr1212_csr { size_t bus_info_len; /* bus info block length in bytes */ size_t crc_len; /* crc length in bytes */ - u_int32_t *bus_info_data; /* bus info data incl bus name and EUI */ + u32 *bus_info_data; /* bus info data incl bus name and EUI */ void *private; /* private, bus specific data */ struct csr1212_bus_ops *ops; @@ -305,52 +198,38 @@ struct csr1212_bus_ops { * from remote nodes when parsing a Config ROM (i.e., read Config ROM * entries located in the Units Space. Must return 0 on success * anything else indicates an error. */ - int (*bus_read) (struct csr1212_csr *csr, u_int64_t addr, - u_int16_t length, void *buffer, void *private); + int (*bus_read) (struct csr1212_csr *csr, u64 addr, + u16 length, void *buffer, void *private); /* This function is used by csr1212 to allocate a region in units space * in the event that Config ROM entries don't all fit in the predefined * 1K region. The void *private parameter is private member of struct * csr1212_csr. */ - u_int64_t (*allocate_addr_range) (u_int64_t size, u_int32_t alignment, - void *private); - + u64 (*allocate_addr_range) (u64 size, u32 alignment, void *private); /* This function is used by csr1212 to release a region in units space * that is no longer needed. */ - void (*release_addr) (u_int64_t addr, void *private); + void (*release_addr) (u64 addr, void *private); /* This function is used by csr1212 to determine the max read request * supported by a remote node when reading the ConfigROM space. Must * return 0, 1, or 2 per IEEE 1212. */ - int (*get_max_rom) (u_int32_t *bus_info, void *private); + int (*get_max_rom) (u32 *bus_info, void *private); }; - - /* Descriptor Leaf manipulation macros */ #define CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT 24 #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK 0xffffff -#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) +#define CSR1212_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) #define CSR1212_DESCRIPTOR_LEAF_TYPE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) >> CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) + (be32_to_cpu((kv)->value.leaf.data[0]) >> \ + CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) #define CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[0]) & \ + (be32_to_cpu((kv)->value.leaf.data[0]) & \ CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK) -#define CSR1212_DESCRIPTOR_LEAF_DATA(kv) \ - (&((kv)->value.leaf.data[1])) - -#define CSR1212_DESCRIPTOR_LEAF_SET_TYPE(kv, type) \ - ((kv)->value.leaf.data[0] = \ - CSR1212_CPU_TO_BE32(CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID(kv) | \ - ((type) << CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT))) -#define CSR1212_DESCRIPTOR_LEAF_SET_SPECIFIER_ID(kv, spec_id) \ - ((kv)->value.leaf.data[0] = \ - CSR1212_CPU_TO_BE32((CSR1212_DESCRIPTOR_LEAF_TYPE(kv) << \ - CSR1212_DESCRIPTOR_LEAF_TYPE_SHIFT) | \ - ((spec_id) & CSR1212_DESCRIPTOR_LEAF_SPECIFIER_ID_MASK))) + /* Text Descriptor Leaf manipulation macros */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT 28 @@ -358,182 +237,21 @@ struct csr1212_bus_ops { #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT 16 #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK 0xfff /* after shift */ #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u_int32_t)) +#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_OVERHEAD (1 * sizeof(u32)) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ + (be32_to_cpu((kv)->value.leaf.data[1]) >> \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) >> \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) + ((be32_to_cpu((kv)->value.leaf.data[1]) >> \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT) & \ + CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1]) & \ + (be32_to_cpu((kv)->value.leaf.data[1]) & \ CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK) #define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(kv) \ (&((kv)->value.leaf.data[2])) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_WIDTH(kv, width) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((width) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_MASK) << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_WIDTH_SHIFT)) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_CHAR_SET(kv, char_set) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((char_set) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_MASK) << \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_CHAR_SET_SHIFT)) -#define CSR1212_TEXTUAL_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ - ((kv)->value.leaf.data[1] = \ - ((kv)->value.leaf.data[1] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ - CSR1212_CPU_TO_BE32(((language) & \ - CSR1212_TEXTUAL_DESCRIPTOR_LEAF_LANGUAGE_MASK))) - - -/* Icon Descriptor Leaf manipulation macros */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK 0xffffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT 30 -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK 0x3 /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT 16 -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK 0xf /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK 0xffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT 16 -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK 0xffff /* after shift */ -#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK 0xffff -#define CSR1212_ICON_DESCRIPTOR_LEAF_OVERHEAD (3 * sizeof(u_int32_t)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_VERSION(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[2]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[3]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN(kv) \ - ((CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) >> \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_SHIFT) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_HSCAN_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN(kv) \ - (CSR1212_BE32_TO_CPU((kv)->value.leaf.data[4]) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE(kv) \ - (&((kv)->value.leaf.data[5])) - -static inline u_int32_t *CSR1212_ICON_DESCRIPTOR_LEAF_PIXELS(struct csr1212_keyval *kv) -{ - static const int pd[4] = { 0, 4, 16, 256 }; - static const int cs[16] = { 4, 2 }; - int ps = pd[CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH(kv)]; - - return &kv->value.leaf.data[5 + - (ps * cs[CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE(kv)]) / - sizeof(u_int32_t)]; -} - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VERSION(kv, version) \ - ((kv)->value.leaf.data[2] = \ - ((kv)->value.leaf.data[2] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) | \ - CSR1212_CPU_TO_BE32(((version) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VERSION_MASK))) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_PALETTE_DEPTH(kv, palette_depth) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((palette_depth) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_PALETTE_DEPTH_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_COLOR_SPACE(kv, color_space) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((color_space) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_COLOR_SPACE_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_LANGUAGE(kv, language) \ - ((kv)->value.leaf.data[3] = \ - ((kv)->value.leaf.data[3] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) | \ - CSR1212_CPU_TO_BE32(((language) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_LANGUAGE_MASK))) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_HSCAN(kv, hscan) \ - ((kv)->value.leaf.data[4] = \ - ((kv)->value.leaf.data[4] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK << \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((hscan) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_MASK) << \ - CSR1212_ICON_DESCRIPTOR_LEAF_HSCAN_SHIFT)) - -#define CSR1212_ICON_DESCRIPTOR_LEAF_SET_VSCAN(kv, vscan) \ - ((kv)->value.leaf.data[4] = \ - (((kv)->value.leaf.data[4] & \ - CSR1212_CPU_TO_BE32(~CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) | \ - CSR1212_CPU_TO_BE32(((vscan) & \ - CSR1212_ICON_DESCRIPTOR_LEAF_VSCAN_MASK))) - - -/* Modifiable Descriptor Leaf manipulation macros */ -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT 16 -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK 0xffff -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_SHIFT 32 -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK 0xffff -#define CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK 0xffffffffULL - -#define CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE(kv) \ - CSR1212_BE16_TO_CPU((kv)->value.leaf.data[0] >> CSR1212_MODIFIABLE_DESCRIPTOR_MAX_SIZE_SHIFT) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_ADDRESS(kv) \ - (CSR1212_BE16_TO_CPU(((u_int64_t)((kv)->value.leaf.data[0])) << \ - CSR1212_MODIFIABLE_DESCRIPTOR_ADDR_HI_SHIFT) | \ - CSR1212_BE32_TO_CPU((kv)->value.leaf.data[1])) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_MAX_SIZE(kv, size) \ - ((kv)->value.leaf.data[0] = \ - ((kv)->value.leaf.data[0] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK << \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT))) | \ - CSR1212_CPU_TO_BE32(((size) & \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_MASK) << \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_MAX_SIZE_SHIFT)) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_HI(kv, addr) \ - ((kv)->value.leaf.data[0] = \ - ((kv)->value.leaf.data[0] & \ - CSR1212_CPU_TO_BE32(~(CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) | \ - CSR1212_CPU_TO_BE32(((addr) & \ - CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_HI_MASK))) - -#define CSR1212_MODIFIABLE_DESCRIPTOR_SET_ADDRESS_LO(kv, addr) \ - ((kv)->value.leaf.data[1] = \ - CSR1212_CPU_TO_BE32(addr & CSR1212_MODIFIABLE_DESCRIPTOR_LEAF_ADDR_LO_MASK)) - - /* The following 2 function are for creating new Configuration ROM trees. The * first function is used for both creating local trees and parsing remote @@ -543,11 +261,10 @@ extern struct csr1212_csr *csr1212_create_csr(struct csr1212_bus_ops *ops, size_t bus_info_size, void *private); extern void csr1212_init_local_csr(struct csr1212_csr *csr, - const u_int32_t *bus_info_data, int max_rom); + const u32 *bus_info_data, int max_rom); -/* The following function destroys a Configuration ROM tree and release all - * memory taken by the tree. */ +/* Destroy a Configuration ROM tree and release all memory taken by the tree. */ extern void csr1212_destroy_csr(struct csr1212_csr *csr); @@ -555,50 +272,20 @@ extern void csr1212_destroy_csr(struct csr1212_csr *csr); * a Configuration ROM tree. Code that creates new keyvals with these functions * must release those keyvals with csr1212_release_keyval() when they are no * longer needed. */ -extern struct csr1212_keyval *csr1212_new_immediate(u_int8_t key, u_int32_t value); -extern struct csr1212_keyval *csr1212_new_leaf(u_int8_t key, const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_csr_offset(u_int8_t key, - u_int32_t csr_offset); -extern struct csr1212_keyval *csr1212_new_directory(u_int8_t key); -extern struct csr1212_keyval *csr1212_new_extended_immediate(u_int32_t spec, - u_int32_t key, - u_int32_t value); -extern struct csr1212_keyval *csr1212_new_extended_leaf(u_int32_t spec, - u_int32_t key, - const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_descriptor_leaf(u_int8_t dtype, - u_int32_t specifier_id, - const void *data, - size_t data_len); -extern struct csr1212_keyval *csr1212_new_textual_descriptor_leaf(u_int8_t cwidth, - u_int16_t cset, - u_int16_t language, - const void *data, - size_t data_len); +extern struct csr1212_keyval *csr1212_new_immediate(u8 key, u32 value); +extern struct csr1212_keyval *csr1212_new_directory(u8 key); extern struct csr1212_keyval *csr1212_new_string_descriptor_leaf(const char *s); -extern struct csr1212_keyval *csr1212_new_icon_descriptor_leaf(u_int32_t version, - u_int8_t palette_depth, - u_int8_t color_space, - u_int16_t language, - u_int16_t hscan, - u_int16_t vscan, - u_int32_t *palette, - u_int32_t *pixels); -extern struct csr1212_keyval *csr1212_new_modifiable_descriptor_leaf(u_int16_t max_size, - u_int64_t address); -extern struct csr1212_keyval *csr1212_new_keyword_leaf(int strc, - const char *strv[]); - - -/* The following functions manage association between keyvals. Typically, + + +/* The following function manages association between keyvals. Typically, * Descriptor Leaves and Directories will be associated with another keyval and * it is desirable for the Descriptor keyval to be place immediately after the - * keyval that it is associated with.*/ -extern int csr1212_associate_keyval(struct csr1212_keyval *kv, - struct csr1212_keyval *associate); -extern void csr1212_disassociate_keyval(struct csr1212_keyval *kv); + * keyval that it is associated with. + * Take care with subsequent ROM modifications: There is no function to remove + * previously specified associations. + */ +extern void csr1212_associate_keyval(struct csr1212_keyval *kv, + struct csr1212_keyval *associate); /* The following functions manage the association of a keyval and directories. @@ -609,23 +296,15 @@ extern void csr1212_detach_keyval_from_directory(struct csr1212_keyval *dir, struct csr1212_keyval *kv); -/* The following functions create a Configuration ROM image from the tree of - * keyvals provided. csr1212_generate_csr_image() creates a complete image in - * the list of caches available via csr->cache_head. The other functions are - * provided should there be a need to create a flat image without restrictions - * placed by IEEE 1212. */ -extern struct csr1212_keyval *csr1212_generate_positions(struct csr1212_csr_rom_cache *cache, - struct csr1212_keyval *start_kv, - int start_pos); -extern size_t csr1212_generate_layout_order(struct csr1212_keyval *kv); -extern void csr1212_fill_cache(struct csr1212_csr_rom_cache *cache); +/* Creates a complete Configuration ROM image in the list of caches available + * via csr->cache_head. */ extern int csr1212_generate_csr_image(struct csr1212_csr *csr); /* This is a convience function for reading a block of data out of one of the * caches in the csr->cache_head list. */ -extern int csr1212_read(struct csr1212_csr *csr, u_int32_t offset, void *buffer, - u_int32_t len); +extern int csr1212_read(struct csr1212_csr *csr, u32 offset, void *buffer, + u32 len); /* The following functions are in place for parsing Configuration ROM images. @@ -635,15 +314,11 @@ extern int csr1212_parse_keyval(struct csr1212_keyval *kv, struct csr1212_csr_rom_cache *cache); extern int csr1212_parse_csr(struct csr1212_csr *csr); -/* These are internal functions referenced by inline functions below. */ -extern int _csr1212_read_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); -extern void _csr1212_destroy_keyval(struct csr1212_keyval *kv); - /* This function allocates a new cache which may be used for either parsing or * generating sub-sets of Configuration ROM images. */ -static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t offset, - size_t size) +static inline struct csr1212_csr_rom_cache * +csr1212_rom_cache_malloc(u32 offset, size_t size) { struct csr1212_csr_rom_cache *cache; @@ -667,16 +342,8 @@ static inline struct csr1212_csr_rom_cache *csr1212_rom_cache_malloc(u_int32_t o /* This function ensures that a keyval contains data when referencing a keyval * created by parsing a Configuration ROM. */ -static inline struct csr1212_keyval *csr1212_get_keyval(struct csr1212_csr *csr, - struct csr1212_keyval *kv) -{ - if (!kv) - return NULL; - if (!kv->valid) - if (_csr1212_read_keyval(csr, kv) != CSR1212_SUCCESS) - return NULL; - return kv; -} +extern struct csr1212_keyval * +csr1212_get_keyval(struct csr1212_csr *csr, struct csr1212_keyval *kv); /* This function increments the reference count for a keyval should there be a @@ -691,37 +358,29 @@ static inline void csr1212_keep_keyval(struct csr1212_keyval *kv) * keyval when there are no more users of the keyval. This should be called by * any code that calls csr1212_keep_keyval() or any of the keyval creation * routines csr1212_new_*(). */ -static inline void csr1212_release_keyval(struct csr1212_keyval *kv) -{ - if (kv->refcnt > 1) - kv->refcnt--; - else - _csr1212_destroy_keyval(kv); -} +extern void csr1212_release_keyval(struct csr1212_keyval *kv); /* * This macro allows for looping over the keyval entries in a directory and it * ensures that keyvals from remote ConfigROMs are parsed properly. * - * _csr is a struct csr1212_csr * that points to CSR associated with dir. - * _kv is a struct csr1212_keyval * that'll point to the current keyval (loop index). - * _dir is a struct csr1212_keyval * that points to the directory to be looped. - * _pos is a struct csr1212_dentry * that is used internally for indexing. + * struct csr1212_csr *_csr points to the CSR associated with dir. + * struct csr1212_keyval *_kv points to the current keyval (loop index). + * struct csr1212_keyval *_dir points to the directory to be looped. + * struct csr1212_dentry *_pos is used internally for indexing. * * kv will be NULL upon exit of the loop. */ -#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ - for (csr1212_get_keyval((_csr), (_dir)), \ - _pos = (_dir)->value.directory.dentries_head, \ - _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL; \ - (_kv) && (_pos); \ - (_kv->associate == NULL) ? \ - ((_pos = _pos->next), \ - (_kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : \ - NULL)) : \ +#define csr1212_for_each_dir_entry(_csr, _kv, _dir, _pos) \ + for (csr1212_get_keyval((_csr), (_dir)), \ + _pos = (_dir)->value.directory.dentries_head, \ + _kv = (_pos) ? csr1212_get_keyval((_csr), _pos->kv) : NULL;\ + (_kv) && (_pos); \ + (_kv->associate == NULL) ? \ + ((_pos = _pos->next), (_kv = (_pos) ? \ + csr1212_get_keyval((_csr), _pos->kv) : \ + NULL)) : \ (_kv = csr1212_get_keyval((_csr), _kv->associate))) - - #endif /* __CSR1212_H__ */ diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c index c68f328..45d6055 100644 --- a/drivers/ieee1394/dma.c +++ b/drivers/ieee1394/dma.c @@ -62,6 +62,9 @@ void dma_prog_region_free(struct dma_prog_region *prog) /* dma_region */ +/** + * dma_region_init - clear out all fields but do not allocate anything + */ void dma_region_init(struct dma_region *dma) { dma->kvirt = NULL; @@ -71,6 +74,9 @@ void dma_region_init(struct dma_region *dma) dma->sglist = NULL; } +/** + * dma_region_alloc - allocate the buffer and map it to the IOMMU + */ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction) { @@ -128,6 +134,9 @@ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, return -ENOMEM; } +/** + * dma_region_free - unmap and free the buffer + */ void dma_region_free(struct dma_region *dma) { if (dma->n_dma_pages) { @@ -167,6 +176,12 @@ static inline int dma_region_find(struct dma_region *dma, unsigned long offset, return i; } +/** + * dma_region_offset_to_bus - get bus address of an offset within a DMA region + * + * Returns the DMA bus address of the byte with the given @offset relative to + * the beginning of the @dma. + */ dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, unsigned long offset) { @@ -177,6 +192,9 @@ dma_addr_t dma_region_offset_to_bus(struct dma_region * dma, return sg_dma_address(sg) + rem; } +/** + * dma_region_sync_for_cpu - sync the CPU's view of the buffer + */ void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len) { @@ -193,6 +211,9 @@ void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, dma->direction); } +/** + * dma_region_sync_for_device - sync the IO bus' view of the buffer + */ void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len) { @@ -244,6 +265,9 @@ static struct vm_operations_struct dma_region_vm_ops = { .nopage = dma_region_pagefault, }; +/** + * dma_region_mmap - map the buffer into a user space process + */ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma) { diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h index a1682ab..2727bcd 100644 --- a/drivers/ieee1394/dma.h +++ b/drivers/ieee1394/dma.h @@ -66,35 +66,23 @@ struct dma_region { int direction; }; -/* clear out all fields but do not allocate anything */ void dma_region_init(struct dma_region *dma); - -/* allocate the buffer and map it to the IOMMU */ int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction); - -/* unmap and free the buffer */ void dma_region_free(struct dma_region *dma); - -/* sync the CPU's view of the buffer */ void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len); - -/* sync the IO bus' view of the buffer */ void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len); - -/* map the buffer into a user space process */ int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma); +dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, + unsigned long offset); -/* macro to index into a DMA region (or dma_prog_region) */ +/** + * dma_region_i - macro to index into a DMA region (or dma_prog_region) + */ #define dma_region_i(_dma, _type, _index) \ ( ((_type*) ((_dma)->kvirt)) + (_index) ) -/* return the DMA bus address of the byte with the given offset - * relative to the beginning of the dma_region */ -dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, - unsigned long offset); - #endif /* IEEE1394_DMA_H */ diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c index dee9529..026e38f 100644 --- a/drivers/ieee1394/dv1394.c +++ b/drivers/ieee1394/dv1394.c @@ -2564,8 +2564,8 @@ static int __init dv1394_init_module(void) int ret; printk(KERN_WARNING - "WARNING: The dv1394 driver is unsupported and will be removed " - "from Linux soon. Use raw1394 instead.\n"); + "NOTE: The dv1394 driver is unsupported and may be removed in a " + "future Linux release. Use raw1394 instead.\n"); cdev_init(&dv1394_cdev, &dv1394_fops); dv1394_cdev.owner = THIS_MODULE; diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c index a953135..2296d43 100644 --- a/drivers/ieee1394/eth1394.c +++ b/drivers/ieee1394/eth1394.c @@ -1,5 +1,5 @@ /* - * eth1394.c -- Ethernet driver for Linux IEEE-1394 Subsystem + * eth1394.c -- IPv4 driver for Linux IEEE-1394 Subsystem * * Copyright (C) 2001-2003 Ben Collins <bcollins@debian.org> * 2000 Bonin Franck <boninf@free.fr> @@ -22,10 +22,9 @@ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ -/* This driver intends to support RFC 2734, which describes a method for - * transporting IPv4 datagrams over IEEE-1394 serial busses. This driver - * will ultimately support that method, but currently falls short in - * several areas. +/* + * This driver intends to support RFC 2734, which describes a method for + * transporting IPv4 datagrams over IEEE-1394 serial busses. * * TODO: * RFC 2734 related: @@ -40,7 +39,6 @@ * - Consider garbage collecting old partial datagrams after X amount of time */ - #include <linux/module.h> #include <linux/kernel.h> @@ -52,7 +50,6 @@ #include <linux/netdevice.h> #include <linux/inetdevice.h> -#include <linux/etherdevice.h> #include <linux/if_arp.h> #include <linux/if_ether.h> #include <linux/ip.h> @@ -84,10 +81,6 @@ #define ETH1394_PRINT(level, dev_name, fmt, args...) \ printk(level "%s: %s: " fmt, driver_name, dev_name, ## args) -#define DEBUG(fmt, args...) \ - printk(KERN_ERR "%s:%s[%d]: " fmt "\n", driver_name, __FUNCTION__, __LINE__, ## args) -#define TRACE() printk(KERN_ERR "%s:%s[%d] ---- TRACE\n", driver_name, __FUNCTION__, __LINE__) - struct fragment_info { struct list_head list; int offset; @@ -105,9 +98,9 @@ struct partial_datagram { }; struct pdg_list { - struct list_head list; /* partial datagram list per node */ - unsigned int sz; /* partial datagram list size per node */ - spinlock_t lock; /* partial datagram lock */ + struct list_head list; /* partial datagram list per node */ + unsigned int sz; /* partial datagram list size per node */ + spinlock_t lock; /* partial datagram lock */ }; struct eth1394_host_info { @@ -121,16 +114,14 @@ struct eth1394_node_ref { }; struct eth1394_node_info { - u16 maxpayload; /* Max payload */ - u8 sspd; /* Max speed */ - u64 fifo; /* FIFO address */ - struct pdg_list pdg; /* partial RX datagram lists */ - int dgl; /* Outgoing datagram label */ + u16 maxpayload; /* max payload */ + u8 sspd; /* max speed */ + u64 fifo; /* FIFO address */ + struct pdg_list pdg; /* partial RX datagram lists */ + int dgl; /* outgoing datagram label */ }; -/* Our ieee1394 highlevel driver */ -#define ETH1394_DRIVER_NAME "eth1394" -static const char driver_name[] = ETH1394_DRIVER_NAME; +static const char driver_name[] = "eth1394"; static struct kmem_cache *packet_task_cache; @@ -138,18 +129,12 @@ static struct hpsb_highlevel eth1394_highlevel; /* Use common.lf to determine header len */ static const int hdr_type_len[] = { - sizeof (struct eth1394_uf_hdr), - sizeof (struct eth1394_ff_hdr), - sizeof (struct eth1394_sf_hdr), - sizeof (struct eth1394_sf_hdr) + sizeof(struct eth1394_uf_hdr), + sizeof(struct eth1394_ff_hdr), + sizeof(struct eth1394_sf_hdr), + sizeof(struct eth1394_sf_hdr) }; -/* Change this to IEEE1394_SPEED_S100 to make testing easier */ -#define ETH1394_SPEED_DEF IEEE1394_SPEED_MAX - -/* For now, this needs to be 1500, so that XP works with us */ -#define ETH1394_DATA_LEN ETH_DATA_LEN - static const u16 eth1394_speedto_maxpayload[] = { /* S100, S200, S400, S800, S1600, S3200 */ 512, 1024, 2048, 4096, 4096, 4096 @@ -159,7 +144,8 @@ MODULE_AUTHOR("Ben Collins (bcollins@debian.org)"); MODULE_DESCRIPTION("IEEE 1394 IPv4 Driver (IPv4-over-1394 as per RFC 2734)"); MODULE_LICENSE("GPL"); -/* The max_partial_datagrams parameter is the maximum number of fragmented +/* + * The max_partial_datagrams parameter is the maximum number of fragmented * datagrams per node that eth1394 will keep in memory. Providing an upper * bound allows us to limit the amount of memory that partial datagrams * consume in the event that some partial datagrams are never completed. @@ -179,10 +165,7 @@ static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr); static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh); static void ether1394_header_cache_update(struct hh_cache *hh, struct net_device *dev, - unsigned char * haddr); -static int ether1394_mac_addr(struct net_device *dev, void *p); - -static void purge_partial_datagram(struct list_head *old); + unsigned char *haddr); static int ether1394_tx(struct sk_buff *skb, struct net_device *dev); static void ether1394_iso(struct hpsb_iso *iso); @@ -190,9 +173,9 @@ static struct ethtool_ops ethtool_ops; static int ether1394_write(struct hpsb_host *host, int srcid, int destid, quadlet_t *data, u64 addr, size_t len, u16 flags); -static void ether1394_add_host (struct hpsb_host *host); -static void ether1394_remove_host (struct hpsb_host *host); -static void ether1394_host_reset (struct hpsb_host *host); +static void ether1394_add_host(struct hpsb_host *host); +static void ether1394_remove_host(struct hpsb_host *host); +static void ether1394_host_reset(struct hpsb_host *host); /* Function for incoming 1394 packets */ static struct hpsb_address_ops addr_ops = { @@ -207,89 +190,107 @@ static struct hpsb_highlevel eth1394_highlevel = { .host_reset = ether1394_host_reset, }; +static int ether1394_recv_init(struct eth1394_priv *priv) +{ + unsigned int iso_buf_size; + + /* FIXME: rawiso limits us to PAGE_SIZE */ + iso_buf_size = min((unsigned int)PAGE_SIZE, + 2 * (1U << (priv->host->csr.max_rec + 1))); + + priv->iso = hpsb_iso_recv_init(priv->host, + ETHER1394_GASP_BUFFERS * iso_buf_size, + ETHER1394_GASP_BUFFERS, + priv->broadcast_channel, + HPSB_ISO_DMA_PACKET_PER_BUFFER, + 1, ether1394_iso); + if (priv->iso == NULL) { + ETH1394_PRINT_G(KERN_ERR, "Failed to allocate IR context\n"); + priv->bc_state = ETHER1394_BC_ERROR; + return -EAGAIN; + } + + if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) + priv->bc_state = ETHER1394_BC_STOPPED; + else + priv->bc_state = ETHER1394_BC_RUNNING; + return 0; +} /* This is called after an "ifup" */ -static int ether1394_open (struct net_device *dev) +static int ether1394_open(struct net_device *dev) { struct eth1394_priv *priv = netdev_priv(dev); - int ret = 0; + int ret; - /* Something bad happened, don't even try */ if (priv->bc_state == ETHER1394_BC_ERROR) { - /* we'll try again */ - priv->iso = hpsb_iso_recv_init(priv->host, - ETHER1394_ISO_BUF_SIZE, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT(KERN_ERR, dev->name, - "Could not allocate isochronous receive " - "context for the broadcast channel\n"); - priv->bc_state = ETHER1394_BC_ERROR; - ret = -EAGAIN; - } else { - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - } + ret = ether1394_recv_init(priv); + if (ret) + return ret; } - - if (ret) - return ret; - - netif_start_queue (dev); + netif_start_queue(dev); return 0; } /* This is called after an "ifdown" */ -static int ether1394_stop (struct net_device *dev) +static int ether1394_stop(struct net_device *dev) { - netif_stop_queue (dev); + netif_stop_queue(dev); return 0; } /* Return statistics to the caller */ -static struct net_device_stats *ether1394_stats (struct net_device *dev) +static struct net_device_stats *ether1394_stats(struct net_device *dev) { return &(((struct eth1394_priv *)netdev_priv(dev))->stats); } -/* What to do if we timeout. I think a host reset is probably in order, so - * that's what we do. Should we increment the stat counters too? */ -static void ether1394_tx_timeout (struct net_device *dev) +/* FIXME: What to do if we timeout? I think a host reset is probably in order, + * so that's what we do. Should we increment the stat counters too? */ +static void ether1394_tx_timeout(struct net_device *dev) { - ETH1394_PRINT (KERN_ERR, dev->name, "Timeout, resetting host %s\n", - ((struct eth1394_priv *)netdev_priv(dev))->host->driver->name); + struct hpsb_host *host = + ((struct eth1394_priv *)netdev_priv(dev))->host; - highlevel_host_reset (((struct eth1394_priv *)netdev_priv(dev))->host); + ETH1394_PRINT(KERN_ERR, dev->name, "Timeout, resetting host\n"); + ether1394_host_reset(host); +} - netif_wake_queue (dev); +static inline int ether1394_max_mtu(struct hpsb_host* host) +{ + return (1 << (host->csr.max_rec + 1)) + - sizeof(union eth1394_hdr) - ETHER1394_GASP_OVERHEAD; } static int ether1394_change_mtu(struct net_device *dev, int new_mtu) { - struct eth1394_priv *priv = netdev_priv(dev); + int max_mtu; - if ((new_mtu < 68) || - (new_mtu > min(ETH1394_DATA_LEN, - (int)((1 << (priv->host->csr.max_rec + 1)) - - (sizeof(union eth1394_hdr) + - ETHER1394_GASP_OVERHEAD))))) + if (new_mtu < 68) return -EINVAL; + + max_mtu = ether1394_max_mtu( + ((struct eth1394_priv *)netdev_priv(dev))->host); + if (new_mtu > max_mtu) { + ETH1394_PRINT(KERN_INFO, dev->name, + "Local node constrains MTU to %d\n", max_mtu); + return -ERANGE; + } + dev->mtu = new_mtu; return 0; } static void purge_partial_datagram(struct list_head *old) { - struct partial_datagram *pd = list_entry(old, struct partial_datagram, list); + struct partial_datagram *pd; struct list_head *lh, *n; + struct fragment_info *fi; + + pd = list_entry(old, struct partial_datagram, list); list_for_each_safe(lh, n, &pd->frag_info) { - struct fragment_info *fi = list_entry(lh, struct fragment_info, list); + fi = list_entry(lh, struct fragment_info, list); list_del(lh); kfree(fi); } @@ -330,35 +331,26 @@ static struct eth1394_node_ref *eth1394_find_node_nodeid(struct list_head *inl, nodeid_t nodeid) { struct eth1394_node_ref *node; - list_for_each_entry(node, inl, list) { + + list_for_each_entry(node, inl, list) if (node->ud->ne->nodeid == nodeid) return node; - } return NULL; } -static int eth1394_probe(struct device *dev) +static int eth1394_new_node(struct eth1394_host_info *hi, + struct unit_directory *ud) { - struct unit_directory *ud; - struct eth1394_host_info *hi; struct eth1394_priv *priv; struct eth1394_node_ref *new_node; struct eth1394_node_info *node_info; - ud = container_of(dev, struct unit_directory, device); - - hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); - if (!hi) - return -ENOENT; - - new_node = kmalloc(sizeof(*new_node), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + new_node = kmalloc(sizeof(*new_node), GFP_KERNEL); if (!new_node) return -ENOMEM; - node_info = kmalloc(sizeof(*node_info), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + node_info = kmalloc(sizeof(*node_info), GFP_KERNEL); if (!node_info) { kfree(new_node); return -ENOMEM; @@ -374,10 +366,22 @@ static int eth1394_probe(struct device *dev) priv = netdev_priv(hi->dev); list_add_tail(&new_node->list, &priv->ip_node_list); - return 0; } +static int eth1394_probe(struct device *dev) +{ + struct unit_directory *ud; + struct eth1394_host_info *hi; + + ud = container_of(dev, struct unit_directory, device); + hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); + if (!hi) + return -ENOENT; + + return eth1394_new_node(hi, ud); +} + static int eth1394_remove(struct device *dev) { struct unit_directory *ud; @@ -396,24 +400,23 @@ static int eth1394_remove(struct device *dev) priv = netdev_priv(hi->dev); old_node = eth1394_find_node(&priv->ip_node_list, ud); + if (!old_node) + return 0; - if (old_node) { - list_del(&old_node->list); - kfree(old_node); + list_del(&old_node->list); + kfree(old_node); - node_info = (struct eth1394_node_info*)ud->device.driver_data; + node_info = (struct eth1394_node_info*)ud->device.driver_data; - spin_lock_irqsave(&node_info->pdg.lock, flags); - /* The partial datagram list should be empty, but we'll just - * make sure anyway... */ - list_for_each_safe(lh, n, &node_info->pdg.list) { - purge_partial_datagram(lh); - } - spin_unlock_irqrestore(&node_info->pdg.lock, flags); + spin_lock_irqsave(&node_info->pdg.lock, flags); + /* The partial datagram list should be empty, but we'll just + * make sure anyway... */ + list_for_each_safe(lh, n, &node_info->pdg.list) + purge_partial_datagram(lh); + spin_unlock_irqrestore(&node_info->pdg.lock, flags); - kfree(node_info); - ud->device.driver_data = NULL; - } + kfree(node_info); + ud->device.driver_data = NULL; return 0; } @@ -422,44 +425,19 @@ static int eth1394_update(struct unit_directory *ud) struct eth1394_host_info *hi; struct eth1394_priv *priv; struct eth1394_node_ref *node; - struct eth1394_node_info *node_info; hi = hpsb_get_hostinfo(ð1394_highlevel, ud->ne->host); if (!hi) return -ENOENT; priv = netdev_priv(hi->dev); - node = eth1394_find_node(&priv->ip_node_list, ud); + if (node) + return 0; - if (!node) { - node = kmalloc(sizeof(*node), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); - if (!node) - return -ENOMEM; - - node_info = kmalloc(sizeof(*node_info), - in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); - if (!node_info) { - kfree(node); - return -ENOMEM; - } - - spin_lock_init(&node_info->pdg.lock); - INIT_LIST_HEAD(&node_info->pdg.list); - node_info->pdg.sz = 0; - - ud->device.driver_data = node_info; - node->ud = ud; - - priv = netdev_priv(hi->dev); - list_add_tail(&node->list, &priv->ip_node_list); - } - - return 0; + return eth1394_new_node(hi, ud); } - static struct ieee1394_device_id eth1394_id_table[] = { { .match_flags = (IEEE1394_MATCH_SPECIFIER_ID | @@ -473,7 +451,7 @@ static struct ieee1394_device_id eth1394_id_table[] = { MODULE_DEVICE_TABLE(ieee1394, eth1394_id_table); static struct hpsb_protocol_driver eth1394_proto_driver = { - .name = ETH1394_DRIVER_NAME, + .name = driver_name, .id_table = eth1394_id_table, .update = eth1394_update, .driver = { @@ -482,47 +460,50 @@ static struct hpsb_protocol_driver eth1394_proto_driver = { }, }; - -static void ether1394_reset_priv (struct net_device *dev, int set_mtu) +static void ether1394_reset_priv(struct net_device *dev, int set_mtu) { unsigned long flags; int i; struct eth1394_priv *priv = netdev_priv(dev); struct hpsb_host *host = priv->host; - u64 guid = get_unaligned((u64*)&(host->csr.rom->bus_info_data[3])); - u16 maxpayload = 1 << (host->csr.max_rec + 1); + u64 guid = get_unaligned((u64 *)&(host->csr.rom->bus_info_data[3])); int max_speed = IEEE1394_SPEED_MAX; - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); - memset(priv->ud_list, 0, sizeof(struct node_entry*) * ALL_NODES); + memset(priv->ud_list, 0, sizeof(priv->ud_list)); priv->bc_maxpayload = 512; /* Determine speed limit */ - for (i = 0; i < host->node_count; i++) + /* FIXME: This is broken for nodes with link speed < PHY speed, + * and it is suboptimal for S200B...S800B hardware. + * The result of nodemgr's speed probe should be used somehow. */ + for (i = 0; i < host->node_count; i++) { + /* take care of S100B...S400B PHY ports */ + if (host->speed[i] == SELFID_SPEED_UNKNOWN) { + max_speed = IEEE1394_SPEED_100; + break; + } if (max_speed > host->speed[i]) max_speed = host->speed[i]; + } priv->bc_sspd = max_speed; - /* We'll use our maxpayload as the default mtu */ if (set_mtu) { - dev->mtu = min(ETH1394_DATA_LEN, - (int)(maxpayload - - (sizeof(union eth1394_hdr) + - ETHER1394_GASP_OVERHEAD))); + /* Use the RFC 2734 default 1500 octets or the maximum payload + * as initial MTU */ + dev->mtu = min(1500, ether1394_max_mtu(host)); /* Set our hardware address while we're at it */ memcpy(dev->dev_addr, &guid, sizeof(u64)); memset(dev->broadcast, 0xff, sizeof(u64)); } - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); } -/* This function is called right before register_netdev */ -static void ether1394_init_dev (struct net_device *dev) +static void ether1394_init_dev(struct net_device *dev) { - /* Our functions */ dev->open = ether1394_open; dev->stop = ether1394_stop; dev->hard_start_xmit = ether1394_tx; @@ -535,10 +516,9 @@ static void ether1394_init_dev (struct net_device *dev) dev->hard_header_cache = ether1394_header_cache; dev->header_cache_update= ether1394_header_cache_update; dev->hard_header_parse = ether1394_header_parse; - dev->set_mac_address = ether1394_mac_addr; + SET_ETHTOOL_OPS(dev, ðtool_ops); - /* Some constants */ dev->watchdog_timeo = ETHER1394_TIMEOUT; dev->flags = IFF_BROADCAST | IFF_MULTICAST; dev->features = NETIF_F_HIGHDMA; @@ -546,7 +526,8 @@ static void ether1394_init_dev (struct net_device *dev) dev->hard_header_len = ETH1394_HLEN; dev->type = ARPHRD_IEEE1394; - ether1394_reset_priv (dev, 1); + /* FIXME: This value was copied from ether_setup(). Is it too much? */ + dev->tx_queue_len = 1000; } /* @@ -554,64 +535,61 @@ static void ether1394_init_dev (struct net_device *dev) * when the module is installed. This is where we add all of our ethernet * devices. One for each host. */ -static void ether1394_add_host (struct hpsb_host *host) +static void ether1394_add_host(struct hpsb_host *host) { struct eth1394_host_info *hi = NULL; struct net_device *dev = NULL; struct eth1394_priv *priv; u64 fifo_addr; - if (!(host->config_roms & HPSB_CONFIG_ROM_ENTRY_IP1394)) + if (hpsb_config_rom_ip1394_add(host) != 0) { + ETH1394_PRINT_G(KERN_ERR, "Can't add IP-over-1394 ROM entry\n"); return; + } fifo_addr = hpsb_allocate_and_register_addrspace( ð1394_highlevel, host, &addr_ops, ETHER1394_REGION_ADDR_LEN, ETHER1394_REGION_ADDR_LEN, CSR1212_INVALID_ADDR_SPACE, CSR1212_INVALID_ADDR_SPACE); - if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) - goto out; - - /* We should really have our own alloc_hpsbdev() function in - * net_init.c instead of calling the one for ethernet then hijacking - * it for ourselves. That way we'd be a real networking device. */ - dev = alloc_etherdev(sizeof (struct eth1394_priv)); + if (fifo_addr == CSR1212_INVALID_ADDR_SPACE) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register CSR space\n"); + hpsb_config_rom_ip1394_remove(host); + return; + } + dev = alloc_netdev(sizeof(*priv), "eth%d", ether1394_init_dev); if (dev == NULL) { - ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to allocate " - "etherdevice for IEEE 1394 device %s-%d\n", - host->driver->name, host->id); + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); goto out; - } + } SET_MODULE_OWNER(dev); +#if 0 + /* FIXME - Is this the correct parent device anyway? */ SET_NETDEV_DEV(dev, &host->device); +#endif priv = netdev_priv(dev); - INIT_LIST_HEAD(&priv->ip_node_list); - spin_lock_init(&priv->lock); priv->host = host; priv->local_fifo = fifo_addr; hi = hpsb_create_hostinfo(ð1394_highlevel, host, sizeof(*hi)); - if (hi == NULL) { - ETH1394_PRINT_G (KERN_ERR, "Out of memory trying to create " - "hostinfo for IEEE 1394 device %s-%d\n", - host->driver->name, host->id); + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); goto out; - } + } - ether1394_init_dev(dev); + ether1394_reset_priv(dev, 1); - if (register_netdev (dev)) { - ETH1394_PRINT (KERN_ERR, dev->name, "Error registering network driver\n"); + if (register_netdev(dev)) { + ETH1394_PRINT_G(KERN_ERR, "Cannot register the driver\n"); goto out; } - ETH1394_PRINT (KERN_INFO, dev->name, "IEEE-1394 IPv4 over 1394 Ethernet (fw-host%d)\n", - host->id); + ETH1394_PRINT(KERN_INFO, dev->name, "IPv4 over IEEE 1394 (fw-host%d)\n", + host->id); hi->host = host; hi->dev = dev; @@ -620,61 +598,37 @@ static void ether1394_add_host (struct hpsb_host *host) * be checked when the eth device is opened. */ priv->broadcast_channel = host->csr.broadcast_channel & 0x3f; - priv->iso = hpsb_iso_recv_init(host, - ETHER1394_ISO_BUF_SIZE, - ETHER1394_GASP_BUFFERS, - priv->broadcast_channel, - HPSB_ISO_DMA_PACKET_PER_BUFFER, - 1, ether1394_iso); - if (priv->iso == NULL) { - ETH1394_PRINT(KERN_ERR, dev->name, - "Could not allocate isochronous receive context " - "for the broadcast channel\n"); - priv->bc_state = ETHER1394_BC_ERROR; - } else { - if (hpsb_iso_recv_start(priv->iso, -1, (1 << 3), -1) < 0) - priv->bc_state = ETHER1394_BC_STOPPED; - else - priv->bc_state = ETHER1394_BC_RUNNING; - } - + ether1394_recv_init(priv); return; - out: - if (dev != NULL) + if (dev) free_netdev(dev); if (hi) hpsb_destroy_hostinfo(ð1394_highlevel, host); - - return; + hpsb_unregister_addrspace(ð1394_highlevel, host, fifo_addr); + hpsb_config_rom_ip1394_remove(host); } /* Remove a card from our list */ -static void ether1394_remove_host (struct hpsb_host *host) +static void ether1394_remove_host(struct hpsb_host *host) { struct eth1394_host_info *hi; + struct eth1394_priv *priv; hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (hi != NULL) { - struct eth1394_priv *priv = netdev_priv(hi->dev); - - hpsb_unregister_addrspace(ð1394_highlevel, host, - priv->local_fifo); - - if (priv->iso != NULL) - hpsb_iso_shutdown(priv->iso); - - if (hi->dev) { - unregister_netdev (hi->dev); - free_netdev(hi->dev); - } - } - - return; + if (!hi) + return; + priv = netdev_priv(hi->dev); + hpsb_unregister_addrspace(ð1394_highlevel, host, priv->local_fifo); + hpsb_config_rom_ip1394_remove(host); + if (priv->iso) + hpsb_iso_shutdown(priv->iso); + unregister_netdev(hi->dev); + free_netdev(hi->dev); } -/* A reset has just arisen */ -static void ether1394_host_reset (struct hpsb_host *host) +/* A bus reset happened */ +static void ether1394_host_reset(struct hpsb_host *host) { struct eth1394_host_info *hi; struct eth1394_priv *priv; @@ -687,24 +641,23 @@ static void ether1394_host_reset (struct hpsb_host *host) hi = hpsb_get_hostinfo(ð1394_highlevel, host); /* This can happen for hosts that we don't use */ - if (hi == NULL) + if (!hi) return; dev = hi->dev; - priv = (struct eth1394_priv *)netdev_priv(dev); + priv = netdev_priv(dev); - /* Reset our private host data, but not our mtu */ - netif_stop_queue (dev); - ether1394_reset_priv (dev, 0); + /* Reset our private host data, but not our MTU */ + netif_stop_queue(dev); + ether1394_reset_priv(dev, 0); list_for_each_entry(node, &priv->ip_node_list, list) { - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; + node_info = node->ud->device.driver_data; spin_lock_irqsave(&node_info->pdg.lock, flags); - list_for_each_safe(lh, n, &node_info->pdg.list) { + list_for_each_safe(lh, n, &node_info->pdg.list) purge_partial_datagram(lh); - } INIT_LIST_HEAD(&(node_info->pdg.list)); node_info->pdg.sz = 0; @@ -712,7 +665,7 @@ static void ether1394_host_reset (struct hpsb_host *host) spin_unlock_irqrestore(&node_info->pdg.lock, flags); } - netif_wake_queue (dev); + netif_wake_queue(dev); } /****************************************** @@ -720,7 +673,6 @@ static void ether1394_host_reset (struct hpsb_host *host) ******************************************/ /* These functions have been adapted from net/ethernet/eth.c */ - /* Create a fake MAC header for an arbitrary protocol layer. * saddr=NULL means use device source address * daddr=NULL means leave destination address (eg unresolved arp). */ @@ -728,25 +680,24 @@ static int ether1394_header(struct sk_buff *skb, struct net_device *dev, unsigned short type, void *daddr, void *saddr, unsigned len) { - struct eth1394hdr *eth = (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); + struct eth1394hdr *eth = + (struct eth1394hdr *)skb_push(skb, ETH1394_HLEN); eth->h_proto = htons(type); - if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)) { + if (dev->flags & (IFF_LOOPBACK | IFF_NOARP)) { memset(eth->h_dest, 0, dev->addr_len); - return(dev->hard_header_len); + return dev->hard_header_len; } if (daddr) { - memcpy(eth->h_dest,daddr,dev->addr_len); + memcpy(eth->h_dest, daddr, dev->addr_len); return dev->hard_header_len; } return -dev->hard_header_len; - } - /* Rebuild the faked MAC header. This is called after an ARP * (or in future other address resolution) has completed on this * sk_buff. We now let ARP fill in the other fields. @@ -757,38 +708,30 @@ static int ether1394_header(struct sk_buff *skb, struct net_device *dev, static int ether1394_rebuild_header(struct sk_buff *skb) { struct eth1394hdr *eth = (struct eth1394hdr *)skb->data; - struct net_device *dev = skb->dev; - switch (eth->h_proto) { - -#ifdef CONFIG_INET - case __constant_htons(ETH_P_IP): - return arp_find((unsigned char*)ð->h_dest, skb); -#endif - default: - ETH1394_PRINT(KERN_DEBUG, dev->name, - "unable to resolve type %04x addresses.\n", - ntohs(eth->h_proto)); - break; - } + if (eth->h_proto == htons(ETH_P_IP)) + return arp_find((unsigned char *)ð->h_dest, skb); + ETH1394_PRINT(KERN_DEBUG, skb->dev->name, + "unable to resolve type %04x addresses\n", + ntohs(eth->h_proto)); return 0; } static int ether1394_header_parse(struct sk_buff *skb, unsigned char *haddr) { struct net_device *dev = skb->dev; + memcpy(haddr, dev->dev_addr, ETH1394_ALEN); return ETH1394_ALEN; } - static int ether1394_header_cache(struct neighbour *neigh, struct hh_cache *hh) { unsigned short type = hh->hh_type; - struct eth1394hdr *eth = (struct eth1394hdr*)(((u8*)hh->hh_data) + - (16 - ETH1394_HLEN)); struct net_device *dev = neigh->dev; + struct eth1394hdr *eth = + (struct eth1394hdr *)((u8 *)hh->hh_data + 16 - ETH1394_HLEN); if (type == htons(ETH_P_802_3)) return -1; @@ -805,38 +748,25 @@ static void ether1394_header_cache_update(struct hh_cache *hh, struct net_device *dev, unsigned char * haddr) { - memcpy(((u8*)hh->hh_data) + (16 - ETH1394_HLEN), haddr, dev->addr_len); + memcpy((u8 *)hh->hh_data + 16 - ETH1394_HLEN, haddr, dev->addr_len); } -static int ether1394_mac_addr(struct net_device *dev, void *p) -{ - if (netif_running(dev)) - return -EBUSY; - - /* Not going to allow setting the MAC address, we really need to use - * the real one supplied by the hardware */ - return -EINVAL; - } - - - /****************************************** * Datagram reception code ******************************************/ /* Copied from net/ethernet/eth.c */ -static inline u16 ether1394_type_trans(struct sk_buff *skb, - struct net_device *dev) +static u16 ether1394_type_trans(struct sk_buff *skb, struct net_device *dev) { struct eth1394hdr *eth; unsigned char *rawp; - skb->mac.raw = skb->data; - skb_pull (skb, ETH1394_HLEN); + skb_reset_mac_header(skb); + skb_pull(skb, ETH1394_HLEN); eth = eth1394_hdr(skb); if (*eth->h_dest & 1) { - if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len)==0) + if (memcmp(eth->h_dest, dev->broadcast, dev->addr_len) == 0) skb->pkt_type = PACKET_BROADCAST; #if 0 else @@ -845,47 +775,45 @@ static inline u16 ether1394_type_trans(struct sk_buff *skb, } else { if (memcmp(eth->h_dest, dev->dev_addr, dev->addr_len)) skb->pkt_type = PACKET_OTHERHOST; - } + } - if (ntohs (eth->h_proto) >= 1536) + if (ntohs(eth->h_proto) >= 1536) return eth->h_proto; rawp = skb->data; - if (*(unsigned short *)rawp == 0xFFFF) - return htons (ETH_P_802_3); + if (*(unsigned short *)rawp == 0xFFFF) + return htons(ETH_P_802_3); - return htons (ETH_P_802_2); + return htons(ETH_P_802_2); } /* Parse an encapsulated IP1394 header into an ethernet frame packet. * We also perform ARP translation here, if need be. */ -static inline u16 ether1394_parse_encap(struct sk_buff *skb, - struct net_device *dev, - nodeid_t srcid, nodeid_t destid, - u16 ether_type) +static u16 ether1394_parse_encap(struct sk_buff *skb, struct net_device *dev, + nodeid_t srcid, nodeid_t destid, + u16 ether_type) { struct eth1394_priv *priv = netdev_priv(dev); u64 dest_hw; unsigned short ret = 0; - /* Setup our hw addresses. We use these to build the - * ethernet header. */ + /* Setup our hw addresses. We use these to build the ethernet header. */ if (destid == (LOCAL_BUS | ALL_NODES)) dest_hw = ~0ULL; /* broadcast */ else - dest_hw = cpu_to_be64((((u64)priv->host->csr.guid_hi) << 32) | + dest_hw = cpu_to_be64((u64)priv->host->csr.guid_hi << 32 | priv->host->csr.guid_lo); /* If this is an ARP packet, convert it. First, we want to make * use of some of the fields, since they tell us a little bit * about the sending machine. */ if (ether_type == htons(ETH_P_ARP)) { - struct eth1394_arp *arp1394 = (struct eth1394_arp*)skb->data; + struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); u64 fifo_addr = (u64)ntohs(arp1394->fifo_hi) << 32 | - ntohl(arp1394->fifo_lo); + ntohl(arp1394->fifo_lo); u8 max_rec = min(priv->host->csr.max_rec, (u8)(arp1394->max_rec)); int sspd = arp1394->sspd; @@ -899,16 +827,17 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb, if (sspd > 5 || sspd < 0) sspd = 0; - maxpayload = min(eth1394_speedto_maxpayload[sspd], (u16)(1 << (max_rec + 1))); + maxpayload = min(eth1394_speedto_maxpayload[sspd], + (u16)(1 << (max_rec + 1))); guid = get_unaligned(&arp1394->s_uniq_id); node = eth1394_find_node_guid(&priv->ip_node_list, be64_to_cpu(guid)); - if (!node) { + if (!node) return 0; - } - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; + node_info = + (struct eth1394_node_info *)node->ud->device.driver_data; /* Update our speed/payload/fifo_offset table */ node_info->maxpayload = maxpayload; @@ -927,7 +856,7 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb, arp->ar_hln = 8; arp_ptr += arp->ar_hln; /* skip over sender unique id */ - *(u32*)arp_ptr = arp1394->sip; /* move sender IP addr */ + *(u32 *)arp_ptr = arp1394->sip; /* move sender IP addr */ arp_ptr += arp->ar_pln; /* skip over sender IP addr */ if (arp->ar_op == htons(ARPOP_REQUEST)) @@ -944,65 +873,65 @@ static inline u16 ether1394_parse_encap(struct sk_buff *skb, return ret; } -static inline int fragment_overlap(struct list_head *frag_list, int offset, int len) +static int fragment_overlap(struct list_head *frag_list, int offset, int len) { struct fragment_info *fi; + int end = offset + len; - list_for_each_entry(fi, frag_list, list) { - if ( ! ((offset > (fi->offset + fi->len - 1)) || - ((offset + len - 1) < fi->offset))) + list_for_each_entry(fi, frag_list, list) + if (offset < fi->offset + fi->len && end > fi->offset) return 1; - } + return 0; } -static inline struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) +static struct list_head *find_partial_datagram(struct list_head *pdgl, int dgl) { struct partial_datagram *pd; - list_for_each_entry(pd, pdgl, list) { + list_for_each_entry(pd, pdgl, list) if (pd->dgl == dgl) return &pd->list; - } + return NULL; } /* Assumes that new fragment does not overlap any existing fragments */ -static inline int new_fragment(struct list_head *frag_info, int offset, int len) +static int new_fragment(struct list_head *frag_info, int offset, int len) { struct list_head *lh; struct fragment_info *fi, *fi2, *new; list_for_each(lh, frag_info) { fi = list_entry(lh, struct fragment_info, list); - if ((fi->offset + fi->len) == offset) { + if (fi->offset + fi->len == offset) { /* The new fragment can be tacked on to the end */ fi->len += len; /* Did the new fragment plug a hole? */ fi2 = list_entry(lh->next, struct fragment_info, list); - if ((fi->offset + fi->len) == fi2->offset) { + if (fi->offset + fi->len == fi2->offset) { /* glue fragments together */ fi->len += fi2->len; list_del(lh->next); kfree(fi2); } return 0; - } else if ((offset + len) == fi->offset) { + } else if (offset + len == fi->offset) { /* The new fragment can be tacked on to the beginning */ fi->offset = offset; fi->len += len; /* Did the new fragment plug a hole? */ fi2 = list_entry(lh->prev, struct fragment_info, list); - if ((fi2->offset + fi2->len) == fi->offset) { + if (fi2->offset + fi2->len == fi->offset) { /* glue fragments together */ fi2->len += fi->len; list_del(lh); kfree(fi); } return 0; - } else if (offset > (fi->offset + fi->len)) { + } else if (offset > fi->offset + fi->len) { break; - } else if ((offset + len) < fi->offset) { + } else if (offset + len < fi->offset) { lh = lh->prev; break; } @@ -1016,14 +945,12 @@ static inline int new_fragment(struct list_head *frag_info, int offset, int len) new->len = len; list_add(&new->list, lh); - return 0; } -static inline int new_partial_datagram(struct net_device *dev, - struct list_head *pdgl, int dgl, - int dg_size, char *frag_buf, - int frag_off, int frag_len) +static int new_partial_datagram(struct net_device *dev, struct list_head *pdgl, + int dgl, int dg_size, char *frag_buf, + int frag_off, int frag_len) { struct partial_datagram *new; @@ -1056,33 +983,33 @@ static inline int new_partial_datagram(struct net_device *dev, memcpy(new->pbuf + frag_off, frag_buf, frag_len); list_add(&new->list, pdgl); - return 0; } -static inline int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, - char *frag_buf, int frag_off, int frag_len) +static int update_partial_datagram(struct list_head *pdgl, struct list_head *lh, + char *frag_buf, int frag_off, int frag_len) { - struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list); + struct partial_datagram *pd = + list_entry(lh, struct partial_datagram, list); - if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) { + if (new_fragment(&pd->frag_info, frag_off, frag_len) < 0) return -ENOMEM; - } memcpy(pd->pbuf + frag_off, frag_buf, frag_len); /* Move list entry to beginnig of list so that oldest partial * datagrams percolate to the end of the list */ list_move(lh, pdgl); - return 0; } -static inline int is_datagram_complete(struct list_head *lh, int dg_size) +static int is_datagram_complete(struct list_head *lh, int dg_size) { - struct partial_datagram *pd = list_entry(lh, struct partial_datagram, list); - struct fragment_info *fi = list_entry(pd->frag_info.next, - struct fragment_info, list); + struct partial_datagram *pd; + struct fragment_info *fi; + + pd = list_entry(lh, struct partial_datagram, list); + fi = list_entry(pd->frag_info.next, struct fragment_info, list); return (fi->len == dg_size); } @@ -1105,7 +1032,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, if (!ud) { struct eth1394_node_ref *node; node = eth1394_find_node_nodeid(&priv->ip_node_list, srcid); - if (!node) { + if (unlikely(!node)) { HPSB_PRINT(KERN_ERR, "ether1394 rx: sender nodeid " "lookup failure: " NODE_BUS_FMT, NODE_BUS_ARGS(priv->host, srcid)); @@ -1117,7 +1044,7 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, priv->ud_list[NODEID_TO_NODE(srcid)] = ud; } - node_info = (struct eth1394_node_info*)ud->device.driver_data; + node_info = (struct eth1394_node_info *)ud->device.driver_data; /* First, did we receive a fragmented or unfragmented datagram? */ hdr->words.word1 = ntohs(hdr->words.word1); @@ -1130,13 +1057,14 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, * high level network layer. */ skb = dev_alloc_skb(len + dev->hard_header_len + 15); - if (!skb) { - HPSB_PRINT (KERN_ERR, "ether1394 rx: low on mem\n"); + if (unlikely(!skb)) { + ETH1394_PRINT_G(KERN_ERR, "Out of memory\n"); priv->stats.rx_dropped++; return -1; } skb_reserve(skb, (dev->hard_header_len + 15) & ~15); - memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, len - hdr_len); + memcpy(skb_put(skb, len - hdr_len), buf + hdr_len, + len - hdr_len); ether_type = hdr->uf.ether_type; } else { /* A datagram fragment has been received, now the fun begins. */ @@ -1221,9 +1149,8 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, pd = list_entry(lh, struct partial_datagram, list); - if (hdr->common.lf == ETH1394_HDR_LF_FF) { + if (hdr->common.lf == ETH1394_HDR_LF_FF) pd->ether_type = ether_type; - } if (is_datagram_complete(lh, dg_size)) { ether_type = pd->ether_type; @@ -1250,8 +1177,8 @@ static int ether1394_data_handler(struct net_device *dev, int srcid, int destid, skb->protocol = ether1394_parse_encap(skb, dev, srcid, destid, ether_type); - spin_lock_irqsave(&priv->lock, flags); + if (!skb->protocol) { priv->stats.rx_errors++; priv->stats.rx_dropped++; @@ -1285,9 +1212,9 @@ static int ether1394_write(struct hpsb_host *host, int srcid, int destid, struct eth1394_host_info *hi; hi = hpsb_get_hostinfo(ð1394_highlevel, host); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", - host->driver->name); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + host->id); return RCODE_ADDRESS_ERROR; } @@ -1311,9 +1238,9 @@ static void ether1394_iso(struct hpsb_iso *iso) int nready; hi = hpsb_get_hostinfo(ð1394_highlevel, iso->host); - if (hi == NULL) { - ETH1394_PRINT_G(KERN_ERR, "Could not find net device for host %s\n", - iso->host->driver->name); + if (unlikely(!hi)) { + ETH1394_PRINT_G(KERN_ERR, "No net device at fw-host%d\n", + iso->host->id); return; } @@ -1323,20 +1250,20 @@ static void ether1394_iso(struct hpsb_iso *iso) for (i = 0; i < nready; i++) { struct hpsb_iso_packet_info *info = &iso->infos[(iso->first_packet + i) % iso->buf_packets]; - data = (quadlet_t*) (iso->data_buf.kvirt + info->offset); + data = (quadlet_t *)(iso->data_buf.kvirt + info->offset); /* skip over GASP header */ buf = (char *)data + 8; len = info->len - 8; - specifier_id = (((be32_to_cpu(data[0]) & 0xffff) << 8) | - ((be32_to_cpu(data[1]) & 0xff000000) >> 24)); + specifier_id = (be32_to_cpu(data[0]) & 0xffff) << 8 | + (be32_to_cpu(data[1]) & 0xff000000) >> 24; source_id = be32_to_cpu(data[0]) >> 16; priv = netdev_priv(dev); - if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) || - specifier_id != ETHER1394_GASP_SPECIFIER_ID) { + if (info->channel != (iso->host->csr.broadcast_channel & 0x3f) + || specifier_id != ETHER1394_GASP_SPECIFIER_ID) { /* This packet is not for us */ continue; } @@ -1364,35 +1291,31 @@ static void ether1394_iso(struct hpsb_iso *iso) * speed, and unicast FIFO address information between the sender_unique_id * and the IP addresses. */ -static inline void ether1394_arp_to_1394arp(struct sk_buff *skb, - struct net_device *dev) +static void ether1394_arp_to_1394arp(struct sk_buff *skb, + struct net_device *dev) { struct eth1394_priv *priv = netdev_priv(dev); - struct arphdr *arp = (struct arphdr *)skb->data; unsigned char *arp_ptr = (unsigned char *)(arp + 1); struct eth1394_arp *arp1394 = (struct eth1394_arp *)skb->data; - /* Believe it or not, all that need to happen is sender IP get moved - * and set hw_addr_len, max_rec, sspd, fifo_hi and fifo_lo. */ arp1394->hw_addr_len = 16; arp1394->sip = *(u32*)(arp_ptr + ETH1394_ALEN); arp1394->max_rec = priv->host->csr.max_rec; arp1394->sspd = priv->host->csr.lnk_spd; - arp1394->fifo_hi = htons (priv->local_fifo >> 32); - arp1394->fifo_lo = htonl (priv->local_fifo & ~0x0); - - return; + arp1394->fifo_hi = htons(priv->local_fifo >> 32); + arp1394->fifo_lo = htonl(priv->local_fifo & ~0x0); } /* We need to encapsulate the standard header with our own. We use the * ethernet header's proto for our own. */ -static inline unsigned int ether1394_encapsulate_prep(unsigned int max_payload, - __be16 proto, - union eth1394_hdr *hdr, - u16 dg_size, u16 dgl) +static unsigned int ether1394_encapsulate_prep(unsigned int max_payload, + __be16 proto, + union eth1394_hdr *hdr, + u16 dg_size, u16 dgl) { - unsigned int adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; + unsigned int adj_max_payload = + max_payload - hdr_type_len[ETH1394_HDR_LF_UF]; /* Does it all fit in one packet? */ if (dg_size <= adj_max_payload) { @@ -1405,19 +1328,19 @@ static inline unsigned int ether1394_encapsulate_prep(unsigned int max_payload, hdr->ff.dgl = dgl; adj_max_payload = max_payload - hdr_type_len[ETH1394_HDR_LF_FF]; } - return((dg_size + (adj_max_payload - 1)) / adj_max_payload); + return (dg_size + adj_max_payload - 1) / adj_max_payload; } -static inline unsigned int ether1394_encapsulate(struct sk_buff *skb, - unsigned int max_payload, - union eth1394_hdr *hdr) +static unsigned int ether1394_encapsulate(struct sk_buff *skb, + unsigned int max_payload, + union eth1394_hdr *hdr) { union eth1394_hdr *bufhdr; int ftype = hdr->common.lf; int hdrsz = hdr_type_len[ftype]; unsigned int adj_max_payload = max_payload - hdrsz; - switch(ftype) { + switch (ftype) { case ETH1394_HDR_LF_UF: bufhdr = (union eth1394_hdr *)skb_push(skb, hdrsz); bufhdr->words.word1 = htons(hdr->words.word1); @@ -1446,11 +1369,10 @@ static inline unsigned int ether1394_encapsulate(struct sk_buff *skb, bufhdr->words.word3 = htons(hdr->words.word3); bufhdr->words.word4 = 0; } - return min(max_payload, skb->len); } -static inline struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) +static struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host *host) { struct hpsb_packet *p; @@ -1463,61 +1385,57 @@ static inline struct hpsb_packet *ether1394_alloc_common_packet(struct hpsb_host return p; } -static inline int ether1394_prep_write_packet(struct hpsb_packet *p, - struct hpsb_host *host, - nodeid_t node, u64 addr, - void * data, int tx_len) +static int ether1394_prep_write_packet(struct hpsb_packet *p, + struct hpsb_host *host, nodeid_t node, + u64 addr, void *data, int tx_len) { p->node_id = node; p->data = NULL; p->tcode = TCODE_WRITEB; - p->header[1] = (host->node_id << 16) | (addr >> 32); + p->header[1] = host->node_id << 16 | addr >> 32; p->header[2] = addr & 0xffffffff; p->header_size = 16; p->expect_response = 1; if (hpsb_get_tlabel(p)) { - ETH1394_PRINT_G(KERN_ERR, "No more tlabels left while sending " - "to node " NODE_BUS_FMT "\n", NODE_BUS_ARGS(host, node)); + ETH1394_PRINT_G(KERN_ERR, "Out of tlabels\n"); return -1; } - p->header[0] = (p->node_id << 16) | (p->tlabel << 10) - | (1 << 8) | (TCODE_WRITEB << 4); + p->header[0] = + p->node_id << 16 | p->tlabel << 10 | 1 << 8 | TCODE_WRITEB << 4; p->header[3] = tx_len << 16; p->data_size = (tx_len + 3) & ~3; - p->data = (quadlet_t*)data; + p->data = data; return 0; } -static inline void ether1394_prep_gasp_packet(struct hpsb_packet *p, - struct eth1394_priv *priv, - struct sk_buff *skb, int length) +static void ether1394_prep_gasp_packet(struct hpsb_packet *p, + struct eth1394_priv *priv, + struct sk_buff *skb, int length) { p->header_size = 4; p->tcode = TCODE_STREAM_DATA; - p->header[0] = (length << 16) | (3 << 14) - | ((priv->broadcast_channel) << 8) - | (TCODE_STREAM_DATA << 4); + p->header[0] = length << 16 | 3 << 14 | priv->broadcast_channel << 8 | + TCODE_STREAM_DATA << 4; p->data_size = length; - p->data = ((quadlet_t*)skb->data) - 2; - p->data[0] = cpu_to_be32((priv->host->node_id << 16) | + p->data = (quadlet_t *)skb->data - 2; + p->data[0] = cpu_to_be32(priv->host->node_id << 16 | ETHER1394_GASP_SPECIFIER_ID_HI); - p->data[1] = cpu_to_be32((ETHER1394_GASP_SPECIFIER_ID_LO << 24) | + p->data[1] = cpu_to_be32(ETHER1394_GASP_SPECIFIER_ID_LO << 24 | ETHER1394_GASP_VERSION); - /* Setting the node id to ALL_NODES (not LOCAL_BUS | ALL_NODES) - * prevents hpsb_send_packet() from setting the speed to an arbitrary - * value based on packet->node_id if packet->node_id is not set. */ - p->node_id = ALL_NODES; p->speed_code = priv->bc_sspd; + + /* prevent hpsb_send_packet() from overriding our speed code */ + p->node_id = LOCAL_BUS | ALL_NODES; } -static inline void ether1394_free_packet(struct hpsb_packet *packet) +static void ether1394_free_packet(struct hpsb_packet *packet) { if (packet->tcode != TCODE_STREAM_DATA) hpsb_free_tlabel(packet); @@ -1536,7 +1454,7 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) return -1; if (ptask->tx_type == ETH1394_GASP) { - int length = tx_len + (2 * sizeof(quadlet_t)); + int length = tx_len + 2 * sizeof(quadlet_t); ether1394_prep_gasp_packet(packet, priv, ptask->skb, length); } else if (ether1394_prep_write_packet(packet, priv->host, @@ -1559,13 +1477,11 @@ static int ether1394_send_packet(struct packet_task *ptask, unsigned int tx_len) return 0; } - /* Task function to be run when a datagram transmission is completed */ -static inline void ether1394_dg_complete(struct packet_task *ptask, int fail) +static void ether1394_dg_complete(struct packet_task *ptask, int fail) { struct sk_buff *skb = ptask->skb; - struct net_device *dev = skb->dev; - struct eth1394_priv *priv = netdev_priv(dev); + struct eth1394_priv *priv = netdev_priv(skb->dev); unsigned long flags; /* Statistics */ @@ -1583,7 +1499,6 @@ static inline void ether1394_dg_complete(struct packet_task *ptask, int fail) kmem_cache_free(packet_task_cache, ptask); } - /* Callback for when a packet has been sent and the status of that packet is * known */ static void ether1394_complete_cb(void *__ptask) @@ -1611,19 +1526,15 @@ static void ether1394_complete_cb(void *__ptask) } } - - /* Transmit a packet (called by kernel) */ -static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) +static int ether1394_tx(struct sk_buff *skb, struct net_device *dev) { - gfp_t kmflags = in_interrupt() ? GFP_ATOMIC : GFP_KERNEL; struct eth1394hdr *eth; struct eth1394_priv *priv = netdev_priv(dev); __be16 proto; unsigned long flags; nodeid_t dest_node; eth1394_tx_type tx_type; - int ret = 0; unsigned int tx_len; unsigned int max_payload; u16 dg_size; @@ -1632,29 +1543,24 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) struct eth1394_node_ref *node; struct eth1394_node_info *node_info = NULL; - ptask = kmem_cache_alloc(packet_task_cache, kmflags); - if (ptask == NULL) { - ret = -ENOMEM; + ptask = kmem_cache_alloc(packet_task_cache, GFP_ATOMIC); + if (ptask == NULL) goto fail; - } /* XXX Ignore this for now. Noticed that when MacOSX is the IRM, * it does not set our validity bit. We need to compensate for * that somewhere else, but not in eth1394. */ #if 0 - if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) { - ret = -EAGAIN; + if ((priv->host->csr.broadcast_channel & 0xc0000000) != 0xc0000000) goto fail; - } #endif - if ((skb = skb_share_check (skb, kmflags)) == NULL) { - ret = -ENOMEM; + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) goto fail; - } /* Get rid of the fake eth1394 header, but save a pointer */ - eth = (struct eth1394hdr*)skb->data; + eth = (struct eth1394hdr *)skb->data; skb_pull(skb, ETH1394_HLEN); proto = eth->h_proto; @@ -1665,11 +1571,11 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) if (memcmp(eth->h_dest, dev->broadcast, ETH1394_ALEN) == 0 || proto == htons(ETH_P_ARP) || (proto == htons(ETH_P_IP) && - IN_MULTICAST(ntohl(skb->nh.iph->daddr)))) { + IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { tx_type = ETH1394_GASP; dest_node = LOCAL_BUS | ALL_NODES; max_payload = priv->bc_maxpayload - ETHER1394_GASP_OVERHEAD; - BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD)); + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); dgl = priv->bc_dgl; if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) priv->bc_dgl++; @@ -1678,19 +1584,17 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) node = eth1394_find_node_guid(&priv->ip_node_list, be64_to_cpu(guid)); - if (!node) { - ret = -EAGAIN; + if (!node) goto fail; - } - node_info = (struct eth1394_node_info*)node->ud->device.driver_data; - if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) { - ret = -EAGAIN; + + node_info = + (struct eth1394_node_info *)node->ud->device.driver_data; + if (node_info->fifo == CSR1212_INVALID_ADDR_SPACE) goto fail; - } dest_node = node->ud->ne->nodeid; max_payload = node_info->maxpayload; - BUG_ON(max_payload < (512 - ETHER1394_GASP_OVERHEAD)); + BUG_ON(max_payload < 512 - ETHER1394_GASP_OVERHEAD); dgl = node_info->dgl; if (max_payload < dg_size + hdr_type_len[ETH1394_HDR_LF_UF]) @@ -1700,7 +1604,7 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) /* If this is an ARP packet, convert it */ if (proto == htons(ETH_P_ARP)) - ether1394_arp_to_1394arp (skb, dev); + ether1394_arp_to_1394arp(skb, dev); ptask->hdr.words.word1 = 0; ptask->hdr.words.word2 = 0; @@ -1723,9 +1627,8 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) ptask->tx_type = tx_type; ptask->max_payload = max_payload; - ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, proto, - &ptask->hdr, dg_size, - dgl); + ptask->outstanding_pkts = ether1394_encapsulate_prep(max_payload, + proto, &ptask->hdr, dg_size, dgl); /* Add the encapsulation header to the fragment */ tx_len = ether1394_encapsulate(skb, max_payload, &ptask->hdr); @@ -1734,7 +1637,7 @@ static int ether1394_tx (struct sk_buff *skb, struct net_device *dev) goto fail; netif_wake_queue(dev); - return 0; + return NETDEV_TX_OK; fail: if (ptask) kmem_cache_free(packet_task_cache, ptask); @@ -1742,40 +1645,56 @@ fail: if (skb != NULL) dev_kfree_skb(skb); - spin_lock_irqsave (&priv->lock, flags); + spin_lock_irqsave(&priv->lock, flags); priv->stats.tx_dropped++; priv->stats.tx_errors++; - spin_unlock_irqrestore (&priv->lock, flags); + spin_unlock_irqrestore(&priv->lock, flags); if (netif_queue_stopped(dev)) netif_wake_queue(dev); - return 0; /* returning non-zero causes serious problems */ + /* + * FIXME: According to a patch from 2003-02-26, "returning non-zero + * causes serious problems" here, allegedly. Before that patch, + * -ERRNO was returned which is not appropriate under Linux 2.6. + * Perhaps more needs to be done? Stop the queue in serious + * conditions and restart it elsewhere? + */ + /* return NETDEV_TX_BUSY; */ + return NETDEV_TX_OK; } -static void ether1394_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) +static void ether1394_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) { - strcpy (info->driver, driver_name); - /* FIXME XXX provide sane businfo */ - strcpy (info->bus_info, "ieee1394"); + strcpy(info->driver, driver_name); + strcpy(info->bus_info, "ieee1394"); /* FIXME provide more detail? */ } static struct ethtool_ops ethtool_ops = { .get_drvinfo = ether1394_get_drvinfo }; -static int __init ether1394_init_module (void) +static int __init ether1394_init_module(void) { - packet_task_cache = kmem_cache_create("packet_task", sizeof(struct packet_task), + int err; + + packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct packet_task), 0, 0, NULL, NULL); + if (!packet_task_cache) + return -ENOMEM; - /* Register ourselves as a highlevel driver */ hpsb_register_highlevel(ð1394_highlevel); - - return hpsb_register_protocol(ð1394_proto_driver); + err = hpsb_register_protocol(ð1394_proto_driver); + if (err) { + hpsb_unregister_highlevel(ð1394_highlevel); + kmem_cache_destroy(packet_task_cache); + } + return err; } -static void __exit ether1394_exit_module (void) +static void __exit ether1394_exit_module(void) { hpsb_unregister_protocol(ð1394_proto_driver); hpsb_unregister_highlevel(ð1394_highlevel); diff --git a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h index c45cbff..a3439ee 100644 --- a/drivers/ieee1394/eth1394.h +++ b/drivers/ieee1394/eth1394.h @@ -25,8 +25,11 @@ #define __ETH1394_H #include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <asm/byteorder.h> #include "ieee1394.h" +#include "ieee1394_types.h" /* Register for incoming packets. This is 4096 bytes, which supports up to * S3200 (per Table 16-3 of IEEE 1394b-2002). */ @@ -34,22 +37,15 @@ /* GASP identifier numbers for IPv4 over IEEE 1394 */ #define ETHER1394_GASP_SPECIFIER_ID 0x00005E -#define ETHER1394_GASP_SPECIFIER_ID_HI ((ETHER1394_GASP_SPECIFIER_ID >> 8) & 0xffff) -#define ETHER1394_GASP_SPECIFIER_ID_LO (ETHER1394_GASP_SPECIFIER_ID & 0xff) +#define ETHER1394_GASP_SPECIFIER_ID_HI ((0x00005E >> 8) & 0xffff) +#define ETHER1394_GASP_SPECIFIER_ID_LO (0x00005E & 0xff) #define ETHER1394_GASP_VERSION 1 -#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* GASP header overhead */ +#define ETHER1394_GASP_OVERHEAD (2 * sizeof(quadlet_t)) /* for GASP header */ -#define ETHER1394_GASP_BUFFERS 16 +#define ETHER1394_GASP_BUFFERS 16 -/* rawiso buffer size - due to a limitation in rawiso, we must limit each - * GASP buffer to be less than PAGE_SIZE. */ -#define ETHER1394_ISO_BUF_SIZE ETHER1394_GASP_BUFFERS * \ - min((unsigned int)PAGE_SIZE, \ - 2 * (1U << (priv->host->csr.max_rec + 1))) - -/* Node set == 64 */ -#define NODE_SET (ALL_NODES + 1) +#define NODE_SET (ALL_NODES + 1) /* Node set == 64 */ enum eth1394_bc_states { ETHER1394_BC_ERROR, ETHER1394_BC_RUNNING, @@ -85,19 +81,14 @@ struct eth1394hdr { unsigned short h_proto; /* packet type ID field */ } __attribute__((packed)); -#ifdef __KERNEL__ -#include <linux/skbuff.h> - static inline struct eth1394hdr *eth1394_hdr(const struct sk_buff *skb) { - return (struct eth1394hdr *)skb->mac.raw; + return (struct eth1394hdr *)skb_mac_header(skb); } -#endif typedef enum {ETH1394_GASP, ETH1394_WRREQ} eth1394_tx_type; /* IP1394 headers */ -#include <asm/byteorder.h> /* Unfragmented */ #if defined __BIG_ENDIAN_BITFIELD diff --git a/drivers/ieee1394/highlevel.c b/drivers/ieee1394/highlevel.c index 694da82..83a49331 100644 --- a/drivers/ieee1394/highlevel.c +++ b/drivers/ieee1394/highlevel.c @@ -70,8 +70,12 @@ static struct hl_host_info *hl_get_hostinfo(struct hpsb_highlevel *hl, return NULL; } -/* Returns a per host/driver data structure that was previously stored by - * hpsb_create_hostinfo. */ +/** + * hpsb_get_hostinfo - retrieve a hostinfo pointer bound to this driver/host + * + * Returns a per @host and @hl driver data structure that was previously stored + * by hpsb_create_hostinfo. + */ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) { struct hl_host_info *hi = hl_get_hostinfo(hl, host); @@ -79,7 +83,13 @@ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) return hi ? hi->data : NULL; } -/* If size is zero, then the return here is only valid for error checking */ +/** + * hpsb_create_hostinfo - allocate a hostinfo pointer bound to this driver/host + * + * Allocate a hostinfo pointer backed by memory with @data_size and bind it to + * to this @hl driver and @host. If @data_size is zero, then the return here is + * only valid for error checking. + */ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, size_t data_size) { @@ -113,6 +123,11 @@ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, return data; } +/** + * hpsb_set_hostinfo - set the hostinfo pointer to something useful + * + * Usually follows a call to hpsb_create_hostinfo, where the size is 0. + */ int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data) { @@ -132,6 +147,11 @@ int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, return -EINVAL; } +/** + * hpsb_destroy_hostinfo - free and remove a hostinfo pointer + * + * Free and remove the hostinfo pointer bound to this @hl driver and @host. + */ void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) { struct hl_host_info *hi; @@ -147,6 +167,12 @@ void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host) return; } +/** + * hpsb_set_hostinfo_key - set an alternate lookup key for an hostinfo + * + * Sets an alternate lookup key for the hostinfo bound to this @hl driver and + * @host. + */ void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key) { @@ -158,6 +184,9 @@ void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, return; } +/** + * hpsb_get_hostinfo_bykey - retrieve a hostinfo pointer by its alternate key + */ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key) { struct hl_host_info *hi; @@ -189,6 +218,12 @@ static int highlevel_for_each_host_reg(struct hpsb_host *host, void *__data) return 0; } +/** + * hpsb_register_highlevel - register highlevel driver + * + * The name pointer in @hl has to stay valid at all times because the string is + * not copied. + */ void hpsb_register_highlevel(struct hpsb_highlevel *hl) { unsigned long flags; @@ -258,6 +293,9 @@ static int highlevel_for_each_host_unreg(struct hpsb_host *host, void *__data) return 0; } +/** + * hpsb_unregister_highlevel - unregister highlevel driver + */ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) { unsigned long flags; @@ -273,6 +311,19 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl) nodemgr_for_each_host(hl, highlevel_for_each_host_unreg); } +/** + * hpsb_allocate_and_register_addrspace - alloc' and reg' a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, @@ -348,6 +399,19 @@ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, return retval; } +/** + * hpsb_register_addrspace - register a host address space + * + * @start and @end are 48 bit pointers and have to be quadlet aligned. + * @end points to the first address behind the handled addresses. This + * function can be called multiple times for a single hpsb_highlevel @hl to + * implement sparse register sets. The requested region must not overlap any + * previously allocated region, otherwise registering will fail. + * + * It returns true for successful allocation. Address spaces can be + * unregistered with hpsb_unregister_addrspace. All remaining address spaces + * are automatically deallocated together with the hpsb_highlevel @hl. + */ int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, u64 start, u64 end) { @@ -419,6 +483,11 @@ int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, return retval; } +/** + * hpsb_listen_channel - enable receving a certain isochronous channel + * + * Reception is handled through the @hl's iso_receive op. + */ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel) { @@ -431,6 +500,9 @@ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, return 0; } +/** + * hpsb_unlisten_channel - disable receving a certain isochronous channel + */ void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel) { @@ -528,6 +600,17 @@ void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, read_unlock_irqrestore(&hl_irqs_lock, flags); } +/* + * highlevel_read, highlevel_write, highlevel_lock, highlevel_lock64: + * + * These functions are called to handle transactions. They are called when a + * packet arrives. The flags argument contains the second word of the first + * header quadlet of the incoming packet (containing transaction label, retry + * code, transaction code and priority). These functions either return a + * response code or a negative number. In the first case a response will be + * generated. In the latter case, no response will be sent and the driver which + * handled the request will send the response itself. + */ int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, unsigned int length, u16 flags) { diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h index 4b33011..63474f7 100644 --- a/drivers/ieee1394/highlevel.h +++ b/drivers/ieee1394/highlevel.h @@ -99,16 +99,6 @@ struct hpsb_address_ops { void highlevel_add_host(struct hpsb_host *host); void highlevel_remove_host(struct hpsb_host *host); void highlevel_host_reset(struct hpsb_host *host); - -/* - * These functions are called to handle transactions. They are called when a - * packet arrives. The flags argument contains the second word of the first - * header quadlet of the incoming packet (containing transaction label, retry - * code, transaction code and priority). These functions either return a - * response code or a negative number. In the first case a response will be - * generated. In the latter case, no response will be sent and the driver which - * handled the request will send the response itself. - */ int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr, unsigned int length, u16 flags); int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data, @@ -119,30 +109,13 @@ int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store, int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store, u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags); - void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length); void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction, void *data, size_t length); -/* - * Register highlevel driver. The name pointer has to stay valid at all times - * because the string is not copied. - */ void hpsb_register_highlevel(struct hpsb_highlevel *hl); void hpsb_unregister_highlevel(struct hpsb_highlevel *hl); -/* - * Register handlers for host address spaces. Start and end are 48 bit pointers - * and have to be quadlet aligned. Argument "end" points to the first address - * behind the handled addresses. This function can be called multiple times for - * a single hpsb_highlevel to implement sparse register sets. The requested - * region must not overlap any previously allocated region, otherwise - * registering will fail. - * - * It returns true for successful allocation. Address spaces can be - * unregistered with hpsb_unregister_addrspace. All remaining address spaces - * are automatically deallocated together with the hpsb_highlevel. - */ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, @@ -152,45 +125,19 @@ int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, struct hpsb_address_ops *ops, u64 start, u64 end); int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host, u64 start); - -/* - * Enable or disable receving a certain isochronous channel through the - * iso_receive op. - */ int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, - unsigned int channel); + unsigned int channel); void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned int channel); -/* Retrieve a hostinfo pointer bound to this driver/host */ void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); - -/* Allocate a hostinfo pointer of data_size bound to this driver/host */ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, size_t data_size); - -/* Free and remove the hostinfo pointer bound to this driver/host */ void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host); - -/* Set an alternate lookup key for the hostinfo bound to this driver/host */ void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key); - -/* Retrieve the alternate lookup key for the hostinfo bound to this - * driver/host */ -unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, - struct hpsb_host *host); - -/* Retrieve a hostinfo pointer bound to this driver using its alternate key */ void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key); - -/* Set the hostinfo pointer to something useful. Usually follows a call to - * hpsb_create_hostinfo, where the size is 0. */ int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data); -/* Retrieve hpsb_host using a highlevel handle and a key */ -struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, - unsigned long key); - #endif /* IEEE1394_HIGHLEVEL_H */ diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c index 32a1309..6164a9a 100644 --- a/drivers/ieee1394/hosts.c +++ b/drivers/ieee1394/hosts.c @@ -94,14 +94,6 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data) return 0; } -/* - * The pending_packet_queue is special in that it's processed - * from hardirq context too (such as hpsb_bus_reset()). Hence - * split the lock class from the usual networking skb-head - * lock class by using a separate key for it: - */ -static struct lock_class_key pending_packet_queue_key; - static DEFINE_MUTEX(host_num_alloc); /** @@ -137,9 +129,7 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, h->hostdata = h + 1; h->driver = drv; - skb_queue_head_init(&h->pending_packet_queue); - lockdep_set_class(&h->pending_packet_queue.lock, - &pending_packet_queue_key); + INIT_LIST_HEAD(&h->pending_packets); INIT_LIST_HEAD(&h->addr_space); for (i = 2; i < 16; i++) @@ -190,7 +180,7 @@ int hpsb_add_host(struct hpsb_host *host) { if (hpsb_default_host_entry(host)) return -ENOMEM; - hpsb_add_extra_config_roms(host); + highlevel_add_host(host); return 0; } @@ -212,12 +202,19 @@ void hpsb_remove_host(struct hpsb_host *host) host->driver = &dummy_driver; highlevel_remove_host(host); - hpsb_remove_extra_config_roms(host); class_device_unregister(&host->class_dev); device_unregister(&host->device); } +/** + * hpsb_update_config_rom_image - updates configuration ROM image of a host + * + * Updates the configuration ROM image of a host. rom_version must be the + * current version, otherwise it will fail with return value -1. If this + * host does not support config-rom-update, it will return -%EINVAL. + * Return value 0 indicates success. + */ int hpsb_update_config_rom_image(struct hpsb_host *host) { unsigned long reset_delay; diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h index 4bf4fb7..feb55d0 100644 --- a/drivers/ieee1394/hosts.h +++ b/drivers/ieee1394/hosts.h @@ -3,7 +3,6 @@ #include <linux/device.h> #include <linux/list.h> -#include <linux/skbuff.h> #include <linux/timer.h> #include <linux/types.h> #include <linux/workqueue.h> @@ -25,8 +24,7 @@ struct hpsb_host { atomic_t generation; - struct sk_buff_head pending_packet_queue; - + struct list_head pending_packets; struct timer_list timeout; unsigned long timeout_interval; @@ -202,12 +200,6 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra, int hpsb_add_host(struct hpsb_host *host); void hpsb_resume_host(struct hpsb_host *host); void hpsb_remove_host(struct hpsb_host *host); - -/* Updates the configuration rom image of a host. rom_version must be the - * current version, otherwise it will fail with return value -1. If this - * host does not support config-rom-update, it will return -EINVAL. - * Return value 0 indicates success. - */ int hpsb_update_config_rom_image(struct hpsb_host *host); #endif /* _IEEE1394_HOSTS_H */ diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h index 8f20750..46878fe 100644 --- a/drivers/ieee1394/ieee1394-ioctl.h +++ b/drivers/ieee1394/ieee1394-ioctl.h @@ -100,5 +100,7 @@ _IO ('#', 0x28) #define RAW1394_IOC_ISO_RECV_FLUSH \ _IO ('#', 0x29) +#define RAW1394_IOC_GET_CYCLE_TIMER \ + _IOR ('#', 0x30, struct raw1394_cycle_timer) #endif /* __IEEE1394_IOCTL_H */ diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c index 1521e57..8f71b6a 100644 --- a/drivers/ieee1394/ieee1394_core.c +++ b/drivers/ieee1394/ieee1394_core.c @@ -30,10 +30,12 @@ #include <linux/moduleparam.h> #include <linux/bitops.h> #include <linux/kdev_t.h> -#include <linux/skbuff.h> #include <linux/suspend.h> #include <linux/kthread.h> +#include <linux/preempt.h> +#include <linux/time.h> +#include <asm/system.h> #include <asm/byteorder.h> #include "ieee1394_types.h" @@ -93,13 +95,15 @@ static void queue_packet_complete(struct hpsb_packet *packet); /** - * hpsb_set_packet_complete_task - set the task that runs when a packet - * completes. You cannot call this more than once on a single packet - * before it is sent. - * + * hpsb_set_packet_complete_task - set task that runs when a packet completes * @packet: the packet whose completion we want the task added to * @routine: function to call * @data: data (if any) to pass to the above function + * + * Set the task that runs when a packet completes. You cannot call this more + * than once on a single packet before it is sent. + * + * Typically, the complete @routine is responsible to call hpsb_free_packet(). */ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, void (*routine)(void *), void *data) @@ -112,12 +116,12 @@ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, /** * hpsb_alloc_packet - allocate new packet structure - * @data_size: size of the data block to be allocated + * @data_size: size of the data block to be allocated, in bytes * * This function allocates, initializes and returns a new &struct hpsb_packet. - * It can be used in interrupt context. A header block is always included, its - * size is big enough to contain all possible 1394 headers. The data block is - * only allocated when @data_size is not zero. + * It can be used in interrupt context. A header block is always included and + * initialized with zeros. Its size is big enough to contain all possible 1394 + * headers. The data block is only allocated if @data_size is not zero. * * For packets for which responses will be received the @data_size has to be big * enough to contain the response's data block since no further allocation @@ -132,50 +136,49 @@ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, */ struct hpsb_packet *hpsb_alloc_packet(size_t data_size) { - struct hpsb_packet *packet = NULL; - struct sk_buff *skb; + struct hpsb_packet *packet; data_size = ((data_size + 3) & ~3); - skb = alloc_skb(data_size + sizeof(*packet), GFP_ATOMIC); - if (skb == NULL) + packet = kzalloc(sizeof(*packet) + data_size, GFP_ATOMIC); + if (!packet) return NULL; - memset(skb->data, 0, data_size + sizeof(*packet)); - - packet = (struct hpsb_packet *)skb->data; - packet->skb = skb; - - packet->header = packet->embedded_header; packet->state = hpsb_unused; packet->generation = -1; INIT_LIST_HEAD(&packet->driver_list); + INIT_LIST_HEAD(&packet->queue); atomic_set(&packet->refcnt, 1); if (data_size) { - packet->data = (quadlet_t *)(skb->data + sizeof(*packet)); - packet->data_size = data_size; + packet->data = packet->embedded_data; + packet->allocated_data_size = data_size; } - return packet; } - /** * hpsb_free_packet - free packet and data associated with it * @packet: packet to free (is NULL safe) * - * This function will free packet->data and finally the packet itself. + * Frees @packet->data only if it was allocated through hpsb_alloc_packet(). */ void hpsb_free_packet(struct hpsb_packet *packet) { if (packet && atomic_dec_and_test(&packet->refcnt)) { - BUG_ON(!list_empty(&packet->driver_list)); - kfree_skb(packet->skb); + BUG_ON(!list_empty(&packet->driver_list) || + !list_empty(&packet->queue)); + kfree(packet); } } - +/** + * hpsb_reset_bus - initiate bus reset on the given host + * @host: host controller whose bus to reset + * @type: one of enum reset_types + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ int hpsb_reset_bus(struct hpsb_host *host, int type) { if (!host->in_bus_reset) { @@ -186,7 +189,54 @@ int hpsb_reset_bus(struct hpsb_host *host, int type) } } +/** + * hpsb_read_cycle_timer - read cycle timer register and system time + * @host: host whose isochronous cycle timer register is read + * @cycle_timer: address of bitfield to return the register contents + * @local_time: address to return the system time + * + * The format of * @cycle_timer, is described in OHCI 1.1 clause 5.13. This + * format is also read from non-OHCI controllers. * @local_time contains the + * system time in microseconds since the Epoch, read at the moment when the + * cycle timer was read. + * + * Return value: 0 for success or error number otherwise. + */ +int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, + u64 *local_time) +{ + int ctr; + struct timeval tv; + unsigned long flags; + + if (!host || !cycle_timer || !local_time) + return -EINVAL; + + preempt_disable(); + local_irq_save(flags); + ctr = host->driver->devctl(host, GET_CYCLE_COUNTER, 0); + if (ctr) + do_gettimeofday(&tv); + + local_irq_restore(flags); + preempt_enable(); + + if (!ctr) + return -EIO; + *cycle_timer = ctr; + *local_time = tv.tv_sec * 1000000ULL + tv.tv_usec; + return 0; +} + +/** + * hpsb_bus_reset - notify a bus reset to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Returns 1 if bus reset already in progress, 0 otherwise. + */ int hpsb_bus_reset(struct hpsb_host *host) { if (host->in_bus_reset) { @@ -363,6 +413,14 @@ static void build_speed_map(struct hpsb_host *host, int nodecount) } +/** + * hpsb_selfid_received - hand over received selfid packet to the core + * + * For host driver module usage. Safe to use in interrupt context. + * + * The host driver should have done a successful complement check (second + * quadlet is complement of first) beforehand. + */ void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) { if (host->in_bus_reset) { @@ -374,6 +432,15 @@ void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid) } } +/** + * hpsb_selfid_complete - notify completion of SelfID stage to the core + * + * For host driver module usage. Safe to use in interrupt context, although + * quite complex; so you may want to run it in the bottom rather than top half. + * + * Notify completion of SelfID stage to the core and report new physical ID + * and whether host is root now. + */ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) { if (!host->in_bus_reset) @@ -420,30 +487,41 @@ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot) highlevel_host_reset(host); } +static spinlock_t pending_packets_lock = SPIN_LOCK_UNLOCKED; +/** + * hpsb_packet_sent - notify core of sending a packet + * + * For host driver module usage. Safe to call from within a transmit packet + * routine. + * + * Notify core of sending a packet. Ackcode is the ack code returned for async + * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE + * for other cases (internal errors that don't justify a panic). + */ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode) { unsigned long flags; - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + spin_lock_irqsave(&pending_packets_lock, flags); packet->ack_code = ackcode; if (packet->no_waiter || packet->state == hpsb_complete) { /* if packet->no_waiter, must not have a tlabel allocated */ - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + spin_unlock_irqrestore(&pending_packets_lock, flags); hpsb_free_packet(packet); return; } atomic_dec(&packet->refcnt); /* drop HC's reference */ - /* here the packet must be on the host->pending_packet_queue */ + /* here the packet must be on the host->pending_packets queue */ if (ackcode != ACK_PENDING || !packet->expect_response) { packet->state = hpsb_complete; - __skb_unlink(packet->skb, &host->pending_packet_queue); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); queue_packet_complete(packet); return; } @@ -451,7 +529,7 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, packet->state = hpsb_pending; packet->sendtime = jiffies; - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + spin_unlock_irqrestore(&pending_packets_lock, flags); mod_timer(&host->timeout, jiffies + host->timeout_interval); } @@ -462,9 +540,10 @@ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, * @rootid: root whose force_root bit should get set (-1 = don't set force_root) * @gapcnt: gap count value to set (-1 = don't set gap count) * - * This function sends a PHY config packet on the bus through the specified host. + * This function sends a PHY config packet on the bus through the specified + * host. * - * Return value: 0 for success or error number otherwise. + * Return value: 0 for success or negative error number otherwise. */ int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt) { @@ -525,12 +604,16 @@ int hpsb_send_packet(struct hpsb_packet *packet) WARN_ON(packet->no_waiter && packet->expect_response); if (!packet->no_waiter || packet->expect_response) { + unsigned long flags; + atomic_inc(&packet->refcnt); /* Set the initial "sendtime" to 10 seconds from now, to prevent premature expiry. If a packet takes more than 10 seconds to hit the wire, we have bigger problems :) */ packet->sendtime = jiffies + 10 * HZ; - skb_queue_tail(&host->pending_packet_queue, packet->skb); + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &host->pending_packets); + spin_unlock_irqrestore(&pending_packets_lock, flags); } if (packet->node_id == host->node_id) { @@ -579,6 +662,12 @@ static void complete_packet(void *data) complete((struct completion *) data); } +/** + * hpsb_send_packet_and_wait - enqueue packet, block until transaction completes + * @packet: packet to send + * + * Return value: 0 on success, negative errno on failure. + */ int hpsb_send_packet_and_wait(struct hpsb_packet *packet) { struct completion done; @@ -600,86 +689,97 @@ static void send_packet_nocare(struct hpsb_packet *packet) } } +static size_t packet_size_to_data_size(size_t packet_size, size_t header_size, + size_t buffer_size, int tcode) +{ + size_t ret = packet_size <= header_size ? 0 : packet_size - header_size; + + if (unlikely(ret > buffer_size)) + ret = buffer_size; + + if (unlikely(ret + header_size != packet_size)) + HPSB_ERR("unexpected packet size %zd (tcode %d), bug?", + packet_size, tcode); + return ret; +} static void handle_packet_response(struct hpsb_host *host, int tcode, quadlet_t *data, size_t size) { - struct hpsb_packet *packet = NULL; - struct sk_buff *skb; - int tcode_match = 0; - int tlabel; + struct hpsb_packet *packet; + int tlabel = (data[0] >> 10) & 0x3f; + size_t header_size; unsigned long flags; - tlabel = (data[0] >> 10) & 0x3f; - - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + spin_lock_irqsave(&pending_packets_lock, flags); - skb_queue_walk(&host->pending_packet_queue, skb) { - packet = (struct hpsb_packet *)skb->data; - if ((packet->tlabel == tlabel) - && (packet->node_id == (data[1] >> 16))){ - break; - } - - packet = NULL; - } + list_for_each_entry(packet, &host->pending_packets, queue) + if (packet->tlabel == tlabel && + packet->node_id == (data[1] >> 16)) + goto found; - if (packet == NULL) { - HPSB_DEBUG("unsolicited response packet received - no tlabel match"); - dump_packet("contents", data, 16, -1); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); - return; - } + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "no tlabel match"); + dump_packet("contents", data, 16, -1); + return; +found: switch (packet->tcode) { case TCODE_WRITEQ: case TCODE_WRITEB: - if (tcode != TCODE_WRITE_RESPONSE) + if (unlikely(tcode != TCODE_WRITE_RESPONSE)) break; - tcode_match = 1; - memcpy(packet->header, data, 12); - break; + header_size = 12; + size = 0; + goto dequeue; + case TCODE_READQ: - if (tcode != TCODE_READQ_RESPONSE) + if (unlikely(tcode != TCODE_READQ_RESPONSE)) break; - tcode_match = 1; - memcpy(packet->header, data, 16); - break; + header_size = 16; + size = 0; + goto dequeue; + case TCODE_READB: - if (tcode != TCODE_READB_RESPONSE) + if (unlikely(tcode != TCODE_READB_RESPONSE)) break; - tcode_match = 1; - BUG_ON(packet->skb->len - sizeof(*packet) < size - 16); - memcpy(packet->header, data, 16); - memcpy(packet->data, data + 4, size - 16); - break; + header_size = 16; + size = packet_size_to_data_size(size, header_size, + packet->allocated_data_size, + tcode); + goto dequeue; + case TCODE_LOCK_REQUEST: - if (tcode != TCODE_LOCK_RESPONSE) + if (unlikely(tcode != TCODE_LOCK_RESPONSE)) break; - tcode_match = 1; - size = min((size - 16), (size_t)8); - BUG_ON(packet->skb->len - sizeof(*packet) < size); - memcpy(packet->header, data, 16); - memcpy(packet->data, data + 4, size); - break; + header_size = 16; + size = packet_size_to_data_size(min(size, (size_t)(16 + 8)), + header_size, + packet->allocated_data_size, + tcode); + goto dequeue; } - if (!tcode_match) { - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); - HPSB_INFO("unsolicited response packet received - tcode mismatch"); - dump_packet("contents", data, 16, -1); - return; - } + spin_unlock_irqrestore(&pending_packets_lock, flags); + HPSB_DEBUG("unsolicited response packet received - %s", + "tcode mismatch"); + dump_packet("contents", data, 16, -1); + return; - __skb_unlink(skb, &host->pending_packet_queue); +dequeue: + list_del_init(&packet->queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); if (packet->state == hpsb_queued) { packet->sendtime = jiffies; packet->ack_code = ACK_PENDING; } - packet->state = hpsb_complete; - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + + memcpy(packet->header, data, header_size); + if (size) + memcpy(packet->data, data + 4, size); queue_packet_complete(packet); } @@ -693,6 +793,7 @@ static struct hpsb_packet *create_reply_packet(struct hpsb_host *host, p = hpsb_alloc_packet(dsize); if (unlikely(p == NULL)) { /* FIXME - send data_error response */ + HPSB_ERR("out of memory, cannot send response packet"); return NULL; } @@ -742,7 +843,6 @@ static void fill_async_readblock_resp(struct hpsb_packet *packet, int rcode, static void fill_async_write_resp(struct hpsb_packet *packet, int rcode) { PREP_ASYNC_HEAD_RCODE(TCODE_WRITE_RESPONSE); - packet->header[2] = 0; packet->header_size = 12; packet->data_size = 0; } @@ -759,12 +859,9 @@ static void fill_async_lock_resp(struct hpsb_packet *packet, int rcode, int extc packet->data_size = length; } -#define PREP_REPLY_PACKET(length) \ - packet = create_reply_packet(host, data, length); \ - if (packet == NULL) break - static void handle_incoming_packet(struct hpsb_host *host, int tcode, - quadlet_t *data, size_t size, int write_acked) + quadlet_t *data, size_t size, + int write_acked) { struct hpsb_packet *packet; int length, rcode, extcode; @@ -774,74 +871,72 @@ static void handle_incoming_packet(struct hpsb_host *host, int tcode, u16 flags = (u16) data[0]; u64 addr; - /* big FIXME - no error checking is done for an out of bounds length */ + /* FIXME? + * Out-of-bounds lengths are left for highlevel_read|write to cap. */ switch (tcode) { case TCODE_WRITEQ: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data+3, + rcode = highlevel_write(host, source, dest, data + 3, addr, 4, flags); - - if (!write_acked - && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) - && (rcode >= 0)) { - /* not a broadcast write, reply */ - PREP_REPLY_PACKET(0); - fill_async_write_resp(packet, rcode); - send_packet_nocare(packet); - } - break; + goto handle_write_request; case TCODE_WRITEB: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - rcode = highlevel_write(host, source, dest, data+4, - addr, data[3]>>16, flags); - - if (!write_acked - && (NODEID_TO_NODE(data[0] >> 16) != NODE_MASK) - && (rcode >= 0)) { - /* not a broadcast write, reply */ - PREP_REPLY_PACKET(0); + rcode = highlevel_write(host, source, dest, data + 4, + addr, data[3] >> 16, flags); +handle_write_request: + if (rcode < 0 || write_acked || + NODEID_TO_NODE(data[0] >> 16) == NODE_MASK) + return; + /* not a broadcast write, reply */ + packet = create_reply_packet(host, data, 0); + if (packet) { fill_async_write_resp(packet, rcode); send_packet_nocare(packet); } - break; + return; case TCODE_READQ: addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_read(host, source, &buffer, addr, 4, flags); + if (rcode < 0) + return; - if (rcode >= 0) { - PREP_REPLY_PACKET(0); + packet = create_reply_packet(host, data, 0); + if (packet) { fill_async_readquad_resp(packet, rcode, buffer); send_packet_nocare(packet); } - break; + return; case TCODE_READB: length = data[3] >> 16; - PREP_REPLY_PACKET(length); + packet = create_reply_packet(host, data, length); + if (!packet) + return; addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; rcode = highlevel_read(host, source, packet->data, addr, length, flags); - - if (rcode >= 0) { - fill_async_readblock_resp(packet, rcode, length); - send_packet_nocare(packet); - } else { + if (rcode < 0) { hpsb_free_packet(packet); + return; } - break; + fill_async_readblock_resp(packet, rcode, length); + send_packet_nocare(packet); + return; case TCODE_LOCK_REQUEST: length = data[3] >> 16; extcode = data[3] & 0xffff; addr = (((u64)(data[1] & 0xffff)) << 32) | data[2]; - PREP_REPLY_PACKET(8); + packet = create_reply_packet(host, data, 8); + if (!packet) + return; - if ((extcode == 0) || (extcode >= 7)) { + if (extcode == 0 || extcode >= 7) { /* let switch default handle error */ length = 0; } @@ -849,12 +944,12 @@ static void handle_incoming_packet(struct hpsb_host *host, int tcode, switch (length) { case 4: rcode = highlevel_lock(host, source, packet->data, addr, - data[4], 0, extcode,flags); + data[4], 0, extcode, flags); fill_async_lock_resp(packet, rcode, extcode, 4); break; case 8: - if ((extcode != EXTCODE_FETCH_ADD) - && (extcode != EXTCODE_LITTLE_ADD)) { + if (extcode != EXTCODE_FETCH_ADD && + extcode != EXTCODE_LITTLE_ADD) { rcode = highlevel_lock(host, source, packet->data, addr, data[5], data[4], @@ -878,29 +973,38 @@ static void handle_incoming_packet(struct hpsb_host *host, int tcode, break; default: rcode = RCODE_TYPE_ERROR; - fill_async_lock_resp(packet, rcode, - extcode, 0); + fill_async_lock_resp(packet, rcode, extcode, 0); } - if (rcode >= 0) { - send_packet_nocare(packet); - } else { + if (rcode < 0) hpsb_free_packet(packet); - } - break; + else + send_packet_nocare(packet); + return; } - } -#undef PREP_REPLY_PACKET - +/** + * hpsb_packet_received - hand over received packet to the core + * + * For host driver module usage. + * + * The contents of data are expected to be the full packet but with the CRCs + * left out (data block follows header immediately), with the header (i.e. the + * first four quadlets) in machine byte order and the data block in big endian. + * *@data can be safely overwritten after this call. + * + * If the packet is a write request, @write_acked is to be set to true if it was + * ack_complete'd already, false otherwise. This argument is ignored for any + * other packet type. + */ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, int write_acked) { int tcode; - if (host->in_bus_reset) { - HPSB_INFO("received packet during reset; ignoring"); + if (unlikely(host->in_bus_reset)) { + HPSB_DEBUG("received packet during reset; ignoring"); return; } @@ -934,23 +1038,27 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, break; default: - HPSB_NOTICE("received packet with bogus transaction code %d", - tcode); + HPSB_DEBUG("received packet with bogus transaction code %d", + tcode); break; } } - static void abort_requests(struct hpsb_host *host) { - struct hpsb_packet *packet; - struct sk_buff *skb; + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags; host->driver->devctl(host, CANCEL_REQUESTS, 0); - while ((skb = skb_dequeue(&host->pending_packet_queue)) != NULL) { - packet = (struct hpsb_packet *)skb->data; + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); + list_splice_init(&host->pending_packets, &tmp); + spin_unlock_irqrestore(&pending_packets_lock, flags); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); packet->state = hpsb_complete; packet->ack_code = ACKX_ABORTED; queue_packet_complete(packet); @@ -960,87 +1068,90 @@ static void abort_requests(struct hpsb_host *host) void abort_timedouts(unsigned long __opaque) { struct hpsb_host *host = (struct hpsb_host *)__opaque; - unsigned long flags; - struct hpsb_packet *packet; - struct sk_buff *skb; - unsigned long expire; + struct hpsb_packet *packet, *p; + struct list_head tmp; + unsigned long flags, expire, j; spin_lock_irqsave(&host->csr.lock, flags); expire = host->csr.expire; spin_unlock_irqrestore(&host->csr.lock, flags); - /* Hold the lock around this, since we aren't dequeuing all - * packets, just ones we need. */ - spin_lock_irqsave(&host->pending_packet_queue.lock, flags); + j = jiffies; + INIT_LIST_HEAD(&tmp); + spin_lock_irqsave(&pending_packets_lock, flags); - while (!skb_queue_empty(&host->pending_packet_queue)) { - skb = skb_peek(&host->pending_packet_queue); - - packet = (struct hpsb_packet *)skb->data; - - if (time_before(packet->sendtime + expire, jiffies)) { - __skb_unlink(skb, &host->pending_packet_queue); - packet->state = hpsb_complete; - packet->ack_code = ACKX_TIMEOUT; - queue_packet_complete(packet); - } else { + list_for_each_entry_safe(packet, p, &host->pending_packets, queue) { + if (time_before(packet->sendtime + expire, j)) + list_move_tail(&packet->queue, &tmp); + else /* Since packets are added to the tail, the oldest * ones are first, always. When we get to one that * isn't timed out, the rest aren't either. */ break; - } } + if (!list_empty(&host->pending_packets)) + mod_timer(&host->timeout, j + host->timeout_interval); - if (!skb_queue_empty(&host->pending_packet_queue)) - mod_timer(&host->timeout, jiffies + host->timeout_interval); + spin_unlock_irqrestore(&pending_packets_lock, flags); - spin_unlock_irqrestore(&host->pending_packet_queue.lock, flags); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->state = hpsb_complete; + packet->ack_code = ACKX_TIMEOUT; + queue_packet_complete(packet); + } } - -/* Kernel thread and vars, which handles packets that are completed. Only - * packets that have a "complete" function are sent here. This way, the - * completion is run out of kernel context, and doesn't block the rest of - * the stack. */ static struct task_struct *khpsbpkt_thread; -static struct sk_buff_head hpsbpkt_queue; +static LIST_HEAD(hpsbpkt_queue); static void queue_packet_complete(struct hpsb_packet *packet) { + unsigned long flags; + if (packet->no_waiter) { hpsb_free_packet(packet); return; } if (packet->complete_routine != NULL) { - skb_queue_tail(&hpsbpkt_queue, packet->skb); + spin_lock_irqsave(&pending_packets_lock, flags); + list_add_tail(&packet->queue, &hpsbpkt_queue); + spin_unlock_irqrestore(&pending_packets_lock, flags); wake_up_process(khpsbpkt_thread); } return; } +/* + * Kernel thread which handles packets that are completed. This way the + * packet's "complete" function is asynchronously run in process context. + * Only packets which have a "complete" function may be sent here. + */ static int hpsbpkt_thread(void *__hi) { - struct sk_buff *skb; - struct hpsb_packet *packet; - void (*complete_routine)(void*); - void *complete_data; + struct hpsb_packet *packet, *p; + struct list_head tmp; + int may_schedule; current->flags |= PF_NOFREEZE; while (!kthread_should_stop()) { - while ((skb = skb_dequeue(&hpsbpkt_queue)) != NULL) { - packet = (struct hpsb_packet *)skb->data; - complete_routine = packet->complete_routine; - complete_data = packet->complete_data; + INIT_LIST_HEAD(&tmp); + spin_lock_irq(&pending_packets_lock); + list_splice_init(&hpsbpkt_queue, &tmp); + spin_unlock_irq(&pending_packets_lock); - packet->complete_routine = packet->complete_data = NULL; - - complete_routine(complete_data); + list_for_each_entry_safe(packet, p, &tmp, queue) { + list_del_init(&packet->queue); + packet->complete_routine(packet->complete_data); } set_current_state(TASK_INTERRUPTIBLE); - if (!skb_peek(&hpsbpkt_queue)) + spin_lock_irq(&pending_packets_lock); + may_schedule = list_empty(&hpsbpkt_queue); + spin_unlock_irq(&pending_packets_lock); + if (may_schedule) schedule(); __set_current_state(TASK_RUNNING); } @@ -1051,8 +1162,6 @@ static int __init ieee1394_init(void) { int i, ret; - skb_queue_head_init(&hpsbpkt_queue); - /* non-fatal error */ if (hpsb_init_config_roms()) { HPSB_ERR("Failed to initialize some config rom entries.\n"); @@ -1190,6 +1299,7 @@ EXPORT_SYMBOL(hpsb_alloc_packet); EXPORT_SYMBOL(hpsb_free_packet); EXPORT_SYMBOL(hpsb_send_packet); EXPORT_SYMBOL(hpsb_reset_bus); +EXPORT_SYMBOL(hpsb_read_cycle_timer); EXPORT_SYMBOL(hpsb_bus_reset); EXPORT_SYMBOL(hpsb_selfid_received); EXPORT_SYMBOL(hpsb_selfid_complete); @@ -1225,7 +1335,6 @@ EXPORT_SYMBOL(hpsb_destroy_hostinfo); EXPORT_SYMBOL(hpsb_set_hostinfo_key); EXPORT_SYMBOL(hpsb_get_hostinfo_bykey); EXPORT_SYMBOL(hpsb_set_hostinfo); -EXPORT_SYMBOL(highlevel_host_reset); /** nodemgr.c **/ EXPORT_SYMBOL(hpsb_node_fill_packet); @@ -1268,11 +1377,10 @@ EXPORT_SYMBOL(hpsb_iso_wake); EXPORT_SYMBOL(hpsb_iso_recv_flush); /** csr1212.c **/ -EXPORT_SYMBOL(csr1212_new_directory); EXPORT_SYMBOL(csr1212_attach_keyval_to_directory); EXPORT_SYMBOL(csr1212_detach_keyval_from_directory); -EXPORT_SYMBOL(csr1212_release_keyval); -EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_get_keyval); +EXPORT_SYMBOL(csr1212_new_directory); EXPORT_SYMBOL(csr1212_parse_keyval); -EXPORT_SYMBOL(_csr1212_read_keyval); -EXPORT_SYMBOL(_csr1212_destroy_keyval); +EXPORT_SYMBOL(csr1212_read); +EXPORT_SYMBOL(csr1212_release_keyval); diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h index 536ba3f..ad52652 100644 --- a/drivers/ieee1394/ieee1394_core.h +++ b/drivers/ieee1394/ieee1394_core.h @@ -4,7 +4,6 @@ #include <linux/device.h> #include <linux/fs.h> #include <linux/list.h> -#include <linux/skbuff.h> #include <linux/types.h> #include <asm/atomic.h> @@ -13,7 +12,7 @@ struct hpsb_packet { /* This struct is basically read-only for hosts with the exception of - * the data buffer contents and xnext - see below. */ + * the data buffer contents and driver_list. */ /* This can be used for host driver internal linking. * @@ -49,131 +48,65 @@ struct hpsb_packet { /* Speed to transmit with: 0 = 100Mbps, 1 = 200Mbps, 2 = 400Mbps */ unsigned speed_code:2; - /* - * *header and *data are guaranteed to be 32-bit DMAable and may be - * overwritten to allow in-place byte swapping. Neither of these is - * CRCed (the sizes also don't include CRC), but contain space for at - * least one additional quadlet to allow in-place CRCing. The memory is - * also guaranteed to be DMA mappable. - */ - quadlet_t *header; - quadlet_t *data; - size_t header_size; - size_t data_size; - struct hpsb_host *host; unsigned int generation; atomic_t refcnt; + struct list_head queue; /* Function (and possible data to pass to it) to call when this * packet is completed. */ void (*complete_routine)(void *); void *complete_data; - /* XXX This is just a hack at the moment */ - struct sk_buff *skb; - /* Store jiffies for implementing bus timeouts. */ unsigned long sendtime; - quadlet_t embedded_header[5]; + /* Sizes are in bytes. *data can be DMA-mapped. */ + size_t allocated_data_size; /* as allocated */ + size_t data_size; /* as filled in */ + size_t header_size; /* as filled in, not counting the CRC */ + quadlet_t *data; + quadlet_t header[5]; + quadlet_t embedded_data[0]; /* keep as last member */ }; -/* Set a task for when a packet completes */ void hpsb_set_packet_complete_task(struct hpsb_packet *packet, void (*routine)(void *), void *data); - static inline struct hpsb_packet *driver_packet(struct list_head *l) { return list_entry(l, struct hpsb_packet, driver_list); } - void abort_timedouts(unsigned long __opaque); - struct hpsb_packet *hpsb_alloc_packet(size_t data_size); void hpsb_free_packet(struct hpsb_packet *packet); -/* - * Generation counter for the complete 1394 subsystem. Generation gets - * incremented on every change in the subsystem (e.g. bus reset). +/** + * get_hpsb_generation - generation counter for the complete 1394 subsystem * - * Use the functions, not the variable. + * Generation gets incremented on every change in the subsystem (notably on bus + * resets). Use the functions, not the variable. */ static inline unsigned int get_hpsb_generation(struct hpsb_host *host) { return atomic_read(&host->generation); } -/* - * Send a PHY configuration packet, return 0 on success, negative - * errno on failure. - */ int hpsb_send_phy_config(struct hpsb_host *host, int rootid, int gapcnt); - -/* - * Queue packet for transmitting, return 0 on success, negative errno - * on failure. - */ int hpsb_send_packet(struct hpsb_packet *packet); - -/* - * Queue packet for transmitting, and block until the transaction - * completes. Return 0 on success, negative errno on failure. - */ int hpsb_send_packet_and_wait(struct hpsb_packet *packet); - -/* Initiate bus reset on the given host. Returns 1 if bus reset already in - * progress, 0 otherwise. */ int hpsb_reset_bus(struct hpsb_host *host, int type); +int hpsb_read_cycle_timer(struct hpsb_host *host, u32 *cycle_timer, + u64 *local_time); -/* - * The following functions are exported for host driver module usage. All of - * them are safe to use in interrupt contexts, although some are quite - * complicated so you may want to run them in bottom halves instead of calling - * them directly. - */ - -/* Notify a bus reset to the core. Returns 1 if bus reset already in progress, - * 0 otherwise. */ int hpsb_bus_reset(struct hpsb_host *host); - -/* - * Hand over received selfid packet to the core. Complement check (second - * quadlet is complement of first) is expected to be done and successful. - */ void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid); - -/* - * Notify completion of SelfID stage to the core and report new physical ID - * and whether host is root now. - */ void hpsb_selfid_complete(struct hpsb_host *host, int phyid, int isroot); - -/* - * Notify core of sending a packet. Ackcode is the ack code returned for async - * transmits or ACKX_SEND_ERROR if the transmission failed completely; ACKX_NONE - * for other cases (internal errors that don't justify a panic). Safe to call - * from within a transmit packet routine. - */ void hpsb_packet_sent(struct hpsb_host *host, struct hpsb_packet *packet, int ackcode); - -/* - * Hand over received packet to the core. The contents of data are expected to - * be the full packet but with the CRCs left out (data block follows header - * immediately), with the header (i.e. the first four quadlets) in machine byte - * order and the data block in big endian. *data can be safely overwritten - * after this call. - * - * If the packet is a write request, write_acked is to be set to true if it was - * ack_complete'd already, false otherwise. This arg is ignored for any other - * packet type. - */ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, int write_acked); - /* * CHARACTER DEVICE DISPATCHING * @@ -214,7 +147,9 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size, #define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \ IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16) -/* return the index (within a minor number block) of a file */ +/** + * ieee1394_file_to_instance - get the index within a minor number block + */ static inline unsigned char ieee1394_file_to_instance(struct file *file) { return file->f_path.dentry->d_inode->i_cindex; diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c index 0833fc9..40078ce 100644 --- a/drivers/ieee1394/ieee1394_transactions.c +++ b/drivers/ieee1394/ieee1394_transactions.c @@ -10,11 +10,16 @@ */ #include <linux/bitops.h> +#include <linux/compiler.h> +#include <linux/hardirq.h> #include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/sched.h> /* because linux/wait.h is broken if CONFIG_SMP=n */ #include <linux/wait.h> #include <asm/bug.h> #include <asm/errno.h> +#include <asm/system.h> #include "ieee1394.h" #include "ieee1394_types.h" @@ -32,7 +37,7 @@ #ifndef HPSB_DEBUG_TLABELS static #endif -spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED; +DEFINE_SPINLOCK(hpsb_tlabel_lock); static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq); @@ -212,6 +217,15 @@ void hpsb_free_tlabel(struct hpsb_packet *packet) wake_up_interruptible(&tlabel_wq); } +/** + * hpsb_packet_success - Make sense of the ack and reply codes + * + * Make sense of the ack and reply codes and return more convenient error codes: + * 0 = success. -%EBUSY = node is busy, try again. -%EAGAIN = error which can + * probably resolved by retry. -%EREMOTEIO = node suffers from an internal + * error. -%EACCES = this transaction is not allowed on requested address. + * -%EINVAL = invalid address at node. + */ int hpsb_packet_success(struct hpsb_packet *packet) { switch (packet->ack_code) { @@ -364,6 +378,13 @@ struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 * buffer, } packet->host = host; + /* Because it is too difficult to determine all PHY speeds and link + * speeds here, we use S100... */ + packet->speed_code = IEEE1394_SPEED_100; + + /* ...and prevent hpsb_send_packet() from overriding it. */ + packet->node_id = LOCAL_BUS | ALL_NODES; + if (hpsb_get_tlabel(packet)) { hpsb_free_packet(packet); return NULL; @@ -493,6 +514,16 @@ struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, * avoid in kernel buffers for user space callers */ +/** + * hpsb_read - generic read function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet read request if @length == 4 and and a block read request otherwise. + * It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) { @@ -532,6 +563,16 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, return retval; } +/** + * hpsb_write - generic write function + * + * Recognizes the local node ID and act accordingly. Automatically uses a + * quadlet write request if @length == 4 and and a block write request + * otherwise. It does not yet support lengths that are not a multiple of 4. + * + * You must explicitly specifiy the @generation for which the node ID is valid, + * to avoid sending packets to the wrong nodes when we race with a bus reset. + */ int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t * buffer, size_t length) { diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h index c1369c4..86b8ee6 100644 --- a/drivers/ieee1394/ieee1394_transactions.h +++ b/drivers/ieee1394/ieee1394_transactions.h @@ -27,27 +27,7 @@ struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host, struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer, int length, int channel, int tag, int sync); - -/* - * hpsb_packet_success - Make sense of the ack and reply codes and - * return more convenient error codes: - * 0 success - * -EBUSY node is busy, try again - * -EAGAIN error which can probably resolved by retry - * -EREMOTEIO node suffers from an internal error - * -EACCES this transaction is not allowed on requested address - * -EINVAL invalid address at node - */ int hpsb_packet_success(struct hpsb_packet *packet); - -/* - * The generic read and write functions. All recognize the local node ID - * and act accordingly. Read and write automatically use quadlet commands if - * length == 4 and and block commands otherwise (however, they do not yet - * support lengths that are not a multiple of 4). You must explicitly specifiy - * the generation for which the node ID is valid, to avoid sending packets to - * the wrong nodes when we race with a bus reset. - */ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation, u64 addr, quadlet_t *buffer, size_t length); int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation, diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c index c6227e5..07ca35c 100644 --- a/drivers/ieee1394/iso.c +++ b/drivers/ieee1394/iso.c @@ -10,11 +10,15 @@ */ #include <linux/pci.h> +#include <linux/sched.h> #include <linux/slab.h> #include "hosts.h" #include "iso.h" +/** + * hpsb_iso_stop - stop DMA + */ void hpsb_iso_stop(struct hpsb_iso *iso) { if (!(iso->flags & HPSB_ISO_DRIVER_STARTED)) @@ -25,6 +29,9 @@ void hpsb_iso_stop(struct hpsb_iso *iso) iso->flags &= ~HPSB_ISO_DRIVER_STARTED; } +/** + * hpsb_iso_shutdown - deallocate buffer and DMA context + */ void hpsb_iso_shutdown(struct hpsb_iso *iso) { if (iso->flags & HPSB_ISO_DRIVER_INIT) { @@ -130,6 +137,9 @@ static struct hpsb_iso *hpsb_iso_common_init(struct hpsb_host *host, return NULL; } +/** + * hpsb_iso_n_ready - returns number of packets ready to send or receive + */ int hpsb_iso_n_ready(struct hpsb_iso *iso) { unsigned long flags; @@ -142,6 +152,9 @@ int hpsb_iso_n_ready(struct hpsb_iso *iso) return val; } +/** + * hpsb_iso_xmit_init - allocate the buffer and DMA context + */ struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -172,6 +185,11 @@ struct hpsb_iso *hpsb_iso_xmit_init(struct hpsb_host *host, return NULL; } +/** + * hpsb_iso_recv_init - allocate the buffer and DMA context + * + * Note, if channel = -1, multi-channel receive is enabled. + */ struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -199,6 +217,11 @@ struct hpsb_iso *hpsb_iso_recv_init(struct hpsb_host *host, return NULL; } +/** + * hpsb_iso_recv_listen_channel + * + * multi-channel only + */ int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) @@ -206,6 +229,11 @@ int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel) return iso->host->driver->isoctl(iso, RECV_LISTEN_CHANNEL, channel); } +/** + * hpsb_iso_recv_unlisten_channel + * + * multi-channel only + */ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1 || channel >= 64) @@ -213,6 +241,11 @@ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel) return iso->host->driver->isoctl(iso, RECV_UNLISTEN_CHANNEL, channel); } +/** + * hpsb_iso_recv_set_channel_mask + * + * multi-channel only + */ int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) { if (iso->type != HPSB_ISO_RECV || iso->channel != -1) @@ -221,6 +254,12 @@ int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask) (unsigned long)&mask); } +/** + * hpsb_iso_recv_flush - check for arrival of new packets + * + * check for arrival of new packets immediately (even if irq_interval + * has not yet been reached) + */ int hpsb_iso_recv_flush(struct hpsb_iso *iso) { if (iso->type != HPSB_ISO_RECV) @@ -238,6 +277,9 @@ static int do_iso_xmit_start(struct hpsb_iso *iso, int cycle) return retval; } +/** + * hpsb_iso_xmit_start - start DMA + */ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) { if (iso->type != HPSB_ISO_XMIT) @@ -270,6 +312,9 @@ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int cycle, int prebuffer) return 0; } +/** + * hpsb_iso_recv_start - start DMA + */ int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) { int retval = 0; @@ -306,8 +351,7 @@ int hpsb_iso_recv_start(struct hpsb_iso *iso, int cycle, int tag_mask, int sync) } /* check to make sure the user has not supplied bogus values of offset/len - that would cause the kernel to access memory outside the buffer */ - + * that would cause the kernel to access memory outside the buffer */ static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, unsigned int offset, unsigned short len, unsigned int *out_offset, @@ -331,6 +375,12 @@ static int hpsb_iso_check_offset_len(struct hpsb_iso *iso, return 0; } +/** + * hpsb_iso_xmit_queue_packet - queue a packet for transmission. + * + * @offset is relative to the beginning of the DMA buffer, where the packet's + * data payload should already have been placed. + */ int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy) { @@ -380,6 +430,9 @@ int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, return rv; } +/** + * hpsb_iso_xmit_sync - wait until all queued packets have been transmitted + */ int hpsb_iso_xmit_sync(struct hpsb_iso *iso) { if (iso->type != HPSB_ISO_XMIT) @@ -390,6 +443,15 @@ int hpsb_iso_xmit_sync(struct hpsb_iso *iso) iso->buf_packets); } +/** + * hpsb_iso_packet_sent + * + * Available to low-level drivers. + * + * Call after a packet has been transmitted to the bus (interrupt context is + * OK). @cycle is the _exact_ cycle the packet was sent on. @error should be + * non-zero if some sort of error occurred when sending the packet. + */ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) { unsigned long flags; @@ -413,6 +475,13 @@ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error) spin_unlock_irqrestore(&iso->lock, flags); } +/** + * hpsb_iso_packet_received + * + * Available to low-level drivers. + * + * Call after a packet has been received (interrupt context is OK). + */ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy) @@ -442,6 +511,11 @@ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, spin_unlock_irqrestore(&iso->lock, flags); } +/** + * hpsb_iso_recv_release_packets - release packets, reuse buffer + * + * @n_packets have been read out of the buffer, re-use the buffer space + */ int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) { unsigned long flags; @@ -477,6 +551,13 @@ int hpsb_iso_recv_release_packets(struct hpsb_iso *iso, unsigned int n_packets) return rv; } +/** + * hpsb_iso_wake + * + * Available to low-level drivers. + * + * Call to wake waiting processes after buffer space has opened up. + */ void hpsb_iso_wake(struct hpsb_iso *iso) { wake_up_interruptible(&iso->waitq); diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h index 1210a97..b94e55e 100644 --- a/drivers/ieee1394/iso.h +++ b/drivers/ieee1394/iso.h @@ -150,8 +150,6 @@ struct hpsb_iso { /* functions available to high-level drivers (e.g. raw1394) */ -/* allocate the buffer and DMA context */ - struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -159,8 +157,6 @@ struct hpsb_iso* hpsb_iso_xmit_init(struct hpsb_host *host, int speed, int irq_interval, void (*callback)(struct hpsb_iso*)); - -/* note: if channel = -1, multi-channel receive is enabled */ struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, unsigned int data_buf_size, unsigned int buf_packets, @@ -168,56 +164,29 @@ struct hpsb_iso* hpsb_iso_recv_init(struct hpsb_host *host, int dma_mode, int irq_interval, void (*callback)(struct hpsb_iso*)); - -/* multi-channel only */ int hpsb_iso_recv_listen_channel(struct hpsb_iso *iso, unsigned char channel); int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel); int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask); - -/* start/stop DMA */ int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer); int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync); void hpsb_iso_stop(struct hpsb_iso *iso); - -/* deallocate buffer and DMA context */ void hpsb_iso_shutdown(struct hpsb_iso *iso); - -/* queue a packet for transmission. - * 'offset' is relative to the beginning of the DMA buffer, where the packet's - * data payload should already have been placed. */ int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy); - -/* wait until all queued packets have been transmitted to the bus */ int hpsb_iso_xmit_sync(struct hpsb_iso *iso); - -/* N packets have been read out of the buffer, re-use the buffer space */ -int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, - unsigned int n_packets); - -/* check for arrival of new packets immediately (even if irq_interval - * has not yet been reached) */ +int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, + unsigned int n_packets); int hpsb_iso_recv_flush(struct hpsb_iso *iso); - -/* returns # of packets ready to send or receive */ int hpsb_iso_n_ready(struct hpsb_iso *iso); /* the following are callbacks available to low-level drivers */ -/* call after a packet has been transmitted to the bus (interrupt context is OK) - * 'cycle' is the _exact_ cycle the packet was sent on - * 'error' should be non-zero if some sort of error occurred when sending the - * packet */ void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error); - -/* call after a packet has been received (interrupt context OK) */ void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len, u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy); - -/* call to wake waiting processes after buffer space has opened up. */ void hpsb_iso_wake(struct hpsb_iso *iso); #endif /* IEEE1394_ISO_H */ diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c index ba9faef..6a1a057 100644 --- a/drivers/ieee1394/nodemgr.c +++ b/drivers/ieee1394/nodemgr.c @@ -16,6 +16,7 @@ #include <linux/kthread.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/mutex.h> #include <linux/freezer.h> #include <asm/atomic.h> @@ -115,7 +116,7 @@ static int nodemgr_bus_read(struct csr1212_csr *csr, u64 addr, u16 length, static int nodemgr_get_max_rom(quadlet_t *bus_info_data, void *__ci) { - return (CSR1212_BE32_TO_CPU(bus_info_data[2]) >> 8) & 0x3; + return (be32_to_cpu(bus_info_data[2]) >> 8) & 0x3; } static struct csr1212_bus_ops nodemgr_csr_ops = { @@ -370,9 +371,7 @@ static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute if (state == 1) { ud->ignore_driver = 1; - down_write(&ieee1394_bus_type.subsys.rwsem); device_release_driver(dev); - up_write(&ieee1394_bus_type.subsys.rwsem); } else if (state == 0) ud->ignore_driver = 0; @@ -582,7 +581,7 @@ static void nodemgr_create_drv_files(struct hpsb_protocol_driver *driver) goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for driver %s", driver->name); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -606,8 +605,7 @@ static void nodemgr_create_ne_dev_files(struct node_entry *ne) goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for node %016Lx", - (unsigned long long)ne->guid); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -621,7 +619,7 @@ static void nodemgr_create_host_dev_files(struct hpsb_host *host) goto fail; return; fail: - HPSB_ERR("Failed to add sysfs attribute for host %d", host->id); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -681,8 +679,7 @@ static void nodemgr_create_ud_dev_files(struct unit_directory *ud) } return; fail: - HPSB_ERR("Failed to add sysfs attributes for unit %s", - ud->device.bus_id); + HPSB_ERR("Failed to add sysfs attribute"); } @@ -1146,13 +1143,13 @@ static void nodemgr_process_root_directory(struct host_info *hi, struct node_ent last_key_id = kv->key.id; } - if (ne->vendor_name_kv && - device_create_file(&ne->device, &dev_attr_ne_vendor_name_kv)) - goto fail; - return; -fail: - HPSB_ERR("Failed to add sysfs attribute for node %016Lx", - (unsigned long long)ne->guid); + if (ne->vendor_name_kv) { + int error = device_create_file(&ne->device, + &dev_attr_ne_vendor_name_kv); + + if (error && error != -EEXIST) + HPSB_ERR("Failed to add sysfs attribute"); + } } #ifdef CONFIG_HOTPLUG @@ -1163,6 +1160,7 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp, struct unit_directory *ud; int i = 0; int length = 0; + int retval = 0; /* ieee1394:venNmoNspNverN */ char buf[8 + 1 + 3 + 8 + 2 + 8 + 2 + 8 + 3 + 8 + 1]; @@ -1176,14 +1174,11 @@ static int nodemgr_uevent(struct class_device *cdev, char **envp, int num_envp, #define PUT_ENVP(fmt,val) \ do { \ - int printed; \ - envp[i++] = buffer; \ - printed = snprintf(buffer, buffer_size - length, \ - fmt, val); \ - if ((buffer_size - (length+printed) <= 0) || (i >= num_envp)) \ - return -ENOMEM; \ - length += printed+1; \ - buffer += printed+1; \ + retval = add_uevent_var(envp, num_envp, &i, \ + buffer, buffer_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ } while (0) PUT_ENVP("VENDOR_ID=%06x", ud->vendor_id); @@ -1393,12 +1388,10 @@ static void nodemgr_suspend_ne(struct node_entry *ne) if (ud->ne != ne) continue; - down_write(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver && (!ud->device.driver->suspend || ud->device.driver->suspend(&ud->device, PMSG_SUSPEND))) device_release_driver(&ud->device); - up_write(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); } @@ -1418,10 +1411,8 @@ static void nodemgr_resume_ne(struct node_entry *ne) if (ud->ne != ne) continue; - down_read(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver && ud->device.driver->resume) ud->device.driver->resume(&ud->device); - up_read(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); @@ -1442,7 +1433,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne) if (ud->ne != ne) continue; - down_write(&ieee1394_bus_type.subsys.rwsem); if (ud->device.driver) { pdrv = container_of(ud->device.driver, struct hpsb_protocol_driver, @@ -1450,7 +1440,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne) if (pdrv->update && pdrv->update(ud)) device_release_driver(&ud->device); } - up_write(&ieee1394_bus_type.subsys.rwsem); } up(&nodemgr_ud_class.sem); } @@ -1681,7 +1670,8 @@ static int nodemgr_host_thread(void *__hi) for (;;) { /* Sleep until next bus reset */ set_current_state(TASK_INTERRUPTIBLE); - if (get_hpsb_generation(host) == generation) + if (get_hpsb_generation(host) == generation && + !kthread_should_stop()) schedule(); __set_current_state(TASK_RUNNING); @@ -1747,7 +1737,19 @@ exit: return 0; } -int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) +/** + * nodemgr_for_each_host - call a function for each IEEE 1394 host + * @data: an address to supply to the callback + * @cb: function to call for each host + * + * Iterate the hosts, calling a given function with supplied data for each host. + * If the callback fails on a host, i.e. if it returns a non-zero value, the + * iteration is stopped. + * + * Return value: 0 on success, non-zero on failure (same as returned by last run + * of the callback). + */ +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)) { struct class_device *cdev; struct hpsb_host *host; @@ -1757,7 +1759,7 @@ int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) list_for_each_entry(cdev, &hpsb_host_class.children, node) { host = container_of(cdev, struct hpsb_host, class_dev); - if ((error = cb(host, __data))) + if ((error = cb(host, data))) break; } up(&hpsb_host_class.sem); @@ -1765,7 +1767,7 @@ int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) return error; } -/* The following four convenience functions use a struct node_entry +/* The following two convenience functions use a struct node_entry * for addressing a node on the bus. They are intended for use by any * process context, not just the nodemgr thread, so we need to be a * little careful when reading out the node ID and generation. The @@ -1780,12 +1782,20 @@ int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)) * ID's. */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt) +/** + * hpsb_node_fill_packet - fill some destination information into a packet + * @ne: destination node + * @packet: packet to fill in + * + * This will fill in the given, pre-initialised hpsb_packet with the current + * information from the node entry (host, node ID, bus generation number). + */ +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet) { - pkt->host = ne->host; - pkt->generation = ne->generation; + packet->host = ne->host; + packet->generation = ne->generation; barrier(); - pkt->node_id = ne->nodeid; + packet->node_id = ne->nodeid; } int hpsb_node_write(struct node_entry *ne, u64 addr, diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h index 4147303..e7ac683 100644 --- a/drivers/ieee1394/nodemgr.h +++ b/drivers/ieee1394/nodemgr.h @@ -153,30 +153,10 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne) { return ne->generation == get_hpsb_generation(ne->host); } - -/* - * This will fill in the given, pre-initialised hpsb_packet with the current - * information from the node entry (host, node ID, generation number). It will - * return false if the node owning the GUID is not accessible (and not modify - * the hpsb_packet) and return true otherwise. - * - * Note that packet sending may still fail in hpsb_send_packet if a bus reset - * happens while you are trying to set up the packet (due to obsolete generation - * number). It will at least reliably fail so that you don't accidentally and - * unknowingly send your packet to the wrong node. - */ -void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *pkt); - -int hpsb_node_read(struct node_entry *ne, u64 addr, - quadlet_t *buffer, size_t length); +void hpsb_node_fill_packet(struct node_entry *ne, struct hpsb_packet *packet); int hpsb_node_write(struct node_entry *ne, u64 addr, quadlet_t *buffer, size_t length); -int hpsb_node_lock(struct node_entry *ne, u64 addr, - int extcode, quadlet_t *data, quadlet_t arg); - -/* Iterate the hosts, calling a given function with supplied data for each - * host. */ -int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *)); +int nodemgr_for_each_host(void *data, int (*cb)(struct hpsb_host *, void *)); int init_ieee1394_nodemgr(void); void cleanup_ieee1394_nodemgr(void); diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c index e982d60..5dadfd2 100644 --- a/drivers/ieee1394/ohci1394.c +++ b/drivers/ieee1394/ohci1394.c @@ -180,7 +180,7 @@ static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d, static void ohci1394_pci_remove(struct pci_dev *pdev); #ifndef __LITTLE_ENDIAN -const static size_t hdr_sizes[] = { +static const size_t hdr_sizes[] = { 3, /* TCODE_WRITEQ */ 4, /* TCODE_WRITEB */ 3, /* TCODE_WRITE_RESPONSE */ @@ -507,9 +507,8 @@ static void ohci_initialize(struct ti_ohci *ohci) /* Set up self-id dma buffer */ reg_write(ohci, OHCI1394_SelfIDBuffer, ohci->selfid_buf_bus); - /* enable self-id and phys */ - reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID | - OHCI1394_LinkControl_RcvPhyPkt); + /* enable self-id */ + reg_write(ohci, OHCI1394_LinkControlSet, OHCI1394_LinkControl_RcvSelfID); /* Set the Config ROM mapping register */ reg_write(ohci, OHCI1394_ConfigROMmap, ohci->csr_config_rom_bus); @@ -518,9 +517,6 @@ static void ohci_initialize(struct ti_ohci *ohci) ohci->max_packet_size = 1<<(((reg_read(ohci, OHCI1394_BusOptions)>>12)&0xf)+1); - /* Don't accept phy packets into AR request context */ - reg_write(ohci, OHCI1394_LinkControlClear, 0x00000400); - /* Clear the interrupt mask */ reg_write(ohci, OHCI1394_IsoRecvIntMaskClear, 0xffffffff); reg_write(ohci, OHCI1394_IsoRecvIntEventClear, 0xffffffff); @@ -617,7 +613,7 @@ static void ohci_initialize(struct ti_ohci *ohci) #endif PRINT(KERN_DEBUG, "Serial EEPROM has suspicious values, " - "attempting to setting max_packet_size to 512 bytes"); + "attempting to set max_packet_size to 512 bytes"); reg_write(ohci, OHCI1394_BusOptions, (reg_read(ohci, OHCI1394_BusOptions) & 0xf007) | 0x8002); ohci->max_packet_size = 512; @@ -2377,6 +2373,7 @@ static irqreturn_t ohci_irq_handler(int irq, void *dev_id) if (event & OHCI1394_postedWriteErr) { PRINT(KERN_ERR, "physical posted write error"); /* no recovery strategy yet, had to involve protocol drivers */ + event &= ~OHCI1394_postedWriteErr; } if (event & OHCI1394_cycleTooLong) { if(printk_ratelimit()) @@ -3658,6 +3655,7 @@ static struct pci_driver ohci1394_pci_driver = { /* essentially the only purpose of this code is to allow another module to hook into ohci's interrupt handler */ +/* returns zero if successful, one if DMA context is locked up */ int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg) { int i=0; diff --git a/drivers/ieee1394/ohci1394.h b/drivers/ieee1394/ohci1394.h index fa05f11..f1ad539 100644 --- a/drivers/ieee1394/ohci1394.h +++ b/drivers/ieee1394/ohci1394.h @@ -461,9 +461,7 @@ int ohci1394_register_iso_tasklet(struct ti_ohci *ohci, struct ohci1394_iso_tasklet *tasklet); void ohci1394_unregister_iso_tasklet(struct ti_ohci *ohci, struct ohci1394_iso_tasklet *tasklet); - -/* returns zero if successful, one if DMA context is locked up */ -int ohci1394_stop_context (struct ti_ohci *ohci, int reg, char *msg); +int ohci1394_stop_context(struct ti_ohci *ohci, int reg, char *msg); struct ti_ohci *ohci1394_get_struct(int card_num); #endif diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c index aa9ca83..c6aefd9 100644 --- a/drivers/ieee1394/raw1394.c +++ b/drivers/ieee1394/raw1394.c @@ -938,7 +938,8 @@ static int handle_async_send(struct file_info *fi, struct pending_request *req) int header_length = req->req.misc & 0xffff; int expect_response = req->req.misc >> 16; - if ((header_length > req->req.length) || (header_length < 12)) { + if (header_length > req->req.length || header_length < 12 || + header_length > FIELD_SIZEOF(struct hpsb_packet, header)) { req->req.error = RAW1394_ERROR_INVALID_ARG; req->req.length = 0; queue_complete_req(req); @@ -2669,6 +2670,18 @@ static void raw1394_iso_shutdown(struct file_info *fi) fi->iso_state = RAW1394_ISO_INACTIVE; } +static int raw1394_read_cycle_timer(struct file_info *fi, void __user * uaddr) +{ + struct raw1394_cycle_timer ct; + int err; + + err = hpsb_read_cycle_timer(fi->host, &ct.cycle_timer, &ct.local_time); + if (!err) + if (copy_to_user(uaddr, &ct, sizeof(ct))) + err = -EFAULT; + return err; +} + /* mmap the rawiso xmit/recv buffer */ static int raw1394_mmap(struct file *file, struct vm_area_struct *vma) { @@ -2777,6 +2790,14 @@ static int raw1394_ioctl(struct inode *inode, struct file *file, break; } + /* state-independent commands */ + switch(cmd) { + case RAW1394_IOC_GET_CYCLE_TIMER: + return raw1394_read_cycle_timer(fi, argp); + default: + break; + } + return -EINVAL; } diff --git a/drivers/ieee1394/raw1394.h b/drivers/ieee1394/raw1394.h index 35bfc38..7bd22ee 100644 --- a/drivers/ieee1394/raw1394.h +++ b/drivers/ieee1394/raw1394.h @@ -178,4 +178,14 @@ struct raw1394_iso_status { __s16 xmit_cycle; }; +/* argument to RAW1394_IOC_GET_CYCLE_TIMER ioctl */ +struct raw1394_cycle_timer { + /* contents of Isochronous Cycle Timer register, + as in OHCI 1.1 clause 5.13 (also with non-OHCI hosts) */ + __u32 cycle_timer; + + /* local time in microseconds since Epoch, + simultaneously read with cycle timer */ + __u64 local_time; +}; #endif /* IEEE1394_RAW1394_H */ diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c index 4edfff4..4cb6fa2 100644 --- a/drivers/ieee1394/sbp2.c +++ b/drivers/ieee1394/sbp2.c @@ -59,8 +59,10 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/list.h> +#include <linux/mm.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/stat.h> @@ -469,19 +471,13 @@ static void sbp2util_write_doorbell(struct work_struct *work) static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) { struct sbp2_fwhost_info *hi = lu->hi; - int i; - unsigned long flags, orbs; struct sbp2_command_info *cmd; + int i, orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - orbs = sbp2_serialize_io ? 2 : SBP2_MAX_CMDS; - - spin_lock_irqsave(&lu->cmd_orb_lock, flags); for (i = 0; i < orbs; i++) { - cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); - if (!cmd) { - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) return -ENOMEM; - } cmd->command_orb_dma = dma_map_single(hi->host->device.parent, &cmd->command_orb, sizeof(struct sbp2_command_orb), @@ -489,11 +485,10 @@ static int sbp2util_create_command_orb_pool(struct sbp2_lu *lu) cmd->sge_dma = dma_map_single(hi->host->device.parent, &cmd->scatter_gather_element, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); INIT_LIST_HEAD(&cmd->list); list_add_tail(&cmd->list, &lu->cmd_orb_completed); } - spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); return 0; } @@ -514,7 +509,7 @@ static void sbp2util_remove_command_orb_pool(struct sbp2_lu *lu) DMA_TO_DEVICE); dma_unmap_single(host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); kfree(cmd); } spin_unlock_irqrestore(&lu->cmd_orb_lock, flags); @@ -757,6 +752,11 @@ static struct sbp2_lu *sbp2_alloc_device(struct unit_directory *ud) SBP2_ERR("failed to register lower 4GB address range"); goto failed_alloc; } +#else + if (dma_set_mask(hi->host->device.parent, DMA_32BIT_MASK)) { + SBP2_ERR("failed to set 4GB DMA mask"); + goto failed_alloc; + } #endif } @@ -865,11 +865,8 @@ static int sbp2_start_device(struct sbp2_lu *lu) if (!lu->login_orb) goto alloc_fail; - if (sbp2util_create_command_orb_pool(lu)) { - SBP2_ERR("sbp2util_create_command_orb_pool failed!"); - sbp2_remove_device(lu); - return -ENOMEM; - } + if (sbp2util_create_command_orb_pool(lu)) + goto alloc_fail; /* Wait a second before trying to log in. Previously logged in * initiators need a chance to reconnect. */ @@ -1628,7 +1625,7 @@ static void sbp2_link_orb_command(struct sbp2_lu *lu, DMA_TO_DEVICE); dma_sync_single_for_device(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); /* check to see if there are any previous orbs to use */ spin_lock_irqsave(&lu->cmd_orb_lock, flags); @@ -1794,7 +1791,7 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, DMA_TO_DEVICE); dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); /* Grab SCSI command pointers and check status. */ /* * FIXME: If the src field in the status is 1, the ORB DMA must @@ -1926,7 +1923,7 @@ static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status) DMA_TO_DEVICE); dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); sbp2util_mark_command_completed(lu, cmd); if (cmd->Current_SCpnt) { cmd->Current_SCpnt->result = status << 16; @@ -2057,7 +2054,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt) dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma, sizeof(cmd->scatter_gather_element), - DMA_BIDIRECTIONAL); + DMA_TO_DEVICE); sbp2util_mark_command_completed(lu, cmd); if (cmd->Current_SCpnt) { cmd->Current_SCpnt->result = DID_ABORT << 16; diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h index 9ae8423..44402b9 100644 --- a/drivers/ieee1394/sbp2.h +++ b/drivers/ieee1394/sbp2.h @@ -250,15 +250,15 @@ enum sbp2_dma_types { /* Per SCSI command */ struct sbp2_command_info { struct list_head list; - struct sbp2_command_orb command_orb ____cacheline_aligned; - dma_addr_t command_orb_dma ____cacheline_aligned; + struct sbp2_command_orb command_orb; + dma_addr_t command_orb_dma; struct scsi_cmnd *Current_SCpnt; void (*Current_done)(struct scsi_cmnd *); /* Also need s/g structure for each sbp2 command */ struct sbp2_unrestricted_page_table - scatter_gather_element[SG_ALL] ____cacheline_aligned; - dma_addr_t sge_dma ____cacheline_aligned; + scatter_gather_element[SG_ALL] __attribute__((aligned(8))); + dma_addr_t sge_dma; void *sge_buffer; dma_addr_t cmd_dma; enum sbp2_dma_types dma_type; diff --git a/drivers/infiniband/core/Makefile b/drivers/infiniband/core/Makefile index 50fb1cd..189e5d4 100644 --- a/drivers/infiniband/core/Makefile +++ b/drivers/infiniband/core/Makefile @@ -12,7 +12,7 @@ ib_core-y := packer.o ud_header.o verbs.o sysfs.o \ ib_mad-y := mad.o smi.o agent.o mad_rmpp.o -ib_sa-y := sa_query.o +ib_sa-y := sa_query.o multicast.o ib_cm-y := cm.o diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index d446998..842cd0b 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -88,7 +88,6 @@ struct cm_port { struct cm_device { struct list_head list; struct ib_device *device; - __be64 ca_guid; struct cm_port port[0]; }; @@ -739,8 +738,8 @@ retest: ib_cancel_mad(cm_id_priv->av.port->mad_agent, cm_id_priv->msg); spin_unlock_irqrestore(&cm_id_priv->lock, flags); ib_send_cm_rej(cm_id, IB_CM_REJ_TIMEOUT, - &cm_id_priv->av.port->cm_dev->ca_guid, - sizeof cm_id_priv->av.port->cm_dev->ca_guid, + &cm_id_priv->id.device->node_guid, + sizeof cm_id_priv->id.device->node_guid, NULL, 0); break; case IB_CM_REQ_RCVD: @@ -883,7 +882,7 @@ static void cm_format_req(struct cm_req_msg *req_msg, req_msg->local_comm_id = cm_id_priv->id.local_id; req_msg->service_id = param->service_id; - req_msg->local_ca_guid = cm_id_priv->av.port->cm_dev->ca_guid; + req_msg->local_ca_guid = cm_id_priv->id.device->node_guid; cm_req_set_local_qpn(req_msg, cpu_to_be32(param->qp_num)); cm_req_set_resp_res(req_msg, param->responder_resources); cm_req_set_init_depth(req_msg, param->initiator_depth); @@ -1442,7 +1441,7 @@ static void cm_format_rep(struct cm_rep_msg *rep_msg, cm_rep_set_flow_ctrl(rep_msg, param->flow_control); cm_rep_set_rnr_retry_count(rep_msg, param->rnr_retry_count); cm_rep_set_srq(rep_msg, param->srq); - rep_msg->local_ca_guid = cm_id_priv->av.port->cm_dev->ca_guid; + rep_msg->local_ca_guid = cm_id_priv->id.device->node_guid; if (param->private_data && param->private_data_len) memcpy(rep_msg->private_data, param->private_data, @@ -3385,7 +3384,6 @@ static void cm_add_one(struct ib_device *device) return; cm_dev->device = device; - cm_dev->ca_guid = device->node_guid; set_bit(IB_MGMT_METHOD_SEND, reg_req.method_mask); for (i = 1; i <= device->phys_port_cnt; i++) { diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index db88e60..fde92ce 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -71,12 +71,12 @@ static struct workqueue_struct *cma_wq; static DEFINE_IDR(sdp_ps); static DEFINE_IDR(tcp_ps); static DEFINE_IDR(udp_ps); +static DEFINE_IDR(ipoib_ps); static int next_port; struct cma_device { struct list_head list; struct ib_device *device; - __be64 node_guid; struct completion comp; atomic_t refcount; struct list_head id_list; @@ -116,6 +116,7 @@ struct rdma_id_private { struct list_head list; struct list_head listen_list; struct cma_device *cma_dev; + struct list_head mc_list; enum cma_state state; spinlock_t lock; @@ -134,10 +135,23 @@ struct rdma_id_private { } cm_id; u32 seq_num; + u32 qkey; u32 qp_num; u8 srq; }; +struct cma_multicast { + struct rdma_id_private *id_priv; + union { + struct ib_sa_multicast *ib; + } multicast; + struct list_head list; + void *context; + struct sockaddr addr; + u8 pad[sizeof(struct sockaddr_in6) - + sizeof(struct sockaddr)]; +}; + struct cma_work { struct work_struct work; struct rdma_id_private *id; @@ -243,6 +257,11 @@ static inline void sdp_set_ip_ver(struct sdp_hh *hh, u8 ip_ver) hh->ip_version = (ip_ver << 4) | (hh->ip_version & 0xF); } +static inline int cma_is_ud_ps(enum rdma_port_space ps) +{ + return (ps == RDMA_PS_UDP || ps == RDMA_PS_IPOIB); +} + static void cma_attach_to_dev(struct rdma_id_private *id_priv, struct cma_device *cma_dev) { @@ -265,19 +284,41 @@ static void cma_detach_from_dev(struct rdma_id_private *id_priv) id_priv->cma_dev = NULL; } +static int cma_set_qkey(struct ib_device *device, u8 port_num, + enum rdma_port_space ps, + struct rdma_dev_addr *dev_addr, u32 *qkey) +{ + struct ib_sa_mcmember_rec rec; + int ret = 0; + + switch (ps) { + case RDMA_PS_UDP: + *qkey = RDMA_UDP_QKEY; + break; + case RDMA_PS_IPOIB: + ib_addr_get_mgid(dev_addr, &rec.mgid); + ret = ib_sa_get_mcmember_rec(device, port_num, &rec.mgid, &rec); + *qkey = be32_to_cpu(rec.qkey); + break; + default: + break; + } + return ret; +} + static int cma_acquire_dev(struct rdma_id_private *id_priv) { - enum rdma_node_type dev_type = id_priv->id.route.addr.dev_addr.dev_type; + struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; struct cma_device *cma_dev; union ib_gid gid; int ret = -ENODEV; - switch (rdma_node_get_transport(dev_type)) { + switch (rdma_node_get_transport(dev_addr->dev_type)) { case RDMA_TRANSPORT_IB: - ib_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid); + ib_addr_get_sgid(dev_addr, &gid); break; case RDMA_TRANSPORT_IWARP: - iw_addr_get_sgid(&id_priv->id.route.addr.dev_addr, &gid); + iw_addr_get_sgid(dev_addr, &gid); break; default: return -ENODEV; @@ -287,7 +328,12 @@ static int cma_acquire_dev(struct rdma_id_private *id_priv) ret = ib_find_cached_gid(cma_dev->device, &gid, &id_priv->id.port_num, NULL); if (!ret) { - cma_attach_to_dev(id_priv, cma_dev); + ret = cma_set_qkey(cma_dev->device, + id_priv->id.port_num, + id_priv->id.ps, dev_addr, + &id_priv->qkey); + if (!ret) + cma_attach_to_dev(id_priv, cma_dev); break; } } @@ -325,40 +371,50 @@ struct rdma_cm_id *rdma_create_id(rdma_cm_event_handler event_handler, init_waitqueue_head(&id_priv->wait_remove); atomic_set(&id_priv->dev_remove, 0); INIT_LIST_HEAD(&id_priv->listen_list); + INIT_LIST_HEAD(&id_priv->mc_list); get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num); return &id_priv->id; } EXPORT_SYMBOL(rdma_create_id); -static int cma_init_ib_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) +static int cma_init_ud_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) { struct ib_qp_attr qp_attr; - struct rdma_dev_addr *dev_addr; - int ret; + int qp_attr_mask, ret; - dev_addr = &id_priv->id.route.addr.dev_addr; - ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num, - ib_addr_get_pkey(dev_addr), - &qp_attr.pkey_index); + qp_attr.qp_state = IB_QPS_INIT; + ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask); if (ret) return ret; - qp_attr.qp_state = IB_QPS_INIT; - qp_attr.qp_access_flags = 0; - qp_attr.port_num = id_priv->id.port_num; - return ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS | - IB_QP_PKEY_INDEX | IB_QP_PORT); + ret = ib_modify_qp(qp, &qp_attr, qp_attr_mask); + if (ret) + return ret; + + qp_attr.qp_state = IB_QPS_RTR; + ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE); + if (ret) + return ret; + + qp_attr.qp_state = IB_QPS_RTS; + qp_attr.sq_psn = 0; + ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN); + + return ret; } -static int cma_init_iw_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) +static int cma_init_conn_qp(struct rdma_id_private *id_priv, struct ib_qp *qp) { struct ib_qp_attr qp_attr; + int qp_attr_mask, ret; qp_attr.qp_state = IB_QPS_INIT; - qp_attr.qp_access_flags = IB_ACCESS_LOCAL_WRITE; + ret = rdma_init_qp_attr(&id_priv->id, &qp_attr, &qp_attr_mask); + if (ret) + return ret; - return ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_ACCESS_FLAGS); + return ib_modify_qp(qp, &qp_attr, qp_attr_mask); } int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, @@ -376,18 +432,10 @@ int rdma_create_qp(struct rdma_cm_id *id, struct ib_pd *pd, if (IS_ERR(qp)) return PTR_ERR(qp); - switch (rdma_node_get_transport(id->device->node_type)) { - case RDMA_TRANSPORT_IB: - ret = cma_init_ib_qp(id_priv, qp); - break; - case RDMA_TRANSPORT_IWARP: - ret = cma_init_iw_qp(id_priv, qp); - break; - default: - ret = -ENOSYS; - break; - } - + if (cma_is_ud_ps(id_priv->id.ps)) + ret = cma_init_ud_qp(id_priv, qp); + else + ret = cma_init_conn_qp(id_priv, qp); if (ret) goto err; @@ -460,23 +508,55 @@ static int cma_modify_qp_err(struct rdma_cm_id *id) return ib_modify_qp(id->qp, &qp_attr, IB_QP_STATE); } +static int cma_ib_init_qp_attr(struct rdma_id_private *id_priv, + struct ib_qp_attr *qp_attr, int *qp_attr_mask) +{ + struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + int ret; + + ret = ib_find_cached_pkey(id_priv->id.device, id_priv->id.port_num, + ib_addr_get_pkey(dev_addr), + &qp_attr->pkey_index); + if (ret) + return ret; + + qp_attr->port_num = id_priv->id.port_num; + *qp_attr_mask = IB_QP_STATE | IB_QP_PKEY_INDEX | IB_QP_PORT; + + if (cma_is_ud_ps(id_priv->id.ps)) { + qp_attr->qkey = id_priv->qkey; + *qp_attr_mask |= IB_QP_QKEY; + } else { + qp_attr->qp_access_flags = 0; + *qp_attr_mask |= IB_QP_ACCESS_FLAGS; + } + return 0; +} + int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, int *qp_attr_mask) { struct rdma_id_private *id_priv; - int ret; + int ret = 0; id_priv = container_of(id, struct rdma_id_private, id); switch (rdma_node_get_transport(id_priv->id.device->node_type)) { case RDMA_TRANSPORT_IB: - ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr, - qp_attr_mask); + if (!id_priv->cm_id.ib || cma_is_ud_ps(id_priv->id.ps)) + ret = cma_ib_init_qp_attr(id_priv, qp_attr, qp_attr_mask); + else + ret = ib_cm_init_qp_attr(id_priv->cm_id.ib, qp_attr, + qp_attr_mask); if (qp_attr->qp_state == IB_QPS_RTR) qp_attr->rq_psn = id_priv->seq_num; break; case RDMA_TRANSPORT_IWARP: - ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, - qp_attr_mask); + if (!id_priv->cm_id.iw) { + qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE; + *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; + } else + ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, + qp_attr_mask); break; default: ret = -ENOSYS; @@ -698,6 +778,19 @@ static void cma_release_port(struct rdma_id_private *id_priv) mutex_unlock(&lock); } +static void cma_leave_mc_groups(struct rdma_id_private *id_priv) +{ + struct cma_multicast *mc; + + while (!list_empty(&id_priv->mc_list)) { + mc = container_of(id_priv->mc_list.next, + struct cma_multicast, list); + list_del(&mc->list); + ib_sa_free_multicast(mc->multicast.ib); + kfree(mc); + } +} + void rdma_destroy_id(struct rdma_cm_id *id) { struct rdma_id_private *id_priv; @@ -722,6 +815,7 @@ void rdma_destroy_id(struct rdma_cm_id *id) default: break; } + cma_leave_mc_groups(id_priv); mutex_lock(&lock); cma_detach_from_dev(id_priv); } @@ -972,7 +1066,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event) memset(&event, 0, sizeof event); offset = cma_user_data_offset(listen_id->id.ps); event.event = RDMA_CM_EVENT_CONNECT_REQUEST; - if (listen_id->id.ps == RDMA_PS_UDP) { + if (cma_is_ud_ps(listen_id->id.ps)) { conn_id = cma_new_udp_id(&listen_id->id, ib_event); event.param.ud.private_data = ib_event->private_data + offset; event.param.ud.private_data_len = @@ -1397,11 +1491,13 @@ static int cma_query_ib_route(struct rdma_id_private *id_priv, int timeout_ms, ib_addr_get_dgid(addr, &path_rec.dgid); path_rec.pkey = cpu_to_be16(ib_addr_get_pkey(addr)); path_rec.numb_path = 1; + path_rec.reversible = 1; id_priv->query_id = ib_sa_path_rec_get(&sa_client, id_priv->id.device, id_priv->id.port_num, &path_rec, IB_SA_PATH_REC_DGID | IB_SA_PATH_REC_SGID | - IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH, + IB_SA_PATH_REC_PKEY | IB_SA_PATH_REC_NUMB_PATH | + IB_SA_PATH_REC_REVERSIBLE, timeout_ms, GFP_KERNEL, cma_query_handler, work, &id_priv->query); @@ -1847,6 +1943,9 @@ static int cma_get_port(struct rdma_id_private *id_priv) case RDMA_PS_UDP: ps = &udp_ps; break; + case RDMA_PS_IPOIB: + ps = &ipoib_ps; + break; default: return -EPROTONOSUPPORT; } @@ -1961,7 +2060,7 @@ static int cma_sidr_rep_handler(struct ib_cm_id *cm_id, event.status = ib_event->param.sidr_rep_rcvd.status; break; } - if (rep->qkey != RDMA_UD_QKEY) { + if (id_priv->qkey != rep->qkey) { event.event = RDMA_CM_EVENT_UNREACHABLE; event.status = -EINVAL; break; @@ -2160,7 +2259,7 @@ int rdma_connect(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) switch (rdma_node_get_transport(id->device->node_type)) { case RDMA_TRANSPORT_IB: - if (id->ps == RDMA_PS_UDP) + if (cma_is_ud_ps(id->ps)) ret = cma_resolve_ib_udp(id_priv, conn_param); else ret = cma_connect_ib(id_priv, conn_param); @@ -2256,7 +2355,7 @@ static int cma_send_sidr_rep(struct rdma_id_private *id_priv, rep.status = status; if (status == IB_SIDR_SUCCESS) { rep.qp_num = id_priv->qp_num; - rep.qkey = RDMA_UD_QKEY; + rep.qkey = id_priv->qkey; } rep.private_data = private_data; rep.private_data_len = private_data_len; @@ -2280,7 +2379,7 @@ int rdma_accept(struct rdma_cm_id *id, struct rdma_conn_param *conn_param) switch (rdma_node_get_transport(id->device->node_type)) { case RDMA_TRANSPORT_IB: - if (id->ps == RDMA_PS_UDP) + if (cma_is_ud_ps(id->ps)) ret = cma_send_sidr_rep(id_priv, IB_SIDR_SUCCESS, conn_param->private_data, conn_param->private_data_len); @@ -2341,7 +2440,7 @@ int rdma_reject(struct rdma_cm_id *id, const void *private_data, switch (rdma_node_get_transport(id->device->node_type)) { case RDMA_TRANSPORT_IB: - if (id->ps == RDMA_PS_UDP) + if (cma_is_ud_ps(id->ps)) ret = cma_send_sidr_rep(id_priv, IB_SIDR_REJECT, private_data, private_data_len); else @@ -2392,6 +2491,178 @@ out: } EXPORT_SYMBOL(rdma_disconnect); +static int cma_ib_mc_handler(int status, struct ib_sa_multicast *multicast) +{ + struct rdma_id_private *id_priv; + struct cma_multicast *mc = multicast->context; + struct rdma_cm_event event; + int ret; + + id_priv = mc->id_priv; + atomic_inc(&id_priv->dev_remove); + if (!cma_comp(id_priv, CMA_ADDR_BOUND) && + !cma_comp(id_priv, CMA_ADDR_RESOLVED)) + goto out; + + if (!status && id_priv->id.qp) + status = ib_attach_mcast(id_priv->id.qp, &multicast->rec.mgid, + multicast->rec.mlid); + + memset(&event, 0, sizeof event); + event.status = status; + event.param.ud.private_data = mc->context; + if (!status) { + event.event = RDMA_CM_EVENT_MULTICAST_JOIN; + ib_init_ah_from_mcmember(id_priv->id.device, + id_priv->id.port_num, &multicast->rec, + &event.param.ud.ah_attr); + event.param.ud.qp_num = 0xFFFFFF; + event.param.ud.qkey = be32_to_cpu(multicast->rec.qkey); + } else + event.event = RDMA_CM_EVENT_MULTICAST_ERROR; + + ret = id_priv->id.event_handler(&id_priv->id, &event); + if (ret) { + cma_exch(id_priv, CMA_DESTROYING); + cma_release_remove(id_priv); + rdma_destroy_id(&id_priv->id); + return 0; + } +out: + cma_release_remove(id_priv); + return 0; +} + +static void cma_set_mgid(struct rdma_id_private *id_priv, + struct sockaddr *addr, union ib_gid *mgid) +{ + unsigned char mc_map[MAX_ADDR_LEN]; + struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + struct sockaddr_in *sin = (struct sockaddr_in *) addr; + struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) addr; + + if (cma_any_addr(addr)) { + memset(mgid, 0, sizeof *mgid); + } else if ((addr->sa_family == AF_INET6) && + ((be32_to_cpu(sin6->sin6_addr.s6_addr32[0]) & 0xFF10A01B) == + 0xFF10A01B)) { + /* IPv6 address is an SA assigned MGID. */ + memcpy(mgid, &sin6->sin6_addr, sizeof *mgid); + } else { + ip_ib_mc_map(sin->sin_addr.s_addr, mc_map); + if (id_priv->id.ps == RDMA_PS_UDP) + mc_map[7] = 0x01; /* Use RDMA CM signature */ + mc_map[8] = ib_addr_get_pkey(dev_addr) >> 8; + mc_map[9] = (unsigned char) ib_addr_get_pkey(dev_addr); + *mgid = *(union ib_gid *) (mc_map + 4); + } +} + +static int cma_join_ib_multicast(struct rdma_id_private *id_priv, + struct cma_multicast *mc) +{ + struct ib_sa_mcmember_rec rec; + struct rdma_dev_addr *dev_addr = &id_priv->id.route.addr.dev_addr; + ib_sa_comp_mask comp_mask; + int ret; + + ib_addr_get_mgid(dev_addr, &rec.mgid); + ret = ib_sa_get_mcmember_rec(id_priv->id.device, id_priv->id.port_num, + &rec.mgid, &rec); + if (ret) + return ret; + + cma_set_mgid(id_priv, &mc->addr, &rec.mgid); + if (id_priv->id.ps == RDMA_PS_UDP) + rec.qkey = cpu_to_be32(RDMA_UDP_QKEY); + ib_addr_get_sgid(dev_addr, &rec.port_gid); + rec.pkey = cpu_to_be16(ib_addr_get_pkey(dev_addr)); + rec.join_state = 1; + + comp_mask = IB_SA_MCMEMBER_REC_MGID | IB_SA_MCMEMBER_REC_PORT_GID | + IB_SA_MCMEMBER_REC_PKEY | IB_SA_MCMEMBER_REC_JOIN_STATE | + IB_SA_MCMEMBER_REC_QKEY | IB_SA_MCMEMBER_REC_SL | + IB_SA_MCMEMBER_REC_FLOW_LABEL | + IB_SA_MCMEMBER_REC_TRAFFIC_CLASS; + + mc->multicast.ib = ib_sa_join_multicast(&sa_client, id_priv->id.device, + id_priv->id.port_num, &rec, + comp_mask, GFP_KERNEL, + cma_ib_mc_handler, mc); + if (IS_ERR(mc->multicast.ib)) + return PTR_ERR(mc->multicast.ib); + + return 0; +} + +int rdma_join_multicast(struct rdma_cm_id *id, struct sockaddr *addr, + void *context) +{ + struct rdma_id_private *id_priv; + struct cma_multicast *mc; + int ret; + + id_priv = container_of(id, struct rdma_id_private, id); + if (!cma_comp(id_priv, CMA_ADDR_BOUND) && + !cma_comp(id_priv, CMA_ADDR_RESOLVED)) + return -EINVAL; + + mc = kmalloc(sizeof *mc, GFP_KERNEL); + if (!mc) + return -ENOMEM; + + memcpy(&mc->addr, addr, ip_addr_size(addr)); + mc->context = context; + mc->id_priv = id_priv; + + spin_lock(&id_priv->lock); + list_add(&mc->list, &id_priv->mc_list); + spin_unlock(&id_priv->lock); + + switch (rdma_node_get_transport(id->device->node_type)) { + case RDMA_TRANSPORT_IB: + ret = cma_join_ib_multicast(id_priv, mc); + break; + default: + ret = -ENOSYS; + break; + } + + if (ret) { + spin_lock_irq(&id_priv->lock); + list_del(&mc->list); + spin_unlock_irq(&id_priv->lock); + kfree(mc); + } + return ret; +} +EXPORT_SYMBOL(rdma_join_multicast); + +void rdma_leave_multicast(struct rdma_cm_id *id, struct sockaddr *addr) +{ + struct rdma_id_private *id_priv; + struct cma_multicast *mc; + + id_priv = container_of(id, struct rdma_id_private, id); + spin_lock_irq(&id_priv->lock); + list_for_each_entry(mc, &id_priv->mc_list, list) { + if (!memcmp(&mc->addr, addr, ip_addr_size(addr))) { + list_del(&mc->list); + spin_unlock_irq(&id_priv->lock); + + if (id->qp) + ib_detach_mcast(id->qp, + &mc->multicast.ib->rec.mgid, + mc->multicast.ib->rec.mlid); + ib_sa_free_multicast(mc->multicast.ib); + kfree(mc); + return; + } + } + spin_unlock_irq(&id_priv->lock); +} +EXPORT_SYMBOL(rdma_leave_multicast); + static void cma_add_one(struct ib_device *device) { struct cma_device *cma_dev; @@ -2402,7 +2673,6 @@ static void cma_add_one(struct ib_device *device) return; cma_dev->device = device; - cma_dev->node_guid = device->node_guid; init_completion(&cma_dev->comp); atomic_set(&cma_dev->refcount, 1); @@ -2522,6 +2792,7 @@ static void cma_cleanup(void) idr_destroy(&sdp_ps); idr_destroy(&tcp_ps); idr_destroy(&udp_ps); + idr_destroy(&ipoib_ps); } module_init(cma_init); diff --git a/drivers/infiniband/core/fmr_pool.c b/drivers/infiniband/core/fmr_pool.c index 8926a2b..1d796e7 100644 --- a/drivers/infiniband/core/fmr_pool.c +++ b/drivers/infiniband/core/fmr_pool.c @@ -301,7 +301,7 @@ struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd *pd, { struct ib_pool_fmr *fmr; - struct ib_fmr_attr attr = { + struct ib_fmr_attr fmr_attr = { .max_pages = params->max_pages_per_fmr, .max_maps = pool->max_remaps, .page_shift = params->page_shift @@ -321,7 +321,7 @@ struct ib_fmr_pool *ib_create_fmr_pool(struct ib_pd *pd, fmr->ref_count = 0; INIT_HLIST_NODE(&fmr->cache_node); - fmr->fmr = ib_alloc_fmr(pd, params->access, &attr); + fmr->fmr = ib_alloc_fmr(pd, params->access, &fmr_attr); if (IS_ERR(fmr->fmr)) { printk(KERN_WARNING "fmr_create failed for FMR %d", i); kfree(fmr); diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c index 1039ad5..891d1fa 100644 --- a/drivers/infiniband/core/iwcm.c +++ b/drivers/infiniband/core/iwcm.c @@ -146,6 +146,12 @@ static int copy_private_data(struct iw_cm_event *event) return 0; } +static void free_cm_id(struct iwcm_id_private *cm_id_priv) +{ + dealloc_work_entries(cm_id_priv); + kfree(cm_id_priv); +} + /* * Release a reference on cm_id. If the last reference is being * released, enable the waiting thread (in iw_destroy_cm_id) to @@ -153,21 +159,14 @@ static int copy_private_data(struct iw_cm_event *event) */ static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) { - int ret = 0; - BUG_ON(atomic_read(&cm_id_priv->refcount)==0); if (atomic_dec_and_test(&cm_id_priv->refcount)) { BUG_ON(!list_empty(&cm_id_priv->work_list)); - if (waitqueue_active(&cm_id_priv->destroy_comp.wait)) { - BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); - BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, - &cm_id_priv->flags)); - ret = 1; - } complete(&cm_id_priv->destroy_comp); + return 1; } - return ret; + return 0; } static void add_ref(struct iw_cm_id *cm_id) @@ -181,7 +180,11 @@ static void rem_ref(struct iw_cm_id *cm_id) { struct iwcm_id_private *cm_id_priv; cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); - iwcm_deref_id(cm_id_priv); + if (iwcm_deref_id(cm_id_priv) && + test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { + BUG_ON(!list_empty(&cm_id_priv->work_list)); + free_cm_id(cm_id_priv); + } } static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); @@ -355,7 +358,9 @@ static void destroy_cm_id(struct iw_cm_id *cm_id) case IW_CM_STATE_CONN_RECV: /* * App called destroy before/without calling accept after - * receiving connection request event notification. + * receiving connection request event notification or + * returned non zero from the event callback function. + * In either case, must tell the provider to reject. */ cm_id_priv->state = IW_CM_STATE_DESTROYING; break; @@ -391,9 +396,7 @@ void iw_destroy_cm_id(struct iw_cm_id *cm_id) wait_for_completion(&cm_id_priv->destroy_comp); - dealloc_work_entries(cm_id_priv); - - kfree(cm_id_priv); + free_cm_id(cm_id_priv); } EXPORT_SYMBOL(iw_destroy_cm_id); @@ -647,10 +650,11 @@ static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, /* Call the client CM handler */ ret = cm_id->cm_handler(cm_id, iw_event); if (ret) { + iw_cm_reject(cm_id, NULL, 0); set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); destroy_cm_id(cm_id); if (atomic_read(&cm_id_priv->refcount)==0) - kfree(cm_id); + free_cm_id(cm_id_priv); } out: @@ -854,13 +858,12 @@ static void cm_work_handler(struct work_struct *_work) destroy_cm_id(&cm_id_priv->id); } BUG_ON(atomic_read(&cm_id_priv->refcount)==0); - if (iwcm_deref_id(cm_id_priv)) - return; - - if (atomic_read(&cm_id_priv->refcount)==0 && - test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { - dealloc_work_entries(cm_id_priv); - kfree(cm_id_priv); + if (iwcm_deref_id(cm_id_priv)) { + if (test_bit(IWCM_F_CALLBACK_DESTROY, + &cm_id_priv->flags)) { + BUG_ON(!list_empty(&cm_id_priv->work_list)); + free_cm_id(cm_id_priv); + } return; } spin_lock_irqsave(&cm_id_priv->lock, flags); diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c index 13efd41..6edfecf 100644 --- a/drivers/infiniband/core/mad.c +++ b/drivers/infiniband/core/mad.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2005 Voltaire, Inc. All rights reserved. + * Copyright (c) 2004-2007 Voltaire, Inc. All rights reserved. * Copyright (c) 2005 Intel Corporation. All rights reserved. * Copyright (c) 2005 Mellanox Technologies Ltd. All rights reserved. * @@ -31,7 +31,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: mad.c 5596 2006-03-03 01:00:07Z sean.hefty $ */ #include <linux/dma-mapping.h> #include <rdma/ib_cache.h> @@ -668,7 +667,7 @@ static void build_smp_wc(struct ib_qp *qp, static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv, struct ib_mad_send_wr_private *mad_send_wr) { - int ret; + int ret = 0; struct ib_smp *smp = mad_send_wr->send_buf.mad; unsigned long flags; struct ib_mad_local_private *local; @@ -688,14 +687,15 @@ static int handle_outgoing_dr_smp(struct ib_mad_agent_private *mad_agent_priv, */ if ((ib_get_smp_direction(smp) ? smp->dr_dlid : smp->dr_slid) == IB_LID_PERMISSIVE && - !smi_handle_dr_smp_send(smp, device->node_type, port_num)) { + smi_handle_dr_smp_send(smp, device->node_type, port_num) == + IB_SMI_DISCARD) { ret = -EINVAL; printk(KERN_ERR PFX "Invalid directed route\n"); goto out; } + /* Check to post send on QP or process locally */ - ret = smi_check_local_smp(smp, device); - if (!ret) + if (smi_check_local_smp(smp, device) == IB_SMI_DISCARD) goto out; local = kmalloc(sizeof *local, GFP_ATOMIC); @@ -1874,18 +1874,22 @@ static void ib_mad_recv_done_handler(struct ib_mad_port_private *port_priv, if (recv->mad.mad.mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) { - if (!smi_handle_dr_smp_recv(&recv->mad.smp, - port_priv->device->node_type, - port_priv->port_num, - port_priv->device->phys_port_cnt)) + if (smi_handle_dr_smp_recv(&recv->mad.smp, + port_priv->device->node_type, + port_priv->port_num, + port_priv->device->phys_port_cnt) == + IB_SMI_DISCARD) goto out; - if (!smi_check_forward_dr_smp(&recv->mad.smp)) + + if (smi_check_forward_dr_smp(&recv->mad.smp) == IB_SMI_LOCAL) goto local; - if (!smi_handle_dr_smp_send(&recv->mad.smp, - port_priv->device->node_type, - port_priv->port_num)) + + if (smi_handle_dr_smp_send(&recv->mad.smp, + port_priv->device->node_type, + port_priv->port_num) == IB_SMI_DISCARD) goto out; - if (!smi_check_local_smp(&recv->mad.smp, port_priv->device)) + + if (smi_check_local_smp(&recv->mad.smp, port_priv->device) == IB_SMI_DISCARD) goto out; } diff --git a/drivers/infiniband/core/multicast.c b/drivers/infiniband/core/multicast.c new file mode 100644 index 0000000..4a579b3 --- /dev/null +++ b/drivers/infiniband/core/multicast.c @@ -0,0 +1,837 @@ +/* + * Copyright (c) 2006 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include <linux/completion.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/bitops.h> +#include <linux/random.h> + +#include <rdma/ib_cache.h> +#include "sa.h" + +static void mcast_add_one(struct ib_device *device); +static void mcast_remove_one(struct ib_device *device); + +static struct ib_client mcast_client = { + .name = "ib_multicast", + .add = mcast_add_one, + .remove = mcast_remove_one +}; + +static struct ib_sa_client sa_client; +static struct workqueue_struct *mcast_wq; +static union ib_gid mgid0; + +struct mcast_device; + +struct mcast_port { + struct mcast_device *dev; + spinlock_t lock; + struct rb_root table; + atomic_t refcount; + struct completion comp; + u8 port_num; +}; + +struct mcast_device { + struct ib_device *device; + struct ib_event_handler event_handler; + int start_port; + int end_port; + struct mcast_port port[0]; +}; + +enum mcast_state { + MCAST_IDLE, + MCAST_JOINING, + MCAST_MEMBER, + MCAST_BUSY, + MCAST_ERROR +}; + +struct mcast_member; + +struct mcast_group { + struct ib_sa_mcmember_rec rec; + struct rb_node node; + struct mcast_port *port; + spinlock_t lock; + struct work_struct work; + struct list_head pending_list; + struct list_head active_list; + struct mcast_member *last_join; + int members[3]; + atomic_t refcount; + enum mcast_state state; + struct ib_sa_query *query; + int query_id; +}; + +struct mcast_member { + struct ib_sa_multicast multicast; + struct ib_sa_client *client; + struct mcast_group *group; + struct list_head list; + enum mcast_state state; + atomic_t refcount; + struct completion comp; +}; + +static void join_handler(int status, struct ib_sa_mcmember_rec *rec, + void *context); +static void leave_handler(int status, struct ib_sa_mcmember_rec *rec, + void *context); + +static struct mcast_group *mcast_find(struct mcast_port *port, + union ib_gid *mgid) +{ + struct rb_node *node = port->table.rb_node; + struct mcast_group *group; + int ret; + + while (node) { + group = rb_entry(node, struct mcast_group, node); + ret = memcmp(mgid->raw, group->rec.mgid.raw, sizeof *mgid); + if (!ret) + return group; + + if (ret < 0) + node = node->rb_left; + else + node = node->rb_right; + } + return NULL; +} + +static struct mcast_group *mcast_insert(struct mcast_port *port, + struct mcast_group *group, + int allow_duplicates) +{ + struct rb_node **link = &port->table.rb_node; + struct rb_node *parent = NULL; + struct mcast_group *cur_group; + int ret; + + while (*link) { + parent = *link; + cur_group = rb_entry(parent, struct mcast_group, node); + + ret = memcmp(group->rec.mgid.raw, cur_group->rec.mgid.raw, + sizeof group->rec.mgid); + if (ret < 0) + link = &(*link)->rb_left; + else if (ret > 0) + link = &(*link)->rb_right; + else if (allow_duplicates) + link = &(*link)->rb_left; + else + return cur_group; + } + rb_link_node(&group->node, parent, link); + rb_insert_color(&group->node, &port->table); + return NULL; +} + +static void deref_port(struct mcast_port *port) +{ + if (atomic_dec_and_test(&port->refcount)) + complete(&port->comp); +} + +static void release_group(struct mcast_group *group) +{ + struct mcast_port *port = group->port; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + if (atomic_dec_and_test(&group->refcount)) { + rb_erase(&group->node, &port->table); + spin_unlock_irqrestore(&port->lock, flags); + kfree(group); + deref_port(port); + } else + spin_unlock_irqrestore(&port->lock, flags); +} + +static void deref_member(struct mcast_member *member) +{ + if (atomic_dec_and_test(&member->refcount)) + complete(&member->comp); +} + +static void queue_join(struct mcast_member *member) +{ + struct mcast_group *group = member->group; + unsigned long flags; + + spin_lock_irqsave(&group->lock, flags); + list_add(&member->list, &group->pending_list); + if (group->state == MCAST_IDLE) { + group->state = MCAST_BUSY; + atomic_inc(&group->refcount); + queue_work(mcast_wq, &group->work); + } + spin_unlock_irqrestore(&group->lock, flags); +} + +/* + * A multicast group has three types of members: full member, non member, and + * send only member. We need to keep track of the number of members of each + * type based on their join state. Adjust the number of members the belong to + * the specified join states. + */ +static void adjust_membership(struct mcast_group *group, u8 join_state, int inc) +{ + int i; + + for (i = 0; i < 3; i++, join_state >>= 1) + if (join_state & 0x1) + group->members[i] += inc; +} + +/* + * If a multicast group has zero members left for a particular join state, but + * the group is still a member with the SA, we need to leave that join state. + * Determine which join states we still belong to, but that do not have any + * active members. + */ +static u8 get_leave_state(struct mcast_group *group) +{ + u8 leave_state = 0; + int i; + + for (i = 0; i < 3; i++) + if (!group->members[i]) + leave_state |= (0x1 << i); + + return leave_state & group->rec.join_state; +} + +static int check_selector(ib_sa_comp_mask comp_mask, + ib_sa_comp_mask selector_mask, + ib_sa_comp_mask value_mask, + u8 selector, u8 src_value, u8 dst_value) +{ + int err; + + if (!(comp_mask & selector_mask) || !(comp_mask & value_mask)) + return 0; + + switch (selector) { + case IB_SA_GT: + err = (src_value <= dst_value); + break; + case IB_SA_LT: + err = (src_value >= dst_value); + break; + case IB_SA_EQ: + err = (src_value != dst_value); + break; + default: + err = 0; + break; + } + + return err; +} + +static int cmp_rec(struct ib_sa_mcmember_rec *src, + struct ib_sa_mcmember_rec *dst, ib_sa_comp_mask comp_mask) +{ + /* MGID must already match */ + + if (comp_mask & IB_SA_MCMEMBER_REC_PORT_GID && + memcmp(&src->port_gid, &dst->port_gid, sizeof src->port_gid)) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_QKEY && src->qkey != dst->qkey) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_MLID && src->mlid != dst->mlid) + return -EINVAL; + if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_MTU_SELECTOR, + IB_SA_MCMEMBER_REC_MTU, dst->mtu_selector, + src->mtu, dst->mtu)) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_TRAFFIC_CLASS && + src->traffic_class != dst->traffic_class) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_PKEY && src->pkey != dst->pkey) + return -EINVAL; + if (check_selector(comp_mask, IB_SA_MCMEMBER_REC_RATE_SELECTOR, + IB_SA_MCMEMBER_REC_RATE, dst->rate_selector, + src->rate, dst->rate)) + return -EINVAL; + if (check_selector(comp_mask, + IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME_SELECTOR, + IB_SA_MCMEMBER_REC_PACKET_LIFE_TIME, + dst->packet_life_time_selector, + src->packet_life_time, dst->packet_life_time)) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_SL && src->sl != dst->sl) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_FLOW_LABEL && + src->flow_label != dst->flow_label) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_HOP_LIMIT && + src->hop_limit != dst->hop_limit) + return -EINVAL; + if (comp_mask & IB_SA_MCMEMBER_REC_SCOPE && src->scope != dst->scope) + return -EINVAL; + + /* join_state checked separately, proxy_join ignored */ + + return 0; +} + +static int send_join(struct mcast_group *group, struct mcast_member *member) +{ + struct mcast_port *port = group->port; + int ret; + + group->last_join = member; + ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device, + port->port_num, IB_MGMT_METHOD_SET, + &member->multicast.rec, + member->multicast.comp_mask, + 3000, GFP_KERNEL, join_handler, group, + &group->query); + if (ret >= 0) { + group->query_id = ret; + ret = 0; + } + return ret; +} + +static int send_leave(struct mcast_group *group, u8 leave_state) +{ + struct mcast_port *port = group->port; + struct ib_sa_mcmember_rec rec; + int ret; + + rec = group->rec; + rec.join_state = leave_state; + + ret = ib_sa_mcmember_rec_query(&sa_client, port->dev->device, + port->port_num, IB_SA_METHOD_DELETE, &rec, + IB_SA_MCMEMBER_REC_MGID | + IB_SA_MCMEMBER_REC_PORT_GID | + IB_SA_MCMEMBER_REC_JOIN_STATE, + 3000, GFP_KERNEL, leave_handler, + group, &group->query); + if (ret >= 0) { + group->query_id = ret; + ret = 0; + } + return ret; +} + +static void join_group(struct mcast_group *group, struct mcast_member *member, + u8 join_state) +{ + member->state = MCAST_MEMBER; + adjust_membership(group, join_state, 1); + group->rec.join_state |= join_state; + member->multicast.rec = group->rec; + member->multicast.rec.join_state = join_state; + list_move(&member->list, &group->active_list); +} + +static int fail_join(struct mcast_group *group, struct mcast_member *member, + int status) +{ + spin_lock_irq(&group->lock); + list_del_init(&member->list); + spin_unlock_irq(&group->lock); + return member->multicast.callback(status, &member->multicast); +} + +static void process_group_error(struct mcast_group *group) +{ + struct mcast_member *member; + int ret; + + spin_lock_irq(&group->lock); + while (!list_empty(&group->active_list)) { + member = list_entry(group->active_list.next, + struct mcast_member, list); + atomic_inc(&member->refcount); + list_del_init(&member->list); + adjust_membership(group, member->multicast.rec.join_state, -1); + member->state = MCAST_ERROR; + spin_unlock_irq(&group->lock); + + ret = member->multicast.callback(-ENETRESET, + &member->multicast); + deref_member(member); + if (ret) + ib_sa_free_multicast(&member->multicast); + spin_lock_irq(&group->lock); + } + + group->rec.join_state = 0; + group->state = MCAST_BUSY; + spin_unlock_irq(&group->lock); +} + +static void mcast_work_handler(struct work_struct *work) +{ + struct mcast_group *group; + struct mcast_member *member; + struct ib_sa_multicast *multicast; + int status, ret; + u8 join_state; + + group = container_of(work, typeof(*group), work); +retest: + spin_lock_irq(&group->lock); + while (!list_empty(&group->pending_list) || + (group->state == MCAST_ERROR)) { + + if (group->state == MCAST_ERROR) { + spin_unlock_irq(&group->lock); + process_group_error(group); + goto retest; + } + + member = list_entry(group->pending_list.next, + struct mcast_member, list); + multicast = &member->multicast; + join_state = multicast->rec.join_state; + atomic_inc(&member->refcount); + + if (join_state == (group->rec.join_state & join_state)) { + status = cmp_rec(&group->rec, &multicast->rec, + multicast->comp_mask); + if (!status) + join_group(group, member, join_state); + else + list_del_init(&member->list); + spin_unlock_irq(&group->lock); + ret = multicast->callback(status, multicast); + } else { + spin_unlock_irq(&group->lock); + status = send_join(group, member); + if (!status) { + deref_member(member); + return; + } + ret = fail_join(group, member, status); + } + + deref_member(member); + if (ret) + ib_sa_free_multicast(&member->multicast); + spin_lock_irq(&group->lock); + } + + join_state = get_leave_state(group); + if (join_state) { + group->rec.join_state &= ~join_state; + spin_unlock_irq(&group->lock); + if (send_leave(group, join_state)) + goto retest; + } else { + group->state = MCAST_IDLE; + spin_unlock_irq(&group->lock); + release_group(group); + } +} + +/* + * Fail a join request if it is still active - at the head of the pending queue. + */ +static void process_join_error(struct mcast_group *group, int status) +{ + struct mcast_member *member; + int ret; + + spin_lock_irq(&group->lock); + member = list_entry(group->pending_list.next, + struct mcast_member, list); + if (group->last_join == member) { + atomic_inc(&member->refcount); + list_del_init(&member->list); + spin_unlock_irq(&group->lock); + ret = member->multicast.callback(status, &member->multicast); + deref_member(member); + if (ret) + ib_sa_free_multicast(&member->multicast); + } else + spin_unlock_irq(&group->lock); +} + +static void join_handler(int status, struct ib_sa_mcmember_rec *rec, + void *context) +{ + struct mcast_group *group = context; + + if (status) + process_join_error(group, status); + else { + spin_lock_irq(&group->port->lock); + group->rec = *rec; + if (!memcmp(&mgid0, &group->rec.mgid, sizeof mgid0)) { + rb_erase(&group->node, &group->port->table); + mcast_insert(group->port, group, 1); + } + spin_unlock_irq(&group->port->lock); + } + mcast_work_handler(&group->work); +} + +static void leave_handler(int status, struct ib_sa_mcmember_rec *rec, + void *context) +{ + struct mcast_group *group = context; + + mcast_work_handler(&group->work); +} + +static struct mcast_group *acquire_group(struct mcast_port *port, + union ib_gid *mgid, gfp_t gfp_mask) +{ + struct mcast_group *group, *cur_group; + unsigned long flags; + int is_mgid0; + + is_mgid0 = !memcmp(&mgid0, mgid, sizeof mgid0); + if (!is_mgid0) { + spin_lock_irqsave(&port->lock, flags); + group = mcast_find(port, mgid); + if (group) + goto found; + spin_unlock_irqrestore(&port->lock, flags); + } + + group = kzalloc(sizeof *group, gfp_mask); + if (!group) + return NULL; + + group->port = port; + group->rec.mgid = *mgid; + INIT_LIST_HEAD(&group->pending_list); + INIT_LIST_HEAD(&group->active_list); + INIT_WORK(&group->work, mcast_work_handler); + spin_lock_init(&group->lock); + + spin_lock_irqsave(&port->lock, flags); + cur_group = mcast_insert(port, group, is_mgid0); + if (cur_group) { + kfree(group); + group = cur_group; + } else + atomic_inc(&port->refcount); +found: + atomic_inc(&group->refcount); + spin_unlock_irqrestore(&port->lock, flags); + return group; +} + +/* + * We serialize all join requests to a single group to make our lives much + * easier. Otherwise, two users could try to join the same group + * simultaneously, with different configurations, one could leave while the + * join is in progress, etc., which makes locking around error recovery + * difficult. + */ +struct ib_sa_multicast * +ib_sa_join_multicast(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + struct ib_sa_mcmember_rec *rec, + ib_sa_comp_mask comp_mask, gfp_t gfp_mask, + int (*callback)(int status, + struct ib_sa_multicast *multicast), + void *context) +{ + struct mcast_device *dev; + struct mcast_member *member; + struct ib_sa_multicast *multicast; + int ret; + + dev = ib_get_client_data(device, &mcast_client); + if (!dev) + return ERR_PTR(-ENODEV); + + member = kmalloc(sizeof *member, gfp_mask); + if (!member) + return ERR_PTR(-ENOMEM); + + ib_sa_client_get(client); + member->client = client; + member->multicast.rec = *rec; + member->multicast.comp_mask = comp_mask; + member->multicast.callback = callback; + member->multicast.context = context; + init_completion(&member->comp); + atomic_set(&member->refcount, 1); + member->state = MCAST_JOINING; + + member->group = acquire_group(&dev->port[port_num - dev->start_port], + &rec->mgid, gfp_mask); + if (!member->group) { + ret = -ENOMEM; + goto err; + } + + /* + * The user will get the multicast structure in their callback. They + * could then free the multicast structure before we can return from + * this routine. So we save the pointer to return before queuing + * any callback. + */ + multicast = &member->multicast; + queue_join(member); + return multicast; + +err: + ib_sa_client_put(client); + kfree(member); + return ERR_PTR(ret); +} +EXPORT_SYMBOL(ib_sa_join_multicast); + +void ib_sa_free_multicast(struct ib_sa_multicast *multicast) +{ + struct mcast_member *member; + struct mcast_group *group; + + member = container_of(multicast, struct mcast_member, multicast); + group = member->group; + + spin_lock_irq(&group->lock); + if (member->state == MCAST_MEMBER) + adjust_membership(group, multicast->rec.join_state, -1); + + list_del_init(&member->list); + + if (group->state == MCAST_IDLE) { + group->state = MCAST_BUSY; + spin_unlock_irq(&group->lock); + /* Continue to hold reference on group until callback */ + queue_work(mcast_wq, &group->work); + } else { + spin_unlock_irq(&group->lock); + release_group(group); + } + + deref_member(member); + wait_for_completion(&member->comp); + ib_sa_client_put(member->client); + kfree(member); +} +EXPORT_SYMBOL(ib_sa_free_multicast); + +int ib_sa_get_mcmember_rec(struct ib_device *device, u8 port_num, + union ib_gid *mgid, struct ib_sa_mcmember_rec *rec) +{ + struct mcast_device *dev; + struct mcast_port *port; + struct mcast_group *group; + unsigned long flags; + int ret = 0; + + dev = ib_get_client_data(device, &mcast_client); + if (!dev) + return -ENODEV; + + port = &dev->port[port_num - dev->start_port]; + spin_lock_irqsave(&port->lock, flags); + group = mcast_find(port, mgid); + if (group) + *rec = group->rec; + else + ret = -EADDRNOTAVAIL; + spin_unlock_irqrestore(&port->lock, flags); + + return ret; +} +EXPORT_SYMBOL(ib_sa_get_mcmember_rec); + +int ib_init_ah_from_mcmember(struct ib_device *device, u8 port_num, + struct ib_sa_mcmember_rec *rec, + struct ib_ah_attr *ah_attr) +{ + int ret; + u16 gid_index; + u8 p; + + ret = ib_find_cached_gid(device, &rec->port_gid, &p, &gid_index); + if (ret) + return ret; + + memset(ah_attr, 0, sizeof *ah_attr); + ah_attr->dlid = be16_to_cpu(rec->mlid); + ah_attr->sl = rec->sl; + ah_attr->port_num = port_num; + ah_attr->static_rate = rec->rate; + + ah_attr->ah_flags = IB_AH_GRH; + ah_attr->grh.dgid = rec->mgid; + + ah_attr->grh.sgid_index = (u8) gid_index; + ah_attr->grh.flow_label = be32_to_cpu(rec->flow_label); + ah_attr->grh.hop_limit = rec->hop_limit; + ah_attr->grh.traffic_class = rec->traffic_class; + + return 0; +} +EXPORT_SYMBOL(ib_init_ah_from_mcmember); + +static void mcast_groups_lost(struct mcast_port *port) +{ + struct mcast_group *group; + struct rb_node *node; + unsigned long flags; + + spin_lock_irqsave(&port->lock, flags); + for (node = rb_first(&port->table); node; node = rb_next(node)) { + group = rb_entry(node, struct mcast_group, node); + spin_lock(&group->lock); + if (group->state == MCAST_IDLE) { + atomic_inc(&group->refcount); + queue_work(mcast_wq, &group->work); + } + group->state = MCAST_ERROR; + spin_unlock(&group->lock); + } + spin_unlock_irqrestore(&port->lock, flags); +} + +static void mcast_event_handler(struct ib_event_handler *handler, + struct ib_event *event) +{ + struct mcast_device *dev; + + dev = container_of(handler, struct mcast_device, event_handler); + + switch (event->event) { + case IB_EVENT_PORT_ERR: + case IB_EVENT_LID_CHANGE: + case IB_EVENT_SM_CHANGE: + case IB_EVENT_CLIENT_REREGISTER: + mcast_groups_lost(&dev->port[event->element.port_num - + dev->start_port]); + break; + default: + break; + } +} + +static void mcast_add_one(struct ib_device *device) +{ + struct mcast_device *dev; + struct mcast_port *port; + int i; + + if (rdma_node_get_transport(device->node_type) != RDMA_TRANSPORT_IB) + return; + + dev = kmalloc(sizeof *dev + device->phys_port_cnt * sizeof *port, + GFP_KERNEL); + if (!dev) + return; + + if (device->node_type == RDMA_NODE_IB_SWITCH) + dev->start_port = dev->end_port = 0; + else { + dev->start_port = 1; + dev->end_port = device->phys_port_cnt; + } + + for (i = 0; i <= dev->end_port - dev->start_port; i++) { + port = &dev->port[i]; + port->dev = dev; + port->port_num = dev->start_port + i; + spin_lock_init(&port->lock); + port->table = RB_ROOT; + init_completion(&port->comp); + atomic_set(&port->refcount, 1); + } + + dev->device = device; + ib_set_client_data(device, &mcast_client, dev); + + INIT_IB_EVENT_HANDLER(&dev->event_handler, device, mcast_event_handler); + ib_register_event_handler(&dev->event_handler); +} + +static void mcast_remove_one(struct ib_device *device) +{ + struct mcast_device *dev; + struct mcast_port *port; + int i; + + dev = ib_get_client_data(device, &mcast_client); + if (!dev) + return; + + ib_unregister_event_handler(&dev->event_handler); + flush_workqueue(mcast_wq); + + for (i = 0; i <= dev->end_port - dev->start_port; i++) { + port = &dev->port[i]; + deref_port(port); + wait_for_completion(&port->comp); + } + + kfree(dev); +} + +int mcast_init(void) +{ + int ret; + + mcast_wq = create_singlethread_workqueue("ib_mcast"); + if (!mcast_wq) + return -ENOMEM; + + ib_sa_register_client(&sa_client); + + ret = ib_register_client(&mcast_client); + if (ret) + goto err; + return 0; + +err: + ib_sa_unregister_client(&sa_client); + destroy_workqueue(mcast_wq); + return ret; +} + +void mcast_cleanup(void) +{ + ib_unregister_client(&mcast_client); + ib_sa_unregister_client(&sa_client); + destroy_workqueue(mcast_wq); +} diff --git a/drivers/infiniband/core/sa.h b/drivers/infiniband/core/sa.h new file mode 100644 index 0000000..24c93fd --- /dev/null +++ b/drivers/infiniband/core/sa.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2004 Topspin Communications. All rights reserved. + * Copyright (c) 2005 Voltaire, Inc. All rights reserved. + * Copyright (c) 2006 Intel Corporation. All rights reserved. + * + * This software is available to you under a choice of one of two + * licenses. You may choose to be licensed under the terms of the GNU + * General Public License (GPL) Version 2, available from the file + * COPYING in the main directory of this source tree, or the + * OpenIB.org BSD license below: + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SA_H +#define SA_H + +#include <rdma/ib_sa.h> + +static inline void ib_sa_client_get(struct ib_sa_client *client) +{ + atomic_inc(&client->users); +} + +static inline void ib_sa_client_put(struct ib_sa_client *client) +{ + if (atomic_dec_and_test(&client->users)) + complete(&client->comp); +} + +int ib_sa_mcmember_rec_query(struct ib_sa_client *client, + struct ib_device *device, u8 port_num, + u8 method, + struct ib_sa_mcmember_rec *rec, + ib_sa_comp_mask comp_mask, + int timeout_ms, gfp_t gfp_mask, + void (*callback)(int status, + struct ib_sa_mcmember_rec *resp, + void *context), + void *context, + struct ib_sa_query **sa_query); + +int mcast_init(void); +void mcast_cleanup(void); + +#endif /* SA_H */ diff --git a/drivers/infiniband/core/sa_query.c b/drivers/infiniband/core/sa_query.c index e45afba..9a7eaad 100644 --- a/drivers/infiniband/core/sa_query.c +++ b/drivers/infiniband/core/sa_query.c @@ -47,8 +47,8 @@ #include <linux/workqueue.h> #include <rdma/ib_pack.h> -#include <rdma/ib_sa.h> #include <rdma/ib_cache.h> +#include "sa.h" MODULE_AUTHOR("Roland Dreier"); MODULE_DESCRIPTION("InfiniBand subnet administration query support"); @@ -57,6 +57,7 @@ MODULE_LICENSE("Dual BSD/GPL"); struct ib_sa_sm_ah { struct ib_ah *ah; struct kref ref; + u8 src_path_mask; }; struct ib_sa_port { @@ -380,6 +381,7 @@ static void update_sm_ah(struct work_struct *work) } kref_init(&new_ah->ref); + new_ah->src_path_mask = (1 << port_attr.lmc) - 1; memset(&ah_attr, 0, sizeof ah_attr); ah_attr.dlid = port_attr.sm_lid; @@ -425,17 +427,6 @@ void ib_sa_register_client(struct ib_sa_client *client) } EXPORT_SYMBOL(ib_sa_register_client); -static inline void ib_sa_client_get(struct ib_sa_client *client) -{ - atomic_inc(&client->users); -} - -static inline void ib_sa_client_put(struct ib_sa_client *client) -{ - if (atomic_dec_and_test(&client->users)) - complete(&client->comp); -} - void ib_sa_unregister_client(struct ib_sa_client *client) { ib_sa_client_put(client); @@ -471,6 +462,25 @@ void ib_sa_cancel_query(int id, struct ib_sa_query *query) } EXPORT_SYMBOL(ib_sa_cancel_query); +static u8 get_src_path_mask(struct ib_device *device, u8 port_num) +{ + struct ib_sa_device *sa_dev; + struct ib_sa_port *port; + unsigned long flags; + u8 src_path_mask; + + sa_dev = ib_get_client_data(device, &sa_client); + if (!sa_dev) + return 0x7f; + + port = &sa_dev->port[port_num - sa_dev->start_port]; + spin_lock_irqsave(&port->ah_lock, flags); + src_path_mask = port->sm_ah ? port->sm_ah->src_path_mask : 0x7f; + spin_unlock_irqrestore(&port->ah_lock, flags); + + return src_path_mask; +} + int ib_init_ah_from_path(struct ib_device *device, u8 port_num, struct ib_sa_path_rec *rec, struct ib_ah_attr *ah_attr) { @@ -480,8 +490,10 @@ int ib_init_ah_from_path(struct ib_device *device, u8 port_num, memset(ah_attr, 0, sizeof *ah_attr); ah_attr->dlid = be16_to_cpu(rec->dlid); ah_attr->sl = rec->sl; - ah_attr->src_path_bits = be16_to_cpu(rec->slid) & 0x7f; + ah_attr->src_path_bits = be16_to_cpu(rec->slid) & + get_src_path_mask(device, port_num); ah_attr->port_num = port_num; + ah_attr->static_rate = rec->rate; if (rec->hop_limit > 1) { ah_attr->ah_flags = IB_AH_GRH; @@ -901,7 +913,6 @@ err1: kfree(query); return ret; } -EXPORT_SYMBOL(ib_sa_mcmember_rec_query); static void send_handler(struct ib_mad_agent *agent, struct ib_mad_send_wc *mad_send_wc) @@ -1053,14 +1064,27 @@ static int __init ib_sa_init(void) get_random_bytes(&tid, sizeof tid); ret = ib_register_client(&sa_client); - if (ret) + if (ret) { printk(KERN_ERR "Couldn't register ib_sa client\n"); + goto err1; + } + + ret = mcast_init(); + if (ret) { + printk(KERN_ERR "Couldn't initialize multicast handling\n"); + goto err2; + } + return 0; +err2: + ib_unregister_client(&sa_client); +err1: return ret; } static void __exit ib_sa_cleanup(void) { + mcast_cleanup(); ib_unregister_client(&sa_client); idr_destroy(&query_idr); } diff --git a/drivers/infiniband/core/smi.c b/drivers/infiniband/core/smi.c index 54b81e1..2bca753 100644 --- a/drivers/infiniband/core/smi.c +++ b/drivers/infiniband/core/smi.c @@ -3,7 +3,7 @@ * Copyright (c) 2004, 2005 Infinicon Corporation. All rights reserved. * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. * Copyright (c) 2004, 2005 Topspin Corporation. All rights reserved. - * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved. + * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved. * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -34,7 +34,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: smi.c 1389 2004-12-27 22:56:47Z roland $ */ #include <rdma/ib_smi.h> @@ -44,9 +43,8 @@ * Fixup a directed route SMP for sending * Return 0 if the SMP should be discarded */ -int smi_handle_dr_smp_send(struct ib_smp *smp, - u8 node_type, - int port_num) +enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp, + u8 node_type, int port_num) { u8 hop_ptr, hop_cnt; @@ -59,18 +57,18 @@ int smi_handle_dr_smp_send(struct ib_smp *smp, if (hop_cnt && hop_ptr == 0) { smp->hop_ptr++; return (smp->initial_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:2 */ if (hop_ptr && hop_ptr < hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; /* smp->return_path set when received */ smp->hop_ptr++; return (smp->initial_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:3 -- We're at the end of the DR segment of path */ @@ -78,29 +76,30 @@ int smi_handle_dr_smp_send(struct ib_smp *smp, /* smp->return_path set when received */ smp->hop_ptr++; return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_dlid == IB_LID_PERMISSIVE); + smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ /* C14-9:5 -- Fail unreasonable hop pointer */ - return (hop_ptr == hop_cnt + 1); + return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } else { /* C14-13:1 */ if (hop_cnt && hop_ptr == hop_cnt + 1) { smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:2 */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:3 -- at the end of the DR segment of path */ @@ -108,15 +107,16 @@ int smi_handle_dr_smp_send(struct ib_smp *smp, smp->hop_ptr--; /* C14-13:3 -- SMPs destined for SM shouldn't be here */ return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_slid == IB_LID_PERMISSIVE); + smp->dr_slid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:4 -- hop_ptr = 0 -> should have gone to SM */ if (hop_ptr == 0) - return 1; + return IB_SMI_HANDLE; /* C14-13:5 -- Check for unreasonable hop pointer */ - return 0; + return IB_SMI_DISCARD; } } @@ -124,10 +124,8 @@ int smi_handle_dr_smp_send(struct ib_smp *smp, * Adjust information for a received SMP * Return 0 if the SMP should be dropped */ -int smi_handle_dr_smp_recv(struct ib_smp *smp, - u8 node_type, - int port_num, - int phys_port_cnt) +enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type, + int port_num, int phys_port_cnt) { u8 hop_ptr, hop_cnt; @@ -138,16 +136,17 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp, if (!ib_get_smp_direction(smp)) { /* C14-9:1 -- sender should have incremented hop_ptr */ if (hop_cnt && hop_ptr == 0) - return 0; + return IB_SMI_DISCARD; /* C14-9:2 -- intermediate hop */ if (hop_ptr && hop_ptr < hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; smp->return_path[hop_ptr] = port_num; /* smp->hop_ptr updated when sending */ - return (smp->initial_path[hop_ptr+1] <= phys_port_cnt); + return (smp->initial_path[hop_ptr+1] <= phys_port_cnt ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:3 -- We're at the end of the DR segment of path */ @@ -157,12 +156,13 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp, /* smp->hop_ptr updated when sending */ return (node_type == RDMA_NODE_IB_SWITCH || - smp->dr_dlid == IB_LID_PERMISSIVE); + smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ /* C14-9:5 -- fail unreasonable hop pointer */ - return (hop_ptr == hop_cnt + 1); + return (hop_ptr == hop_cnt + 1 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } else { @@ -170,16 +170,17 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp, if (hop_cnt && hop_ptr == hop_cnt + 1) { smp->hop_ptr--; return (smp->return_path[smp->hop_ptr] == - port_num); + port_num ? IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:2 */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) { if (node_type != RDMA_NODE_IB_SWITCH) - return 0; + return IB_SMI_DISCARD; /* smp->hop_ptr updated when sending */ - return (smp->return_path[hop_ptr-1] <= phys_port_cnt); + return (smp->return_path[hop_ptr-1] <= phys_port_cnt ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } /* C14-13:3 -- We're at the end of the DR segment of path */ @@ -187,23 +188,20 @@ int smi_handle_dr_smp_recv(struct ib_smp *smp, if (smp->dr_slid == IB_LID_PERMISSIVE) { /* giving SMP to SM - update hop_ptr */ smp->hop_ptr--; - return 1; + return IB_SMI_HANDLE; } /* smp->hop_ptr updated when sending */ - return (node_type == RDMA_NODE_IB_SWITCH); + return (node_type == RDMA_NODE_IB_SWITCH ? + IB_SMI_HANDLE: IB_SMI_DISCARD); } /* C14-13:4 -- hop_ptr = 0 -> give to SM */ /* C14-13:5 -- Check for unreasonable hop pointer */ - return (hop_ptr == 0); + return (hop_ptr == 0 ? IB_SMI_HANDLE : IB_SMI_DISCARD); } } -/* - * Return 1 if the received DR SMP should be forwarded to the send queue - * Return 0 if the SMP should be completed up the stack - */ -int smi_check_forward_dr_smp(struct ib_smp *smp) +enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp) { u8 hop_ptr, hop_cnt; @@ -213,23 +211,25 @@ int smi_check_forward_dr_smp(struct ib_smp *smp) if (!ib_get_smp_direction(smp)) { /* C14-9:2 -- intermediate hop */ if (hop_ptr && hop_ptr < hop_cnt) - return 1; + return IB_SMI_SEND; /* C14-9:3 -- at the end of the DR segment of path */ if (hop_ptr == hop_cnt) - return (smp->dr_dlid == IB_LID_PERMISSIVE); + return (smp->dr_dlid == IB_LID_PERMISSIVE ? + IB_SMI_SEND : IB_SMI_LOCAL); /* C14-9:4 -- hop_ptr = hop_cnt + 1 -> give to SMA/SM */ if (hop_ptr == hop_cnt + 1) - return 1; + return IB_SMI_SEND; } else { - /* C14-13:2 */ + /* C14-13:2 -- intermediate hop */ if (2 <= hop_ptr && hop_ptr <= hop_cnt) - return 1; + return IB_SMI_SEND; /* C14-13:3 -- at the end of the DR segment of path */ if (hop_ptr == 1) - return (smp->dr_slid != IB_LID_PERMISSIVE); + return (smp->dr_slid != IB_LID_PERMISSIVE ? + IB_SMI_SEND : IB_SMI_LOCAL); } - return 0; + return IB_SMI_LOCAL; } diff --git a/drivers/infiniband/core/smi.h b/drivers/infiniband/core/smi.h index 3011bfd..9a4b349 100644 --- a/drivers/infiniband/core/smi.h +++ b/drivers/infiniband/core/smi.h @@ -3,7 +3,7 @@ * Copyright (c) 2004 Infinicon Corporation. All rights reserved. * Copyright (c) 2004 Intel Corporation. All rights reserved. * Copyright (c) 2004 Topspin Corporation. All rights reserved. - * Copyright (c) 2004 Voltaire Corporation. All rights reserved. + * Copyright (c) 2004-2007 Voltaire Corporation. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -33,7 +33,6 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * - * $Id: smi.h 1389 2004-12-27 22:56:47Z roland $ */ #ifndef __SMI_H_ @@ -41,26 +40,33 @@ #include <rdma/ib_smi.h> -int smi_handle_dr_smp_recv(struct ib_smp *smp, - u8 node_type, - int port_num, - int phys_port_cnt); -extern int smi_check_forward_dr_smp(struct ib_smp *smp); -extern int smi_handle_dr_smp_send(struct ib_smp *smp, - u8 node_type, - int port_num); +enum smi_action { + IB_SMI_DISCARD, + IB_SMI_HANDLE +}; + +enum smi_forward_action { + IB_SMI_LOCAL, /* SMP should be completed up the stack */ + IB_SMI_SEND, /* received DR SMP should be forwarded to the send queue */ +}; + +enum smi_action smi_handle_dr_smp_recv(struct ib_smp *smp, u8 node_type, + int port_num, int phys_port_cnt); +extern enum smi_forward_action smi_check_forward_dr_smp(struct ib_smp *smp); +extern enum smi_action smi_handle_dr_smp_send(struct ib_smp *smp, + u8 node_type, int port_num); /* * Return 1 if the SMP should be handled by the local SMA/SM via process_mad */ -static inline int smi_check_local_smp(struct ib_smp *smp, - struct ib_device *device) +static inline enum smi_action smi_check_local_smp(struct ib_smp *smp, + struct ib_device *device) { /* C14-9:3 -- We're at the end of the DR segment of path */ /* C14-9:4 -- Hop Pointer = Hop Count + 1 -> give to SMA/SM */ return ((device->process_mad && !ib_get_smp_direction(smp) && - (smp->hop_ptr == smp->hop_cnt + 1))); + (smp->hop_ptr == smp->hop_cnt + 1)) ? + IB_SMI_HANDLE : IB_SMI_DISCARD); } - #endif /* __SMI_H_ */ diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 709323c..08c299e 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -683,6 +683,7 @@ int ib_device_register_sysfs(struct ib_device *device) class_dev->class = &ib_class; class_dev->class_data = device; + class_dev->dev = device->dma_device; strlcpy(class_dev->class_id, device->name, BUS_ID_SIZE); INIT_LIST_HEAD(&device->port_list); @@ -714,8 +715,6 @@ int ib_device_register_sysfs(struct ib_device *device) if (ret) goto err_put; } else { - int i; - for (i = 1; i <= device->phys_port_cnt; ++i) { ret = add_port(device, i); if (ret) diff --git a/drivers/infiniband/core/ucm.c b/drivers/infiniband/core/ucm.c index ee51d79..2586a3e 100644 --- a/drivers/infiniband/core/ucm.c +++ b/drivers/infiniband/core/ucm.c @@ -407,29 +407,18 @@ static ssize_t ib_ucm_event(struct ib_ucm_file *file, mutex_lock(&file->file_mutex); while (list_empty(&file->events)) { + mutex_unlock(&file->file_mutex); - if (file->filp->f_flags & O_NONBLOCK) { - result = -EAGAIN; - break; - } + if (file->filp->f_flags & O_NONBLOCK) + return -EAGAIN; - if (signal_pending(current)) { - result = -ERESTARTSYS; - break; - } + if (wait_event_interruptible(file->poll_wait, + !list_empty(&file->events))) + return -ERESTARTSYS; - prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE); - - mutex_unlock(&file->file_mutex); - schedule(); mutex_lock(&file->file_mutex); - - finish_wait(&file->poll_wait, &wait); } - if (result) - goto done; - uevent = list_entry(file->events.next, struct ib_ucm_event, file_list); if (ib_ucm_new_cm_id(uevent->resp.event)) { diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c index 6b81b98..53b4c94 100644 --- a/drivers/infiniband/core/ucma.c +++ b/drivers/infiniband/core/ucma.c @@ -70,10 +70,24 @@ struct ucma_context { u64 uid; struct list_head list; + struct list_head mc_list; +}; + +struct ucma_multicast { + struct ucma_context *ctx; + int id; + int events_reported; + + u64 uid; + struct list_head list; + struct sockaddr addr; + u8 pad[sizeof(struct sockaddr_in6) - + sizeof(struct sockaddr)]; }; struct ucma_event { struct ucma_context *ctx; + struct ucma_multicast *mc; struct list_head list; struct rdma_cm_id *cm_id; struct rdma_ucm_event_resp resp; @@ -81,6 +95,7 @@ struct ucma_event { static DEFINE_MUTEX(mut); static DEFINE_IDR(ctx_idr); +static DEFINE_IDR(multicast_idr); static inline struct ucma_context *_ucma_find_context(int id, struct ucma_file *file) @@ -124,6 +139,7 @@ static struct ucma_context *ucma_alloc_ctx(struct ucma_file *file) atomic_set(&ctx->ref, 1); init_completion(&ctx->comp); + INIT_LIST_HEAD(&ctx->mc_list); ctx->file = file; do { @@ -147,6 +163,37 @@ error: return NULL; } +static struct ucma_multicast* ucma_alloc_multicast(struct ucma_context *ctx) +{ + struct ucma_multicast *mc; + int ret; + + mc = kzalloc(sizeof(*mc), GFP_KERNEL); + if (!mc) + return NULL; + + do { + ret = idr_pre_get(&multicast_idr, GFP_KERNEL); + if (!ret) + goto error; + + mutex_lock(&mut); + ret = idr_get_new(&multicast_idr, mc, &mc->id); + mutex_unlock(&mut); + } while (ret == -EAGAIN); + + if (ret) + goto error; + + mc->ctx = ctx; + list_add_tail(&mc->list, &ctx->mc_list); + return mc; + +error: + kfree(mc); + return NULL; +} + static void ucma_copy_conn_event(struct rdma_ucm_conn_param *dst, struct rdma_conn_param *src) { @@ -180,8 +227,19 @@ static void ucma_set_event_context(struct ucma_context *ctx, struct ucma_event *uevent) { uevent->ctx = ctx; - uevent->resp.uid = ctx->uid; - uevent->resp.id = ctx->id; + switch (event->event) { + case RDMA_CM_EVENT_MULTICAST_JOIN: + case RDMA_CM_EVENT_MULTICAST_ERROR: + uevent->mc = (struct ucma_multicast *) + event->param.ud.private_data; + uevent->resp.uid = uevent->mc->uid; + uevent->resp.id = uevent->mc->id; + break; + default: + uevent->resp.uid = ctx->uid; + uevent->resp.id = ctx->id; + break; + } } static int ucma_event_handler(struct rdma_cm_id *cm_id, @@ -199,7 +257,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id, ucma_set_event_context(ctx, event, uevent); uevent->resp.event = event->event; uevent->resp.status = event->status; - if (cm_id->ps == RDMA_PS_UDP) + if (cm_id->ps == RDMA_PS_UDP || cm_id->ps == RDMA_PS_IPOIB) ucma_copy_ud_event(&uevent->resp.param.ud, &event->param.ud); else ucma_copy_conn_event(&uevent->resp.param.conn, @@ -208,7 +266,7 @@ static int ucma_event_handler(struct rdma_cm_id *cm_id, mutex_lock(&ctx->file->mut); if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) { if (!ctx->backlog) { - ret = -EDQUOT; + ret = -ENOMEM; kfree(uevent); goto out; } @@ -248,26 +306,18 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf, mutex_lock(&file->mut); while (list_empty(&file->event_list)) { - if (file->filp->f_flags & O_NONBLOCK) { - ret = -EAGAIN; - break; - } + mutex_unlock(&file->mut); - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } + if (file->filp->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(file->poll_wait, + !list_empty(&file->event_list))) + return -ERESTARTSYS; - prepare_to_wait(&file->poll_wait, &wait, TASK_INTERRUPTIBLE); - mutex_unlock(&file->mut); - schedule(); mutex_lock(&file->mut); - finish_wait(&file->poll_wait, &wait); } - if (ret) - goto done; - uevent = list_entry(file->event_list.next, struct ucma_event, list); if (uevent->resp.event == RDMA_CM_EVENT_CONNECT_REQUEST) { @@ -290,6 +340,8 @@ static ssize_t ucma_get_event(struct ucma_file *file, const char __user *inbuf, list_del(&uevent->list); uevent->ctx->events_reported++; + if (uevent->mc) + uevent->mc->events_reported++; kfree(uevent); done: mutex_unlock(&file->mut); @@ -342,6 +394,19 @@ err1: return ret; } +static void ucma_cleanup_multicast(struct ucma_context *ctx) +{ + struct ucma_multicast *mc, *tmp; + + mutex_lock(&mut); + list_for_each_entry_safe(mc, tmp, &ctx->mc_list, list) { + list_del(&mc->list); + idr_remove(&multicast_idr, mc->id); + kfree(mc); + } + mutex_unlock(&mut); +} + static void ucma_cleanup_events(struct ucma_context *ctx) { struct ucma_event *uevent, *tmp; @@ -360,6 +425,19 @@ static void ucma_cleanup_events(struct ucma_context *ctx) } } +static void ucma_cleanup_mc_events(struct ucma_multicast *mc) +{ + struct ucma_event *uevent, *tmp; + + list_for_each_entry_safe(uevent, tmp, &mc->ctx->file->event_list, list) { + if (uevent->mc != mc) + continue; + + list_del(&uevent->list); + kfree(uevent); + } +} + static int ucma_free_ctx(struct ucma_context *ctx) { int events_reported; @@ -367,6 +445,8 @@ static int ucma_free_ctx(struct ucma_context *ctx) /* No new events will be generated after destroying the id. */ rdma_destroy_id(ctx->cm_id); + ucma_cleanup_multicast(ctx); + /* Cleanup events not yet reported to the user. */ mutex_lock(&ctx->file->mut); ucma_cleanup_events(ctx); @@ -731,6 +811,114 @@ static ssize_t ucma_notify(struct ucma_file *file, const char __user *inbuf, return ret; } +static ssize_t ucma_join_multicast(struct ucma_file *file, + const char __user *inbuf, + int in_len, int out_len) +{ + struct rdma_ucm_join_mcast cmd; + struct rdma_ucm_create_id_resp resp; + struct ucma_context *ctx; + struct ucma_multicast *mc; + int ret; + + if (out_len < sizeof(resp)) + return -ENOSPC; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + ctx = ucma_get_ctx(file, cmd.id); + if (IS_ERR(ctx)) + return PTR_ERR(ctx); + + mutex_lock(&file->mut); + mc = ucma_alloc_multicast(ctx); + if (IS_ERR(mc)) { + ret = PTR_ERR(mc); + goto err1; + } + + mc->uid = cmd.uid; + memcpy(&mc->addr, &cmd.addr, sizeof cmd.addr); + ret = rdma_join_multicast(ctx->cm_id, &mc->addr, mc); + if (ret) + goto err2; + + resp.id = mc->id; + if (copy_to_user((void __user *)(unsigned long)cmd.response, + &resp, sizeof(resp))) { + ret = -EFAULT; + goto err3; + } + + mutex_unlock(&file->mut); + ucma_put_ctx(ctx); + return 0; + +err3: + rdma_leave_multicast(ctx->cm_id, &mc->addr); + ucma_cleanup_mc_events(mc); +err2: + mutex_lock(&mut); + idr_remove(&multicast_idr, mc->id); + mutex_unlock(&mut); + list_del(&mc->list); + kfree(mc); +err1: + mutex_unlock(&file->mut); + ucma_put_ctx(ctx); + return ret; +} + +static ssize_t ucma_leave_multicast(struct ucma_file *file, + const char __user *inbuf, + int in_len, int out_len) +{ + struct rdma_ucm_destroy_id cmd; + struct rdma_ucm_destroy_id_resp resp; + struct ucma_multicast *mc; + int ret = 0; + + if (out_len < sizeof(resp)) + return -ENOSPC; + + if (copy_from_user(&cmd, inbuf, sizeof(cmd))) + return -EFAULT; + + mutex_lock(&mut); + mc = idr_find(&multicast_idr, cmd.id); + if (!mc) + mc = ERR_PTR(-ENOENT); + else if (mc->ctx->file != file) + mc = ERR_PTR(-EINVAL); + else { + idr_remove(&multicast_idr, mc->id); + atomic_inc(&mc->ctx->ref); + } + mutex_unlock(&mut); + + if (IS_ERR(mc)) { + ret = PTR_ERR(mc); + goto out; + } + + rdma_leave_multicast(mc->ctx->cm_id, &mc->addr); + mutex_lock(&mc->ctx->file->mut); + ucma_cleanup_mc_events(mc); + list_del(&mc->list); + mutex_unlock(&mc->ctx->file->mut); + + ucma_put_ctx(mc->ctx); + resp.events_reported = mc->events_reported; + kfree(mc); + + if (copy_to_user((void __user *)(unsigned long)cmd.response, + &resp, sizeof(resp))) + ret = -EFAULT; +out: + return ret; +} + static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, const char __user *inbuf, int in_len, int out_len) = { @@ -750,6 +938,8 @@ static ssize_t (*ucma_cmd_table[])(struct ucma_file *file, [RDMA_USER_CM_CMD_GET_OPTION] = NULL, [RDMA_USER_CM_CMD_SET_OPTION] = NULL, [RDMA_USER_CM_CMD_NOTIFY] = ucma_notify, + [RDMA_USER_CM_CMD_JOIN_MCAST] = ucma_join_multicast, + [RDMA_USER_CM_CMD_LEAVE_MCAST] = ucma_leave_multicast, }; static ssize_t ucma_write(struct file *filp, const char __user *buf, diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c index c069ebeb..8199b83 100644 --- a/drivers/infiniband/core/user_mad.c +++ b/drivers/infiniband/core/user_mad.c @@ -135,7 +135,7 @@ static const dev_t base_dev = MKDEV(IB_UMAD_MAJOR, IB_UMAD_MINOR_BASE); static DEFINE_SPINLOCK(port_lock); static struct ib_umad_port *umad_port[IB_UMAD_MAX_PORTS]; -static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS * 2); +static DECLARE_BITMAP(dev_map, IB_UMAD_MAX_PORTS); static void ib_umad_add_one(struct ib_device *device); static void ib_umad_remove_one(struct ib_device *device); @@ -231,12 +231,17 @@ static void recv_handler(struct ib_mad_agent *agent, packet->mad.hdr.path_bits = mad_recv_wc->wc->dlid_path_bits; packet->mad.hdr.grh_present = !!(mad_recv_wc->wc->wc_flags & IB_WC_GRH); if (packet->mad.hdr.grh_present) { - /* XXX parse GRH */ - packet->mad.hdr.gid_index = 0; - packet->mad.hdr.hop_limit = 0; - packet->mad.hdr.traffic_class = 0; - memset(packet->mad.hdr.gid, 0, 16); - packet->mad.hdr.flow_label = 0; + struct ib_ah_attr ah_attr; + + ib_init_ah_from_wc(agent->device, agent->port_num, + mad_recv_wc->wc, mad_recv_wc->recv_buf.grh, + &ah_attr); + + packet->mad.hdr.gid_index = ah_attr.grh.sgid_index; + packet->mad.hdr.hop_limit = ah_attr.grh.hop_limit; + packet->mad.hdr.traffic_class = ah_attr.grh.traffic_class; + memcpy(packet->mad.hdr.gid, &ah_attr.grh.dgid, 16); + packet->mad.hdr.flow_label = cpu_to_be32(ah_attr.grh.flow_label); } if (queue_packet(file, agent, packet)) @@ -473,6 +478,7 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf, if (packet->mad.hdr.grh_present) { ah_attr.ah_flags = IB_AH_GRH; memcpy(ah_attr.grh.dgid.raw, packet->mad.hdr.gid, 16); + ah_attr.grh.sgid_index = packet->mad.hdr.gid_index; ah_attr.grh.flow_label = be32_to_cpu(packet->mad.hdr.flow_label); ah_attr.grh.hop_limit = packet->mad.hdr.hop_limit; ah_attr.grh.traffic_class = packet->mad.hdr.traffic_class; diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c index df1efbc..4fd75af 100644 --- a/drivers/infiniband/core/uverbs_cmd.c +++ b/drivers/infiniband/core/uverbs_cmd.c @@ -622,8 +622,10 @@ ssize_t ib_uverbs_reg_mr(struct ib_uverbs_file *file, obj->umem.virt_base = cmd.hca_va; pd = idr_read_pd(cmd.pd_handle, file->ucontext); - if (!pd) + if (!pd) { + ret = -EINVAL; goto err_release; + } mr = pd->device->reg_user_mr(pd, &obj->umem, cmd.access_flags, &udata); if (IS_ERR(mr)) { diff --git a/drivers/infiniband/core/verbs.c b/drivers/infiniband/core/verbs.c index 8b5dd36..ccdf93d 100644 --- a/drivers/infiniband/core/verbs.c +++ b/drivers/infiniband/core/verbs.c @@ -167,7 +167,7 @@ int ib_init_ah_from_wc(struct ib_device *device, u8 port_num, struct ib_wc *wc, ah_attr->grh.sgid_index = (u8) gid_index; flow_class = be32_to_cpu(grh->version_tclass_flow); ah_attr->grh.flow_label = flow_class & 0xFFFFF; - ah_attr->grh.hop_limit = grh->hop_limit; + ah_attr->grh.hop_limit = 0xFF; ah_attr->grh.traffic_class = (flow_class >> 20) & 0xFF; } return 0; diff --git a/drivers/infiniband/hw/amso1100/c2.c b/drivers/infiniband/hw/amso1100/c2.c index 59243d9..58bc272 100644 --- a/drivers/infiniband/hw/amso1100/c2.c +++ b/drivers/infiniband/hw/amso1100/c2.c @@ -439,7 +439,8 @@ static void c2_rx_error(struct c2_port *c2_port, struct c2_element *elem) } /* Setup the skb for reuse since we're dropping this pkt */ - elem->skb->tail = elem->skb->data = elem->skb->head; + elem->skb->data = elem->skb->head; + skb_reset_tail_pointer(elem->skb); /* Zero out the rxp hdr in the sk_buff */ memset(elem->skb->data, 0, sizeof(*rxp_hdr)); @@ -521,9 +522,8 @@ static void c2_rx_interrupt(struct net_device *netdev) * "sizeof(struct c2_rxp_hdr)". */ skb->data += sizeof(*rxp_hdr); - skb->tail = skb->data + buflen; + skb_set_tail_pointer(skb, buflen); skb->len = buflen; - skb->dev = netdev; skb->protocol = eth_type_trans(skb, netdev); netif_rx(skb); diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c index fef9727..607c09b 100644 --- a/drivers/infiniband/hw/amso1100/c2_provider.c +++ b/drivers/infiniband/hw/amso1100/c2_provider.c @@ -796,7 +796,6 @@ int c2_register_device(struct c2_dev *dev) memcpy(&dev->ibdev.node_guid, dev->pseudo_netdev->dev_addr, 6); dev->ibdev.phys_port_cnt = 1; dev->ibdev.dma_device = &dev->pcidev->dev; - dev->ibdev.class_dev.dev = &dev->pcidev->dev; dev->ibdev.query_device = c2_query_device; dev->ibdev.query_port = c2_query_port; dev->ibdev.modify_port = c2_modify_port; diff --git a/drivers/infiniband/hw/cxgb3/Makefile b/drivers/infiniband/hw/cxgb3/Makefile index 0e110f3..36b9898 100644 --- a/drivers/infiniband/hw/cxgb3/Makefile +++ b/drivers/infiniband/hw/cxgb3/Makefile @@ -8,5 +8,4 @@ iw_cxgb3-y := iwch_cm.o iwch_ev.o iwch_cq.o iwch_qp.o iwch_mem.o \ ifdef CONFIG_INFINIBAND_CXGB3_DEBUG EXTRA_CFLAGS += -DDEBUG -iw_cxgb3-y += cxio_dbg.o endif diff --git a/drivers/infiniband/hw/cxgb3/cxio_dbg.c b/drivers/infiniband/hw/cxgb3/cxio_dbg.c index 5a7306f..75f7b16 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_dbg.c +++ b/drivers/infiniband/hw/cxgb3/cxio_dbg.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.c b/drivers/infiniband/hw/cxgb3/cxio_hal.c index 82fa720..f5e9aee 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.c +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -37,6 +36,7 @@ #include <linux/sched.h> #include <linux/spinlock.h> #include <linux/pci.h> +#include <linux/dma-mapping.h> #include "cxio_resource.h" #include "cxio_hal.h" @@ -46,7 +46,7 @@ static LIST_HEAD(rdev_list); static cxio_hal_ev_callback_func_t cxio_ev_cb = NULL; -static inline struct cxio_rdev *cxio_hal_find_rdev_by_name(char *dev_name) +static struct cxio_rdev *cxio_hal_find_rdev_by_name(char *dev_name) { struct cxio_rdev *rdev; @@ -56,8 +56,7 @@ static inline struct cxio_rdev *cxio_hal_find_rdev_by_name(char *dev_name) return NULL; } -static inline struct cxio_rdev *cxio_hal_find_rdev_by_t3cdev(struct t3cdev - *tdev) +static struct cxio_rdev *cxio_hal_find_rdev_by_t3cdev(struct t3cdev *tdev) { struct cxio_rdev *rdev; @@ -119,7 +118,7 @@ int cxio_hal_cq_op(struct cxio_rdev *rdev_p, struct t3_cq *cq, return 0; } -static inline int cxio_hal_clear_cq_ctx(struct cxio_rdev *rdev_p, u32 cqid) +static int cxio_hal_clear_cq_ctx(struct cxio_rdev *rdev_p, u32 cqid) { struct rdma_cq_setup setup; setup.id = cqid; @@ -131,7 +130,7 @@ static inline int cxio_hal_clear_cq_ctx(struct cxio_rdev *rdev_p, u32 cqid) return (rdev_p->t3cdev_p->ctl(rdev_p->t3cdev_p, RDMA_CQ_SETUP, &setup)); } -int cxio_hal_clear_qp_ctx(struct cxio_rdev *rdev_p, u32 qpid) +static int cxio_hal_clear_qp_ctx(struct cxio_rdev *rdev_p, u32 qpid) { u64 sge_cmd; struct t3_modify_qp_wr *wqe; @@ -426,7 +425,7 @@ void cxio_flush_hw_cq(struct t3_cq *cq) } } -static inline int cqe_completes_wr(struct t3_cqe *cqe, struct t3_wq *wq) +static int cqe_completes_wr(struct t3_cqe *cqe, struct t3_wq *wq) { if (CQE_OPCODE(*cqe) == T3_TERMINATE) return 0; @@ -499,9 +498,9 @@ static int cxio_hal_init_ctrl_qp(struct cxio_rdev *rdev_p) u64 sge_cmd, ctx0, ctx1; u64 base_addr; struct t3_modify_qp_wr *wqe; - struct sk_buff *skb = alloc_skb(sizeof(*wqe), GFP_KERNEL); - + struct sk_buff *skb; + skb = alloc_skb(sizeof(*wqe), GFP_KERNEL); if (!skb) { PDBG("%s alloc_skb failed\n", __FUNCTION__); return -ENOMEM; @@ -509,7 +508,7 @@ static int cxio_hal_init_ctrl_qp(struct cxio_rdev *rdev_p) err = cxio_hal_init_ctrl_cq(rdev_p); if (err) { PDBG("%s err %d initializing ctrl_cq\n", __FUNCTION__, err); - return err; + goto err; } rdev_p->ctrl_qp.workq = dma_alloc_coherent( &(rdev_p->rnic_info.pdev->dev), @@ -519,7 +518,8 @@ static int cxio_hal_init_ctrl_qp(struct cxio_rdev *rdev_p) GFP_KERNEL); if (!rdev_p->ctrl_qp.workq) { PDBG("%s dma_alloc_coherent failed\n", __FUNCTION__); - return -ENOMEM; + err = -ENOMEM; + goto err; } pci_unmap_addr_set(&rdev_p->ctrl_qp, mapping, rdev_p->ctrl_qp.dma_addr); @@ -557,6 +557,9 @@ static int cxio_hal_init_ctrl_qp(struct cxio_rdev *rdev_p) rdev_p->ctrl_qp.workq, 1 << T3_CTRL_QP_SIZE_LOG2); skb->priority = CPL_PRIORITY_CONTROL; return (cxgb3_ofld_send(rdev_p->t3cdev_p, skb)); +err: + kfree_skb(skb); + return err; } static int cxio_hal_destroy_ctrl_qp(struct cxio_rdev *rdev_p) @@ -761,17 +764,6 @@ ret: return err; } -/* IN : stag key, pdid, pbl_size - * Out: stag index, actaul pbl_size, and pbl_addr allocated. - */ -int cxio_allocate_stag(struct cxio_rdev *rdev_p, u32 * stag, u32 pdid, - enum tpt_mem_perm perm, u32 * pbl_size, u32 * pbl_addr) -{ - *stag = T3_STAG_UNSET; - return (__cxio_tpt_op(rdev_p, 0, stag, 0, pdid, TPT_NON_SHARED_MR, - perm, 0, 0ULL, 0, 0, NULL, pbl_size, pbl_addr)); -} - int cxio_register_phys_mem(struct cxio_rdev *rdev_p, u32 *stag, u32 pdid, enum tpt_mem_perm perm, u32 zbva, u64 to, u32 len, u8 page_size, __be64 *pbl, u32 *pbl_size, @@ -1030,7 +1022,7 @@ void __exit cxio_hal_exit(void) cxio_hal_destroy_rhdl_resource(); } -static inline void flush_completed_wrs(struct t3_wq *wq, struct t3_cq *cq) +static void flush_completed_wrs(struct t3_wq *wq, struct t3_cq *cq) { struct t3_swsq *sqp; __u32 ptr = wq->sq_rptr; @@ -1059,9 +1051,8 @@ static inline void flush_completed_wrs(struct t3_wq *wq, struct t3_cq *cq) break; } -static inline void create_read_req_cqe(struct t3_wq *wq, - struct t3_cqe *hw_cqe, - struct t3_cqe *read_cqe) +static void create_read_req_cqe(struct t3_wq *wq, struct t3_cqe *hw_cqe, + struct t3_cqe *read_cqe) { read_cqe->u.scqe.wrid_hi = wq->oldest_read->sq_wptr; read_cqe->len = wq->oldest_read->read_len; @@ -1074,7 +1065,7 @@ static inline void create_read_req_cqe(struct t3_wq *wq, /* * Return a ptr to the next read wr in the SWSQ or NULL. */ -static inline void advance_oldest_read(struct t3_wq *wq) +static void advance_oldest_read(struct t3_wq *wq) { u32 rptr = wq->oldest_read - wq->sq + 1; diff --git a/drivers/infiniband/hw/cxgb3/cxio_hal.h b/drivers/infiniband/hw/cxgb3/cxio_hal.h index 1b97e80..99543d6 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_hal.h +++ b/drivers/infiniband/hw/cxgb3/cxio_hal.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -144,7 +143,6 @@ int cxio_rdev_open(struct cxio_rdev *rdev); void cxio_rdev_close(struct cxio_rdev *rdev); int cxio_hal_cq_op(struct cxio_rdev *rdev, struct t3_cq *cq, enum t3_cq_opcode op, u32 credit); -int cxio_hal_clear_qp_ctx(struct cxio_rdev *rdev, u32 qpid); int cxio_create_cq(struct cxio_rdev *rdev, struct t3_cq *cq); int cxio_destroy_cq(struct cxio_rdev *rdev, struct t3_cq *cq); int cxio_resize_cq(struct cxio_rdev *rdev, struct t3_cq *cq); @@ -155,8 +153,6 @@ int cxio_create_qp(struct cxio_rdev *rdev, u32 kernel_domain, struct t3_wq *wq, int cxio_destroy_qp(struct cxio_rdev *rdev, struct t3_wq *wq, struct cxio_ucontext *uctx); int cxio_peek_cq(struct t3_wq *wr, struct t3_cq *cq, int opcode); -int cxio_allocate_stag(struct cxio_rdev *rdev, u32 * stag, u32 pdid, - enum tpt_mem_perm perm, u32 * pbl_size, u32 * pbl_addr); int cxio_register_phys_mem(struct cxio_rdev *rdev, u32 * stag, u32 pdid, enum tpt_mem_perm perm, u32 zbva, u64 to, u32 len, u8 page_size, __be64 *pbl, u32 *pbl_size, @@ -172,8 +168,6 @@ int cxio_deallocate_window(struct cxio_rdev *rdev, u32 stag); int cxio_rdma_init(struct cxio_rdev *rdev, struct t3_rdma_init_attr *attr); void cxio_register_ev_cb(cxio_hal_ev_callback_func_t ev_cb); void cxio_unregister_ev_cb(cxio_hal_ev_callback_func_t ev_cb); -u32 cxio_hal_get_rhdl(void); -void cxio_hal_put_rhdl(u32 rhdl); u32 cxio_hal_get_pdid(struct cxio_hal_resource *rscp); void cxio_hal_put_pdid(struct cxio_hal_resource *rscp, u32 pdid); int __init cxio_hal_init(void); diff --git a/drivers/infiniband/hw/cxgb3/cxio_resource.c b/drivers/infiniband/hw/cxgb3/cxio_resource.c index 997aa32..d3095ae 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_resource.c +++ b/drivers/infiniband/hw/cxgb3/cxio_resource.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -180,7 +179,7 @@ tpt_err: /* * returns 0 if no resource available */ -static inline u32 cxio_hal_get_resource(struct kfifo *fifo) +static u32 cxio_hal_get_resource(struct kfifo *fifo) { u32 entry; if (kfifo_get(fifo, (unsigned char *) &entry, sizeof(u32))) @@ -189,21 +188,11 @@ static inline u32 cxio_hal_get_resource(struct kfifo *fifo) return 0; /* fifo emptry */ } -static inline void cxio_hal_put_resource(struct kfifo *fifo, u32 entry) +static void cxio_hal_put_resource(struct kfifo *fifo, u32 entry) { BUG_ON(kfifo_put(fifo, (unsigned char *) &entry, sizeof(u32)) == 0); } -u32 cxio_hal_get_rhdl(void) -{ - return cxio_hal_get_resource(rhdl_fifo); -} - -void cxio_hal_put_rhdl(u32 rhdl) -{ - cxio_hal_put_resource(rhdl_fifo, rhdl); -} - u32 cxio_hal_get_stag(struct cxio_hal_resource *rscp) { return cxio_hal_get_resource(rscp->tpt_fifo); diff --git a/drivers/infiniband/hw/cxgb3/cxio_resource.h b/drivers/infiniband/hw/cxgb3/cxio_resource.h index a6bbe83..a2703a3 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_resource.h +++ b/drivers/infiniband/hw/cxgb3/cxio_resource.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/cxio_wr.h b/drivers/infiniband/hw/cxgb3/cxio_wr.h index 103fc42..90d7b89 100644 --- a/drivers/infiniband/hw/cxgb3/cxio_wr.h +++ b/drivers/infiniband/hw/cxgb3/cxio_wr.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch.c b/drivers/infiniband/hw/cxgb3/iwch.c index 4611afa..0315c9d 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.c +++ b/drivers/infiniband/hw/cxgb3/iwch.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch.h b/drivers/infiniband/hw/cxgb3/iwch.h index 6517ef8..caf4e60 100644 --- a/drivers/infiniband/hw/cxgb3/iwch.h +++ b/drivers/infiniband/hw/cxgb3/iwch.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index a522b1b..3b4b0ac 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -210,8 +209,7 @@ static enum iwch_ep_state state_read(struct iwch_ep_common *epc) return state; } -static inline void __state_set(struct iwch_ep_common *epc, - enum iwch_ep_state new) +static void __state_set(struct iwch_ep_common *epc, enum iwch_ep_state new) { epc->state = new; } @@ -307,8 +305,7 @@ static int status2errno(int status) */ static struct sk_buff *get_skb(struct sk_buff *skb, int len, gfp_t gfp) { - if (skb) { - BUG_ON(skb_cloned(skb)); + if (skb && !skb_is_nonlinear(skb) && !skb_cloned(skb)) { skb_trim(skb, 0); skb_get(skb); } else { @@ -480,7 +477,7 @@ static void send_mpa_req(struct iwch_ep *ep, struct sk_buff *skb) BUG_ON(skb_cloned(skb)); mpalen = sizeof(*mpa) + ep->plen; - if (skb->data + mpalen + sizeof(*req) > skb->end) { + if (skb->data + mpalen + sizeof(*req) > skb_end_pointer(skb)) { kfree_skb(skb); skb=alloc_skb(mpalen + sizeof(*req), GFP_KERNEL); if (!skb) { @@ -510,7 +507,7 @@ static void send_mpa_req(struct iwch_ep *ep, struct sk_buff *skb) */ skb_get(skb); set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); len = skb->len; req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); @@ -562,7 +559,7 @@ static int send_mpa_reject(struct iwch_ep *ep, const void *pdata, u8 plen) skb_get(skb); skb->priority = CPL_PRIORITY_DATA; set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); req->wr_lo = htonl(V_WR_TID(ep->hwtid)); @@ -613,7 +610,7 @@ static int send_mpa_reply(struct iwch_ep *ep, const void *pdata, u8 plen) */ skb_get(skb); set_arp_failure_handler(skb, arp_failure_discard); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); len = skb->len; req = (struct tx_data_wr *) skb_push(skb, sizeof(*req)); req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_TX_DATA)); @@ -824,7 +821,8 @@ static void process_mpa_reply(struct iwch_ep *ep, struct sk_buff *skb) /* * copy the new data into our accumulation buffer. */ - memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len); + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); ep->mpa_pkt_len += skb->len; /* @@ -943,7 +941,8 @@ static void process_mpa_request(struct iwch_ep *ep, struct sk_buff *skb) /* * Copy the new data into our accumulation buffer. */ - memcpy(&(ep->mpa_pkt[ep->mpa_pkt_len]), skb->data, skb->len); + skb_copy_from_linear_data(skb, &(ep->mpa_pkt[ep->mpa_pkt_len]), + skb->len); ep->mpa_pkt_len += skb->len; /* @@ -1417,6 +1416,7 @@ static int peer_close(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) wake_up(&ep->com.waitq); break; case FPDU_MODE: + start_ep_timer(ep); __state_set(&ep->com, CLOSING); attrs.next_state = IWCH_QP_STATE_CLOSING; iwch_modify_qp(ep->com.qp->rhp, ep->com.qp, @@ -1427,7 +1427,6 @@ static int peer_close(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) disconnect = 0; break; case CLOSING: - start_ep_timer(ep); __state_set(&ep->com, MORIBUND); disconnect = 0; break; @@ -1460,7 +1459,7 @@ static int peer_close(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) /* * Returns whether an ABORT_REQ_RSS message is a negative advice. */ -static inline int is_neg_adv_abort(unsigned int status) +static int is_neg_adv_abort(unsigned int status) { return status == CPL_ERR_RTX_NEG_ADVICE || status == CPL_ERR_PERSIST_NEG_ADVICE; @@ -1489,8 +1488,10 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) case CONNECTING: break; case MPA_REQ_WAIT: + stop_ep_timer(ep); break; case MPA_REQ_SENT: + stop_ep_timer(ep); connect_reply_upcall(ep, -ECONNRESET); break; case MPA_REP_SENT: @@ -1509,9 +1510,10 @@ static int peer_abort(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) get_ep(&ep->com); break; case MORIBUND: + case CLOSING: stop_ep_timer(ep); + /*FALLTHROUGH*/ case FPDU_MODE: - case CLOSING: if (ep->com.cm_id && ep->com.qp) { attrs.next_state = IWCH_QP_STATE_ERROR; ret = iwch_modify_qp(ep->com.qp->rhp, @@ -1572,7 +1574,6 @@ static int close_con_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) spin_lock_irqsave(&ep->com.lock, flags); switch (ep->com.state) { case CLOSING: - start_ep_timer(ep); __state_set(&ep->com, MORIBUND); break; case MORIBUND: @@ -1588,6 +1589,8 @@ static int close_con_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) __state_set(&ep->com, DEAD); release = 1; break; + case ABORTING: + break; case DEAD: default: BUG_ON(1); @@ -1618,7 +1621,8 @@ static int terminate(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) PDBG("%s ep %p\n", __FUNCTION__, ep); skb_pull(skb, sizeof(struct cpl_rdma_terminate)); PDBG("%s saving %d bytes of term msg\n", __FUNCTION__, skb->len); - memcpy(ep->com.qp->attr.terminate_buffer, skb->data, skb->len); + skb_copy_from_linear_data(skb, ep->com.qp->attr.terminate_buffer, + skb->len); ep->com.qp->attr.terminate_msg_len = skb->len; ep->com.qp->attr.is_terminate_local = 0; return CPL_RET_BUF_DONE; @@ -1636,6 +1640,7 @@ static int ec_status(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) printk(KERN_ERR MOD "%s BAD CLOSE - Aborting tid %u\n", __FUNCTION__, ep->hwtid); + stop_ep_timer(ep); attrs.next_state = IWCH_QP_STATE_ERROR; iwch_modify_qp(ep->com.qp->rhp, ep->com.qp, IWCH_QP_ATTR_NEXT_STATE, @@ -1660,6 +1665,7 @@ static void ep_timeout(unsigned long arg) break; case MPA_REQ_WAIT: break; + case CLOSING: case MORIBUND: if (ep->com.cm_id && ep->com.qp) { attrs.next_state = IWCH_QP_STATE_ERROR; @@ -1688,12 +1694,11 @@ int iwch_reject_cr(struct iw_cm_id *cm_id, const void *pdata, u8 pdata_len) return -ECONNRESET; } BUG_ON(state_read(&ep->com) != MPA_REQ_RCVD); - state_set(&ep->com, CLOSING); if (mpa_rev == 0) abort_connection(ep, NULL, GFP_KERNEL); else { err = send_mpa_reject(ep, pdata, pdata_len); - err = send_halfclose(ep, GFP_KERNEL); + err = iwch_ep_disconnect(ep, 0, GFP_KERNEL); } return 0; } @@ -1958,11 +1963,11 @@ int iwch_ep_disconnect(struct iwch_ep *ep, int abrupt, gfp_t gfp) case MPA_REQ_RCVD: case MPA_REP_SENT: case FPDU_MODE: + start_ep_timer(ep); ep->com.state = CLOSING; close = 1; break; case CLOSING: - start_ep_timer(ep); ep->com.state = MORIBUND; close = 1; break; @@ -2024,6 +2029,17 @@ static int sched(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) return 0; } +static int set_tcb_rpl(struct t3cdev *tdev, struct sk_buff *skb, void *ctx) +{ + struct cpl_set_tcb_rpl *rpl = cplhdr(skb); + + if (rpl->status != CPL_ERR_NONE) { + printk(KERN_ERR MOD "Unexpected SET_TCB_RPL status %u " + "for tid %u\n", rpl->status, GET_TID(rpl)); + } + return CPL_RET_BUF_DONE; +} + int __init iwch_cm_init(void) { skb_queue_head_init(&rxq); @@ -2051,6 +2067,7 @@ int __init iwch_cm_init(void) t3c_handlers[CPL_ABORT_REQ_RSS] = sched; t3c_handlers[CPL_RDMA_TERMINATE] = sched; t3c_handlers[CPL_RDMA_EC_STATUS] = sched; + t3c_handlers[CPL_SET_TCB_RPL] = set_tcb_rpl; /* * These are the real handlers that are called from a diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.h b/drivers/infiniband/hw/cxgb3/iwch_cm.h index 7c810d9..0c6f281 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.h +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch_cq.c b/drivers/infiniband/hw/cxgb3/iwch_cq.c index 98b3bdb..d7624c1 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cq.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cq.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch_ev.c b/drivers/infiniband/hw/cxgb3/iwch_ev.c index a6efa8fe..b406766 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_ev.c +++ b/drivers/infiniband/hw/cxgb3/iwch_ev.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -48,12 +47,6 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp, struct iwch_qp_attributes attrs; struct iwch_qp *qhp; - printk(KERN_ERR "%s - AE qpid 0x%x opcode %d status 0x%x " - "type %d wrid.hi 0x%x wrid.lo 0x%x \n", __FUNCTION__, - CQE_QPID(rsp_msg->cqe), CQE_OPCODE(rsp_msg->cqe), - CQE_STATUS(rsp_msg->cqe), CQE_TYPE(rsp_msg->cqe), - CQE_WRID_HI(rsp_msg->cqe), CQE_WRID_LOW(rsp_msg->cqe)); - spin_lock(&rnicp->lock); qhp = get_qhp(rnicp, CQE_QPID(rsp_msg->cqe)); @@ -74,6 +67,12 @@ static void post_qp_event(struct iwch_dev *rnicp, struct iwch_cq *chp, return; } + printk(KERN_ERR "%s - AE qpid 0x%x opcode %d status 0x%x " + "type %d wrid.hi 0x%x wrid.lo 0x%x \n", __FUNCTION__, + CQE_QPID(rsp_msg->cqe), CQE_OPCODE(rsp_msg->cqe), + CQE_STATUS(rsp_msg->cqe), CQE_TYPE(rsp_msg->cqe), + CQE_WRID_HI(rsp_msg->cqe), CQE_WRID_LOW(rsp_msg->cqe)); + atomic_inc(&qhp->refcnt); spin_unlock(&rnicp->lock); diff --git a/drivers/infiniband/hw/cxgb3/iwch_mem.c b/drivers/infiniband/hw/cxgb3/iwch_mem.c index 2b6cd53..a6c2c4b 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_mem.c +++ b/drivers/infiniband/hw/cxgb3/iwch_mem.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index 6861087..af28a31 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -332,6 +331,7 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) int ret = 0; struct iwch_mm_entry *mm; struct iwch_ucontext *ucontext; + u64 addr; PDBG("%s pgoff 0x%lx key 0x%x len %d\n", __FUNCTION__, vma->vm_pgoff, key, len); @@ -346,10 +346,11 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) mm = remove_mmap(ucontext, key, len); if (!mm) return -EINVAL; + addr = mm->addr; kfree(mm); - if ((mm->addr >= rdev_p->rnic_info.udbell_physbase) && - (mm->addr < (rdev_p->rnic_info.udbell_physbase + + if ((addr >= rdev_p->rnic_info.udbell_physbase) && + (addr < (rdev_p->rnic_info.udbell_physbase + rdev_p->rnic_info.udbell_len))) { /* @@ -363,7 +364,7 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) vma->vm_flags |= VM_DONTCOPY | VM_DONTEXPAND; vma->vm_flags &= ~VM_MAYREAD; ret = io_remap_pfn_range(vma, vma->vm_start, - mm->addr >> PAGE_SHIFT, + addr >> PAGE_SHIFT, len, vma->vm_page_prot); } else { @@ -371,7 +372,7 @@ static int iwch_mmap(struct ib_ucontext *context, struct vm_area_struct *vma) * Map WQ or CQ contig dma memory... */ ret = remap_pfn_range(vma, vma->vm_start, - mm->addr >> PAGE_SHIFT, + addr >> PAGE_SHIFT, len, vma->vm_page_prot); } @@ -464,9 +465,6 @@ static struct ib_mr *iwch_register_phys_mem(struct ib_pd *pd, php = to_iwch_pd(pd); rhp = php->rhp; - acc = iwch_convert_access(acc); - - mhp = kzalloc(sizeof(*mhp), GFP_KERNEL); if (!mhp) return ERR_PTR(-ENOMEM); @@ -492,12 +490,7 @@ static struct ib_mr *iwch_register_phys_mem(struct ib_pd *pd, mhp->attr.pdid = php->pdid; mhp->attr.zbva = 0; - /* NOTE: TPT perms are backwards from BIND WR perms! */ - mhp->attr.perms = (acc & 0x1) << 3; - mhp->attr.perms |= (acc & 0x2) << 1; - mhp->attr.perms |= (acc & 0x4) >> 1; - mhp->attr.perms |= (acc & 0x8) >> 3; - + mhp->attr.perms = iwch_ib_to_tpt_access(acc); mhp->attr.va_fbo = *iova_start; mhp->attr.page_size = shift - 12; @@ -526,7 +519,6 @@ static int iwch_reregister_phys_mem(struct ib_mr *mr, struct iwch_mr mh, *mhp; struct iwch_pd *php; struct iwch_dev *rhp; - int new_acc; __be64 *page_list = NULL; int shift = 0; u64 total_size; @@ -547,19 +539,20 @@ static int iwch_reregister_phys_mem(struct ib_mr *mr, if (rhp != php->rhp) return -EINVAL; - new_acc = mhp->attr.perms; - memcpy(&mh, mhp, sizeof *mhp); if (mr_rereg_mask & IB_MR_REREG_PD) php = to_iwch_pd(pd); if (mr_rereg_mask & IB_MR_REREG_ACCESS) - mh.attr.perms = iwch_convert_access(acc); - if (mr_rereg_mask & IB_MR_REREG_TRANS) + mh.attr.perms = iwch_ib_to_tpt_access(acc); + if (mr_rereg_mask & IB_MR_REREG_TRANS) { ret = build_phys_page_list(buffer_list, num_phys_buf, iova_start, &total_size, &npages, &shift, &page_list); + if (ret) + return ret; + } ret = iwch_reregister_mem(rhp, php, &mh, shift, page_list, npages); kfree(page_list); @@ -569,7 +562,7 @@ static int iwch_reregister_phys_mem(struct ib_mr *mr, if (mr_rereg_mask & IB_MR_REREG_PD) mhp->attr.pdid = php->pdid; if (mr_rereg_mask & IB_MR_REREG_ACCESS) - mhp->attr.perms = acc; + mhp->attr.perms = iwch_ib_to_tpt_access(acc); if (mr_rereg_mask & IB_MR_REREG_TRANS) { mhp->attr.zbva = 0; mhp->attr.va_fbo = *iova_start; @@ -614,8 +607,6 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, goto err; } - acc = iwch_convert_access(acc); - i = n = 0; list_for_each_entry(chunk, ®ion->chunk_list, list) @@ -631,10 +622,7 @@ static struct ib_mr *iwch_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, mhp->rhp = rhp; mhp->attr.pdid = php->pdid; mhp->attr.zbva = 0; - mhp->attr.perms = (acc & 0x1) << 3; - mhp->attr.perms |= (acc & 0x2) << 1; - mhp->attr.perms |= (acc & 0x4) >> 1; - mhp->attr.perms |= (acc & 0x8) >> 3; + mhp->attr.perms = iwch_ib_to_tpt_access(acc); mhp->attr.va_fbo = region->virt_base; mhp->attr.page_size = shift - 12; mhp->attr.len = (u32) region->length; @@ -737,10 +725,8 @@ static int iwch_destroy_qp(struct ib_qp *ib_qp) qhp = to_iwch_qp(ib_qp); rhp = qhp->rhp; - if (qhp->attr.state == IWCH_QP_STATE_RTS) { - attrs.next_state = IWCH_QP_STATE_ERROR; - iwch_modify_qp(rhp, qhp, IWCH_QP_ATTR_NEXT_STATE, &attrs, 0); - } + attrs.next_state = IWCH_QP_STATE_ERROR; + iwch_modify_qp(rhp, qhp, IWCH_QP_ATTR_NEXT_STATE, &attrs, 0); wait_event(qhp->wait, !qhp->ep); remove_handle(rhp, &rhp->qpidr, qhp->wq.qpid); @@ -949,7 +935,7 @@ void iwch_qp_rem_ref(struct ib_qp *qp) wake_up(&(to_iwch_qp(qp)->wait)); } -struct ib_qp *iwch_get_qp(struct ib_device *dev, int qpn) +static struct ib_qp *iwch_get_qp(struct ib_device *dev, int qpn) { PDBG("%s ib_dev %p qpn 0x%x\n", __FUNCTION__, dev, qpn); return (struct ib_qp *)get_qhp(to_iwch_dev(dev), qpn); @@ -1122,7 +1108,6 @@ int iwch_register_device(struct iwch_dev *dev) memcpy(dev->ibdev.node_desc, IWCH_NODE_DESC, sizeof(IWCH_NODE_DESC)); dev->ibdev.phys_port_cnt = dev->rdev.port_info.nports; dev->ibdev.dma_device = &(dev->rdev.rnic_info.pdev->dev); - dev->ibdev.class_dev.dev = &(dev->rdev.rnic_info.pdev->dev); dev->ibdev.query_device = iwch_query_device; dev->ibdev.query_port = iwch_query_port; dev->ibdev.modify_port = iwch_modify_port; diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.h b/drivers/infiniband/hw/cxgb3/iwch_provider.h index 61e3278..93bcc56 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.h +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -179,7 +178,6 @@ static inline struct iwch_qp *to_iwch_qp(struct ib_qp *ibqp) void iwch_qp_add_ref(struct ib_qp *qp); void iwch_qp_rem_ref(struct ib_qp *qp); -struct ib_qp *iwch_get_qp(struct ib_device *dev, int qpn); struct iwch_ucontext { struct ib_ucontext ibucontext; @@ -288,27 +286,20 @@ static inline int iwch_convert_state(enum ib_qp_state ib_state) } } -enum iwch_mem_perms { - IWCH_MEM_ACCESS_LOCAL_READ = 1 << 0, - IWCH_MEM_ACCESS_LOCAL_WRITE = 1 << 1, - IWCH_MEM_ACCESS_REMOTE_READ = 1 << 2, - IWCH_MEM_ACCESS_REMOTE_WRITE = 1 << 3, - IWCH_MEM_ACCESS_ATOMICS = 1 << 4, - IWCH_MEM_ACCESS_BINDING = 1 << 5, - IWCH_MEM_ACCESS_LOCAL = - (IWCH_MEM_ACCESS_LOCAL_READ | IWCH_MEM_ACCESS_LOCAL_WRITE), - IWCH_MEM_ACCESS_REMOTE = - (IWCH_MEM_ACCESS_REMOTE_WRITE | IWCH_MEM_ACCESS_REMOTE_READ) - /* cannot go beyond 1 << 31 */ -} __attribute__ ((packed)); - -static inline u32 iwch_convert_access(int acc) +static inline u32 iwch_ib_to_tpt_access(int acc) { - return (acc & IB_ACCESS_REMOTE_WRITE ? IWCH_MEM_ACCESS_REMOTE_WRITE : 0) - | (acc & IB_ACCESS_REMOTE_READ ? IWCH_MEM_ACCESS_REMOTE_READ : 0) | - (acc & IB_ACCESS_LOCAL_WRITE ? IWCH_MEM_ACCESS_LOCAL_WRITE : 0) | - (acc & IB_ACCESS_MW_BIND ? IWCH_MEM_ACCESS_BINDING : 0) | - IWCH_MEM_ACCESS_LOCAL_READ; + return (acc & IB_ACCESS_REMOTE_WRITE ? TPT_REMOTE_WRITE : 0) | + (acc & IB_ACCESS_REMOTE_READ ? TPT_REMOTE_READ : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? TPT_LOCAL_WRITE : 0) | + TPT_LOCAL_READ; +} + +static inline u32 iwch_ib_to_mwbind_access(int acc) +{ + return (acc & IB_ACCESS_REMOTE_WRITE ? T3_MEM_ACCESS_REM_WRITE : 0) | + (acc & IB_ACCESS_REMOTE_READ ? T3_MEM_ACCESS_REM_READ : 0) | + (acc & IB_ACCESS_LOCAL_WRITE ? T3_MEM_ACCESS_LOCAL_WRITE : 0) | + T3_MEM_ACCESS_LOCAL_READ; } enum iwch_mmid_state { diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index e066727..0a472c9 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -37,8 +36,8 @@ #define NO_SUPPORT -1 -static inline int iwch_build_rdma_send(union t3_wr *wqe, struct ib_send_wr *wr, - u8 * flit_cnt) +static int iwch_build_rdma_send(union t3_wr *wqe, struct ib_send_wr *wr, + u8 * flit_cnt) { int i; u32 plen; @@ -97,8 +96,8 @@ static inline int iwch_build_rdma_send(union t3_wr *wqe, struct ib_send_wr *wr, return 0; } -static inline int iwch_build_rdma_write(union t3_wr *wqe, struct ib_send_wr *wr, - u8 *flit_cnt) +static int iwch_build_rdma_write(union t3_wr *wqe, struct ib_send_wr *wr, + u8 *flit_cnt) { int i; u32 plen; @@ -138,8 +137,8 @@ static inline int iwch_build_rdma_write(union t3_wr *wqe, struct ib_send_wr *wr, return 0; } -static inline int iwch_build_rdma_read(union t3_wr *wqe, struct ib_send_wr *wr, - u8 *flit_cnt) +static int iwch_build_rdma_read(union t3_wr *wqe, struct ib_send_wr *wr, + u8 *flit_cnt) { if (wr->num_sge > 1) return -EINVAL; @@ -159,9 +158,8 @@ static inline int iwch_build_rdma_read(union t3_wr *wqe, struct ib_send_wr *wr, /* * TBD: this is going to be moved to firmware. Missing pdid/qpid check for now. */ -static inline int iwch_sgl2pbl_map(struct iwch_dev *rhp, - struct ib_sge *sg_list, u32 num_sgle, - u32 * pbl_addr, u8 * page_size) +static int iwch_sgl2pbl_map(struct iwch_dev *rhp, struct ib_sge *sg_list, + u32 num_sgle, u32 * pbl_addr, u8 * page_size) { int i; struct iwch_mr *mhp; @@ -207,9 +205,8 @@ static inline int iwch_sgl2pbl_map(struct iwch_dev *rhp, return 0; } -static inline int iwch_build_rdma_recv(struct iwch_dev *rhp, - union t3_wr *wqe, - struct ib_recv_wr *wr) +static int iwch_build_rdma_recv(struct iwch_dev *rhp, union t3_wr *wqe, + struct ib_recv_wr *wr) { int i, err = 0; u32 pbl_addr[4]; @@ -442,7 +439,7 @@ int iwch_bind_mw(struct ib_qp *qp, wqe->bind.type = T3_VA_BASED_TO; /* TBD: check perms */ - wqe->bind.perms = iwch_convert_access(mw_bind->mw_access_flags); + wqe->bind.perms = iwch_ib_to_mwbind_access(mw_bind->mw_access_flags); wqe->bind.mr_stag = cpu_to_be32(mw_bind->mr->lkey); wqe->bind.mw_stag = cpu_to_be32(mw->rkey); wqe->bind.mw_len = cpu_to_be32(mw_bind->length); @@ -474,8 +471,7 @@ int iwch_bind_mw(struct ib_qp *qp, return err; } -static inline void build_term_codes(int t3err, u8 *layer_type, u8 *ecode, - int tagged) +static void build_term_codes(int t3err, u8 *layer_type, u8 *ecode, int tagged) { switch (t3err) { case TPT_ERR_STAG: @@ -673,7 +669,7 @@ static void __flush_qp(struct iwch_qp *qhp, unsigned long *flag) spin_lock_irqsave(&qhp->lock, *flag); } -static inline void flush_qp(struct iwch_qp *qhp, unsigned long *flag) +static void flush_qp(struct iwch_qp *qhp, unsigned long *flag) { if (t3b_device(qhp->rhp)) cxio_set_wq_in_error(&qhp->wq); @@ -685,7 +681,7 @@ static inline void flush_qp(struct iwch_qp *qhp, unsigned long *flag) /* * Return non zero if at least one RECV was pre-posted. */ -static inline int rqes_posted(struct iwch_qp *qhp) +static int rqes_posted(struct iwch_qp *qhp) { return fw_riwrh_opcode((struct fw_riwrh *)qhp->wq.queue) == T3_WR_RCV; } @@ -846,6 +842,8 @@ int iwch_modify_qp(struct iwch_dev *rhp, struct iwch_qp *qhp, break; case IWCH_QP_STATE_TERMINATE: qhp->attr.state = IWCH_QP_STATE_TERMINATE; + if (t3b_device(qhp->rhp)) + cxio_set_wq_in_error(&qhp->wq); if (!internal) terminate = 1; break; diff --git a/drivers/infiniband/hw/cxgb3/iwch_user.h b/drivers/infiniband/hw/cxgb3/iwch_user.h index c4e7fbe..cb7086f 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_user.h +++ b/drivers/infiniband/hw/cxgb3/iwch_user.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/infiniband/hw/ehca/Kconfig b/drivers/infiniband/hw/ehca/Kconfig index 727b10d..1a85459 100644 --- a/drivers/infiniband/hw/ehca/Kconfig +++ b/drivers/infiniband/hw/ehca/Kconfig @@ -7,11 +7,3 @@ config INFINIBAND_EHCA To compile the driver as a module, choose M here. The module will be called ib_ehca. -config INFINIBAND_EHCA_SCALING - bool "Scaling support (EXPERIMENTAL)" - depends on IBMEBUS && INFINIBAND_EHCA && HOTPLUG_CPU && EXPERIMENTAL - default y - ---help--- - eHCA scaling support schedules the CQ callbacks to different CPUs. - - To enable this feature choose Y here. diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index cf95ee4..10fb8fb 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -42,8 +42,6 @@ #ifndef __EHCA_CLASSES_H__ #define __EHCA_CLASSES_H__ -#include "ehca_classes.h" -#include "ipz_pt_fn.h" struct ehca_module; struct ehca_qp; @@ -54,15 +52,25 @@ struct ehca_mw; struct ehca_pd; struct ehca_av; -#ifdef CONFIG_PPC64 -#include "ehca_classes_pSeries.h" -#endif +#include <linux/wait.h> #include <rdma/ib_verbs.h> #include <rdma/ib_user_verbs.h> +#ifdef CONFIG_PPC64 +#include "ehca_classes_pSeries.h" +#endif +#include "ipz_pt_fn.h" +#include "ehca_qes.h" #include "ehca_irq.h" +#define EHCA_EQE_CACHE_SIZE 20 + +struct ehca_eqe_cache_entry { + struct ehca_eqe *eqe; + struct ehca_cq *cq; +}; + struct ehca_eq { u32 length; struct ipz_queue ipz_queue; @@ -74,6 +82,8 @@ struct ehca_eq { spinlock_t spinlock; struct tasklet_struct interrupt_task; u32 ist; + spinlock_t irq_spinlock; + struct ehca_eqe_cache_entry eqe_cache[EHCA_EQE_CACHE_SIZE]; }; struct ehca_sport { @@ -96,6 +106,7 @@ struct ehca_shca { struct ehca_mr *maxmr; struct ehca_pd *pd; struct h_galpas galpas; + struct mutex modify_mutex; }; struct ehca_pd { @@ -145,7 +156,9 @@ struct ehca_cq { spinlock_t cb_lock; struct hlist_head qp_hashtab[QP_HASHTAB_LEN]; struct list_head entry; - u32 nr_callbacks; + u32 nr_callbacks; /* #events assigned to cpu by scaling code */ + u32 nr_events; /* #events seen */ + wait_queue_head_t wait_completion; spinlock_t task_lock; u32 ownpid; /* mmap counter for resources mapped into user space */ @@ -269,6 +282,7 @@ extern struct idr ehca_cq_idr; extern int ehca_static_rate; extern int ehca_port_act_time; extern int ehca_use_hp_mr; +extern int ehca_scaling_code; struct ipzu_queue_resp { u32 qe_size; /* queue entry size */ diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c index 6ebfa27..e2cdc1a 100644 --- a/drivers/infiniband/hw/ehca/ehca_cq.c +++ b/drivers/infiniband/hw/ehca/ehca_cq.c @@ -146,6 +146,7 @@ struct ib_cq *ehca_create_cq(struct ib_device *device, int cqe, spin_lock_init(&my_cq->spinlock); spin_lock_init(&my_cq->cb_lock); spin_lock_init(&my_cq->task_lock); + init_waitqueue_head(&my_cq->wait_completion); my_cq->ownpid = current->tgid; cq = &my_cq->ib_cq; @@ -302,6 +303,16 @@ create_cq_exit1: return cq; } +static int get_cq_nr_events(struct ehca_cq *my_cq) +{ + int ret; + unsigned long flags; + spin_lock_irqsave(&ehca_cq_idr_lock, flags); + ret = my_cq->nr_events; + spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); + return ret; +} + int ehca_destroy_cq(struct ib_cq *cq) { u64 h_ret; @@ -329,10 +340,11 @@ int ehca_destroy_cq(struct ib_cq *cq) } spin_lock_irqsave(&ehca_cq_idr_lock, flags); - while (my_cq->nr_callbacks) { + while (my_cq->nr_events) { spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); - yield(); + wait_event(my_cq->wait_completion, !get_cq_nr_events(my_cq)); spin_lock_irqsave(&ehca_cq_idr_lock, flags); + /* recheck nr_events to assure no cqe has just arrived */ } idr_remove(&ehca_cq_idr, my_cq->token); diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c index 24ceab0..4961eb8 100644 --- a/drivers/infiniband/hw/ehca/ehca_eq.c +++ b/drivers/infiniband/hw/ehca/ehca_eq.c @@ -61,6 +61,7 @@ int ehca_create_eq(struct ehca_shca *shca, struct ib_device *ib_dev = &shca->ib_device; spin_lock_init(&eq->spinlock); + spin_lock_init(&eq->irq_spinlock); eq->is_initialized = 0; if (type != EHCA_EQ && type != EHCA_NEQ) { diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c index b7be950..32b55a4 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/infiniband/hw/ehca/ehca_hca.c @@ -147,6 +147,7 @@ int ehca_query_port(struct ib_device *ibdev, break; } + props->port_cap_flags = rblock->capability_mask; props->gid_tbl_len = rblock->gid_tbl_len; props->max_msg_sz = rblock->max_msg_sz; props->bad_pkey_cntr = rblock->bad_pkey_cntr; @@ -162,6 +163,9 @@ int ehca_query_port(struct ib_device *ibdev, props->active_width = IB_WIDTH_12X; props->active_speed = 0x1; + /* at the moment (logical) link state is always LINK_UP */ + props->phys_state = 0x5; + query_port1: ehca_free_fw_ctrlblock(rblock); @@ -233,10 +237,60 @@ query_gid1: return ret; } +const u32 allowed_port_caps = ( + IB_PORT_SM | IB_PORT_LED_INFO_SUP | IB_PORT_CM_SUP | + IB_PORT_SNMP_TUNNEL_SUP | IB_PORT_DEVICE_MGMT_SUP | + IB_PORT_VENDOR_CLASS_SUP); + int ehca_modify_port(struct ib_device *ibdev, u8 port, int port_modify_mask, struct ib_port_modify *props) { - /* Not implemented yet */ - return -EFAULT; + int ret = 0; + struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); + struct hipz_query_port *rblock; + u32 cap; + u64 hret; + + if ((props->set_port_cap_mask | props->clr_port_cap_mask) + & ~allowed_port_caps) { + ehca_err(&shca->ib_device, "Non-changeable bits set in masks " + "set=%x clr=%x allowed=%x", props->set_port_cap_mask, + props->clr_port_cap_mask, allowed_port_caps); + return -EINVAL; + } + + if (mutex_lock_interruptible(&shca->modify_mutex)) + return -ERESTARTSYS; + + rblock = ehca_alloc_fw_ctrlblock(GFP_KERNEL); + if (!rblock) { + ehca_err(&shca->ib_device, "Can't allocate rblock memory."); + ret = -ENOMEM; + goto modify_port1; + } + + if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + ehca_err(&shca->ib_device, "Can't query port properties"); + ret = -EINVAL; + goto modify_port2; + } + + cap = (rblock->capability_mask | props->set_port_cap_mask) + & ~props->clr_port_cap_mask; + + hret = hipz_h_modify_port(shca->ipz_hca_handle, port, + cap, props->init_type, port_modify_mask); + if (hret != H_SUCCESS) { + ehca_err(&shca->ib_device, "Modify port failed hret=%lx", hret); + ret = -EINVAL; + } + +modify_port2: + ehca_free_fw_ctrlblock(rblock); + +modify_port1: + mutex_unlock(&shca->modify_mutex); + + return ret; } diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index 6c4f9f9..f284be1 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -63,13 +63,11 @@ #define ERROR_DATA_LENGTH EHCA_BMASK_IBM(52,63) #define ERROR_DATA_TYPE EHCA_BMASK_IBM(0,7) -#ifdef CONFIG_INFINIBAND_EHCA_SCALING - static void queue_comp_task(struct ehca_cq *__cq); static struct ehca_comp_pool* pool; +#ifdef CONFIG_HOTPLUG_CPU static struct notifier_block comp_pool_callback_nb; - #endif static inline void comp_event_callback(struct ehca_cq *cq) @@ -206,7 +204,7 @@ static void qp_event_callback(struct ehca_shca *shca, } static void cq_event_callback(struct ehca_shca *shca, - u64 eqe) + u64 eqe) { struct ehca_cq *cq; unsigned long flags; @@ -318,7 +316,7 @@ static void parse_ec(struct ehca_shca *shca, u64 eqe) "disruptive port %x configuration change", port); ehca_info(&shca->ib_device, - "port %x is inactive.", port); + "port %x is inactive.", port); event.device = &shca->ib_device; event.event = IB_EVENT_PORT_ERR; event.element.port_num = port; @@ -326,7 +324,7 @@ static void parse_ec(struct ehca_shca *shca, u64 eqe) ib_dispatch_event(&event); ehca_info(&shca->ib_device, - "port %x is active.", port); + "port %x is active.", port); event.device = &shca->ib_device; event.event = IB_EVENT_PORT_ACTIVE; event.element.port_num = port; @@ -401,200 +399,274 @@ irqreturn_t ehca_interrupt_eq(int irq, void *dev_id) return IRQ_HANDLED; } -void ehca_tasklet_eq(unsigned long data) + +static inline void process_eqe(struct ehca_shca *shca, struct ehca_eqe *eqe) { - struct ehca_shca *shca = (struct ehca_shca*)data; - struct ehca_eqe *eqe; - int int_state; - int query_cnt = 0; + u64 eqe_value; + u32 token; + unsigned long flags; + struct ehca_cq *cq; - do { - eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq); + eqe_value = eqe->entry; + ehca_dbg(&shca->ib_device, "eqe_value=%lx", eqe_value); + if (EHCA_BMASK_GET(EQE_COMPLETION_EVENT, eqe_value)) { + ehca_dbg(&shca->ib_device, "Got completion event"); + token = EHCA_BMASK_GET(EQE_CQ_TOKEN, eqe_value); + spin_lock_irqsave(&ehca_cq_idr_lock, flags); + cq = idr_find(&ehca_cq_idr, token); + if (cq == NULL) { + spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); + ehca_err(&shca->ib_device, + "Invalid eqe for non-existing cq token=%x", + token); + return; + } + reset_eq_pending(cq); + cq->nr_events++; + spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); + if (ehca_scaling_code) + queue_comp_task(cq); + else { + comp_event_callback(cq); + spin_lock_irqsave(&ehca_cq_idr_lock, flags); + cq->nr_events--; + if (!cq->nr_events) + wake_up(&cq->wait_completion); + spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); + } + } else { + ehca_dbg(&shca->ib_device, "Got non completion event"); + parse_identifier(shca, eqe_value); + } +} - if ((shca->hw_level >= 2) && eqe) - int_state = 1; - else - int_state = 0; - - while ((int_state == 1) || eqe) { - while (eqe) { - u64 eqe_value = eqe->entry; - - ehca_dbg(&shca->ib_device, - "eqe_value=%lx", eqe_value); - - /* TODO: better structure */ - if (EHCA_BMASK_GET(EQE_COMPLETION_EVENT, - eqe_value)) { - unsigned long flags; - u32 token; - struct ehca_cq *cq; - - ehca_dbg(&shca->ib_device, - "... completion event"); - token = - EHCA_BMASK_GET(EQE_CQ_TOKEN, - eqe_value); - spin_lock_irqsave(&ehca_cq_idr_lock, - flags); - cq = idr_find(&ehca_cq_idr, token); - - if (cq == NULL) { - spin_unlock_irqrestore(&ehca_cq_idr_lock, - flags); - break; - } - - reset_eq_pending(cq); -#ifdef CONFIG_INFINIBAND_EHCA_SCALING - queue_comp_task(cq); - spin_unlock_irqrestore(&ehca_cq_idr_lock, - flags); -#else - spin_unlock_irqrestore(&ehca_cq_idr_lock, - flags); - comp_event_callback(cq); -#endif - } else { - ehca_dbg(&shca->ib_device, - "... non completion event"); - parse_identifier(shca, eqe_value); - } - eqe = - (struct ehca_eqe *)ehca_poll_eq(shca, - &shca->eq); - } +void ehca_process_eq(struct ehca_shca *shca, int is_irq) +{ + struct ehca_eq *eq = &shca->eq; + struct ehca_eqe_cache_entry *eqe_cache = eq->eqe_cache; + u64 eqe_value; + unsigned long flags; + int eqe_cnt, i; + int eq_empty = 0; + + spin_lock_irqsave(&eq->irq_spinlock, flags); + if (is_irq) { + const int max_query_cnt = 100; + int query_cnt = 0; + int int_state = 1; + do { + int_state = hipz_h_query_int_state( + shca->ipz_hca_handle, eq->ist); + query_cnt++; + iosync(); + } while (int_state && query_cnt < max_query_cnt); + if (unlikely((query_cnt == max_query_cnt))) + ehca_dbg(&shca->ib_device, "int_state=%x query_cnt=%x", + int_state, query_cnt); + } - if (shca->hw_level >= 2) { - int_state = - hipz_h_query_int_state(shca->ipz_hca_handle, - shca->eq.ist); - query_cnt++; - iosync(); - if (query_cnt >= 100) { - query_cnt = 0; - int_state = 0; - } + /* read out all eqes */ + eqe_cnt = 0; + do { + u32 token; + eqe_cache[eqe_cnt].eqe = + (struct ehca_eqe *)ehca_poll_eq(shca, eq); + if (!eqe_cache[eqe_cnt].eqe) + break; + eqe_value = eqe_cache[eqe_cnt].eqe->entry; + if (EHCA_BMASK_GET(EQE_COMPLETION_EVENT, eqe_value)) { + token = EHCA_BMASK_GET(EQE_CQ_TOKEN, eqe_value); + spin_lock(&ehca_cq_idr_lock); + eqe_cache[eqe_cnt].cq = idr_find(&ehca_cq_idr, token); + if (!eqe_cache[eqe_cnt].cq) { + spin_unlock(&ehca_cq_idr_lock); + ehca_err(&shca->ib_device, + "Invalid eqe for non-existing cq " + "token=%x", token); + continue; } - eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq); - + eqe_cache[eqe_cnt].cq->nr_events++; + spin_unlock(&ehca_cq_idr_lock); + } else + eqe_cache[eqe_cnt].cq = NULL; + eqe_cnt++; + } while (eqe_cnt < EHCA_EQE_CACHE_SIZE); + if (!eqe_cnt) { + if (is_irq) + ehca_dbg(&shca->ib_device, + "No eqe found for irq event"); + goto unlock_irq_spinlock; + } else if (!is_irq) + ehca_dbg(&shca->ib_device, "deadman found %x eqe", eqe_cnt); + if (unlikely(eqe_cnt == EHCA_EQE_CACHE_SIZE)) + ehca_dbg(&shca->ib_device, "too many eqes for one irq event"); + /* enable irq for new packets */ + for (i = 0; i < eqe_cnt; i++) { + if (eq->eqe_cache[i].cq) + reset_eq_pending(eq->eqe_cache[i].cq); + } + /* check eq */ + spin_lock(&eq->spinlock); + eq_empty = (!ipz_eqit_eq_peek_valid(&shca->eq.ipz_queue)); + spin_unlock(&eq->spinlock); + /* call completion handler for cached eqes */ + for (i = 0; i < eqe_cnt; i++) + if (eq->eqe_cache[i].cq) { + if (ehca_scaling_code) + queue_comp_task(eq->eqe_cache[i].cq); + else { + struct ehca_cq *cq = eq->eqe_cache[i].cq; + comp_event_callback(cq); + spin_lock_irqsave(&ehca_cq_idr_lock, flags); + cq->nr_events--; + if (!cq->nr_events) + wake_up(&cq->wait_completion); + spin_unlock_irqrestore(&ehca_cq_idr_lock, + flags); + } + } else { + ehca_dbg(&shca->ib_device, "Got non completion event"); + parse_identifier(shca, eq->eqe_cache[i].eqe->entry); } - } while (int_state != 0); + /* poll eq if not empty */ + if (eq_empty) + goto unlock_irq_spinlock; + do { + struct ehca_eqe *eqe; + eqe = (struct ehca_eqe *)ehca_poll_eq(shca, &shca->eq); + if (!eqe) + break; + process_eqe(shca, eqe); + } while (1); - return; +unlock_irq_spinlock: + spin_unlock_irqrestore(&eq->irq_spinlock, flags); } -#ifdef CONFIG_INFINIBAND_EHCA_SCALING +void ehca_tasklet_eq(unsigned long data) +{ + ehca_process_eq((struct ehca_shca*)data, 1); +} static inline int find_next_online_cpu(struct ehca_comp_pool* pool) { - unsigned long flags_last_cpu; + int cpu; + unsigned long flags; + WARN_ON_ONCE(!in_interrupt()); if (ehca_debug_level) ehca_dmp(&cpu_online_map, sizeof(cpumask_t), ""); - spin_lock_irqsave(&pool->last_cpu_lock, flags_last_cpu); - pool->last_cpu = next_cpu(pool->last_cpu, cpu_online_map); - if (pool->last_cpu == NR_CPUS) - pool->last_cpu = first_cpu(cpu_online_map); - spin_unlock_irqrestore(&pool->last_cpu_lock, flags_last_cpu); + spin_lock_irqsave(&pool->last_cpu_lock, flags); + cpu = next_cpu(pool->last_cpu, cpu_online_map); + if (cpu == NR_CPUS) + cpu = first_cpu(cpu_online_map); + pool->last_cpu = cpu; + spin_unlock_irqrestore(&pool->last_cpu_lock, flags); - return pool->last_cpu; + return cpu; } static void __queue_comp_task(struct ehca_cq *__cq, struct ehca_cpu_comp_task *cct) { - unsigned long flags_cct; - unsigned long flags_cq; + unsigned long flags; - spin_lock_irqsave(&cct->task_lock, flags_cct); - spin_lock_irqsave(&__cq->task_lock, flags_cq); + spin_lock_irqsave(&cct->task_lock, flags); + spin_lock(&__cq->task_lock); if (__cq->nr_callbacks == 0) { __cq->nr_callbacks++; list_add_tail(&__cq->entry, &cct->cq_list); cct->cq_jobs++; wake_up(&cct->wait_queue); - } - else + } else __cq->nr_callbacks++; - spin_unlock_irqrestore(&__cq->task_lock, flags_cq); - spin_unlock_irqrestore(&cct->task_lock, flags_cct); + spin_unlock(&__cq->task_lock); + spin_unlock_irqrestore(&cct->task_lock, flags); } static void queue_comp_task(struct ehca_cq *__cq) { - int cpu; int cpu_id; struct ehca_cpu_comp_task *cct; + int cq_jobs; + unsigned long flags; - cpu = get_cpu(); cpu_id = find_next_online_cpu(pool); - BUG_ON(!cpu_online(cpu_id)); cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); + BUG_ON(!cct); - if (cct->cq_jobs > 0) { + spin_lock_irqsave(&cct->task_lock, flags); + cq_jobs = cct->cq_jobs; + spin_unlock_irqrestore(&cct->task_lock, flags); + if (cq_jobs > 0) { cpu_id = find_next_online_cpu(pool); cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id); + BUG_ON(!cct); } __queue_comp_task(__cq, cct); - - put_cpu(); - - return; } static void run_comp_task(struct ehca_cpu_comp_task* cct) { struct ehca_cq *cq; - unsigned long flags_cct; - unsigned long flags_cq; + unsigned long flags; - spin_lock_irqsave(&cct->task_lock, flags_cct); + spin_lock_irqsave(&cct->task_lock, flags); while (!list_empty(&cct->cq_list)) { cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); - spin_unlock_irqrestore(&cct->task_lock, flags_cct); + spin_unlock_irqrestore(&cct->task_lock, flags); comp_event_callback(cq); - spin_lock_irqsave(&cct->task_lock, flags_cct); - spin_lock_irqsave(&cq->task_lock, flags_cq); + spin_lock_irqsave(&ehca_cq_idr_lock, flags); + cq->nr_events--; + if (!cq->nr_events) + wake_up(&cq->wait_completion); + spin_unlock_irqrestore(&ehca_cq_idr_lock, flags); + + spin_lock_irqsave(&cct->task_lock, flags); + spin_lock(&cq->task_lock); cq->nr_callbacks--; - if (cq->nr_callbacks == 0) { + if (!cq->nr_callbacks) { list_del_init(cct->cq_list.next); cct->cq_jobs--; } - spin_unlock_irqrestore(&cq->task_lock, flags_cq); - + spin_unlock(&cq->task_lock); } - spin_unlock_irqrestore(&cct->task_lock, flags_cct); - - return; + spin_unlock_irqrestore(&cct->task_lock, flags); } static int comp_task(void *__cct) { struct ehca_cpu_comp_task* cct = __cct; + int cql_empty; DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_INTERRUPTIBLE); while(!kthread_should_stop()) { add_wait_queue(&cct->wait_queue, &wait); - if (list_empty(&cct->cq_list)) + spin_lock_irq(&cct->task_lock); + cql_empty = list_empty(&cct->cq_list); + spin_unlock_irq(&cct->task_lock); + if (cql_empty) schedule(); else __set_current_state(TASK_RUNNING); remove_wait_queue(&cct->wait_queue, &wait); - if (!list_empty(&cct->cq_list)) + spin_lock_irq(&cct->task_lock); + cql_empty = list_empty(&cct->cq_list); + spin_unlock_irq(&cct->task_lock); + if (!cql_empty) run_comp_task(__cct); set_current_state(TASK_INTERRUPTIBLE); @@ -637,8 +709,6 @@ static void destroy_comp_task(struct ehca_comp_pool *pool, if (task) kthread_stop(task); - - return; } static void take_over_work(struct ehca_comp_pool *pool, @@ -654,17 +724,18 @@ static void take_over_work(struct ehca_comp_pool *pool, list_splice_init(&cct->cq_list, &list); while(!list_empty(&list)) { - cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); + cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); - list_del(&cq->entry); - __queue_comp_task(cq, per_cpu_ptr(pool->cpu_comp_tasks, - smp_processor_id())); + list_del(&cq->entry); + __queue_comp_task(cq, per_cpu_ptr(pool->cpu_comp_tasks, + smp_processor_id())); } spin_unlock_irqrestore(&cct->task_lock, flags_cct); } +#ifdef CONFIG_HOTPLUG_CPU static int comp_pool_callback(struct notifier_block *nfb, unsigned long action, void *hcpu) @@ -707,15 +778,16 @@ static int comp_pool_callback(struct notifier_block *nfb, return NOTIFY_OK; } - #endif int ehca_create_comp_pool(void) { -#ifdef CONFIG_INFINIBAND_EHCA_SCALING int cpu; struct task_struct *task; + if (!ehca_scaling_code) + return 0; + pool = kzalloc(sizeof(struct ehca_comp_pool), GFP_KERNEL); if (pool == NULL) return -ENOMEM; @@ -737,20 +809,27 @@ int ehca_create_comp_pool(void) } } +#ifdef CONFIG_HOTPLUG_CPU comp_pool_callback_nb.notifier_call = comp_pool_callback; comp_pool_callback_nb.priority =0; register_cpu_notifier(&comp_pool_callback_nb); #endif + printk(KERN_INFO "eHCA scaling code enabled\n"); + return 0; } void ehca_destroy_comp_pool(void) { -#ifdef CONFIG_INFINIBAND_EHCA_SCALING int i; + if (!ehca_scaling_code) + return; + +#ifdef CONFIG_HOTPLUG_CPU unregister_cpu_notifier(&comp_pool_callback_nb); +#endif for (i = 0; i < NR_CPUS; i++) { if (cpu_online(i)) @@ -758,7 +837,4 @@ void ehca_destroy_comp_pool(void) } free_percpu(pool->cpu_comp_tasks); kfree(pool); -#endif - - return; } diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/infiniband/hw/ehca/ehca_irq.h index be579cc..6ed06ee 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.h +++ b/drivers/infiniband/hw/ehca/ehca_irq.h @@ -56,6 +56,7 @@ void ehca_tasklet_neq(unsigned long data); irqreturn_t ehca_interrupt_eq(int irq, void *dev_id); void ehca_tasklet_eq(unsigned long data); +void ehca_process_eq(struct ehca_shca *shca, int is_irq); struct ehca_cpu_comp_task { wait_queue_head_t wait_queue; diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 1155bcf..4700085 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -52,7 +52,7 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>"); MODULE_DESCRIPTION("IBM eServer HCA InfiniBand Device Driver"); -MODULE_VERSION("SVNEHCA_0020"); +MODULE_VERSION("SVNEHCA_0022"); int ehca_open_aqp1 = 0; int ehca_debug_level = 0; @@ -62,6 +62,7 @@ int ehca_use_hp_mr = 0; int ehca_port_act_time = 30; int ehca_poll_all_eqs = 1; int ehca_static_rate = -1; +int ehca_scaling_code = 1; module_param_named(open_aqp1, ehca_open_aqp1, int, 0); module_param_named(debug_level, ehca_debug_level, int, 0); @@ -71,6 +72,7 @@ module_param_named(use_hp_mr, ehca_use_hp_mr, int, 0); module_param_named(port_act_time, ehca_port_act_time, int, 0); module_param_named(poll_all_eqs, ehca_poll_all_eqs, int, 0); module_param_named(static_rate, ehca_static_rate, int, 0); +module_param_named(scaling_code, ehca_scaling_code, int, 0); MODULE_PARM_DESC(open_aqp1, "AQP1 on startup (0: no (default), 1: yes)"); @@ -91,6 +93,8 @@ MODULE_PARM_DESC(poll_all_eqs, " (0: no, 1: yes (default))"); MODULE_PARM_DESC(static_rate, "set permanent static rate (default: disabled)"); +MODULE_PARM_DESC(scaling_code, + "set scaling code (0: disabled, 1: enabled/default)"); spinlock_t ehca_qp_idr_lock; spinlock_t ehca_cq_idr_lock; @@ -432,8 +436,8 @@ static int ehca_destroy_aqp1(struct ehca_sport *sport) static ssize_t ehca_show_debug_level(struct device_driver *ddp, char *buf) { - return snprintf(buf, PAGE_SIZE, "%d\n", - ehca_debug_level); + return snprintf(buf, PAGE_SIZE, "%d\n", + ehca_debug_level); } static ssize_t ehca_store_debug_level(struct device_driver *ddp, @@ -561,11 +565,11 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev, const struct of_device_id *id) { struct ehca_shca *shca; - u64 *handle; + const u64 *handle; struct ib_pd *ibpd; int ret; - handle = (u64 *)get_property(dev->ofdev.node, "ibm,hca-handle", NULL); + handle = get_property(dev->ofdev.node, "ibm,hca-handle", NULL); if (!handle) { ehca_gen_err("Cannot get eHCA handle for adapter: %s.", dev->ofdev.node->full_name); @@ -583,6 +587,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev, ehca_gen_err("Cannot allocate shca memory."); return -ENOMEM; } + mutex_init(&shca->modify_mutex); shca->ibmebus_dev = dev; shca->ipz_hca_handle.handle = *handle; @@ -778,8 +783,24 @@ void ehca_poll_eqs(unsigned long data) spin_lock(&shca_list_lock); list_for_each_entry(shca, &shca_list, shca_list) { - if (shca->eq.is_initialized) - ehca_tasklet_eq((unsigned long)(void*)shca); + if (shca->eq.is_initialized) { + /* call deadman proc only if eq ptr does not change */ + struct ehca_eq *eq = &shca->eq; + int max = 3; + volatile u64 q_ofs, q_ofs2; + u64 flags; + spin_lock_irqsave(&eq->spinlock, flags); + q_ofs = eq->ipz_queue.current_q_offset; + spin_unlock_irqrestore(&eq->spinlock, flags); + do { + spin_lock_irqsave(&eq->spinlock, flags); + q_ofs2 = eq->ipz_queue.current_q_offset; + spin_unlock_irqrestore(&eq->spinlock, flags); + max--; + } while (q_ofs == q_ofs2 && max > 0); + if (q_ofs == q_ofs2) + ehca_process_eq(shca, 0); + } } mod_timer(&poll_eqs_timer, jiffies + HZ); spin_unlock(&shca_list_lock); @@ -790,7 +811,7 @@ int __init ehca_module_init(void) int ret; printk(KERN_INFO "eHCA Infiniband Device Driver " - "(Rel.: SVNEHCA_0020)\n"); + "(Rel.: SVNEHCA_0022)\n"); idr_init(&ehca_qp_idr); idr_init(&ehca_cq_idr); spin_lock_init(&ehca_qp_idr_lock); diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c index 3fb46e6..b564fcd 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.c +++ b/drivers/infiniband/hw/ehca/hcp_if.c @@ -70,6 +70,10 @@ #define H_ALL_RES_QP_SQUEUE_SIZE_PAGES EHCA_BMASK_IBM(0, 31) #define H_ALL_RES_QP_RQUEUE_SIZE_PAGES EHCA_BMASK_IBM(32, 63) +#define H_MP_INIT_TYPE EHCA_BMASK_IBM(44, 47) +#define H_MP_SHUTDOWN EHCA_BMASK_IBM(48, 48) +#define H_MP_RESET_QKEY_CTR EHCA_BMASK_IBM(49, 49) + /* direct access qp controls */ #define DAQP_CTRL_ENABLE 0x01 #define DAQP_CTRL_SEND_COMP 0x20 @@ -364,6 +368,26 @@ u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle, return ret; } +u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle, + const u8 port_id, const u32 port_cap, + const u8 init_type, const int modify_mask) +{ + u64 port_attributes = port_cap; + + if (modify_mask & IB_PORT_SHUTDOWN) + port_attributes |= EHCA_BMASK_SET(H_MP_SHUTDOWN, 1); + if (modify_mask & IB_PORT_INIT_TYPE) + port_attributes |= EHCA_BMASK_SET(H_MP_INIT_TYPE, init_type); + if (modify_mask & IB_PORT_RESET_QKEY_CNTR) + port_attributes |= EHCA_BMASK_SET(H_MP_RESET_QKEY_CTR, 1); + + return ehca_plpar_hcall_norets(H_MODIFY_PORT, + adapter_handle.handle, /* r4 */ + port_id, /* r5 */ + port_attributes, /* r6 */ + 0, 0, 0, 0); +} + u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle, struct hipz_query_hca *query_hca_rblock) { diff --git a/drivers/infiniband/hw/ehca/hcp_if.h b/drivers/infiniband/hw/ehca/hcp_if.h index 587ebd4..2869f7d 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.h +++ b/drivers/infiniband/hw/ehca/hcp_if.h @@ -85,6 +85,10 @@ u64 hipz_h_query_port(const struct ipz_adapter_handle adapter_handle, const u8 port_id, struct hipz_query_port *query_port_response_block); +u64 hipz_h_modify_port(const struct ipz_adapter_handle adapter_handle, + const u8 port_id, const u32 port_cap, + const u8 init_type, const int modify_mask); + u64 hipz_h_query_hca(const struct ipz_adapter_handle adapter_handle, struct hipz_query_hca *query_hca_rblock); diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.h b/drivers/infiniband/hw/ehca/ipz_pt_fn.h index dc3bda2..8199c45 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.h +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.h @@ -79,7 +79,7 @@ static inline void *ipz_qeit_calc(struct ipz_queue *queue, u64 q_offset) if (q_offset >= queue->queue_length) return NULL; current_page = (queue->queue_pages)[q_offset >> EHCA_PAGESHIFT]; - return ¤t_page->entries[q_offset & (EHCA_PAGESIZE - 1)]; + return ¤t_page->entries[q_offset & (EHCA_PAGESIZE - 1)]; } /* @@ -247,6 +247,15 @@ static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue) return ret; } +static inline void *ipz_eqit_eq_peek_valid(struct ipz_queue *queue) +{ + void *ret = ipz_qeit_get(queue); + u32 qe = *(u8 *) ret; + if ((qe >> 7) != (queue->toggle_state & 1)) + return NULL; + return ret; +} + /* returns address (GX) of first queue entry */ static inline u64 ipz_qpt_get_firstpage(struct ipz_qpt *qpt) { diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h index 54139d3..10c008f 100644 --- a/drivers/infiniband/hw/ipath/ipath_common.h +++ b/drivers/infiniband/hw/ipath/ipath_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 QLogic, Inc. All rights reserved. + * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -78,6 +78,8 @@ #define IPATH_IB_LINKINIT 3 #define IPATH_IB_LINKDOWN_SLEEP 4 #define IPATH_IB_LINKDOWN_DISABLE 5 +#define IPATH_IB_LINK_LOOPBACK 6 /* enable local loopback */ +#define IPATH_IB_LINK_EXTERNAL 7 /* normal, disable local loopback */ /* * stats maintained by the driver. For now, at least, this is global @@ -316,11 +318,17 @@ struct ipath_base_info { /* address of readonly memory copy of the rcvhdrq tail register. */ __u64 spi_rcvhdr_tailaddr; - /* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */ + /* shared memory pages for subports if port is shared */ __u64 spi_subport_uregbase; __u64 spi_subport_rcvegrbuf; __u64 spi_subport_rcvhdr_base; + /* shared memory page for hardware port if it is shared */ + __u64 spi_port_uregbase; + __u64 spi_port_rcvegrbuf; + __u64 spi_port_rcvhdr_base; + __u64 spi_port_rcvhdr_tailaddr; + } __attribute__ ((aligned(8))); @@ -344,7 +352,7 @@ struct ipath_base_info { * may not be implemented; the user code must deal with this if it * cares, or it must abort after initialization reports the difference. */ -#define IPATH_USER_SWMINOR 3 +#define IPATH_USER_SWMINOR 5 #define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR) @@ -418,11 +426,14 @@ struct ipath_user_info { #define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */ #define IPATH_CMD_TID_FREE 20 /* free expected TID entries */ #define IPATH_CMD_SET_PART_KEY 21 /* add partition key */ -#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */ +#define __IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes (for old user code) */ #define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */ #define IPATH_CMD_USER_INIT 24 /* set up userspace */ +#define IPATH_CMD_UNUSED_1 25 +#define IPATH_CMD_UNUSED_2 26 +#define IPATH_CMD_PIOAVAILUPD 27 /* force an update of PIOAvail reg */ -#define IPATH_CMD_MAX 24 +#define IPATH_CMD_MAX 27 struct ipath_port_info { __u32 num_active; /* number of active units */ @@ -430,7 +441,7 @@ struct ipath_port_info { __u16 port; /* port on unit assigned to caller */ __u16 subport; /* subport on unit assigned to caller */ __u16 num_ports; /* number of ports available on unit */ - __u16 num_subports; /* number of subport slaves opened on port */ + __u16 num_subports; /* number of subports opened on port */ }; struct ipath_tid_info { diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c index 87462e0..ea78e6d 100644 --- a/drivers/infiniband/hw/ipath/ipath_cq.c +++ b/drivers/infiniband/hw/ipath/ipath_cq.c @@ -76,7 +76,20 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited) } return; } - wc->queue[head] = *entry; + wc->queue[head].wr_id = entry->wr_id; + wc->queue[head].status = entry->status; + wc->queue[head].opcode = entry->opcode; + wc->queue[head].vendor_err = entry->vendor_err; + wc->queue[head].byte_len = entry->byte_len; + wc->queue[head].imm_data = (__u32 __force)entry->imm_data; + wc->queue[head].qp_num = entry->qp->qp_num; + wc->queue[head].src_qp = entry->src_qp; + wc->queue[head].wc_flags = entry->wc_flags; + wc->queue[head].pkey_index = entry->pkey_index; + wc->queue[head].slid = entry->slid; + wc->queue[head].sl = entry->sl; + wc->queue[head].dlid_path_bits = entry->dlid_path_bits; + wc->queue[head].port_num = entry->port_num; wc->head = next; if (cq->notify == IB_CQ_NEXT_COMP || @@ -122,9 +135,30 @@ int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) if (tail > (u32) cq->ibcq.cqe) tail = (u32) cq->ibcq.cqe; for (npolled = 0; npolled < num_entries; ++npolled, ++entry) { + struct ipath_qp *qp; + if (tail == wc->head) break; - *entry = wc->queue[tail]; + + qp = ipath_lookup_qpn(&to_idev(cq->ibcq.device)->qp_table, + wc->queue[tail].qp_num); + entry->qp = &qp->ibqp; + if (atomic_dec_and_test(&qp->refcount)) + wake_up(&qp->wait); + + entry->wr_id = wc->queue[tail].wr_id; + entry->status = wc->queue[tail].status; + entry->opcode = wc->queue[tail].opcode; + entry->vendor_err = wc->queue[tail].vendor_err; + entry->byte_len = wc->queue[tail].byte_len; + entry->imm_data = wc->queue[tail].imm_data; + entry->src_qp = wc->queue[tail].src_qp; + entry->wc_flags = wc->queue[tail].wc_flags; + entry->pkey_index = wc->queue[tail].pkey_index; + entry->slid = wc->queue[tail].slid; + entry->sl = wc->queue[tail].sl; + entry->dlid_path_bits = wc->queue[tail].dlid_path_bits; + entry->port_num = wc->queue[tail].port_num; if (tail >= cq->ibcq.cqe) tail = 0; else diff --git a/drivers/infiniband/hw/ipath/ipath_debug.h b/drivers/infiniband/hw/ipath/ipath_debug.h index df69f0d8..42bfbdb 100644 --- a/drivers/infiniband/hw/ipath/ipath_debug.h +++ b/drivers/infiniband/hw/ipath/ipath_debug.h @@ -57,6 +57,7 @@ #define __IPATH_PROCDBG 0x100 /* print mmap/nopage stuff, not using VDBG any more */ #define __IPATH_MMDBG 0x200 +#define __IPATH_ERRPKTDBG 0x400 #define __IPATH_USER_SEND 0x1000 /* use user mode send */ #define __IPATH_KERNEL_SEND 0x2000 /* use kernel mode send */ #define __IPATH_EPKTDBG 0x4000 /* print ethernet packet data */ diff --git a/drivers/infiniband/hw/ipath/ipath_diag.c b/drivers/infiniband/hw/ipath/ipath_diag.c index 0f13a21..63e8368 100644 --- a/drivers/infiniband/hw/ipath/ipath_diag.c +++ b/drivers/infiniband/hw/ipath/ipath_diag.c @@ -296,7 +296,7 @@ static int ipath_diag_open(struct inode *in, struct file *fp) } fp->private_data = dd; - ipath_diag_inuse = 1; + ipath_diag_inuse = -2; diag_set_link = 0; ret = 0; @@ -461,6 +461,8 @@ static ssize_t ipath_diag_read(struct file *fp, char __user *data, else if ((count % 4) || (*off % 4)) /* address or length is not 32-bit aligned, hence invalid */ ret = -EINVAL; + else if (ipath_diag_inuse < 1 && (*off || count != 8)) + ret = -EINVAL; /* prevent cat /dev/ipath_diag* */ else if ((count % 8) || (*off % 8)) /* address or length not 64-bit aligned; do 32-bit reads */ ret = ipath_read_umem32(dd, data, kreg_base + *off, count); @@ -470,6 +472,8 @@ static ssize_t ipath_diag_read(struct file *fp, char __user *data, if (ret >= 0) { *off += count; ret = count; + if (ipath_diag_inuse == -2) + ipath_diag_inuse++; } return ret; @@ -489,6 +493,9 @@ static ssize_t ipath_diag_write(struct file *fp, const char __user *data, else if ((count % 4) || (*off % 4)) /* address or length is not 32-bit aligned, hence invalid */ ret = -EINVAL; + else if ((ipath_diag_inuse == -1 && (*off || count != 8)) || + ipath_diag_inuse == -2) /* read qw off 0, write qw off 0 */ + ret = -EINVAL; /* before any other write allowed */ else if ((count % 8) || (*off % 8)) /* address or length not 64-bit aligned; do 32-bit writes */ ret = ipath_write_umem32(dd, kreg_base + *off, data, count); @@ -498,6 +505,8 @@ static ssize_t ipath_diag_write(struct file *fp, const char __user *data, if (ret >= 0) { *off += count; ret = count; + if (ipath_diag_inuse == -1) + ipath_diag_inuse = 1; /* all read/write OK now */ } return ret; diff --git a/drivers/infiniband/hw/ipath/ipath_dma.c b/drivers/infiniband/hw/ipath/ipath_dma.c index 6e0f2b8..f87f003 100644 --- a/drivers/infiniband/hw/ipath/ipath_dma.c +++ b/drivers/infiniband/hw/ipath/ipath_dma.c @@ -96,8 +96,8 @@ static void ipath_dma_unmap_page(struct ib_device *dev, BUG_ON(!valid_dma_direction(direction)); } -int ipath_map_sg(struct ib_device *dev, struct scatterlist *sg, int nents, - enum dma_data_direction direction) +static int ipath_map_sg(struct ib_device *dev, struct scatterlist *sg, int nents, + enum dma_data_direction direction) { u64 addr; int i; @@ -167,7 +167,7 @@ static void *ipath_dma_alloc_coherent(struct ib_device *dev, size_t size, } static void ipath_dma_free_coherent(struct ib_device *dev, size_t size, - void *cpu_addr, dma_addr_t dma_handle) + void *cpu_addr, u64 dma_handle) { free_pages((unsigned long) cpu_addr, get_order(size)); } diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index ae7f21a..e3a2232 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -390,15 +390,23 @@ static int __devinit ipath_init_one(struct pci_dev *pdev, /* setup the chip-specific functions, as early as possible. */ switch (ent->device) { -#ifdef CONFIG_HT_IRQ case PCI_DEVICE_ID_INFINIPATH_HT: +#ifdef CONFIG_HT_IRQ ipath_init_iba6110_funcs(dd); break; +#else + ipath_dev_err(dd, "QLogic HT device 0x%x cannot work if " + "CONFIG_HT_IRQ is not enabled\n", ent->device); + return -ENODEV; #endif -#ifdef CONFIG_PCI_MSI case PCI_DEVICE_ID_INFINIPATH_PE800: +#ifdef CONFIG_PCI_MSI ipath_init_iba6120_funcs(dd); break; +#else + ipath_dev_err(dd, "QLogic PCIE device 0x%x cannot work if " + "CONFIG_PCI_MSI is not enabled\n", ent->device); + return -ENODEV; #endif default: ipath_dev_err(dd, "Found unknown QLogic deviceid 0x%x, " @@ -486,7 +494,7 @@ static int __devinit ipath_init_one(struct pci_dev *pdev, ret = ipath_init_chip(dd, 0); /* do the chip-specific init */ if (ret) - goto bail_iounmap; + goto bail_irqsetup; ret = ipath_enable_wc(dd); @@ -505,6 +513,9 @@ static int __devinit ipath_init_one(struct pci_dev *pdev, goto bail; +bail_irqsetup: + if (pdev->irq) free_irq(pdev->irq, dd); + bail_iounmap: iounmap((volatile void __iomem *) dd->ipath_kregbase); @@ -525,8 +536,6 @@ static void __devexit cleanup_device(struct ipath_devdata *dd) { int port; - ipath_shutdown_device(dd); - if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) { /* can't do anything more with chip; needs re-init */ *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT; @@ -594,8 +603,9 @@ static void __devexit cleanup_device(struct ipath_devdata *dd) ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n", dd->ipath_pageshadow); - vfree(dd->ipath_pageshadow); + tmpp = dd->ipath_pageshadow; dd->ipath_pageshadow = NULL; + vfree(tmpp); } /* @@ -622,6 +632,12 @@ static void __devexit ipath_remove_one(struct pci_dev *pdev) ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd); + /* + * disable the IB link early, to be sure no new packets arrive, which + * complicates the shutdown process + */ + ipath_shutdown_device(dd); + if (dd->verbs_dev) ipath_unregister_ib_device(dd->verbs_dev); @@ -754,9 +770,42 @@ static int ipath_wait_linkstate(struct ipath_devdata *dd, u32 state, return (dd->ipath_flags & state) ? 0 : -ETIMEDOUT; } -void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) +/* + * Decode the error status into strings, deciding whether to always + * print * it or not depending on "normal packet errors" vs everything + * else. Return 1 if "real" errors, otherwise 0 if only packet + * errors, so caller can decide what to print with the string. + */ +int ipath_decode_err(char *buf, size_t blen, ipath_err_t err) { + int iserr = 1; *buf = '\0'; + if (err & INFINIPATH_E_PKTERRS) { + if (!(err & ~INFINIPATH_E_PKTERRS)) + iserr = 0; // if only packet errors. + if (ipath_debug & __IPATH_ERRPKTDBG) { + if (err & INFINIPATH_E_REBP) + strlcat(buf, "EBP ", blen); + if (err & INFINIPATH_E_RVCRC) + strlcat(buf, "VCRC ", blen); + if (err & INFINIPATH_E_RICRC) { + strlcat(buf, "CRC ", blen); + // clear for check below, so only once + err &= INFINIPATH_E_RICRC; + } + if (err & INFINIPATH_E_RSHORTPKTLEN) + strlcat(buf, "rshortpktlen ", blen); + if (err & INFINIPATH_E_SDROPPEDDATAPKT) + strlcat(buf, "sdroppeddatapkt ", blen); + if (err & INFINIPATH_E_SPKTLEN) + strlcat(buf, "spktlen ", blen); + } + if ((err & INFINIPATH_E_RICRC) && + !(err&(INFINIPATH_E_RVCRC|INFINIPATH_E_REBP))) + strlcat(buf, "CRC ", blen); + if (!iserr) + goto done; + } if (err & INFINIPATH_E_RHDRLEN) strlcat(buf, "rhdrlen ", blen); if (err & INFINIPATH_E_RBADTID) @@ -767,12 +816,12 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) strlcat(buf, "rhdr ", blen); if (err & INFINIPATH_E_RLONGPKTLEN) strlcat(buf, "rlongpktlen ", blen); - if (err & INFINIPATH_E_RSHORTPKTLEN) - strlcat(buf, "rshortpktlen ", blen); if (err & INFINIPATH_E_RMAXPKTLEN) strlcat(buf, "rmaxpktlen ", blen); if (err & INFINIPATH_E_RMINPKTLEN) strlcat(buf, "rminpktlen ", blen); + if (err & INFINIPATH_E_SMINPKTLEN) + strlcat(buf, "sminpktlen ", blen); if (err & INFINIPATH_E_RFORMATERR) strlcat(buf, "rformaterr ", blen); if (err & INFINIPATH_E_RUNSUPVL) @@ -781,32 +830,20 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) strlcat(buf, "runexpchar ", blen); if (err & INFINIPATH_E_RIBFLOW) strlcat(buf, "ribflow ", blen); - if (err & INFINIPATH_E_REBP) - strlcat(buf, "EBP ", blen); if (err & INFINIPATH_E_SUNDERRUN) strlcat(buf, "sunderrun ", blen); if (err & INFINIPATH_E_SPIOARMLAUNCH) strlcat(buf, "spioarmlaunch ", blen); if (err & INFINIPATH_E_SUNEXPERRPKTNUM) strlcat(buf, "sunexperrpktnum ", blen); - if (err & INFINIPATH_E_SDROPPEDDATAPKT) - strlcat(buf, "sdroppeddatapkt ", blen); if (err & INFINIPATH_E_SDROPPEDSMPPKT) strlcat(buf, "sdroppedsmppkt ", blen); if (err & INFINIPATH_E_SMAXPKTLEN) strlcat(buf, "smaxpktlen ", blen); - if (err & INFINIPATH_E_SMINPKTLEN) - strlcat(buf, "sminpktlen ", blen); if (err & INFINIPATH_E_SUNSUPVL) strlcat(buf, "sunsupVL ", blen); - if (err & INFINIPATH_E_SPKTLEN) - strlcat(buf, "spktlen ", blen); if (err & INFINIPATH_E_INVALIDADDR) strlcat(buf, "invalidaddr ", blen); - if (err & INFINIPATH_E_RICRC) - strlcat(buf, "CRC ", blen); - if (err & INFINIPATH_E_RVCRC) - strlcat(buf, "VCRC ", blen); if (err & INFINIPATH_E_RRCVEGRFULL) strlcat(buf, "rcvegrfull ", blen); if (err & INFINIPATH_E_RRCVHDRFULL) @@ -819,6 +856,8 @@ void ipath_decode_err(char *buf, size_t blen, ipath_err_t err) strlcat(buf, "hardware ", blen); if (err & INFINIPATH_E_RESET) strlcat(buf, "reset ", blen); +done: + return iserr; } /** @@ -1662,6 +1701,22 @@ int ipath_set_linkstate(struct ipath_devdata *dd, u8 newstate) lstate = IPATH_LINKACTIVE; break; + case IPATH_IB_LINK_LOOPBACK: + dev_info(&dd->pcidev->dev, "Enabling IB local loopback\n"); + dd->ipath_ibcctrl |= INFINIPATH_IBCC_LOOPBACK; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + ret = 0; + goto bail; // no state change to wait for + + case IPATH_IB_LINK_EXTERNAL: + dev_info(&dd->pcidev->dev, "Disabling IB local loopback (normal)\n"); + dd->ipath_ibcctrl &= ~INFINIPATH_IBCC_LOOPBACK; + ipath_write_kreg(dd, dd->ipath_kregs->kr_ibcctrl, + dd->ipath_ibcctrl); + ret = 0; + goto bail; // no state change to wait for + default: ipath_dbg("Invalid linkstate 0x%x requested\n", newstate); ret = -EINVAL; @@ -1765,29 +1820,6 @@ int ipath_set_lid(struct ipath_devdata *dd, u32 arg, u8 lmc) return 0; } -/** - * ipath_read_kreg64_port - read a device's per-port 64-bit kernel register - * @dd: the infinipath device - * @regno: the register number to read - * @port: the port containing the register - * - * Registers that vary with the chip implementation constants (port) - * use this routine. - */ -u64 ipath_read_kreg64_port(const struct ipath_devdata *dd, ipath_kreg regno, - unsigned port) -{ - u16 where; - - if (port < dd->ipath_portcnt && - (regno == dd->ipath_kregs->kr_rcvhdraddr || - regno == dd->ipath_kregs->kr_rcvhdrtailaddr)) - where = regno + port; - else - where = -1; - - return ipath_read_kreg64(dd, where); -} /** * ipath_write_kreg_port - write a device's per-port 64-bit kernel register @@ -1973,7 +2005,8 @@ static int __init infinipath_init(void) { int ret; - ipath_dbg(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version); + if (ipath_debug & __IPATH_DBG) + printk(KERN_INFO DRIVER_LOAD_MSG "%s", ib_ipath_version); /* * These must be called before the driver is registered with diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c index a4019a6..030185f 100644 --- a/drivers/infiniband/hw/ipath/ipath_eeprom.c +++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c @@ -626,6 +626,10 @@ void ipath_get_eeprom_info(struct ipath_devdata *dd) } else memcpy(dd->ipath_serial, ifp->if_serial, sizeof ifp->if_serial); + if (!strstr(ifp->if_comment, "Tested successfully")) + ipath_dev_err(dd, "Board SN %s did not pass functional " + "test: %s\n", dd->ipath_serial, + ifp->if_comment); ipath_cdbg(VERBOSE, "Initted GUID to %llx from eeprom\n", (unsigned long long) be64_to_cpu(dd->ipath_guid)); diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c index 5d64ff8..1272aaf 100644 --- a/drivers/infiniband/hw/ipath/ipath_file_ops.c +++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006 QLogic, Inc. All rights reserved. + * Copyright (c) 2006, 2007 QLogic Corporation. All rights reserved. * Copyright (c) 2003, 2004, 2005, 2006 PathScale, Inc. All rights reserved. * * This software is available to you under a choice of one of two @@ -41,12 +41,6 @@ #include "ipath_kernel.h" #include "ipath_common.h" -/* - * mmap64 doesn't allow all 64 bits for 32-bit applications - * so only use the low 43 bits. - */ -#define MMAP64_MASK 0x7FFFFFFFFFFUL - static int ipath_open(struct inode *, struct file *); static int ipath_close(struct inode *, struct file *); static ssize_t ipath_write(struct file *, const char __user *, size_t, @@ -63,6 +57,24 @@ static const struct file_operations ipath_file_ops = { .mmap = ipath_mmap }; +/* + * Convert kernel virtual addresses to physical addresses so they don't + * potentially conflict with the chip addresses used as mmap offsets. + * It doesn't really matter what mmap offset we use as long as we can + * interpret it correctly. + */ +static u64 cvt_kvaddr(void *p) +{ + struct page *page; + u64 paddr = 0; + + page = vmalloc_to_page(p); + if (page) + paddr = page_to_pfn(page) << PAGE_SHIFT; + + return paddr; +} + static int ipath_get_base_info(struct file *fp, void __user *ubase, size_t ubase_size) { @@ -87,7 +99,7 @@ static int ipath_get_base_info(struct file *fp, sz = sizeof(*kinfo); /* If port sharing is not requested, allow the old size structure */ if (!shared) - sz -= 3 * sizeof(u64); + sz -= 7 * sizeof(u64); if (ubase_size < sz) { ipath_cdbg(PROC, "Base size %zu, need %zu (version mismatch?)\n", @@ -165,24 +177,41 @@ static int ipath_get_base_info(struct file *fp, kinfo->spi_piobufbase = (u64) pd->port_piobufs + dd->ipath_palign * (dd->ipath_pbufsport - kinfo->spi_piocnt); - kinfo->__spi_uregbase = (u64) dd->ipath_uregbase + - dd->ipath_palign * pd->port_port; } else { unsigned slave = subport_fp(fp) - 1; kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt; kinfo->spi_piobufbase = (u64) pd->port_piobufs + dd->ipath_palign * kinfo->spi_piocnt * slave; - kinfo->__spi_uregbase = ((u64) pd->subport_uregbase + - PAGE_SIZE * slave) & MMAP64_MASK; + } + if (shared) { + kinfo->spi_port_uregbase = (u64) dd->ipath_uregbase + + dd->ipath_palign * pd->port_port; + kinfo->spi_port_rcvegrbuf = kinfo->spi_rcv_egrbufs; + kinfo->spi_port_rcvhdr_base = kinfo->spi_rcvhdr_base; + kinfo->spi_port_rcvhdr_tailaddr = kinfo->spi_rcvhdr_tailaddr; - kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * slave) & MMAP64_MASK; - kinfo->spi_rcvhdr_tailaddr = - (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK; - kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf + - dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) & - MMAP64_MASK; + kinfo->__spi_uregbase = cvt_kvaddr(pd->subport_uregbase + + PAGE_SIZE * subport_fp(fp)); + + kinfo->spi_rcvhdr_base = cvt_kvaddr(pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport_fp(fp)); + kinfo->spi_rcvhdr_tailaddr = 0; + kinfo->spi_rcv_egrbufs = cvt_kvaddr(pd->subport_rcvegrbuf + + pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size * + subport_fp(fp)); + + kinfo->spi_subport_uregbase = + cvt_kvaddr(pd->subport_uregbase); + kinfo->spi_subport_rcvegrbuf = + cvt_kvaddr(pd->subport_rcvegrbuf); + kinfo->spi_subport_rcvhdr_base = + cvt_kvaddr(pd->subport_rcvhdr_base); + ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n", + kinfo->spi_port, kinfo->spi_runtime_flags, + (unsigned long long) kinfo->spi_subport_uregbase, + (unsigned long long) kinfo->spi_subport_rcvegrbuf, + (unsigned long long) kinfo->spi_subport_rcvhdr_base); } kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) / @@ -199,20 +228,10 @@ static int ipath_get_base_info(struct file *fp, if (master) { kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER; - kinfo->spi_subport_uregbase = - (u64) pd->subport_uregbase & MMAP64_MASK; - kinfo->spi_subport_rcvegrbuf = - (u64) pd->subport_rcvegrbuf & MMAP64_MASK; - kinfo->spi_subport_rcvhdr_base = - (u64) pd->subport_rcvhdr_base & MMAP64_MASK; - ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n", - kinfo->spi_port, kinfo->spi_runtime_flags, - (unsigned long long) kinfo->spi_subport_uregbase, - (unsigned long long) kinfo->spi_subport_rcvegrbuf, - (unsigned long long) kinfo->spi_subport_rcvhdr_base); } - if (copy_to_user(ubase, kinfo, sizeof(*kinfo))) + sz = (ubase_size < sizeof(*kinfo)) ? ubase_size : sizeof(*kinfo); + if (copy_to_user(ubase, kinfo, sz)) ret = -EFAULT; bail: @@ -1132,67 +1151,55 @@ static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr, struct ipath_devdata *dd; void *addr; size_t size; - int ret; + int ret = 0; /* If the port is not shared, all addresses should be physical */ - if (!pd->port_subport_cnt) { - ret = -EINVAL; + if (!pd->port_subport_cnt) goto bail; - } dd = pd->port_dd; size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size; /* - * Master has all the slave uregbase, rcvhdrq, and - * rcvegrbufs mmapped. + * Each process has all the subport uregbase, rcvhdrq, and + * rcvegrbufs mmapped - as an array for all the processes, + * and also separately for this process. */ - if (subport == 0) { - unsigned num_slaves = pd->port_subport_cnt - 1; - - if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) { - addr = pd->subport_uregbase; - size = PAGE_SIZE * num_slaves; - } else if (pgaddr == ((u64) pd->subport_rcvhdr_base & - MMAP64_MASK)) { - addr = pd->subport_rcvhdr_base; - size = pd->port_rcvhdrq_size * num_slaves; - } else if (pgaddr == ((u64) pd->subport_rcvegrbuf & - MMAP64_MASK)) { - addr = pd->subport_rcvegrbuf; - size *= num_slaves; - } else { - ret = -EINVAL; - goto bail; - } - } else if (pgaddr == (((u64) pd->subport_uregbase + - PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) { - addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1); - size = PAGE_SIZE; - } else if (pgaddr == (((u64) pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * (subport - 1)) & - MMAP64_MASK)) { - addr = pd->subport_rcvhdr_base + - pd->port_rcvhdrq_size * (subport - 1); - size = pd->port_rcvhdrq_size; - } else if (pgaddr == (((u64) pd->subport_rcvegrbuf + - size * (subport - 1)) & MMAP64_MASK)) { - addr = pd->subport_rcvegrbuf + size * (subport - 1); - /* rcvegrbufs are read-only on the slave */ - if (vma->vm_flags & VM_WRITE) { - dev_info(&dd->pcidev->dev, - "Can't map eager buffers as " - "writable (flags=%lx)\n", vma->vm_flags); - ret = -EPERM; - goto bail; - } - /* - * Don't allow permission to later change to writeable - * with mprotect. - */ - vma->vm_flags &= ~VM_MAYWRITE; + if (pgaddr == cvt_kvaddr(pd->subport_uregbase)) { + addr = pd->subport_uregbase; + size = PAGE_SIZE * pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base)) { + addr = pd->subport_rcvhdr_base; + size = pd->port_rcvhdrq_size * pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf)) { + addr = pd->subport_rcvegrbuf; + size *= pd->port_subport_cnt; + } else if (pgaddr == cvt_kvaddr(pd->subport_uregbase + + PAGE_SIZE * subport)) { + addr = pd->subport_uregbase + PAGE_SIZE * subport; + size = PAGE_SIZE; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport)) { + addr = pd->subport_rcvhdr_base + + pd->port_rcvhdrq_size * subport; + size = pd->port_rcvhdrq_size; + } else if (pgaddr == cvt_kvaddr(pd->subport_rcvegrbuf + + size * subport)) { + addr = pd->subport_rcvegrbuf + size * subport; + /* rcvegrbufs are read-only on the slave */ + if (vma->vm_flags & VM_WRITE) { + dev_info(&dd->pcidev->dev, + "Can't map eager buffers as " + "writable (flags=%lx)\n", vma->vm_flags); + ret = -EPERM; + goto bail; + } + /* + * Don't allow permission to later change to writeable + * with mprotect. + */ + vma->vm_flags &= ~VM_MAYWRITE; } else { - ret = -EINVAL; goto bail; } len = vma->vm_end - vma->vm_start; @@ -1205,7 +1212,7 @@ static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr, vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT; vma->vm_ops = &ipath_file_vm_ops; vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND; - ret = 0; + ret = 1; bail: return ret; @@ -1265,19 +1272,20 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) * Check for kernel virtual addresses first, anything else must * match a HW or memory address. */ - if (pgaddr >= (1ULL<<40)) { - ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); + ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp)); + if (ret) { + if (ret > 0) + ret = 0; goto bail; } + ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; if (!pd->port_subport_cnt) { /* port is not shared */ - ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; piocnt = dd->ipath_pbufsport; piobufs = pd->port_piobufs; } else if (!subport_fp(fp)) { /* caller is the master */ - ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port; piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) + (dd->ipath_pbufsport % pd->port_subport_cnt); piobufs = pd->port_piobufs + @@ -1286,7 +1294,6 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) unsigned slave = subport_fp(fp) - 1; /* caller is a slave */ - ureg = 0; piocnt = dd->ipath_pbufsport / pd->port_subport_cnt; piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave; } @@ -1300,9 +1307,6 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma) ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0, (void *) dd->ipath_pioavailregs_dma, "pioavail registers"); - else if (subport_fp(fp)) - /* Subports don't mmap the physical receive buffers */ - ret = -EINVAL; else if (pgaddr == pd->port_rcvegr_phys) ret = mmap_rcvegrbufs(vma, pd); else if (pgaddr == (u64) pd->port_rcvhdrq_phys) @@ -1400,32 +1404,41 @@ static int init_subports(struct ipath_devdata *dd, const struct ipath_user_info *uinfo) { int ret = 0; - unsigned num_slaves; + unsigned num_subports; size_t size; - /* Old user binaries don't know about subports */ - if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) - goto bail; /* * If the user is requesting zero or one port, * skip the subport allocation. */ if (uinfo->spu_subport_cnt <= 1) goto bail; - if (uinfo->spu_subport_cnt > 4) { + + /* Old user binaries don't know about new subport implementation */ + if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR) { + dev_info(&dd->pcidev->dev, + "Mismatched user minor version (%d) and driver " + "minor version (%d) while port sharing. Ensure " + "that driver and library are from the same " + "release.\n", + (int) (uinfo->spu_userversion & 0xffff), + IPATH_USER_SWMINOR); + goto bail; + } + if (uinfo->spu_subport_cnt > INFINIPATH_MAX_SUBPORT) { ret = -EINVAL; goto bail; } - num_slaves = uinfo->spu_subport_cnt - 1; - pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves); + num_subports = uinfo->spu_subport_cnt; + pd->subport_uregbase = vmalloc(PAGE_SIZE * num_subports); if (!pd->subport_uregbase) { ret = -ENOMEM; goto bail; } /* Note: pd->port_rcvhdrq_size isn't initialized yet. */ size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize * - sizeof(u32), PAGE_SIZE) * num_slaves; + sizeof(u32), PAGE_SIZE) * num_subports; pd->subport_rcvhdr_base = vmalloc(size); if (!pd->subport_rcvhdr_base) { ret = -ENOMEM; @@ -1434,7 +1447,7 @@ static int init_subports(struct ipath_devdata *dd, pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size * - num_slaves); + num_subports); if (!pd->subport_rcvegrbuf) { ret = -ENOMEM; goto bail_rhdr; @@ -1443,6 +1456,12 @@ static int init_subports(struct ipath_devdata *dd, pd->port_subport_cnt = uinfo->spu_subport_cnt; pd->port_subport_id = uinfo->spu_subport_id; pd->active_slaves = 1; + set_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); + memset(pd->subport_uregbase, 0, PAGE_SIZE * num_subports); + memset(pd->subport_rcvhdr_base, 0, size); + memset(pd->subport_rcvegrbuf, 0, pd->port_rcvegrbuf_chunks * + pd->port_rcvegrbuf_size * + num_subports); goto bail; bail_rhdr: @@ -1573,18 +1592,19 @@ static int find_best_unit(struct file *fp, */ if (!cpus_empty(current->cpus_allowed) && !cpus_full(current->cpus_allowed)) { - int ncpus = num_online_cpus(), curcpu = -1; + int ncpus = num_online_cpus(), curcpu = -1, nset = 0; for (i = 0; i < ncpus; i++) if (cpu_isset(i, current->cpus_allowed)) { ipath_cdbg(PROC, "%s[%u] affinity set for " - "cpu %d\n", current->comm, - current->pid, i); + "cpu %d/%d\n", current->comm, + current->pid, i, ncpus); curcpu = i; + nset++; } - if (curcpu != -1) { + if (curcpu != -1 && nset != ncpus) { if (npresent) { prefunit = curcpu / (ncpus / npresent); - ipath_dbg("%s[%u] %d chips, %d cpus, " + ipath_cdbg(PROC,"%s[%u] %d chips, %d cpus, " "%d cpus/chip, select unit %d\n", current->comm, current->pid, npresent, ncpus, ncpus / npresent, @@ -1764,11 +1784,17 @@ static int ipath_do_user_init(struct file *fp, const struct ipath_user_info *uinfo) { int ret; - struct ipath_portdata *pd; + struct ipath_portdata *pd = port_fp(fp); struct ipath_devdata *dd; u32 head32; - pd = port_fp(fp); + /* Subports don't need to initialize anything since master did it. */ + if (subport_fp(fp)) { + ret = wait_event_interruptible(pd->port_wait, + !test_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag)); + goto done; + } + dd = pd->port_dd; if (uinfo->spu_rcvhdrsize) { @@ -1826,6 +1852,11 @@ static int ipath_do_user_init(struct file *fp, dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD); ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl, dd->ipath_rcvctrl); + /* Notify any waiting slaves */ + if (pd->port_subport_cnt) { + clear_bit(IPATH_PORT_MASTER_UNINIT, &pd->port_flag); + wake_up(&pd->port_wait); + } done: return ret; } @@ -2017,6 +2048,17 @@ static int ipath_get_slave_info(struct ipath_portdata *pd, return ret; } +static int ipath_force_pio_avail_update(struct ipath_devdata *dd) +{ + u64 reg = dd->ipath_sendctrl; + + clear_bit(IPATH_S_PIOBUFAVAILUPD, ®); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, reg); + ipath_write_kreg(dd, dd->ipath_kregs->kr_sendctrl, dd->ipath_sendctrl); + + return 0; +} + static ssize_t ipath_write(struct file *fp, const char __user *data, size_t count, loff_t *off) { @@ -2071,27 +2113,35 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, dest = &cmd.cmd.part_key; src = &ucmd->cmd.part_key; break; - case IPATH_CMD_SLAVE_INFO: + case __IPATH_CMD_SLAVE_INFO: copy = sizeof(cmd.cmd.slave_mask_addr); dest = &cmd.cmd.slave_mask_addr; src = &ucmd->cmd.slave_mask_addr; break; + case IPATH_CMD_PIOAVAILUPD: // force an update of PIOAvail reg + copy = 0; + src = NULL; + dest = NULL; + break; default: ret = -EINVAL; goto bail; } - if ((count - consumed) < copy) { - ret = -EINVAL; - goto bail; - } + if (copy) { + if ((count - consumed) < copy) { + ret = -EINVAL; + goto bail; + } - if (copy_from_user(dest, src, copy)) { - ret = -EFAULT; - goto bail; + if (copy_from_user(dest, src, copy)) { + ret = -EFAULT; + goto bail; + } + + consumed += copy; } - consumed += copy; pd = port_fp(fp); if (!pd && cmd.type != __IPATH_CMD_USER_INIT && cmd.type != IPATH_CMD_ASSIGN_PORT) { @@ -2137,11 +2187,14 @@ static ssize_t ipath_write(struct file *fp, const char __user *data, case IPATH_CMD_SET_PART_KEY: ret = ipath_set_part_key(pd, cmd.cmd.part_key); break; - case IPATH_CMD_SLAVE_INFO: + case __IPATH_CMD_SLAVE_INFO: ret = ipath_get_slave_info(pd, (void __user *) (unsigned long) cmd.cmd.slave_mask_addr); break; + case IPATH_CMD_PIOAVAILUPD: + ret = ipath_force_pio_avail_update(pd->port_dd); + break; } if (ret >= 0) diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c index 5b40a84..ed55979 100644 --- a/drivers/infiniband/hw/ipath/ipath_fs.c +++ b/drivers/infiniband/hw/ipath/ipath_fs.c @@ -451,12 +451,18 @@ bail: return ret; } -static void remove_file(struct dentry *parent, char *name) +static int remove_file(struct dentry *parent, char *name) { struct dentry *tmp; + int ret; tmp = lookup_one_len(name, parent, strlen(name)); + if (IS_ERR(tmp)) { + ret = PTR_ERR(tmp); + goto bail; + } + spin_lock(&dcache_lock); spin_lock(&tmp->d_lock); if (!(d_unhashed(tmp) && tmp->d_inode)) { @@ -469,6 +475,14 @@ static void remove_file(struct dentry *parent, char *name) spin_unlock(&tmp->d_lock); spin_unlock(&dcache_lock); } + + ret = 0; +bail: + /* + * We don't expect clients to care about the return value, but + * it's there if they need it. + */ + return ret; } static int remove_device_files(struct super_block *sb, diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c index 7468477..4171198 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6110.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c @@ -43,6 +43,9 @@ #include "ipath_kernel.h" #include "ipath_registers.h" +static void ipath_setup_ht_setextled(struct ipath_devdata *, u64, u64); + + /* * This lists the InfiniPath registers, in the actual chip layout. * This structure should never be directly accessed. @@ -208,8 +211,8 @@ static const struct ipath_kregs ipath_ht_kregs = { .kr_serdesstatus = IPATH_KREG_OFFSET(SerdesStatus), .kr_xgxsconfig = IPATH_KREG_OFFSET(XGXSConfig), /* - * These should not be used directly via ipath_read_kreg64(), - * use them with ipath_read_kreg64_port(), + * These should not be used directly via ipath_write_kreg64(), + * use them with ipath_write_kreg64_port(), */ .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0) @@ -284,6 +287,14 @@ static const struct ipath_cregs ipath_ht_cregs = { #define INFINIPATH_EXTS_MEMBIST_ENDTEST 0x0000000000004000 #define INFINIPATH_EXTS_MEMBIST_CORRECT 0x0000000000008000 + +/* TID entries (memory), HT-only */ +#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */ +#define INFINIPATH_RT_VALID 0x8000000000000000ULL +#define INFINIPATH_RT_ADDR_SHIFT 0 +#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFFULL +#define INFINIPATH_RT_BUFSIZE_SHIFT 48 + /* * masks and bits that are different in different chips, or present only * in one @@ -402,6 +413,14 @@ static const struct ipath_hwerror_msgs ipath_6110_hwerror_msgs[] = { INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), }; +#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \ + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \ + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) +#define RXE_EAGER_PARITY (INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID \ + << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) + +static int ipath_ht_txe_recover(struct ipath_devdata *); + /** * ipath_ht_handle_hwerrors - display hardware errors. * @dd: the infinipath device @@ -450,13 +469,12 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, /* * make sure we get this much out, unless told to be quiet, + * it's a parity error we may recover from, * or it's occurred within the last 5 seconds */ - if ((hwerrs & ~(dd->ipath_lasthwerror | - ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) || - (ipath_debug & __IPATH_VERBDBG)) + if ((hwerrs & ~(dd->ipath_lasthwerror | TXE_PIO_PARITY | + RXE_EAGER_PARITY)) || + (ipath_debug & __IPATH_VERBDBG)) dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx " "(cleared)\n", (unsigned long long) hwerrs); dd->ipath_lasthwerror |= hwerrs; @@ -467,7 +485,7 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, (hwerrs & ~dd->ipath_hwe_bitsextant)); ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control); - if (ctrl & INFINIPATH_C_FREEZEMODE) { + if ((ctrl & INFINIPATH_C_FREEZEMODE) && !ipath_diag_inuse) { /* * parity errors in send memory are recoverable, * just cancel the send (if indicated in * sendbuffererror), @@ -476,50 +494,14 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, * occur if a processor speculative read is done to the PIO * buffer while we are sending a packet, for example. */ - if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - ipath_stats.sps_txeparity++; - ipath_dbg("Recovering from TXE parity error (%llu), " - "hwerrstatus=%llx\n", - (unsigned long long) ipath_stats.sps_txeparity, - (unsigned long long) hwerrs); - ipath_disarm_senderrbufs(dd); - hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); - if (!hwerrs) { /* else leave in freeze mode */ - ipath_write_kreg(dd, - dd->ipath_kregs->kr_control, - dd->ipath_control); - return; - } - } - if (hwerrs) { - /* - * if any set that we aren't ignoring; only - * make the complaint once, in case it's stuck - * or recurring, and we get here multiple - * times. - */ - if (dd->ipath_flags & IPATH_INITTED) { - ipath_dev_err(dd, "Fatal Hardware Error (freeze " - "mode), no longer usable, SN %.16s\n", - dd->ipath_serial); - isfatal = 1; - } - *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; - /* mark as having had error */ - *dd->ipath_statusp |= IPATH_STATUS_HWERROR; - /* - * mark as not usable, at a minimum until driver - * is reloaded, probably until reboot, since no - * other reset is possible. - */ - dd->ipath_flags &= ~IPATH_INITTED; - } else { - ipath_dbg("Clearing freezemode on ignored hardware " - "error\n"); + if ((hwerrs & TXE_PIO_PARITY) && ipath_ht_txe_recover(dd)) + hwerrs &= ~TXE_PIO_PARITY; + if (hwerrs & RXE_EAGER_PARITY) + ipath_dev_err(dd, "RXE parity, Eager TID error is not " + "recoverable\n"); + if (!hwerrs) { + ipath_dbg("Clearing freezemode on ignored or " + "recovered hardware error\n"); ctrl &= ~INFINIPATH_C_FREEZEMODE; ipath_write_kreg(dd, dd->ipath_kregs->kr_control, ctrl); @@ -587,7 +569,39 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg, dd->ipath_hwerrmask); } - ipath_dev_err(dd, "%s hardware error\n", msg); + if (hwerrs) { + /* + * if any set that we aren't ignoring; only + * make the complaint once, in case it's stuck + * or recurring, and we get here multiple + * times. + * force link down, so switch knows, and + * LEDs are turned off + */ + if (dd->ipath_flags & IPATH_INITTED) { + ipath_set_linkstate(dd, IPATH_IB_LINKDOWN); + ipath_setup_ht_setextled(dd, + INFINIPATH_IBCS_L_STATE_DOWN, + INFINIPATH_IBCS_LT_STATE_DISABLED); + ipath_dev_err(dd, "Fatal Hardware Error (freeze " + "mode), no longer usable, SN %.16s\n", + dd->ipath_serial); + isfatal = 1; + } + *dd->ipath_statusp &= ~IPATH_STATUS_IB_READY; + /* mark as having had error */ + *dd->ipath_statusp |= IPATH_STATUS_HWERROR; + /* + * mark as not usable, at a minimum until driver + * is reloaded, probably until reboot, since no + * other reset is possible. + */ + dd->ipath_flags &= ~IPATH_INITTED; + } + else + *msg = 0; /* recovered from all of them */ + if (*msg) + ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) /* * for status file; if no trailing brace is copied, @@ -658,7 +672,8 @@ static int ipath_ht_boardname(struct ipath_devdata *dd, char *name, if (n) snprintf(name, namelen, "%s", n); - if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || dd->ipath_minrev > 3)) { + if (dd->ipath_majrev != 3 || (dd->ipath_minrev < 2 || + dd->ipath_minrev > 3)) { /* * This version of the driver only supports Rev 3.2 and 3.3 */ @@ -1163,6 +1178,8 @@ static void ipath_ht_init_hwerrors(struct ipath_devdata *dd) if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) ipath_dev_err(dd, "MemBIST did not complete!\n"); + if (extsval & INFINIPATH_EXTS_MEMBIST_CORRECT) + ipath_dbg("MemBIST corrected\n"); ipath_check_htlink(dd); @@ -1366,6 +1383,9 @@ static void ipath_ht_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr, u32 type, unsigned long pa) { + if (!dd->ipath_kregbase) + return; + if (pa != dd->ipath_tidinvalid) { if (unlikely((pa & ~INFINIPATH_RT_ADDR_MASK))) { dev_info(&dd->pcidev->dev, @@ -1382,10 +1402,10 @@ static void ipath_ht_put_tid(struct ipath_devdata *dd, pa |= lenvalid | INFINIPATH_RT_VALID; } } - if (dd->ipath_kregbase) - writeq(pa, tidptr); + writeq(pa, tidptr); } + /** * ipath_ht_clear_tid - clear all TID entries for a port, expected and eager * @dd: the infinipath device @@ -1515,7 +1535,7 @@ static int ipath_ht_early_init(struct ipath_devdata *dd) INFINIPATH_S_ABORT); ipath_get_eeprom_info(dd); - if(dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' && + if (dd->ipath_boardrev == 5 && dd->ipath_serial[0] == '1' && dd->ipath_serial[1] == '2' && dd->ipath_serial[2] == '8') { /* * Later production QHT7040 has same changes as QHT7140, so @@ -1528,13 +1548,31 @@ static int ipath_ht_early_init(struct ipath_devdata *dd) return 0; } + +static int ipath_ht_txe_recover(struct ipath_devdata *dd) +{ + int cnt = ++ipath_stats.sps_txeparity; + if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) { + if (cnt == IPATH_MAX_PARITY_ATTEMPTS) + ipath_dev_err(dd, + "Too many attempts to recover from " + "TXE parity, giving up\n"); + return 0; + } + dev_info(&dd->pcidev->dev, + "Recovering from TXE PIO parity error\n"); + ipath_disarm_senderrbufs(dd, 1); + return 1; +} + + /** * ipath_init_ht_get_base_info - set chip-specific flags for user code * @dd: the infinipath device * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs - * HyperTransport can affect some user packet algorithims. + * HyperTransport can affect some user packet algorithms. */ static int ipath_ht_get_base_info(struct ipath_portdata *pd, void *kbase) { diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c index ae8bf99..1b9c308 100644 --- a/drivers/infiniband/hw/ipath/ipath_iba6120.c +++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c @@ -43,6 +43,8 @@ #include "ipath_kernel.h" #include "ipath_registers.h" +static void ipath_setup_pe_setextled(struct ipath_devdata *, u64, u64); + /* * This file contains all the chip-specific register information and * access functions for the QLogic InfiniPath PCI-Express chip. @@ -207,8 +209,8 @@ static const struct ipath_kregs ipath_pe_kregs = { .kr_ibpllcfg = IPATH_KREG_OFFSET(IBPLLCfg), /* - * These should not be used directly via ipath_read_kreg64(), - * use them with ipath_read_kreg64_port() + * These should not be used directly via ipath_write_kreg64(), + * use them with ipath_write_kreg64_port(), */ .kr_rcvhdraddr = IPATH_KREG_OFFSET(RcvHdrAddr0), .kr_rcvhdrtailaddr = IPATH_KREG_OFFSET(RcvHdrTailAddr0), @@ -321,6 +323,12 @@ static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = { INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"), }; +#define TXE_PIO_PARITY ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | \ + INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) \ + << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) + +static int ipath_pe_txe_recover(struct ipath_devdata *); + /** * ipath_pe_handle_hwerrors - display hardware errors. * @dd: the infinipath device @@ -394,32 +402,21 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, * occur if a processor speculative read is done to the PIO * buffer while we are sending a packet, for example. */ - if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) { - ipath_stats.sps_txeparity++; - ipath_dbg("Recovering from TXE parity error (%llu), " - "hwerrstatus=%llx\n", - (unsigned long long) ipath_stats.sps_txeparity, - (unsigned long long) hwerrs); - ipath_disarm_senderrbufs(dd); - hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF | - INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC) - << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT); - if (!hwerrs) { /* else leave in freeze mode */ - ipath_write_kreg(dd, - dd->ipath_kregs->kr_control, - dd->ipath_control); - return; - } - } + if ((hwerrs & TXE_PIO_PARITY) && ipath_pe_txe_recover(dd)) + hwerrs &= ~TXE_PIO_PARITY; if (hwerrs) { /* * if any set that we aren't ignoring only make the * complaint once, in case it's stuck or recurring, * and we get here multiple times + * Force link down, so switch knows, and + * LEDs are turned off */ if (dd->ipath_flags & IPATH_INITTED) { + ipath_set_linkstate(dd, IPATH_IB_LINKDOWN); + ipath_setup_pe_setextled(dd, + INFINIPATH_IBCS_L_STATE_DOWN, + INFINIPATH_IBCS_LT_STATE_DISABLED); ipath_dev_err(dd, "Fatal Hardware Error (freeze " "mode), no longer usable, SN %.16s\n", dd->ipath_serial); @@ -493,7 +490,8 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg, dd->ipath_hwerrmask); } - ipath_dev_err(dd, "%s hardware error\n", msg); + if (*msg) + ipath_dev_err(dd, "%s hardware error\n", msg); if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) { /* * for /sys status file ; if no trailing } is copied, we'll @@ -581,6 +579,8 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd) if (!(extsval & INFINIPATH_EXTS_MEMBIST_ENDTEST)) ipath_dev_err(dd, "MemBIST did not complete!\n"); + if (extsval & INFINIPATH_EXTS_MEMBIST_FOUND) + ipath_dbg("MemBIST corrected\n"); val = ~0ULL; /* barring bugs, all hwerrors become interrupts, */ @@ -1293,7 +1293,7 @@ int __attribute__((weak)) ipath_unordered_wc(void) * @kbase: ipath_base_info pointer * * We set the PCIE flag because the lower bandwidth on PCIe vs - * HyperTransport can affect some user packet algorithims. + * HyperTransport can affect some user packet algorithms. */ static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase) { @@ -1330,6 +1330,35 @@ static void ipath_pe_free_irq(struct ipath_devdata *dd) dd->ipath_irq = 0; } +/* + * On platforms using this chip, and not having ordered WC stores, we + * can get TXE parity errors due to speculative reads to the PIO buffers, + * and this, due to a chip bug can result in (many) false parity error + * reports. So it's a debug print on those, and an info print on systems + * where the speculative reads don't occur. + * Because we can get lots of false errors, we have no upper limit + * on recovery attempts on those platforms. + */ +static int ipath_pe_txe_recover(struct ipath_devdata *dd) +{ + if (ipath_unordered_wc()) + ipath_dbg("Recovering from TXE PIO parity error\n"); + else { + int cnt = ++ipath_stats.sps_txeparity; + if (cnt >= IPATH_MAX_PARITY_ATTEMPTS) { + if (cnt == IPATH_MAX_PARITY_ATTEMPTS) + ipath_dev_err(dd, + "Too many attempts to recover from " + "TXE parity, giving up\n"); + return 0; + } + dev_info(&dd->pcidev->dev, + "Recovering from TXE PIO parity error\n"); + } + ipath_disarm_senderrbufs(dd, 1); + return 1; +} + /** * ipath_init_iba6120_funcs - set up the chip-specific function pointers * @dd: the infinipath device diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c index d4f6b523..7045ba6 100644 --- a/drivers/infiniband/hw/ipath/ipath_init_chip.c +++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c @@ -216,6 +216,20 @@ static int bringup_link(struct ipath_devdata *dd) return ret; } +static struct ipath_portdata *create_portdata0(struct ipath_devdata *dd) +{ + struct ipath_portdata *pd = NULL; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (pd) { + pd->port_dd = dd; + pd->port_cnt = 1; + /* The port 0 pkey table is used by the layer interface. */ + pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; + } + return pd; +} + static int init_chip_first(struct ipath_devdata *dd, struct ipath_portdata **pdp) { @@ -271,20 +285,16 @@ static int init_chip_first(struct ipath_devdata *dd, goto done; } - dd->ipath_pd[0] = kzalloc(sizeof(*pd), GFP_KERNEL); + pd = create_portdata0(dd); - if (!dd->ipath_pd[0]) { + if (!pd) { ipath_dev_err(dd, "Unable to allocate portdata for port " "0, failing\n"); ret = -ENOMEM; goto done; } - pd = dd->ipath_pd[0]; - pd->port_dd = dd; - pd->port_port = 0; - pd->port_cnt = 1; - /* The port 0 pkey table is used by the layer interface. */ - pd->port_pkeys[0] = IPATH_DEFAULT_P_KEY; + dd->ipath_pd[0] = pd; + dd->ipath_rcvtidcnt = ipath_read_kreg32(dd, dd->ipath_kregs->kr_rcvtidcnt); dd->ipath_rcvtidbase = @@ -590,6 +600,10 @@ static int init_housekeeping(struct ipath_devdata *dd, goto done; } + + /* clear diagctrl register, in case diags were running and crashed */ + ipath_write_kreg (dd, dd->ipath_kregs->kr_hwdiagctrl, 0); + /* clear the initial reset flag, in case first driver load */ ipath_write_kreg(dd, dd->ipath_kregs->kr_errorclear, INFINIPATH_E_RESET); @@ -668,6 +682,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) { int ret = 0, i; u32 val32, kpiobufs; + u32 piobufs, uports; u64 val; struct ipath_portdata *pd = NULL; /* keep gcc4 happy */ gfp_t gfp_flags = GFP_USER | __GFP_COMP; @@ -702,16 +717,17 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) * the in memory DMA'ed copies of the registers. This has to * be done early, before we calculate lastport, etc. */ - val = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; + piobufs = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k; /* * calc number of pioavail registers, and save it; we have 2 * bits per buffer. */ - dd->ipath_pioavregs = ALIGN(val, sizeof(u64) * BITS_PER_BYTE / 2) + dd->ipath_pioavregs = ALIGN(piobufs, sizeof(u64) * BITS_PER_BYTE / 2) / (sizeof(u64) * BITS_PER_BYTE / 2); + uports = dd->ipath_cfgports ? dd->ipath_cfgports - 1 : 0; if (ipath_kpiobufs == 0) { /* not set by user (this is default) */ - if ((dd->ipath_piobcnt2k + dd->ipath_piobcnt4k) > 128) + if (piobufs >= (uports * IPATH_MIN_USER_PORT_BUFCNT) + 32) kpiobufs = 32; else kpiobufs = 16; @@ -719,31 +735,25 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) else kpiobufs = ipath_kpiobufs; - if (kpiobufs > - (dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - - (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT))) { - i = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - - (dd->ipath_cfgports * IPATH_MIN_USER_PORT_BUFCNT); + if (kpiobufs + (uports * IPATH_MIN_USER_PORT_BUFCNT) > piobufs) { + i = (int) piobufs - + (int) (uports * IPATH_MIN_USER_PORT_BUFCNT); if (i < 0) i = 0; - dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs for " - "kernel leaves too few for %d user ports " + dev_info(&dd->pcidev->dev, "Allocating %d PIO bufs of " + "%d for kernel leaves too few for %d user ports " "(%d each); using %u\n", kpiobufs, - dd->ipath_cfgports - 1, - IPATH_MIN_USER_PORT_BUFCNT, i); + piobufs, uports, IPATH_MIN_USER_PORT_BUFCNT, i); /* * shouldn't change ipath_kpiobufs, because could be * different for different devices... */ kpiobufs = i; } - dd->ipath_lastport_piobuf = - dd->ipath_piobcnt2k + dd->ipath_piobcnt4k - kpiobufs; - dd->ipath_pbufsport = dd->ipath_cfgports > 1 - ? dd->ipath_lastport_piobuf / (dd->ipath_cfgports - 1) - : 0; - val32 = dd->ipath_lastport_piobuf - - (dd->ipath_pbufsport * (dd->ipath_cfgports - 1)); + dd->ipath_lastport_piobuf = piobufs - kpiobufs; + dd->ipath_pbufsport = + uports ? dd->ipath_lastport_piobuf / uports : 0; + val32 = dd->ipath_lastport_piobuf - (dd->ipath_pbufsport * uports); if (val32 > 0) { ipath_dbg("allocating %u pbufs/port leaves %u unused, " "add to kernel\n", dd->ipath_pbufsport, val32); @@ -754,8 +764,7 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) dd->ipath_lastpioindex = dd->ipath_lastport_piobuf; ipath_cdbg(VERBOSE, "%d PIO bufs for kernel out of %d total %u " "each for %u user ports\n", kpiobufs, - dd->ipath_piobcnt2k + dd->ipath_piobcnt4k, - dd->ipath_pbufsport, dd->ipath_cfgports - 1); + piobufs, dd->ipath_pbufsport, uports); dd->ipath_f_early_init(dd); @@ -839,11 +848,24 @@ int ipath_init_chip(struct ipath_devdata *dd, int reinit) * Set up the port 0 (kernel) rcvhdr q and egr TIDs. If doing * re-init, the simplest way to handle this is to free * existing, and re-allocate. + * Need to re-create rest of port 0 portdata as well. */ if (reinit) { - struct ipath_portdata *pd = dd->ipath_pd[0]; - dd->ipath_pd[0] = NULL; - ipath_free_pddata(dd, pd); + /* Alloc and init new ipath_portdata for port0, + * Then free old pd. Could lead to fragmentation, but also + * makes later support for hot-swap easier. + */ + struct ipath_portdata *npd; + npd = create_portdata0(dd); + if (npd) { + ipath_free_pddata(dd, pd); + dd->ipath_pd[0] = pd = npd; + } else { + ipath_dev_err(dd, "Unable to allocate portdata for" + " port 0, failing\n"); + ret = -ENOMEM; + goto done; + } } dd->ipath_f_tidtemplate(dd); ret = ipath_create_rcvhdrq(dd, pd); diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 72b9e27..45d0331 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -38,10 +38,39 @@ #include "ipath_common.h" /* + * clear (write) a pio buffer, to clear a parity error. This routine + * should only be called when in freeze mode, and the buffer should be + * canceled afterwards. + */ +static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum) +{ + u32 __iomem *pbuf; + u32 dwcnt; /* dword count to write */ + if (pnum < dd->ipath_piobcnt2k) { + pbuf = (u32 __iomem *) (dd->ipath_pio2kbase + pnum * + dd->ipath_palign); + dwcnt = dd->ipath_piosize2k >> 2; + } + else { + pbuf = (u32 __iomem *) (dd->ipath_pio4kbase + + (pnum - dd->ipath_piobcnt2k) * dd->ipath_4kalign); + dwcnt = dd->ipath_piosize4k >> 2; + } + dev_info(&dd->pcidev->dev, + "Rewrite PIO buffer %u, to recover from parity error\n", + pnum); + *pbuf = dwcnt+1; /* no flush required, since already in freeze */ + while(--dwcnt) + *pbuf++ = 0; +} + +/* * Called when we might have an error that is specific to a particular * PIO buffer, and may need to cancel that buffer, so it can be re-used. + * If rewrite is true, and bits are set in the sendbufferror registers, + * we'll write to the buffer, for error recovery on parity errors. */ -void ipath_disarm_senderrbufs(struct ipath_devdata *dd) +void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite) { u32 piobcnt; unsigned long sbuf[4]; @@ -74,8 +103,11 @@ void ipath_disarm_senderrbufs(struct ipath_devdata *dd) } for (i = 0; i < piobcnt; i++) - if (test_bit(i, sbuf)) + if (test_bit(i, sbuf)) { + if (rewrite) + ipath_clrpiobuf(dd, i); ipath_disarm_piobufs(dd, i, 1); + } dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */ } } @@ -114,7 +146,7 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs) { u64 ignore_this_time = 0; - ipath_disarm_senderrbufs(dd); + ipath_disarm_senderrbufs(dd, 0); if ((errs & E_SUM_LINK_PKTERRS) && !(dd->ipath_flags & IPATH_LINKACTIVE)) { /* @@ -403,10 +435,13 @@ static void handle_supp_msgs(struct ipath_devdata *dd, * happens so often we never want to count it. */ if (dd->ipath_lasterror & ~INFINIPATH_E_IBSTATUSCHANGED) { - ipath_decode_err(msg, sizeof msg, dd->ipath_lasterror & - ~INFINIPATH_E_IBSTATUSCHANGED); + int iserr; + iserr = ipath_decode_err(msg, sizeof msg, + dd->ipath_lasterror & + ~INFINIPATH_E_IBSTATUSCHANGED); if (dd->ipath_lasterror & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ~(INFINIPATH_E_RRCVEGRFULL | + INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS)) ipath_dev_err(dd, "Suppressed %u messages for " "fast-repeating errors (%s) (%llx)\n", supp_msgs, msg, @@ -420,8 +455,13 @@ static void handle_supp_msgs(struct ipath_devdata *dd, * them. So only complain about these at debug * level. */ - ipath_dbg("Suppressed %u messages for %s\n", - supp_msgs, msg); + if (iserr) + ipath_dbg("Suppressed %u messages for %s\n", + supp_msgs, msg); + else + ipath_cdbg(ERRPKT, + "Suppressed %u messages for %s\n", + supp_msgs, msg); } } } @@ -462,7 +502,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) { char msg[512]; u64 ignore_this_time = 0; - int i; + int i, iserr = 0; int chkerrpkts = 0, noprint = 0; unsigned supp_msgs; @@ -502,6 +542,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) } if (supp_msgs == 250000) { + int s_iserr; /* * It's not entirely reasonable assuming that the errors set * in the last clear period are all responsible for the @@ -511,17 +552,17 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) dd->ipath_maskederrs |= dd->ipath_lasterror | errs; ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, ~dd->ipath_maskederrs); - ipath_decode_err(msg, sizeof msg, + s_iserr = ipath_decode_err(msg, sizeof msg, (dd->ipath_maskederrs & ~dd-> ipath_ignorederrs)); if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) - ipath_dev_err(dd, "Disabling error(s) %llx because " - "occurring too frequently (%s)\n", - (unsigned long long) - (dd->ipath_maskederrs & - ~dd->ipath_ignorederrs), msg); + ~(INFINIPATH_E_RRCVEGRFULL | + INFINIPATH_E_RRCVHDRFULL | INFINIPATH_E_PKTERRS)) + ipath_dev_err(dd, "Temporarily disabling " + "error(s) %llx reporting; too frequent (%s)\n", + (unsigned long long) (dd->ipath_maskederrs & + ~dd->ipath_ignorederrs), msg); else { /* * rcvegrfull and rcvhdrqfull are "normal", @@ -530,8 +571,15 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) * processing them. So only complain about * these at debug level. */ - ipath_dbg("Disabling frequent queue full errors " - "(%s)\n", msg); + if (s_iserr) + ipath_dbg("Temporarily disabling reporting " + "too frequent queue full errors (%s)\n", + msg); + else + ipath_cdbg(ERRPKT, + "Temporarily disabling reporting too" + " frequent packet errors (%s)\n", + msg); } /* @@ -589,6 +637,8 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) ipath_stats.sps_crcerrs++; chkerrpkts = 1; } + iserr = errs & ~(E_SUM_PKTERRS | INFINIPATH_E_PKTERRS); + /* * We don't want to print these two as they happen, or we can make @@ -677,8 +727,13 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs) *dd->ipath_statusp &= ~IPATH_STATUS_IB_CONF; } - if (!noprint && *msg) - ipath_dev_err(dd, "%s error\n", msg); + if (!noprint && *msg) { + if (iserr) + ipath_dev_err(dd, "%s error\n", msg); + else + dev_info(&dd->pcidev->dev, "%s packet problems\n", + msg); + } if (dd->ipath_state_wanted & dd->ipath_flags) { ipath_cdbg(VERBOSE, "driver wanted state %x, iflags now %x, " "waking\n", dd->ipath_state_wanted, @@ -819,11 +874,10 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat) struct ipath_portdata *pd = dd->ipath_pd[i]; if (portr & (1 << i) && pd && pd->port_cnt && test_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag)) { - int rcbit; clear_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag); - rcbit = i + INFINIPATH_R_INTRAVAIL_SHIFT; - clear_bit(1UL << rcbit, &dd->ipath_rcvctrl); + clear_bit(i + INFINIPATH_R_INTRAVAIL_SHIFT, + &dd->ipath_rcvctrl); wake_up_interruptible(&pd->port_wait); rcvdint = 1; } diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index 6d8d05f..e900c25 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -590,7 +590,6 @@ int ipath_enable_wc(struct ipath_devdata *dd); void ipath_disable_wc(struct ipath_devdata *dd); int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp); void ipath_shutdown_device(struct ipath_devdata *); -void ipath_disarm_senderrbufs(struct ipath_devdata *); struct file_operations; int ipath_cdev_init(int minor, char *name, const struct file_operations *fops, @@ -611,7 +610,7 @@ struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd, gfp_t); extern int ipath_diag_inuse; irqreturn_t ipath_intr(int irq, void *devid); -void ipath_decode_err(char *buf, size_t blen, ipath_err_t err); +int ipath_decode_err(char *buf, size_t blen, ipath_err_t err); #if __IPATH_INFO || __IPATH_DBG extern const char *ipath_ibcstatus_str[]; #endif @@ -701,6 +700,8 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv); #define IPATH_PORT_WAITING_RCV 2 /* waiting for a PIO buffer to be available */ #define IPATH_PORT_WAITING_PIO 3 + /* master has not finished initializing */ +#define IPATH_PORT_MASTER_UNINIT 4 /* free up any allocated data at closes */ void ipath_free_data(struct ipath_portdata *dd); @@ -711,6 +712,7 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *); void ipath_init_iba6110_funcs(struct ipath_devdata *); void ipath_get_eeprom_info(struct ipath_devdata *); u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg); +void ipath_disarm_senderrbufs(struct ipath_devdata *, int); /* * number of words used for protocol header if not set by ipath_userinit(); @@ -754,8 +756,6 @@ int ipath_eeprom_write(struct ipath_devdata *, u8, const void *, int); /* these are used for the registers that vary with port */ void ipath_write_kreg_port(const struct ipath_devdata *, ipath_kreg, unsigned, u64); -u64 ipath_read_kreg64_port(const struct ipath_devdata *, ipath_kreg, - unsigned); /* * We could have a single register get/put routine, that takes a group type, @@ -897,6 +897,8 @@ dma_addr_t ipath_map_single(struct pci_dev *, void *, size_t, int); extern unsigned ipath_debug; /* debugging bit mask */ +#define IPATH_MAX_PARITY_ATTEMPTS 10000 /* max times to try recovery */ + const char *ipath_get_unit_name(int unit); extern struct mutex ipath_mutex; diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c index 851763d..dd487c1 100644 --- a/drivers/infiniband/hw/ipath/ipath_keys.c +++ b/drivers/infiniband/hw/ipath/ipath_keys.c @@ -61,7 +61,7 @@ int ipath_alloc_lkey(struct ipath_lkey_table *rkt, struct ipath_mregion *mr) r = (r + 1) & (rkt->max - 1); if (r == n) { spin_unlock_irqrestore(&rkt->lock, flags); - ipath_dbg(KERN_INFO "LKEY table full\n"); + ipath_dbg("LKEY table full\n"); ret = 0; goto bail; } @@ -133,6 +133,12 @@ int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge, * being reversible by calling bus_to_virt(). */ if (sge->lkey == 0) { + struct ipath_pd *pd = to_ipd(qp->ibqp.pd); + + if (pd->user) { + ret = 0; + goto bail; + } isge->mr = NULL; isge->vaddr = (void *) sge->addr; isge->length = sge->length; @@ -206,6 +212,12 @@ int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss, * (see ipath_get_dma_mr and ipath_dma.c). */ if (rkey == 0) { + struct ipath_pd *pd = to_ipd(qp->ibqp.pd); + + if (pd->user) { + ret = 0; + goto bail; + } sge->mr = NULL; sge->vaddr = (void *) vaddr; sge->length = len; diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c index 8cc8598..31e7073 100644 --- a/drivers/infiniband/hw/ipath/ipath_mr.c +++ b/drivers/infiniband/hw/ipath/ipath_mr.c @@ -210,9 +210,15 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region, m = 0; n = 0; list_for_each_entry(chunk, ®ion->chunk_list, list) { - for (i = 0; i < chunk->nmap; i++) { - mr->mr.map[m]->segs[n].vaddr = - page_address(chunk->page_list[i].page); + for (i = 0; i < chunk->nents; i++) { + void *vaddr; + + vaddr = page_address(chunk->page_list[i].page); + if (!vaddr) { + ret = ERR_PTR(-EINVAL); + goto bail; + } + mr->mr.map[m]->segs[n].vaddr = vaddr; mr->mr.map[m]->segs[n].length = region->page_size; n++; if (n == IPATH_SEGSZ) { diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c index 64f07b1..16db9ac 100644 --- a/drivers/infiniband/hw/ipath/ipath_qp.c +++ b/drivers/infiniband/hw/ipath/ipath_qp.c @@ -81,11 +81,51 @@ static u32 credit_table[31] = { 32768 /* 1E */ }; -static u32 alloc_qpn(struct ipath_qp_table *qpt) + +static void get_map_page(struct ipath_qp_table *qpt, struct qpn_map *map) +{ + unsigned long page = get_zeroed_page(GFP_KERNEL); + unsigned long flags; + + /* + * Free the page if someone raced with us installing it. + */ + + spin_lock_irqsave(&qpt->lock, flags); + if (map->page) + free_page(page); + else + map->page = (void *)page; + spin_unlock_irqrestore(&qpt->lock, flags); +} + + +static int alloc_qpn(struct ipath_qp_table *qpt, enum ib_qp_type type) { u32 i, offset, max_scan, qpn; struct qpn_map *map; - u32 ret; + u32 ret = -1; + + if (type == IB_QPT_SMI) + ret = 0; + else if (type == IB_QPT_GSI) + ret = 1; + + if (ret != -1) { + map = &qpt->map[0]; + if (unlikely(!map->page)) { + get_map_page(qpt, map); + if (unlikely(!map->page)) { + ret = -ENOMEM; + goto bail; + } + } + if (!test_and_set_bit(ret, map->page)) + atomic_dec(&map->n_free); + else + ret = -EBUSY; + goto bail; + } qpn = qpt->last + 1; if (qpn >= QPN_MAX) @@ -95,19 +135,7 @@ static u32 alloc_qpn(struct ipath_qp_table *qpt) max_scan = qpt->nmaps - !offset; for (i = 0;;) { if (unlikely(!map->page)) { - unsigned long page = get_zeroed_page(GFP_KERNEL); - unsigned long flags; - - /* - * Free the page if someone raced with us - * installing it: - */ - spin_lock_irqsave(&qpt->lock, flags); - if (map->page) - free_page(page); - else - map->page = (void *)page; - spin_unlock_irqrestore(&qpt->lock, flags); + get_map_page(qpt, map); if (unlikely(!map->page)) break; } @@ -151,7 +179,7 @@ static u32 alloc_qpn(struct ipath_qp_table *qpt) qpn = mk_qpn(qpt, map, offset); } - ret = 0; + ret = -ENOMEM; bail: return ret; @@ -180,29 +208,19 @@ static int ipath_alloc_qpn(struct ipath_qp_table *qpt, struct ipath_qp *qp, enum ib_qp_type type) { unsigned long flags; - u32 qpn; int ret; - if (type == IB_QPT_SMI) - qpn = 0; - else if (type == IB_QPT_GSI) - qpn = 1; - else { - /* Allocate the next available QPN */ - qpn = alloc_qpn(qpt); - if (qpn == 0) { - ret = -ENOMEM; - goto bail; - } - } - qp->ibqp.qp_num = qpn; + ret = alloc_qpn(qpt, type); + if (ret < 0) + goto bail; + qp->ibqp.qp_num = ret; /* Add the QP to the hash table. */ spin_lock_irqsave(&qpt->lock, flags); - qpn %= qpt->max; - qp->next = qpt->table[qpn]; - qpt->table[qpn] = qp; + ret %= qpt->max; + qp->next = qpt->table[ret]; + qpt->table[ret] = qp; atomic_inc(&qp->refcount); spin_unlock_irqrestore(&qpt->lock, flags); @@ -245,9 +263,7 @@ static void ipath_free_qp(struct ipath_qp_table *qpt, struct ipath_qp *qp) if (!fnd) return; - /* If QPN is not reserved, mark QPN free in the bitmap. */ - if (qp->ibqp.qp_num > 1) - free_qpn(qpt, qp->ibqp.qp_num); + free_qpn(qpt, qp->ibqp.qp_num); wait_event(qp->wait, !atomic_read(&qp->refcount)); } @@ -270,11 +286,10 @@ void ipath_free_all_qps(struct ipath_qp_table *qpt) while (qp) { nqp = qp->next; - if (qp->ibqp.qp_num > 1) - free_qpn(qpt, qp->ibqp.qp_num); + free_qpn(qpt, qp->ibqp.qp_num); if (!atomic_dec_and_test(&qp->refcount) || !ipath_destroy_qp(&qp->ibqp)) - ipath_dbg(KERN_INFO "QP memory leak!\n"); + ipath_dbg("QP memory leak!\n"); qp = nqp; } } @@ -320,7 +335,8 @@ static void ipath_reset_qp(struct ipath_qp *qp) qp->remote_qpn = 0; qp->qkey = 0; qp->qp_access_flags = 0; - clear_bit(IPATH_S_BUSY, &qp->s_flags); + qp->s_busy = 0; + qp->s_flags &= ~IPATH_S_SIGNAL_REQ_WR; qp->s_hdrwords = 0; qp->s_psn = 0; qp->r_psn = 0; @@ -333,7 +349,6 @@ static void ipath_reset_qp(struct ipath_qp *qp) qp->r_state = IB_OPCODE_UC_SEND_LAST; } qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; - qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE; qp->r_nak_state = 0; qp->r_wrid_valid = 0; qp->s_rnr_timeout = 0; @@ -344,6 +359,10 @@ static void ipath_reset_qp(struct ipath_qp *qp) qp->s_ssn = 1; qp->s_lsn = 0; qp->s_wait_credit = 0; + memset(qp->s_ack_queue, 0, sizeof(qp->s_ack_queue)); + qp->r_head_ack_queue = 0; + qp->s_tail_ack_queue = 0; + qp->s_num_rd_atomic = 0; if (qp->r_rq.wq) { qp->r_rq.wq->head = 0; qp->r_rq.wq->tail = 0; @@ -357,7 +376,7 @@ static void ipath_reset_qp(struct ipath_qp *qp) * @err: the receive completion error to signal if a RWQE is active * * Flushes both send and receive work queues. - * QP s_lock should be held and interrupts disabled. + * The QP s_lock should be held and interrupts disabled. */ void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err) @@ -365,7 +384,7 @@ void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err) struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ib_wc wc; - ipath_dbg(KERN_INFO "QP%d/%d in error state\n", + ipath_dbg("QP%d/%d in error state\n", qp->ibqp.qp_num, qp->remote_qpn); spin_lock(&dev->pending_lock); @@ -389,6 +408,8 @@ void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err) wc.port_num = 0; if (qp->r_wrid_valid) { qp->r_wrid_valid = 0; + wc.wr_id = qp->r_wr_id; + wc.opcode = IB_WC_RECV; wc.status = err; ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1); } @@ -503,13 +524,17 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, attr->path_mig_state != IB_MIG_REARM) goto inval; + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + if (attr->max_dest_rd_atomic > IPATH_MAX_RDMA_ATOMIC) + goto inval; + switch (new_state) { case IB_QPS_RESET: ipath_reset_qp(qp); break; case IB_QPS_ERR: - ipath_error_qp(qp, IB_WC_GENERAL_ERR); + ipath_error_qp(qp, IB_WC_WR_FLUSH_ERR); break; default: @@ -559,6 +584,12 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, if (attr_mask & IB_QP_QKEY) qp->qkey = attr->qkey; + if (attr_mask & IB_QP_MAX_DEST_RD_ATOMIC) + qp->r_max_rd_atomic = attr->max_dest_rd_atomic; + + if (attr_mask & IB_QP_MAX_QP_RD_ATOMIC) + qp->s_max_rd_atomic = attr->max_rd_atomic; + qp->state = new_state; spin_unlock_irqrestore(&qp->s_lock, flags); @@ -598,8 +629,8 @@ int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, attr->alt_pkey_index = 0; attr->en_sqd_async_notify = 0; attr->sq_draining = 0; - attr->max_rd_atomic = 1; - attr->max_dest_rd_atomic = 1; + attr->max_rd_atomic = qp->s_max_rd_atomic; + attr->max_dest_rd_atomic = qp->r_max_rd_atomic; attr->min_rnr_timer = qp->r_min_rnr_timer; attr->port_num = 1; attr->timeout = qp->timeout; @@ -614,7 +645,7 @@ int ipath_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, init_attr->recv_cq = qp->ibqp.recv_cq; init_attr->srq = qp->ibqp.srq; init_attr->cap = attr->cap; - if (qp->s_flags & (1 << IPATH_S_SIGNAL_REQ_WR)) + if (qp->s_flags & IPATH_S_SIGNAL_REQ_WR) init_attr->sq_sig_type = IB_SIGNAL_REQ_WR; else init_attr->sq_sig_type = IB_SIGNAL_ALL_WR; @@ -786,7 +817,7 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd, qp->s_size = init_attr->cap.max_send_wr + 1; qp->s_max_sge = init_attr->cap.max_send_sge; if (init_attr->sq_sig_type == IB_SIGNAL_REQ_WR) - qp->s_flags = 1 << IPATH_S_SIGNAL_REQ_WR; + qp->s_flags = IPATH_S_SIGNAL_REQ_WR; else qp->s_flags = 0; dev = to_idev(ibpd->device); @@ -958,7 +989,7 @@ bail: * @wc: the WC responsible for putting the QP in this state * * Flushes the send work queue. - * The QP s_lock should be held. + * The QP s_lock should be held and interrupts disabled. */ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc) @@ -966,7 +997,7 @@ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc) struct ipath_ibdev *dev = to_idev(qp->ibqp.device); struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last); - ipath_dbg(KERN_INFO "Send queue error on QP%d/%d: err: %d\n", + ipath_dbg("Send queue error on QP%d/%d: err: %d\n", qp->ibqp.qp_num, qp->remote_qpn, wc->status); spin_lock(&dev->pending_lock); @@ -984,12 +1015,12 @@ void ipath_sqerror_qp(struct ipath_qp *qp, struct ib_wc *wc) wc->status = IB_WC_WR_FLUSH_ERR; while (qp->s_last != qp->s_head) { + wqe = get_swqe_ptr(qp, qp->s_last); wc->wr_id = wqe->wr.wr_id; wc->opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; ipath_cq_enter(to_icq(qp->ibqp.send_cq), wc, 1); if (++qp->s_last >= qp->s_size) qp->s_last = 0; - wqe = get_swqe_ptr(qp, qp->s_last); } qp->s_cur = qp->s_tail = qp->s_head; qp->state = IB_QPS_SQE; diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c index 5ff20cb..b4b88d0 100644 --- a/drivers/infiniband/hw/ipath/ipath_rc.c +++ b/drivers/infiniband/hw/ipath/ipath_rc.c @@ -37,6 +37,19 @@ /* cut down ridiculously long IB macro names */ #define OP(x) IB_OPCODE_RC_##x +static u32 restart_sge(struct ipath_sge_state *ss, struct ipath_swqe *wqe, + u32 psn, u32 pmtu) +{ + u32 len; + + len = ((psn - wqe->psn) & IPATH_PSN_MASK) * pmtu; + ss->sge = wqe->sg_list[0]; + ss->sg_list = wqe->sg_list + 1; + ss->num_sge = wqe->wr.num_sge; + ipath_skip_sge(ss, len); + return wqe->length - len; +} + /** * ipath_init_restart- initialize the qp->s_sge after a restart * @qp: the QP who's SGE we're restarting @@ -47,15 +60,9 @@ static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe) { struct ipath_ibdev *dev; - u32 len; - len = ((qp->s_psn - wqe->psn) & IPATH_PSN_MASK) * - ib_mtu_enum_to_int(qp->path_mtu); - qp->s_sge.sge = wqe->sg_list[0]; - qp->s_sge.sg_list = wqe->sg_list + 1; - qp->s_sge.num_sge = wqe->wr.num_sge; - ipath_skip_sge(&qp->s_sge, len); - qp->s_len = wqe->length - len; + qp->s_len = restart_sge(&qp->s_sge, wqe, qp->s_psn, + ib_mtu_enum_to_int(qp->path_mtu)); dev = to_idev(qp->ibqp.device); spin_lock(&dev->pending_lock); if (list_empty(&qp->timerwait)) @@ -70,107 +77,123 @@ static void ipath_init_restart(struct ipath_qp *qp, struct ipath_swqe *wqe) * @ohdr: a pointer to the IB header being constructed * @pmtu: the path MTU * - * Return bth0 if constructed; otherwise, return 0. + * Return 1 if constructed; otherwise, return 0. + * Note that we are in the responder's side of the QP context. * Note the QP s_lock must be held. */ -u32 ipath_make_rc_ack(struct ipath_qp *qp, - struct ipath_other_headers *ohdr, - u32 pmtu) +static int ipath_make_rc_ack(struct ipath_qp *qp, + struct ipath_other_headers *ohdr, + u32 pmtu, u32 *bth0p, u32 *bth2p) { + struct ipath_ack_entry *e; u32 hwords; u32 len; u32 bth0; + u32 bth2; /* header size in 32-bit words LRH+BTH = (8+12)/4. */ hwords = 5; - /* - * Send a response. Note that we are in the responder's - * side of the QP context. - */ switch (qp->s_ack_state) { - case OP(RDMA_READ_REQUEST): - qp->s_cur_sge = &qp->s_rdma_sge; - len = qp->s_rdma_len; - if (len > pmtu) { - len = pmtu; - qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); - } else - qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); - qp->s_rdma_len -= len; + case OP(RDMA_READ_RESPONSE_LAST): + case OP(RDMA_READ_RESPONSE_ONLY): + case OP(ATOMIC_ACKNOWLEDGE): + qp->s_ack_state = OP(ACKNOWLEDGE); + /* FALLTHROUGH */ + case OP(ACKNOWLEDGE): + /* Check for no next entry in the queue. */ + if (qp->r_head_ack_queue == qp->s_tail_ack_queue) { + if (qp->s_flags & IPATH_S_ACK_PENDING) + goto normal; + goto bail; + } + + e = &qp->s_ack_queue[qp->s_tail_ack_queue]; + if (e->opcode == OP(RDMA_READ_REQUEST)) { + /* Copy SGE state in case we need to resend */ + qp->s_ack_rdma_sge = e->rdma_sge; + qp->s_cur_sge = &qp->s_ack_rdma_sge; + len = e->rdma_sge.sge.sge_length; + if (len > pmtu) { + len = pmtu; + qp->s_ack_state = OP(RDMA_READ_RESPONSE_FIRST); + } else { + qp->s_ack_state = OP(RDMA_READ_RESPONSE_ONLY); + if (++qp->s_tail_ack_queue > + IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; + } + ohdr->u.aeth = ipath_compute_aeth(qp); + hwords++; + qp->s_ack_rdma_psn = e->psn; + bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; + } else { + /* COMPARE_SWAP or FETCH_ADD */ + qp->s_cur_sge = NULL; + len = 0; + qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); + ohdr->u.at.aeth = ipath_compute_aeth(qp); + ohdr->u.at.atomic_ack_eth[0] = + cpu_to_be32(e->atomic_data >> 32); + ohdr->u.at.atomic_ack_eth[1] = + cpu_to_be32(e->atomic_data); + hwords += sizeof(ohdr->u.at) / sizeof(u32); + bth2 = e->psn; + if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; + } bth0 = qp->s_ack_state << 24; - ohdr->u.aeth = ipath_compute_aeth(qp); - hwords++; break; case OP(RDMA_READ_RESPONSE_FIRST): qp->s_ack_state = OP(RDMA_READ_RESPONSE_MIDDLE); /* FALLTHROUGH */ case OP(RDMA_READ_RESPONSE_MIDDLE): - qp->s_cur_sge = &qp->s_rdma_sge; - len = qp->s_rdma_len; + len = qp->s_ack_rdma_sge.sge.sge_length; if (len > pmtu) len = pmtu; else { ohdr->u.aeth = ipath_compute_aeth(qp); hwords++; qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); + if (++qp->s_tail_ack_queue > IPATH_MAX_RDMA_ATOMIC) + qp->s_tail_ack_queue = 0; } - qp->s_rdma_len -= len; bth0 = qp->s_ack_state << 24; - break; - - case OP(RDMA_READ_RESPONSE_LAST): - case OP(RDMA_READ_RESPONSE_ONLY): - /* - * We have to prevent new requests from changing - * the r_sge state while a ipath_verbs_send() - * is in progress. - */ - qp->s_ack_state = OP(ACKNOWLEDGE); - bth0 = 0; - goto bail; - - case OP(COMPARE_SWAP): - case OP(FETCH_ADD): - qp->s_cur_sge = NULL; - len = 0; - /* - * Set the s_ack_state so the receive interrupt handler - * won't try to send an ACK (out of order) until this one - * is actually sent. - */ - qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); - bth0 = OP(ATOMIC_ACKNOWLEDGE) << 24; - ohdr->u.at.aeth = ipath_compute_aeth(qp); - ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data); - hwords += sizeof(ohdr->u.at) / 4; + bth2 = qp->s_ack_rdma_psn++ & IPATH_PSN_MASK; break; default: - /* Send a regular ACK. */ - qp->s_cur_sge = NULL; - len = 0; + normal: /* - * Set the s_ack_state so the receive interrupt handler - * won't try to send an ACK (out of order) until this one - * is actually sent. + * Send a regular ACK. + * Set the s_ack_state so we wait until after sending + * the ACK before setting s_ack_state to ACKNOWLEDGE + * (see above). */ - qp->s_ack_state = OP(RDMA_READ_RESPONSE_LAST); - bth0 = OP(ACKNOWLEDGE) << 24; + qp->s_ack_state = OP(ATOMIC_ACKNOWLEDGE); + qp->s_flags &= ~IPATH_S_ACK_PENDING; + qp->s_cur_sge = NULL; if (qp->s_nak_state) - ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | - (qp->s_nak_state << - IPATH_AETH_CREDIT_SHIFT)); + ohdr->u.aeth = + cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | + (qp->s_nak_state << + IPATH_AETH_CREDIT_SHIFT)); else ohdr->u.aeth = ipath_compute_aeth(qp); hwords++; + len = 0; + bth0 = OP(ACKNOWLEDGE) << 24; + bth2 = qp->s_ack_psn & IPATH_PSN_MASK; } qp->s_hdrwords = hwords; qp->s_cur_size = len; + *bth0p = bth0; + *bth2p = bth2; + return 1; bail: - return bth0; + return 0; } /** @@ -197,9 +220,16 @@ int ipath_make_rc_req(struct ipath_qp *qp, u32 bth2; char newreq; + /* Sending responses has higher priority over sending requests. */ + if ((qp->r_head_ack_queue != qp->s_tail_ack_queue || + (qp->s_flags & IPATH_S_ACK_PENDING) || + qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE) && + ipath_make_rc_ack(qp, ohdr, pmtu, bth0p, bth2p)) + goto done; + if (!(ib_ipath_state_ops[qp->state] & IPATH_PROCESS_SEND_OK) || qp->s_rnr_timeout) - goto done; + goto bail; /* Limit the number of packets sent without an ACK. */ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) { @@ -210,7 +240,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, list_add_tail(&qp->timerwait, &dev->pending[dev->pending_index]); spin_unlock(&dev->pending_lock); - goto done; + goto bail; } /* header size in 32-bit words LRH+BTH = (8+12)/4. */ @@ -232,7 +262,16 @@ int ipath_make_rc_req(struct ipath_qp *qp, if (qp->s_cur == qp->s_tail) { /* Check if send work queue is empty. */ if (qp->s_tail == qp->s_head) - goto done; + goto bail; + /* + * If a fence is requested, wait for previous + * RDMA read and atomic operations to finish. + */ + if ((wqe->wr.send_flags & IB_SEND_FENCE) && + qp->s_num_rd_atomic) { + qp->s_flags |= IPATH_S_FENCE_PENDING; + goto bail; + } wqe->psn = qp->s_next_psn; newreq = 1; } @@ -250,7 +289,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, /* If no credit, return. */ if (qp->s_lsn != (u32) -1 && ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) - goto done; + goto bail; wqe->lpsn = wqe->psn; if (len > pmtu) { wqe->lpsn += (len - 1) / pmtu; @@ -281,13 +320,13 @@ int ipath_make_rc_req(struct ipath_qp *qp, /* If no credit, return. */ if (qp->s_lsn != (u32) -1 && ipath_cmp24(wqe->ssn, qp->s_lsn + 1) > 0) - goto done; + goto bail; ohdr->u.rc.reth.vaddr = cpu_to_be64(wqe->wr.wr.rdma.remote_addr); ohdr->u.rc.reth.rkey = cpu_to_be32(wqe->wr.wr.rdma.rkey); ohdr->u.rc.reth.length = cpu_to_be32(len); - hwords += sizeof(struct ib_reth) / 4; + hwords += sizeof(struct ib_reth) / sizeof(u32); wqe->lpsn = wqe->psn; if (len > pmtu) { wqe->lpsn += (len - 1) / pmtu; @@ -312,14 +351,17 @@ int ipath_make_rc_req(struct ipath_qp *qp, break; case IB_WR_RDMA_READ: - ohdr->u.rc.reth.vaddr = - cpu_to_be64(wqe->wr.wr.rdma.remote_addr); - ohdr->u.rc.reth.rkey = - cpu_to_be32(wqe->wr.wr.rdma.rkey); - ohdr->u.rc.reth.length = cpu_to_be32(len); - qp->s_state = OP(RDMA_READ_REQUEST); - hwords += sizeof(ohdr->u.rc.reth) / 4; + /* + * Don't allow more operations to be started + * than the QP limits allow. + */ if (newreq) { + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= IPATH_S_RDMAR_PENDING; + goto bail; + } + qp->s_num_rd_atomic++; if (qp->s_lsn != (u32) -1) qp->s_lsn++; /* @@ -330,6 +372,13 @@ int ipath_make_rc_req(struct ipath_qp *qp, qp->s_next_psn += (len - 1) / pmtu; wqe->lpsn = qp->s_next_psn++; } + ohdr->u.rc.reth.vaddr = + cpu_to_be64(wqe->wr.wr.rdma.remote_addr); + ohdr->u.rc.reth.rkey = + cpu_to_be32(wqe->wr.wr.rdma.rkey); + ohdr->u.rc.reth.length = cpu_to_be32(len); + qp->s_state = OP(RDMA_READ_REQUEST); + hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); ss = NULL; len = 0; if (++qp->s_cur == qp->s_size) @@ -338,32 +387,48 @@ int ipath_make_rc_req(struct ipath_qp *qp, case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) - qp->s_state = OP(COMPARE_SWAP); - else - qp->s_state = OP(FETCH_ADD); - ohdr->u.atomic_eth.vaddr = cpu_to_be64( - wqe->wr.wr.atomic.remote_addr); - ohdr->u.atomic_eth.rkey = cpu_to_be32( - wqe->wr.wr.atomic.rkey); - ohdr->u.atomic_eth.swap_data = cpu_to_be64( - wqe->wr.wr.atomic.swap); - ohdr->u.atomic_eth.compare_data = cpu_to_be64( - wqe->wr.wr.atomic.compare_add); - hwords += sizeof(struct ib_atomic_eth) / 4; + /* + * Don't allow more operations to be started + * than the QP limits allow. + */ if (newreq) { + if (qp->s_num_rd_atomic >= + qp->s_max_rd_atomic) { + qp->s_flags |= IPATH_S_RDMAR_PENDING; + goto bail; + } + qp->s_num_rd_atomic++; if (qp->s_lsn != (u32) -1) qp->s_lsn++; wqe->lpsn = wqe->psn; } - if (++qp->s_cur == qp->s_size) - qp->s_cur = 0; + if (wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + qp->s_state = OP(COMPARE_SWAP); + ohdr->u.atomic_eth.swap_data = cpu_to_be64( + wqe->wr.wr.atomic.swap); + ohdr->u.atomic_eth.compare_data = cpu_to_be64( + wqe->wr.wr.atomic.compare_add); + } else { + qp->s_state = OP(FETCH_ADD); + ohdr->u.atomic_eth.swap_data = cpu_to_be64( + wqe->wr.wr.atomic.compare_add); + ohdr->u.atomic_eth.compare_data = 0; + } + ohdr->u.atomic_eth.vaddr[0] = cpu_to_be32( + wqe->wr.wr.atomic.remote_addr >> 32); + ohdr->u.atomic_eth.vaddr[1] = cpu_to_be32( + wqe->wr.wr.atomic.remote_addr); + ohdr->u.atomic_eth.rkey = cpu_to_be32( + wqe->wr.wr.atomic.rkey); + hwords += sizeof(struct ib_atomic_eth) / sizeof(u32); ss = NULL; len = 0; + if (++qp->s_cur == qp->s_size) + qp->s_cur = 0; break; default: - goto done; + goto bail; } qp->s_sge.sge = wqe->sg_list[0]; qp->s_sge.sg_list = wqe->sg_list + 1; @@ -379,7 +444,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, qp->s_psn = wqe->lpsn + 1; else { qp->s_psn++; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; } /* @@ -406,7 +471,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, /* FALLTHROUGH */ case OP(SEND_MIDDLE): bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = &qp->s_sge; len = qp->s_len; @@ -442,7 +507,7 @@ int ipath_make_rc_req(struct ipath_qp *qp, /* FALLTHROUGH */ case OP(RDMA_WRITE_MIDDLE): bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = &qp->s_sge; len = qp->s_len; @@ -479,9 +544,9 @@ int ipath_make_rc_req(struct ipath_qp *qp, cpu_to_be32(wqe->wr.wr.rdma.rkey); ohdr->u.rc.reth.length = cpu_to_be32(qp->s_len); qp->s_state = OP(RDMA_READ_REQUEST); - hwords += sizeof(ohdr->u.rc.reth) / 4; + hwords += sizeof(ohdr->u.rc.reth) / sizeof(u32); bth2 = qp->s_psn++ & IPATH_PSN_MASK; - if ((int)(qp->s_psn - qp->s_next_psn) > 0) + if (ipath_cmp24(qp->s_psn, qp->s_next_psn) > 0) qp->s_next_psn = qp->s_psn; ss = NULL; len = 0; @@ -489,20 +554,6 @@ int ipath_make_rc_req(struct ipath_qp *qp, if (qp->s_cur == qp->s_size) qp->s_cur = 0; break; - - case OP(RDMA_READ_REQUEST): - case OP(COMPARE_SWAP): - case OP(FETCH_ADD): - /* - * We shouldn't start anything new until this request is - * finished. The ACK will handle rescheduling us. XXX The - * number of outstanding ones is negotiated at connection - * setup time (see pg. 258,289)? XXX Also, if we support - * multiple outstanding requests, we need to check the WQE - * IB_SEND_FENCE flag and not send a new request if a RDMA - * read or atomic is pending. - */ - goto done; } if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0) bth2 |= 1 << 31; /* Request ACK. */ @@ -512,9 +563,10 @@ int ipath_make_rc_req(struct ipath_qp *qp, qp->s_cur_size = len; *bth0p = bth0 | (qp->s_state << 24); *bth2p = bth2; +done: return 1; -done: +bail: return 0; } @@ -524,7 +576,8 @@ done: * * This is called from ipath_rc_rcv() and only uses the receive * side QP state. - * Note that RDMA reads are handled in the send side QP state and tasklet. + * Note that RDMA reads and atomics are handled in the + * send side QP state and tasklet. */ static void send_rc_ack(struct ipath_qp *qp) { @@ -535,6 +588,10 @@ static void send_rc_ack(struct ipath_qp *qp) struct ipath_ib_header hdr; struct ipath_other_headers *ohdr; + /* Don't send ACK or NAK if a RDMA read or atomic is pending. */ + if (qp->r_head_ack_queue != qp->s_tail_ack_queue) + goto queue_ack; + /* Construct the header. */ ohdr = &hdr.u.oth; lrh0 = IPATH_LRH_BTH; @@ -548,19 +605,14 @@ static void send_rc_ack(struct ipath_qp *qp) lrh0 = IPATH_LRH_GRH; } /* read pkey_index w/o lock (its atomic) */ - bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index); + bth0 = ipath_get_pkey(dev->dd, qp->s_pkey_index) | + OP(ACKNOWLEDGE) << 24; if (qp->r_nak_state) ohdr->u.aeth = cpu_to_be32((qp->r_msn & IPATH_MSN_MASK) | (qp->r_nak_state << IPATH_AETH_CREDIT_SHIFT)); else ohdr->u.aeth = ipath_compute_aeth(qp); - if (qp->r_ack_state >= OP(COMPARE_SWAP)) { - bth0 |= OP(ATOMIC_ACKNOWLEDGE) << 24; - ohdr->u.at.atomic_ack_eth = cpu_to_be64(qp->r_atomic_data); - hwords += sizeof(ohdr->u.at.atomic_ack_eth) / 4; - } else - bth0 |= OP(ACKNOWLEDGE) << 24; lrh0 |= qp->remote_ah_attr.sl << 4; hdr.lrh[0] = cpu_to_be16(lrh0); hdr.lrh[1] = cpu_to_be16(qp->remote_ah_attr.dlid); @@ -574,31 +626,31 @@ static void send_rc_ack(struct ipath_qp *qp) * If we can send the ACK, clear the ACK state. */ if (ipath_verbs_send(dev->dd, hwords, (u32 *) &hdr, 0, NULL) == 0) { - qp->r_ack_state = OP(ACKNOWLEDGE); dev->n_unicast_xmit++; - } else { - /* - * We are out of PIO buffers at the moment. - * Pass responsibility for sending the ACK to the - * send tasklet so that when a PIO buffer becomes - * available, the ACK is sent ahead of other outgoing - * packets. - */ - dev->n_rc_qacks++; - spin_lock_irq(&qp->s_lock); - /* Don't coalesce if a RDMA read or atomic is pending. */ - if (qp->s_ack_state == OP(ACKNOWLEDGE) || - qp->s_ack_state < OP(RDMA_READ_REQUEST)) { - qp->s_ack_state = qp->r_ack_state; - qp->s_nak_state = qp->r_nak_state; - qp->s_ack_psn = qp->r_ack_psn; - qp->r_ack_state = OP(ACKNOWLEDGE); - } - spin_unlock_irq(&qp->s_lock); - - /* Call ipath_do_rc_send() in another thread. */ - tasklet_hi_schedule(&qp->s_task); + goto done; } + + /* + * We are out of PIO buffers at the moment. + * Pass responsibility for sending the ACK to the + * send tasklet so that when a PIO buffer becomes + * available, the ACK is sent ahead of other outgoing + * packets. + */ + dev->n_rc_qacks++; + +queue_ack: + spin_lock_irq(&qp->s_lock); + qp->s_flags |= IPATH_S_ACK_PENDING; + qp->s_nak_state = qp->r_nak_state; + qp->s_ack_psn = qp->r_ack_psn; + spin_unlock_irq(&qp->s_lock); + + /* Call ipath_do_rc_send() in another thread. */ + tasklet_hi_schedule(&qp->s_task); + +done: + return; } /** @@ -727,7 +779,7 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc) if (wqe->wr.opcode == IB_WR_RDMA_READ) dev->n_rc_resends++; else - dev->n_rc_resends += (int)qp->s_psn - (int)psn; + dev->n_rc_resends += (qp->s_psn - psn) & IPATH_PSN_MASK; reset_psn(qp, psn); tasklet_hi_schedule(&qp->s_task); @@ -775,10 +827,6 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) list_del_init(&qp->timerwait); spin_unlock(&dev->pending_lock); - /* Nothing is pending to ACK/NAK. */ - if (unlikely(qp->s_last == qp->s_tail)) - goto bail; - /* * Note that NAKs implicitly ACK outstanding SEND and RDMA write * requests and implicitly NAK RDMA read and atomic requests issued @@ -806,7 +854,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) */ if ((wqe->wr.opcode == IB_WR_RDMA_READ && (opcode != OP(RDMA_READ_RESPONSE_LAST) || - ipath_cmp24(ack_psn, wqe->lpsn) != 0)) || + ipath_cmp24(ack_psn, wqe->lpsn) != 0)) || ((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) && (opcode != OP(ATOMIC_ACKNOWLEDGE) || @@ -824,20 +872,33 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) */ goto bail; } - if (wqe->wr.opcode == IB_WR_RDMA_READ || - wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || - wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) - tasklet_hi_schedule(&qp->s_task); + if (qp->s_num_rd_atomic && + (wqe->wr.opcode == IB_WR_RDMA_READ || + wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP || + wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)) { + qp->s_num_rd_atomic--; + /* Restart sending task if fence is complete */ + if ((qp->s_flags & IPATH_S_FENCE_PENDING) && + !qp->s_num_rd_atomic) { + qp->s_flags &= ~IPATH_S_FENCE_PENDING; + tasklet_hi_schedule(&qp->s_task); + } else if (qp->s_flags & IPATH_S_RDMAR_PENDING) { + qp->s_flags &= ~IPATH_S_RDMAR_PENDING; + tasklet_hi_schedule(&qp->s_task); + } + } /* Post a send completion queue entry if requested. */ - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wqe->wr.wr_id; wc.status = IB_WC_SUCCESS; wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; wc.vendor_err = 0; wc.byte_len = wqe->length; + wc.imm_data = 0; wc.qp = &qp->ibqp; wc.src_qp = qp->remote_qpn; + wc.wc_flags = 0; wc.pkey_index = 0; wc.slid = qp->remote_ah_attr.dlid; wc.sl = qp->remote_ah_attr.sl; @@ -854,15 +915,19 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) if (qp->s_last == qp->s_cur) { if (++qp->s_cur >= qp->s_size) qp->s_cur = 0; + qp->s_last = qp->s_cur; + if (qp->s_last == qp->s_tail) + break; wqe = get_swqe_ptr(qp, qp->s_cur); qp->s_state = OP(SEND_LAST); qp->s_psn = wqe->psn; + } else { + if (++qp->s_last >= qp->s_size) + qp->s_last = 0; + if (qp->s_last == qp->s_tail) + break; + wqe = get_swqe_ptr(qp, qp->s_last); } - if (++qp->s_last >= qp->s_size) - qp->s_last = 0; - wqe = get_swqe_ptr(qp, qp->s_last); - if (qp->s_last == qp->s_tail) - break; } switch (aeth >> 29) { @@ -874,6 +939,18 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) list_add_tail(&qp->timerwait, &dev->pending[dev->pending_index]); spin_unlock(&dev->pending_lock); + /* + * If we get a partial ACK for a resent operation, + * we can stop resending the earlier packets and + * continue with the next packet the receiver wants. + */ + if (ipath_cmp24(qp->s_psn, psn) <= 0) { + reset_psn(qp, psn + 1); + tasklet_hi_schedule(&qp->s_task); + } + } else if (ipath_cmp24(qp->s_psn, psn) <= 0) { + qp->s_state = OP(SEND_LAST); + qp->s_psn = psn + 1; } ipath_get_credit(qp, aeth); qp->s_rnr_retry = qp->s_rnr_retry_cnt; @@ -884,22 +961,23 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) case 1: /* RNR NAK */ dev->n_rnr_naks++; + if (qp->s_last == qp->s_tail) + goto bail; if (qp->s_rnr_retry == 0) { - if (qp->s_last == qp->s_tail) - goto bail; - wc.status = IB_WC_RNR_RETRY_EXC_ERR; goto class_b; } if (qp->s_rnr_retry_cnt < 7) qp->s_rnr_retry--; - if (qp->s_last == qp->s_tail) - goto bail; /* The last valid PSN is the previous PSN. */ update_last_psn(qp, psn - 1); - dev->n_rc_resends += (int)qp->s_psn - (int)psn; + if (wqe->wr.opcode == IB_WR_RDMA_READ) + dev->n_rc_resends++; + else + dev->n_rc_resends += + (qp->s_psn - psn) & IPATH_PSN_MASK; reset_psn(qp, psn); @@ -910,26 +988,20 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode) goto bail; case 3: /* NAK */ - /* The last valid PSN seen is the previous request's. */ - if (qp->s_last != qp->s_tail) - update_last_psn(qp, wqe->psn - 1); + if (qp->s_last == qp->s_tail) + goto bail; + /* The last valid PSN is the previous PSN. */ + update_last_psn(qp, psn - 1); switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) & IPATH_AETH_CREDIT_MASK) { case 0: /* PSN sequence error */ dev->n_seq_naks++; /* - * Back up to the responder's expected PSN. XXX + * Back up to the responder's expected PSN. * Note that we might get a NAK in the middle of an * RDMA READ response which terminates the RDMA * READ. */ - if (qp->s_last == qp->s_tail) - break; - - if (ipath_cmp24(psn, wqe->psn) < 0) - break; - - /* Retry the request. */ ipath_restart_rc(qp, psn, &wc); break; @@ -1003,6 +1075,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, u32 psn, u32 hdrsize, u32 pmtu, int header_in_data) { + struct ipath_swqe *wqe; unsigned long flags; struct ib_wc wc; int diff; @@ -1032,6 +1105,10 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, goto ack_done; } + if (unlikely(qp->s_last == qp->s_tail)) + goto ack_done; + wqe = get_swqe_ptr(qp, qp->s_last); + switch (opcode) { case OP(ACKNOWLEDGE): case OP(ATOMIC_ACKNOWLEDGE): @@ -1042,38 +1119,49 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, aeth = be32_to_cpu(((__be32 *) data)[0]); data += sizeof(__be32); } - if (opcode == OP(ATOMIC_ACKNOWLEDGE)) - *(u64 *) qp->s_sge.sge.vaddr = *(u64 *) data; + if (opcode == OP(ATOMIC_ACKNOWLEDGE)) { + u64 val; + + if (!header_in_data) { + __be32 *p = ohdr->u.at.atomic_ack_eth; + + val = ((u64) be32_to_cpu(p[0]) << 32) | + be32_to_cpu(p[1]); + } else + val = be64_to_cpu(((__be64 *) data)[0]); + *(u64 *) wqe->sg_list[0].vaddr = val; + } if (!do_rc_ack(qp, aeth, psn, opcode) || opcode != OP(RDMA_READ_RESPONSE_FIRST)) goto ack_done; hdrsize += 4; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; /* - * do_rc_ack() has already checked the PSN so skip - * the sequence check. + * If this is a response to a resent RDMA read, we + * have to be careful to copy the data to the right + * location. */ - goto rdma_read; + qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, + wqe, psn, pmtu); + goto read_middle; case OP(RDMA_READ_RESPONSE_MIDDLE): /* no AETH, no ACK */ if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { dev->n_rdma_seq++; - if (qp->s_last != qp->s_tail) - ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); goto ack_done; } - rdma_read: - if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) - goto ack_done; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + read_middle: if (unlikely(tlen != (hdrsize + pmtu + 4))) - goto ack_done; - if (unlikely(pmtu >= qp->s_len)) - goto ack_done; + goto ack_len_err; + if (unlikely(pmtu >= qp->s_rdma_read_len)) + goto ack_len_err; + /* We got a response so update the timeout. */ - if (unlikely(qp->s_last == qp->s_tail || - get_swqe_ptr(qp, qp->s_last)->wr.opcode != - IB_WR_RDMA_READ)) - goto ack_done; spin_lock(&dev->pending_lock); if (qp->s_rnr_timeout == 0 && !list_empty(&qp->timerwait)) list_move_tail(&qp->timerwait, @@ -1082,67 +1170,97 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev, /* * Update the RDMA receive state but do the copy w/o * holding the locks and blocking interrupts. - * XXX Yet another place that affects relaxed RDMA order - * since we don't want s_sge modified. */ - qp->s_len -= pmtu; + qp->s_rdma_read_len -= pmtu; update_last_psn(qp, psn); spin_unlock_irqrestore(&qp->s_lock, flags); - ipath_copy_sge(&qp->s_sge, data, pmtu); + ipath_copy_sge(&qp->s_rdma_read_sge, data, pmtu); goto bail; - case OP(RDMA_READ_RESPONSE_LAST): - /* ACKs READ req. */ + case OP(RDMA_READ_RESPONSE_ONLY): if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { dev->n_rdma_seq++; - if (qp->s_last != qp->s_tail) - ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); goto ack_done; } - /* FALLTHROUGH */ - case OP(RDMA_READ_RESPONSE_ONLY): - if (unlikely(qp->s_state != OP(RDMA_READ_REQUEST))) - goto ack_done; + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + /* Get the number of bytes the message was padded by. */ + pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; + /* + * Check that the data size is >= 0 && <= pmtu. + * Remember to account for the AETH header (4) and + * ICRC (4). + */ + if (unlikely(tlen < (hdrsize + pad + 8))) + goto ack_len_err; /* - * Get the number of bytes the message was padded by. + * If this is a response to a resent RDMA read, we + * have to be careful to copy the data to the right + * location. */ + qp->s_rdma_read_len = restart_sge(&qp->s_rdma_read_sge, + wqe, psn, pmtu); + goto read_last; + + case OP(RDMA_READ_RESPONSE_LAST): + /* ACKs READ req. */ + if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) { + dev->n_rdma_seq++; + ipath_restart_rc(qp, qp->s_last_psn + 1, &wc); + goto ack_done; + } + if (unlikely(wqe->wr.opcode != IB_WR_RDMA_READ)) + goto ack_op_err; + /* Get the number of bytes the message was padded by. */ pad = (be32_to_cpu(ohdr->bth[0]) >> 20) & 3; /* * Check that the data size is >= 1 && <= pmtu. * Remember to account for the AETH header (4) and * ICRC (4). */ - if (unlikely(tlen <= (hdrsize + pad + 8))) { - /* XXX Need to generate an error CQ entry. */ - goto ack_done; - } + if (unlikely(tlen <= (hdrsize + pad + 8))) + goto ack_len_err; + read_last: tlen -= hdrsize + pad + 8; - if (unlikely(tlen != qp->s_len)) { - /* XXX Need to generate an error CQ entry. */ - goto ack_done; - } + if (unlikely(tlen != qp->s_rdma_read_len)) + goto ack_len_err; if (!header_in_data) aeth = be32_to_cpu(ohdr->u.aeth); else { aeth = be32_to_cpu(((__be32 *) data)[0]); data += sizeof(__be32); } - ipath_copy_sge(&qp->s_sge, data, tlen); - if (do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST))) { - /* - * Change the state so we contimue - * processing new requests and wake up the - * tasklet if there are posted sends. - */ - qp->s_state = OP(SEND_LAST); - if (qp->s_tail != qp->s_head) - tasklet_hi_schedule(&qp->s_task); - } + ipath_copy_sge(&qp->s_rdma_read_sge, data, tlen); + (void) do_rc_ack(qp, aeth, psn, OP(RDMA_READ_RESPONSE_LAST)); goto ack_done; } ack_done: spin_unlock_irqrestore(&qp->s_lock, flags); + goto bail; + +ack_op_err: + wc.status = IB_WC_LOC_QP_OP_ERR; + goto ack_err; + +ack_len_err: + wc.status = IB_WC_LOC_LEN_ERR; +ack_err: + wc.wr_id = wqe->wr.wr_id; + wc.opcode = ib_ipath_wc_opcode[wqe->wr.opcode]; + wc.vendor_err = 0; + wc.byte_len = 0; + wc.imm_data = 0; + wc.qp = &qp->ibqp; + wc.src_qp = qp->remote_qpn; + wc.wc_flags = 0; + wc.pkey_index = 0; + wc.slid = qp->remote_ah_attr.dlid; + wc.sl = qp->remote_ah_attr.sl; + wc.dlid_path_bits = 0; + wc.port_num = 0; + ipath_sqerror_qp(qp, &wc); bail: return; } @@ -1162,7 +1280,7 @@ bail: * incoming RC packet for the given QP. * Called at interrupt level. * Return 1 if no more processing is needed; otherwise return 0 to - * schedule a response to be sent and the s_lock unlocked. + * schedule a response to be sent. */ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, struct ipath_other_headers *ohdr, @@ -1173,25 +1291,23 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, int diff, int header_in_data) { - struct ib_reth *reth; + struct ipath_ack_entry *e; + u8 i, prev; + int old_req; if (diff > 0) { /* * Packet sequence error. * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or - * NAK is pending though. + * Don't queue the NAK if we already sent one. */ - if (qp->s_ack_state != OP(ACKNOWLEDGE) || - qp->r_nak_state != 0) - goto done; - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - qp->r_ack_state = OP(SEND_ONLY); + if (!qp->r_nak_state) { qp->r_nak_state = IB_NAK_PSN_ERROR; /* Use the expected PSN. */ qp->r_ack_psn = qp->r_psn; + goto send_ack; } - goto send_ack; + goto done; } /* @@ -1204,8 +1320,46 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, * can coalesce an outstanding duplicate ACK. We have to * send the earliest so that RDMA reads can be restarted at * the requester's expected PSN. + * + * First, find where this duplicate PSN falls within the + * ACKs previously sent. */ - if (opcode == OP(RDMA_READ_REQUEST)) { + psn &= IPATH_PSN_MASK; + e = NULL; + old_req = 1; + spin_lock_irq(&qp->s_lock); + for (i = qp->r_head_ack_queue; ; i = prev) { + if (i == qp->s_tail_ack_queue) + old_req = 0; + if (i) + prev = i - 1; + else + prev = IPATH_MAX_RDMA_ATOMIC; + if (prev == qp->r_head_ack_queue) { + e = NULL; + break; + } + e = &qp->s_ack_queue[prev]; + if (!e->opcode) { + e = NULL; + break; + } + if (ipath_cmp24(psn, e->psn) >= 0) + break; + } + switch (opcode) { + case OP(RDMA_READ_REQUEST): { + struct ib_reth *reth; + u32 offset; + u32 len; + + /* + * If we didn't find the RDMA read request in the ack queue, + * or the send tasklet is already backed up to send an + * earlier entry, we can ignore this request. + */ + if (!e || e->opcode != OP(RDMA_READ_REQUEST) || old_req) + goto unlock_done; /* RETH comes after BTH */ if (!header_in_data) reth = &ohdr->u.rc.reth; @@ -1214,88 +1368,87 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev, data += sizeof(*reth); } /* - * If we receive a duplicate RDMA request, it means the - * requester saw a sequence error and needs to restart - * from an earlier point. We can abort the current - * RDMA read send in that case. + * Address range must be a subset of the original + * request and start on pmtu boundaries. + * We reuse the old ack_queue slot since the requester + * should not back up and request an earlier PSN for the + * same request. */ - spin_lock_irq(&qp->s_lock); - if (qp->s_ack_state != OP(ACKNOWLEDGE) && - (qp->s_hdrwords || ipath_cmp24(psn, qp->s_ack_psn) >= 0)) { - /* - * We are already sending earlier requested data. - * Don't abort it to send later out of sequence data. - */ - spin_unlock_irq(&qp->s_lock); - goto done; - } - qp->s_rdma_len = be32_to_cpu(reth->length); - if (qp->s_rdma_len != 0) { + offset = ((psn - e->psn) & IPATH_PSN_MASK) * + ib_mtu_enum_to_int(qp->path_mtu); + len = be32_to_cpu(reth->length); + if (unlikely(offset + len > e->rdma_sge.sge.sge_length)) + goto unlock_done; + if (len != 0) { u32 rkey = be32_to_cpu(reth->rkey); u64 vaddr = be64_to_cpu(reth->vaddr); int ok; - /* - * Address range must be a subset of the original - * request and start on pmtu boundaries. - */ - ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, - qp->s_rdma_len, vaddr, rkey, + ok = ipath_rkey_ok(qp, &e->rdma_sge, + len, vaddr, rkey, IB_ACCESS_REMOTE_READ); - if (unlikely(!ok)) { - spin_unlock_irq(&qp->s_lock); - goto done; - } + if (unlikely(!ok)) + goto unlock_done; } else { - qp->s_rdma_sge.sg_list = NULL; - qp->s_rdma_sge.num_sge = 0; - qp->s_rdma_sge.sge.mr = NULL; - qp->s_rdma_sge.sge.vaddr = NULL; - qp->s_rdma_sge.sge.length = 0; - qp->s_rdma_sge.sge.sge_length = 0; + e->rdma_sge.sg_list = NULL; + e->rdma_sge.num_sge = 0; + e->rdma_sge.sge.mr = NULL; + e->rdma_sge.sge.vaddr = NULL; + e->rdma_sge.sge.length = 0; + e->rdma_sge.sge.sge_length = 0; } - qp->s_ack_state = opcode; - qp->s_ack_psn = psn; - spin_unlock_irq(&qp->s_lock); - tasklet_hi_schedule(&qp->s_task); - goto send_ack; + e->psn = psn; + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = prev; + break; } - /* - * A pending RDMA read will ACK anything before it so - * ignore earlier duplicate requests. - */ - if (qp->s_ack_state != OP(ACKNOWLEDGE)) - goto done; - - /* - * If an ACK is pending, don't replace the pending ACK - * with an earlier one since the later one will ACK the earlier. - * Also, if we already have a pending atomic, send it. - */ - if (qp->r_ack_state != OP(ACKNOWLEDGE) && - (ipath_cmp24(psn, qp->r_ack_psn) <= 0 || - qp->r_ack_state >= OP(COMPARE_SWAP))) - goto send_ack; - switch (opcode) { case OP(COMPARE_SWAP): - case OP(FETCH_ADD): + case OP(FETCH_ADD): { /* - * Check for the PSN of the last atomic operation - * performed and resend the result if found. + * If we didn't find the atomic request in the ack queue + * or the send tasklet is already backed up to send an + * earlier entry, we can ignore this request. */ - if ((psn & IPATH_PSN_MASK) != qp->r_atomic_psn) - goto done; + if (!e || e->opcode != (u8) opcode || old_req) + goto unlock_done; + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = prev; + break; + } + + default: + if (old_req) + goto unlock_done; + /* + * Resend the most recent ACK if this request is + * after all the previous RDMA reads and atomics. + */ + if (i == qp->r_head_ack_queue) { + spin_unlock_irq(&qp->s_lock); + qp->r_nak_state = 0; + qp->r_ack_psn = qp->r_psn - 1; + goto send_ack; + } + /* + * Resend the RDMA read or atomic op which + * ACKs this duplicate request. + */ + qp->s_ack_state = OP(ACKNOWLEDGE); + qp->s_tail_ack_queue = i; break; } - qp->r_ack_state = opcode; qp->r_nak_state = 0; - qp->r_ack_psn = psn; -send_ack: - return 0; + spin_unlock_irq(&qp->s_lock); + tasklet_hi_schedule(&qp->s_task); +unlock_done: + spin_unlock_irq(&qp->s_lock); done: return 1; + +send_ack: + return 0; } static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err) @@ -1391,15 +1544,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, opcode == OP(SEND_LAST_WITH_IMMEDIATE)) break; nack_inv: - /* - * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or NAK - * is pending though. - */ - if (qp->r_ack_state >= OP(COMPARE_SWAP)) - goto send_ack; ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR); - qp->r_ack_state = OP(SEND_ONLY); qp->r_nak_state = IB_NAK_INVALID_REQUEST; qp->r_ack_psn = qp->r_psn; goto send_ack; @@ -1441,9 +1586,8 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, * Don't queue the NAK if a RDMA read or atomic * is pending though. */ - if (qp->r_ack_state >= OP(COMPARE_SWAP)) - goto send_ack; - qp->r_ack_state = OP(SEND_ONLY); + if (qp->r_nak_state) + goto done; qp->r_nak_state = IB_RNR_NAK | qp->r_min_rnr_timer; qp->r_ack_psn = qp->r_psn; goto send_ack; @@ -1567,7 +1711,19 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, goto rnr_nak; goto send_last_imm; - case OP(RDMA_READ_REQUEST): + case OP(RDMA_READ_REQUEST): { + struct ipath_ack_entry *e; + u32 len; + u8 next; + + if (unlikely(!(qp->qp_access_flags & IB_ACCESS_REMOTE_READ))) + goto nack_acc; + next = qp->r_head_ack_queue + 1; + if (next > IPATH_MAX_RDMA_ATOMIC) + next = 0; + if (unlikely(next == qp->s_tail_ack_queue)) + goto nack_inv; + e = &qp->s_ack_queue[qp->r_head_ack_queue]; /* RETH comes after BTH */ if (!header_in_data) reth = &ohdr->u.rc.reth; @@ -1575,72 +1731,75 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, reth = (struct ib_reth *)data; data += sizeof(*reth); } - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_READ))) - goto nack_acc; - spin_lock_irq(&qp->s_lock); - qp->s_rdma_len = be32_to_cpu(reth->length); - if (qp->s_rdma_len != 0) { + len = be32_to_cpu(reth->length); + if (len) { u32 rkey = be32_to_cpu(reth->rkey); u64 vaddr = be64_to_cpu(reth->vaddr); int ok; /* Check rkey & NAK */ - ok = ipath_rkey_ok(qp, &qp->s_rdma_sge, - qp->s_rdma_len, vaddr, rkey, - IB_ACCESS_REMOTE_READ); - if (unlikely(!ok)) { - spin_unlock_irq(&qp->s_lock); + ok = ipath_rkey_ok(qp, &e->rdma_sge, len, vaddr, + rkey, IB_ACCESS_REMOTE_READ); + if (unlikely(!ok)) goto nack_acc; - } /* * Update the next expected PSN. We add 1 later * below, so only add the remainder here. */ - if (qp->s_rdma_len > pmtu) - qp->r_psn += (qp->s_rdma_len - 1) / pmtu; + if (len > pmtu) + qp->r_psn += (len - 1) / pmtu; } else { - qp->s_rdma_sge.sg_list = NULL; - qp->s_rdma_sge.num_sge = 0; - qp->s_rdma_sge.sge.mr = NULL; - qp->s_rdma_sge.sge.vaddr = NULL; - qp->s_rdma_sge.sge.length = 0; - qp->s_rdma_sge.sge.sge_length = 0; + e->rdma_sge.sg_list = NULL; + e->rdma_sge.num_sge = 0; + e->rdma_sge.sge.mr = NULL; + e->rdma_sge.sge.vaddr = NULL; + e->rdma_sge.sge.length = 0; + e->rdma_sge.sge.sge_length = 0; } + e->opcode = opcode; + e->psn = psn; /* * We need to increment the MSN here instead of when we * finish sending the result since a duplicate request would * increment it more than once. */ qp->r_msn++; - - qp->s_ack_state = opcode; - qp->s_ack_psn = psn; - spin_unlock_irq(&qp->s_lock); - qp->r_psn++; qp->r_state = opcode; qp->r_nak_state = 0; + barrier(); + qp->r_head_ack_queue = next; /* Call ipath_do_rc_send() in another thread. */ tasklet_hi_schedule(&qp->s_task); goto done; + } case OP(COMPARE_SWAP): case OP(FETCH_ADD): { struct ib_atomic_eth *ateth; + struct ipath_ack_entry *e; u64 vaddr; + atomic64_t *maddr; u64 sdata; u32 rkey; + u8 next; + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_ATOMIC))) + goto nack_acc; + next = qp->r_head_ack_queue + 1; + if (next > IPATH_MAX_RDMA_ATOMIC) + next = 0; + if (unlikely(next == qp->s_tail_ack_queue)) + goto nack_inv; if (!header_in_data) ateth = &ohdr->u.atomic_eth; - else { + else ateth = (struct ib_atomic_eth *)data; - data += sizeof(*ateth); - } - vaddr = be64_to_cpu(ateth->vaddr); + vaddr = ((u64) be32_to_cpu(ateth->vaddr[0]) << 32) | + be32_to_cpu(ateth->vaddr[1]); if (unlikely(vaddr & (sizeof(u64) - 1))) goto nack_inv; rkey = be32_to_cpu(ateth->rkey); @@ -1649,63 +1808,50 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, sizeof(u64), vaddr, rkey, IB_ACCESS_REMOTE_ATOMIC))) goto nack_acc; - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_ATOMIC))) - goto nack_acc; /* Perform atomic OP and save result. */ + maddr = (atomic64_t *) qp->r_sge.sge.vaddr; sdata = be64_to_cpu(ateth->swap_data); - spin_lock_irq(&dev->pending_lock); - qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; - if (opcode == OP(FETCH_ADD)) - *(u64 *) qp->r_sge.sge.vaddr = - qp->r_atomic_data + sdata; - else if (qp->r_atomic_data == - be64_to_cpu(ateth->compare_data)) - *(u64 *) qp->r_sge.sge.vaddr = sdata; - spin_unlock_irq(&dev->pending_lock); + e = &qp->s_ack_queue[qp->r_head_ack_queue]; + e->atomic_data = (opcode == OP(FETCH_ADD)) ? + (u64) atomic64_add_return(sdata, maddr) - sdata : + (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr, + be64_to_cpu(ateth->compare_data), + sdata); + e->opcode = opcode; + e->psn = psn & IPATH_PSN_MASK; qp->r_msn++; - qp->r_atomic_psn = psn & IPATH_PSN_MASK; - psn |= 1 << 31; - break; + qp->r_psn++; + qp->r_state = opcode; + qp->r_nak_state = 0; + barrier(); + qp->r_head_ack_queue = next; + + /* Call ipath_do_rc_send() in another thread. */ + tasklet_hi_schedule(&qp->s_task); + + goto done; } default: - /* Drop packet for unknown opcodes. */ - goto done; + /* NAK unknown opcodes. */ + goto nack_inv; } qp->r_psn++; qp->r_state = opcode; + qp->r_ack_psn = psn; qp->r_nak_state = 0; /* Send an ACK if requested or required. */ - if (psn & (1 << 31)) { - /* - * Coalesce ACKs unless there is a RDMA READ or - * ATOMIC pending. - */ - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - qp->r_ack_state = opcode; - qp->r_ack_psn = psn; - } + if (psn & (1 << 31)) goto send_ack; - } goto done; nack_acc: - /* - * A NAK will ACK earlier sends and RDMA writes. - * Don't queue the NAK if a RDMA read, atomic, or NAK - * is pending though. - */ - if (qp->r_ack_state < OP(COMPARE_SWAP)) { - ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR); - qp->r_ack_state = OP(RDMA_WRITE_ONLY); - qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; - qp->r_ack_psn = qp->r_psn; - } + ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR); + qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR; + qp->r_ack_psn = qp->r_psn; + send_ack: - /* Send ACK right away unless the send tasklet has a pending ACK. */ - if (qp->s_ack_state == OP(ACKNOWLEDGE)) - send_rc_ack(qp); + send_rc_ack(qp); done: return; diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h index dffc760..c182bcd 100644 --- a/drivers/infiniband/hw/ipath/ipath_registers.h +++ b/drivers/infiniband/hw/ipath/ipath_registers.h @@ -126,9 +126,18 @@ #define INFINIPATH_E_RESET 0x0004000000000000ULL #define INFINIPATH_E_HARDWARE 0x0008000000000000ULL +/* + * this is used to print "common" packet errors only when the + * __IPATH_ERRPKTDBG bit is set in ipath_debug. + */ +#define INFINIPATH_E_PKTERRS ( INFINIPATH_E_SPKTLEN \ + | INFINIPATH_E_SDROPPEDDATAPKT | INFINIPATH_E_RVCRC \ + | INFINIPATH_E_RICRC | INFINIPATH_E_RSHORTPKTLEN \ + | INFINIPATH_E_REBP ) + /* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */ /* TXEMEMPARITYERR bit 0: PIObuf, 1: PIOpbc, 2: launchfifo - * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: eagerTID, 3: expTID + * RXEMEMPARITYERR bit 0: rcvbuf, 1: lookupq, 2: expTID, 3: eagerTID * bit 4: flag buffer, 5: datainfo, 6: header info */ #define INFINIPATH_HWE_TXEMEMPARITYERR_MASK 0xFULL #define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40 @@ -143,8 +152,8 @@ /* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */ #define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL -#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL -#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x08ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x04ULL +#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x08ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL #define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL @@ -299,13 +308,6 @@ #define INFINIPATH_XGXS_RX_POL_SHIFT 19 #define INFINIPATH_XGXS_RX_POL_MASK 0xfULL -#define INFINIPATH_RT_ADDR_MASK 0xFFFFFFFFFFULL /* 40 bits valid */ - -/* TID entries (memory), HT-only */ -#define INFINIPATH_RT_VALID 0x8000000000000000ULL -#define INFINIPATH_RT_ADDR_SHIFT 0 -#define INFINIPATH_RT_BUFSIZE_MASK 0x3FFF -#define INFINIPATH_RT_BUFSIZE_SHIFT 48 /* * IPATH_PIO_MAXIBHDR is the max IB header size allowed for in our diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index e86cb17..d9c2a9b 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -202,6 +202,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) wq->tail = tail; ret = 1; + qp->r_wrid_valid = 1; if (handler) { u32 n; @@ -229,7 +230,6 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) } } spin_unlock_irqrestore(&rq->lock, flags); - qp->r_wrid_valid = 1; bail: return ret; @@ -255,6 +255,7 @@ static void ipath_ruc_loopback(struct ipath_qp *sqp) unsigned long flags; struct ib_wc wc; u64 sdata; + atomic64_t *maddr; qp = ipath_lookup_qpn(&dev->qp_table, sqp->remote_qpn); if (!qp) { @@ -265,7 +266,8 @@ static void ipath_ruc_loopback(struct ipath_qp *sqp) again: spin_lock_irqsave(&sqp->s_lock, flags); - if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK)) { + if (!(ib_ipath_state_ops[sqp->state] & IPATH_PROCESS_SEND_OK) || + qp->s_rnr_timeout) { spin_unlock_irqrestore(&sqp->s_lock, flags); goto done; } @@ -310,7 +312,7 @@ again: sqp->s_rnr_retry--; dev->n_rnr_naks++; sqp->s_rnr_timeout = - ib_ipath_rnr_table[sqp->r_min_rnr_timer]; + ib_ipath_rnr_table[qp->r_min_rnr_timer]; ipath_insert_rnr_queue(sqp); goto done; } @@ -343,20 +345,22 @@ again: wc.sl = sqp->remote_ah_attr.sl; wc.dlid_path_bits = 0; wc.port_num = 0; + spin_lock_irqsave(&sqp->s_lock, flags); ipath_sqerror_qp(sqp, &wc); + spin_unlock_irqrestore(&sqp->s_lock, flags); goto done; } break; case IB_WR_RDMA_READ: + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_READ))) + goto acc_err; if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length, wqe->wr.wr.rdma.remote_addr, wqe->wr.wr.rdma.rkey, IB_ACCESS_REMOTE_READ))) goto acc_err; - if (unlikely(!(qp->qp_access_flags & - IB_ACCESS_REMOTE_READ))) - goto acc_err; qp->r_sge.sge = wqe->sg_list[0]; qp->r_sge.sg_list = wqe->sg_list + 1; qp->r_sge.num_sge = wqe->wr.num_sge; @@ -364,22 +368,22 @@ again: case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: + if (unlikely(!(qp->qp_access_flags & + IB_ACCESS_REMOTE_ATOMIC))) + goto acc_err; if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64), - wqe->wr.wr.rdma.remote_addr, - wqe->wr.wr.rdma.rkey, + wqe->wr.wr.atomic.remote_addr, + wqe->wr.wr.atomic.rkey, IB_ACCESS_REMOTE_ATOMIC))) goto acc_err; /* Perform atomic OP and save result. */ - sdata = wqe->wr.wr.atomic.swap; - spin_lock_irqsave(&dev->pending_lock, flags); - qp->r_atomic_data = *(u64 *) qp->r_sge.sge.vaddr; - if (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) - *(u64 *) qp->r_sge.sge.vaddr = - qp->r_atomic_data + sdata; - else if (qp->r_atomic_data == wqe->wr.wr.atomic.compare_add) - *(u64 *) qp->r_sge.sge.vaddr = sdata; - spin_unlock_irqrestore(&dev->pending_lock, flags); - *(u64 *) sqp->s_sge.sge.vaddr = qp->r_atomic_data; + maddr = (atomic64_t *) qp->r_sge.sge.vaddr; + sdata = wqe->wr.wr.atomic.compare_add; + *(u64 *) sqp->s_sge.sge.vaddr = + (wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) ? + (u64) atomic64_add_return(sdata, maddr) - sdata : + (u64) cmpxchg((u64 *) qp->r_sge.sge.vaddr, + sdata, wqe->wr.wr.atomic.swap); goto send_comp; default: @@ -440,7 +444,7 @@ again: send_comp: sqp->s_rnr_retry = sqp->s_rnr_retry_cnt; - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &sqp->s_flags) || + if (!(sqp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wqe->wr.wr_id; wc.status = IB_WC_SUCCESS; @@ -502,7 +506,7 @@ void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev) * We clear the tasklet flag now since we are committing to return * from the tasklet function. */ - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); tasklet_unlock(&qp->s_task); want_buffer(dev->dd); dev->n_piowait++; @@ -541,6 +545,9 @@ int ipath_post_ruc_send(struct ipath_qp *qp, struct ib_send_wr *wr) wr->sg_list[0].addr & (sizeof(u64) - 1))) { ret = -EINVAL; goto bail; + } else if (wr->opcode >= IB_WR_RDMA_READ && !qp->s_max_rd_atomic) { + ret = -EINVAL; + goto bail; } /* IB spec says that num_sge == 0 is OK. */ if (wr->num_sge > qp->s_max_sge) { @@ -647,7 +654,7 @@ void ipath_do_ruc_send(unsigned long data) u32 pmtu = ib_mtu_enum_to_int(qp->path_mtu); struct ipath_other_headers *ohdr; - if (test_and_set_bit(IPATH_S_BUSY, &qp->s_flags)) + if (test_and_set_bit(IPATH_S_BUSY, &qp->s_busy)) goto bail; if (unlikely(qp->remote_ah_attr.dlid == dev->dd->ipath_lid)) { @@ -683,19 +690,15 @@ again: */ spin_lock_irqsave(&qp->s_lock, flags); - /* Sending responses has higher priority over sending requests. */ - if (qp->s_ack_state != IB_OPCODE_RC_ACKNOWLEDGE && - (bth0 = ipath_make_rc_ack(qp, ohdr, pmtu)) != 0) - bth2 = qp->s_ack_psn++ & IPATH_PSN_MASK; - else if (!((qp->ibqp.qp_type == IB_QPT_RC) ? - ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) : - ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) { + if (!((qp->ibqp.qp_type == IB_QPT_RC) ? + ipath_make_rc_req(qp, ohdr, pmtu, &bth0, &bth2) : + ipath_make_uc_req(qp, ohdr, pmtu, &bth0, &bth2))) { /* * Clear the busy bit before unlocking to avoid races with * adding new work queue items and then failing to process * them. */ - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); spin_unlock_irqrestore(&qp->s_lock, flags); goto bail; } @@ -728,7 +731,7 @@ again: goto again; clear: - clear_bit(IPATH_S_BUSY, &qp->s_flags); + clear_bit(IPATH_S_BUSY, &qp->s_busy); bail: return; } diff --git a/drivers/infiniband/hw/ipath/ipath_stats.c b/drivers/infiniband/hw/ipath/ipath_stats.c index 30a8259..9307f71 100644 --- a/drivers/infiniband/hw/ipath/ipath_stats.c +++ b/drivers/infiniband/hw/ipath/ipath_stats.c @@ -207,7 +207,7 @@ void ipath_get_faststats(unsigned long opaque) * don't access the chip while running diags, or memory diags can * fail */ - if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_PRESENT) || + if (!dd->ipath_kregbase || !(dd->ipath_flags & IPATH_INITTED) || ipath_diag_inuse) /* but re-arm the timer, for diags case; won't hurt other */ goto done; @@ -237,11 +237,13 @@ void ipath_get_faststats(unsigned long opaque) if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) && time_after(jiffies, dd->ipath_unmasktime)) { char ebuf[256]; - ipath_decode_err(ebuf, sizeof ebuf, + int iserr; + iserr = ipath_decode_err(ebuf, sizeof ebuf, (dd->ipath_maskederrs & ~dd-> ipath_ignorederrs)); if ((dd->ipath_maskederrs & ~dd->ipath_ignorederrs) & - ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL)) + ~(INFINIPATH_E_RRCVEGRFULL | INFINIPATH_E_RRCVHDRFULL | + INFINIPATH_E_PKTERRS )) ipath_dev_err(dd, "Re-enabling masked errors " "(%s)\n", ebuf); else { @@ -252,8 +254,12 @@ void ipath_get_faststats(unsigned long opaque) * them. So only complain about these at debug * level. */ - ipath_dbg("Disabling frequent queue full errors " - "(%s)\n", ebuf); + if (iserr) + ipath_dbg("Re-enabling queue full errors (%s)\n", + ebuf); + else + ipath_cdbg(ERRPKT, "Re-enabling packet" + " problem interrupt (%s)\n", ebuf); } dd->ipath_maskederrs = dd->ipath_ignorederrs; ipath_write_kreg(dd, dd->ipath_kregs->kr_errormask, diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c index 325d663..1c2b03c 100644 --- a/drivers/infiniband/hw/ipath/ipath_uc.c +++ b/drivers/infiniband/hw/ipath/ipath_uc.c @@ -42,7 +42,7 @@ static void complete_last_send(struct ipath_qp *qp, struct ipath_swqe *wqe, { if (++qp->s_last == qp->s_size) qp->s_last = 0; - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wqe->wr.send_flags & IB_SEND_SIGNALED)) { wc->wr_id = wqe->wr.wr_id; wc->status = IB_WC_SUCCESS; @@ -344,13 +344,13 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, send_first: if (qp->r_reuse_sge) { qp->r_reuse_sge = 0; - qp->r_sge = qp->s_rdma_sge; + qp->r_sge = qp->s_rdma_read_sge; } else if (!ipath_get_rwqe(qp, 0)) { dev->n_pkt_drops++; goto done; } /* Save the WQE so we can reuse it in case of an error. */ - qp->s_rdma_sge = qp->r_sge; + qp->s_rdma_read_sge = qp->r_sge; qp->r_rcv_len = 0; if (opcode == OP(SEND_ONLY)) goto send_last; diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c index 9a3e546..a518f7c 100644 --- a/drivers/infiniband/hw/ipath/ipath_ud.c +++ b/drivers/infiniband/hw/ipath/ipath_ud.c @@ -308,6 +308,11 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr) goto bail; } + if (wr->wr.ud.ah->pd != qp->ibqp.pd) { + ret = -EPERM; + goto bail; + } + /* IB spec says that num_sge == 0 is OK. */ if (wr->num_sge > qp->s_max_sge) { ret = -EINVAL; @@ -467,7 +472,7 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr) done: /* Queue the completion status entry. */ - if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) || + if (!(qp->s_flags & IPATH_S_SIGNAL_REQ_WR) || (wr->send_flags & IB_SEND_SIGNALED)) { wc.wr_id = wr->wr_id; wc.status = IB_WC_SUCCESS; @@ -647,6 +652,7 @@ void ipath_ud_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr, ipath_skip_sge(&qp->r_sge, sizeof(struct ib_grh)); ipath_copy_sge(&qp->r_sge, data, wc.byte_len - sizeof(struct ib_grh)); + qp->r_wrid_valid = 0; wc.wr_id = qp->r_wr_id; wc.status = IB_WC_SUCCESS; wc.opcode = IB_WC_RECV; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index 2aaacdb..18c6df2 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -438,6 +438,10 @@ void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data, struct ipath_mcast *mcast; struct ipath_mcast_qp *p; + if (lnh != IPATH_LRH_GRH) { + dev->n_pkt_drops++; + goto bail; + } mcast = ipath_mcast_find(&hdr->u.l.grh.dgid); if (mcast == NULL) { dev->n_pkt_drops++; @@ -445,8 +449,7 @@ void ipath_ib_rcv(struct ipath_ibdev *dev, void *rhdr, void *data, } dev->n_multicast_rcv++; list_for_each_entry_rcu(p, &mcast->qp_list, list) - ipath_qp_rcv(dev, hdr, lnh == IPATH_LRH_GRH, data, - tlen, p->qp); + ipath_qp_rcv(dev, hdr, 1, data, tlen, p->qp); /* * Notify ipath_multicast_detach() if it is waiting for us * to finish. @@ -773,7 +776,6 @@ int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords, /* +1 is for the qword padding of pbc */ plen = hdrwords + ((len + 3) >> 2) + 1; if (unlikely((plen << 2) > dd->ipath_ibmaxlen)) { - ipath_dbg("packet len 0x%x too long, failing\n", plen); ret = -EINVAL; goto bail; } @@ -980,14 +982,14 @@ static int ipath_query_device(struct ib_device *ibdev, props->max_cqe = ib_ipath_max_cqes; props->max_mr = dev->lk_table.max; props->max_pd = ib_ipath_max_pds; - props->max_qp_rd_atom = 1; - props->max_qp_init_rd_atom = 1; + props->max_qp_rd_atom = IPATH_MAX_RDMA_ATOMIC; + props->max_qp_init_rd_atom = 255; /* props->max_res_rd_atom */ props->max_srq = ib_ipath_max_srqs; props->max_srq_wr = ib_ipath_max_srq_wrs; props->max_srq_sge = ib_ipath_max_srq_sges; /* props->local_ca_ack_delay */ - props->atomic_cap = IB_ATOMIC_HCA; + props->atomic_cap = IB_ATOMIC_GLOB; props->max_pkeys = ipath_get_npkeys(dev->dd); props->max_mcast_grp = ib_ipath_max_mcast_grps; props->max_mcast_qp_attach = ib_ipath_max_mcast_qp_attached; @@ -1557,7 +1559,6 @@ int ipath_register_ib_device(struct ipath_devdata *dd) dev->node_type = RDMA_NODE_IB_CA; dev->phys_port_cnt = 1; dev->dma_device = &dd->pcidev->dev; - dev->class_dev.dev = dev->dma_device; dev->query_device = ipath_query_device; dev->modify_device = ipath_modify_device; dev->query_port = ipath_query_port; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index c0c8d5b..7c4929f 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -40,9 +40,12 @@ #include <linux/interrupt.h> #include <linux/kref.h> #include <rdma/ib_pack.h> +#include <rdma/ib_user_verbs.h> #include "ipath_layer.h" +#define IPATH_MAX_RDMA_ATOMIC 4 + #define QPN_MAX (1 << 24) #define QPNMAP_ENTRIES (QPN_MAX / PAGE_SIZE / BITS_PER_BYTE) @@ -89,7 +92,7 @@ struct ib_reth { } __attribute__ ((packed)); struct ib_atomic_eth { - __be64 vaddr; + __be32 vaddr[2]; /* unaligned so access as 2 32-bit words */ __be32 rkey; __be64 swap_data; __be64 compare_data; @@ -108,7 +111,7 @@ struct ipath_other_headers { } rc; struct { __be32 aeth; - __be64 atomic_ack_eth; + __be32 atomic_ack_eth[2]; } at; __be32 imm_data; __be32 aeth; @@ -186,7 +189,7 @@ struct ipath_mmap_info { struct ipath_cq_wc { u32 head; /* index of next entry to fill */ u32 tail; /* index of next ib_poll_cq() entry */ - struct ib_wc queue[1]; /* this is actually size ibcq.cqe + 1 */ + struct ib_uverbs_wc queue[1]; /* this is actually size ibcq.cqe + 1 */ }; /* @@ -312,6 +315,19 @@ struct ipath_sge_state { }; /* + * This structure holds the information that the send tasklet needs + * to send a RDMA read response or atomic operation. + */ +struct ipath_ack_entry { + u8 opcode; + u32 psn; + union { + struct ipath_sge_state rdma_sge; + u64 atomic_data; + }; +}; + +/* * Variables prefixed with s_ are for the requester (sender). * Variables prefixed with r_ are for the responder (receiver). * Variables prefixed with ack_ are for responder replies. @@ -333,24 +349,24 @@ struct ipath_qp { struct ipath_mmap_info *ip; struct ipath_sge_state *s_cur_sge; struct ipath_sge_state s_sge; /* current send request data */ - /* current RDMA read send data */ - struct ipath_sge_state s_rdma_sge; + struct ipath_ack_entry s_ack_queue[IPATH_MAX_RDMA_ATOMIC + 1]; + struct ipath_sge_state s_ack_rdma_sge; + struct ipath_sge_state s_rdma_read_sge; struct ipath_sge_state r_sge; /* current receive data */ spinlock_t s_lock; - unsigned long s_flags; + unsigned long s_busy; u32 s_hdrwords; /* size of s_hdr in 32 bit words */ u32 s_cur_size; /* size of send packet in bytes */ u32 s_len; /* total length of s_sge */ - u32 s_rdma_len; /* total length of s_rdma_sge */ + u32 s_rdma_read_len; /* total length of s_rdma_read_sge */ u32 s_next_psn; /* PSN for next request */ u32 s_last_psn; /* last response PSN processed */ u32 s_psn; /* current packet sequence number */ - u32 s_ack_psn; /* PSN for RDMA_READ */ + u32 s_ack_rdma_psn; /* PSN for sending RDMA read responses */ + u32 s_ack_psn; /* PSN for acking sends and RDMA writes */ u32 s_rnr_timeout; /* number of milliseconds for RNR timeout */ u32 r_ack_psn; /* PSN for next ACK or atomic ACK */ u64 r_wr_id; /* ID for current receive WQE */ - u64 r_atomic_data; /* data for last atomic op */ - u32 r_atomic_psn; /* PSN of last atomic op */ u32 r_len; /* total length of r_sge */ u32 r_rcv_len; /* receive data len processed */ u32 r_psn; /* expected rcv packet sequence number */ @@ -360,12 +376,13 @@ struct ipath_qp { u8 s_ack_state; /* opcode of packet to ACK */ u8 s_nak_state; /* non-zero if NAK is pending */ u8 r_state; /* opcode of last packet received */ - u8 r_ack_state; /* opcode of packet to ACK */ u8 r_nak_state; /* non-zero if NAK is pending */ u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */ u8 r_reuse_sge; /* for UC receive errors */ u8 r_sge_inx; /* current index into sg_list */ u8 r_wrid_valid; /* r_wrid set but CQ entry not yet made */ + u8 r_max_rd_atomic; /* max number of RDMA read/atomic to receive */ + u8 r_head_ack_queue; /* index into s_ack_queue[] */ u8 qp_access_flags; u8 s_max_sge; /* size of s_wq->sg_list */ u8 s_retry_cnt; /* number of times to retry */ @@ -374,6 +391,10 @@ struct ipath_qp { u8 s_rnr_retry; /* requester RNR retry counter */ u8 s_wait_credit; /* limit number of unacked packets sent */ u8 s_pkey_index; /* PKEY index to use */ + u8 s_max_rd_atomic; /* max number of RDMA read/atomic to send */ + u8 s_num_rd_atomic; /* number of RDMA read/atomic pending */ + u8 s_tail_ack_queue; /* index into s_ack_queue[] */ + u8 s_flags; u8 timeout; /* Timeout for this QP */ enum ib_mtu path_mtu; u32 remote_qpn; @@ -390,11 +411,16 @@ struct ipath_qp { struct ipath_sge r_sg_list[0]; /* verified SGEs */ }; +/* Bit definition for s_busy. */ +#define IPATH_S_BUSY 0 + /* * Bit definitions for s_flags. */ -#define IPATH_S_BUSY 0 -#define IPATH_S_SIGNAL_REQ_WR 1 +#define IPATH_S_SIGNAL_REQ_WR 0x01 +#define IPATH_S_FENCE_PENDING 0x02 +#define IPATH_S_RDMAR_PENDING 0x04 +#define IPATH_S_ACK_PENDING 0x08 #define IPATH_PSN_CREDIT 2048 @@ -706,8 +732,6 @@ int ipath_query_srq(struct ib_srq *ibsrq, struct ib_srq_attr *attr); int ipath_destroy_srq(struct ib_srq *ibsrq); -void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig); - int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry); struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries, @@ -757,9 +781,6 @@ u32 ipath_make_grh(struct ipath_ibdev *dev, struct ib_grh *hdr, void ipath_do_ruc_send(unsigned long data); -u32 ipath_make_rc_ack(struct ipath_qp *qp, struct ipath_other_headers *ohdr, - u32 pmtu); - int ipath_make_rc_req(struct ipath_qp *qp, struct ipath_other_headers *ohdr, u32 pmtu, u32 *bth0p, u32 *bth2p); diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index 0d9b7d0..773145e 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -1013,14 +1013,14 @@ static struct { u64 latest_fw; u32 flags; } mthca_hca_table[] = { - [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 4, 0), + [TAVOR] = { .latest_fw = MTHCA_FW_VER(3, 5, 0), .flags = 0 }, - [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 7, 600), + [ARBEL_COMPAT] = { .latest_fw = MTHCA_FW_VER(4, 8, 200), .flags = MTHCA_FLAG_PCIE }, - [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 1, 400), + [ARBEL_NATIVE] = { .latest_fw = MTHCA_FW_VER(5, 2, 0), .flags = MTHCA_FLAG_MEMFREE | MTHCA_FLAG_PCIE }, - [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 1, 0), + [SINAI] = { .latest_fw = MTHCA_FW_VER(1, 2, 0), .flags = MTHCA_FLAG_MEMFREE | MTHCA_FLAG_PCIE | MTHCA_FLAG_SINAI_OPT } @@ -1135,7 +1135,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) goto err_cmd; if (mdev->fw_ver < mthca_hca_table[hca_type].latest_fw) { - mthca_warn(mdev, "HCA FW version %d.%d.%d is old (%d.%d.%d is current).\n", + mthca_warn(mdev, "HCA FW version %d.%d.%3d is old (%d.%d.%3d is current).\n", (int) (mdev->fw_ver >> 32), (int) (mdev->fw_ver >> 16) & 0xffff, (int) (mdev->fw_ver & 0xffff), (int) (mthca_hca_table[hca_type].latest_fw >> 32), diff --git a/drivers/infiniband/hw/mthca/mthca_memfree.c b/drivers/infiniband/hw/mthca/mthca_memfree.c index 0b9d053..48f7c65 100644 --- a/drivers/infiniband/hw/mthca/mthca_memfree.c +++ b/drivers/infiniband/hw/mthca/mthca_memfree.c @@ -175,7 +175,9 @@ struct mthca_icm *mthca_alloc_icm(struct mthca_dev *dev, int npages, if (!ret) { ++chunk->npages; - if (!coherent && chunk->npages == MTHCA_ICM_CHUNK_LEN) { + if (coherent) + ++chunk->nsg; + else if (chunk->npages == MTHCA_ICM_CHUNK_LEN) { chunk->nsg = pci_map_sg(dev->pdev, chunk->mem, chunk->npages, PCI_DMA_BIDIRECTIONAL); diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index 6037dd3..aa6c70a 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -297,7 +297,8 @@ out: int mthca_write_mtt_size(struct mthca_dev *dev) { - if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy) + if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || + !(dev->mthca_flags & MTHCA_FLAG_FMR)) /* * Be friendly to WRITE_MTT command * and leave two empty slots for the @@ -310,8 +311,9 @@ int mthca_write_mtt_size(struct mthca_dev *dev) return mthca_is_memfree(dev) ? (PAGE_SIZE / sizeof (u64)) : 0x7ffffff; } -void mthca_tavor_write_mtt_seg(struct mthca_dev *dev, struct mthca_mtt *mtt, - int start_index, u64 *buffer_list, int list_len) +static void mthca_tavor_write_mtt_seg(struct mthca_dev *dev, + struct mthca_mtt *mtt, int start_index, + u64 *buffer_list, int list_len) { u64 __iomem *mtts; int i; @@ -323,8 +325,9 @@ void mthca_tavor_write_mtt_seg(struct mthca_dev *dev, struct mthca_mtt *mtt, mtts + i); } -void mthca_arbel_write_mtt_seg(struct mthca_dev *dev, struct mthca_mtt *mtt, - int start_index, u64 *buffer_list, int list_len) +static void mthca_arbel_write_mtt_seg(struct mthca_dev *dev, + struct mthca_mtt *mtt, int start_index, + u64 *buffer_list, int list_len) { __be64 *mtts; dma_addr_t dma_handle; @@ -353,7 +356,8 @@ int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, int size = mthca_write_mtt_size(dev); int chunk; - if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy) + if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || + !(dev->mthca_flags & MTHCA_FLAG_FMR)) return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len); while (list_len > 0) { @@ -833,6 +837,7 @@ void mthca_arbel_fmr_unmap(struct mthca_dev *dev, struct mthca_fmr *fmr) key = arbel_key_to_hw_index(fmr->ibmr.lkey); key &= dev->limits.num_mpts - 1; + key = adjust_key(dev, key); fmr->ibmr.lkey = fmr->ibmr.rkey = arbel_hw_index_to_key(key); fmr->maps = 0; @@ -879,8 +884,8 @@ int mthca_init_mr_table(struct mthca_dev *dev) } mpts = mtts = 1 << i; } else { - mpts = dev->limits.num_mtt_segs; - mtts = dev->limits.num_mpts; + mtts = dev->limits.num_mtt_segs; + mpts = dev->limits.num_mpts; } if (!mthca_is_memfree(dev) && diff --git a/drivers/infiniband/hw/mthca/mthca_provider.c b/drivers/infiniband/hw/mthca/mthca_provider.c index 0725ad7..47e6fd46d 100644 --- a/drivers/infiniband/hw/mthca/mthca_provider.c +++ b/drivers/infiniband/hw/mthca/mthca_provider.c @@ -1293,7 +1293,6 @@ int mthca_register_device(struct mthca_dev *dev) dev->ib_dev.node_type = RDMA_NODE_IB_CA; dev->ib_dev.phys_port_cnt = dev->limits.num_ports; dev->ib_dev.dma_device = &dev->pdev->dev; - dev->ib_dev.class_dev.dev = &dev->pdev->dev; dev->ib_dev.query_device = mthca_query_device; dev->ib_dev.query_port = mthca_query_port; dev->ib_dev.modify_device = mthca_modify_device; diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 224c93d..8fe6fee 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -573,6 +573,11 @@ int mthca_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, goto out; } + if (cur_state == new_state && cur_state == IB_QPS_RESET) { + err = 0; + goto out; + } + if ((attr_mask & IB_QP_PKEY_INDEX) && attr->pkey_index >= dev->limits.pkey_table_len) { mthca_dbg(dev, "P_Key index (%u) too large. max is %d\n", @@ -1083,21 +1088,21 @@ static void mthca_unmap_memfree(struct mthca_dev *dev, static int mthca_alloc_memfree(struct mthca_dev *dev, struct mthca_qp *qp) { - int ret = 0; - if (mthca_is_memfree(dev)) { qp->rq.db_index = mthca_alloc_db(dev, MTHCA_DB_TYPE_RQ, qp->qpn, &qp->rq.db); if (qp->rq.db_index < 0) - return ret; + return -ENOMEM; qp->sq.db_index = mthca_alloc_db(dev, MTHCA_DB_TYPE_SQ, qp->qpn, &qp->sq.db); - if (qp->sq.db_index < 0) + if (qp->sq.db_index < 0) { mthca_free_db(dev, MTHCA_DB_TYPE_RQ, qp->rq.db_index); + return -ENOMEM; + } } - return ret; + return 0; } static void mthca_free_memfree(struct mthca_dev *dev, @@ -1414,11 +1419,10 @@ void mthca_free_qp(struct mthca_dev *dev, * unref the mem-free tables and free the QPN in our table. */ if (!qp->ibqp.uobject) { - mthca_cq_clean(dev, to_mcq(qp->ibqp.send_cq), qp->qpn, + mthca_cq_clean(dev, recv_cq, qp->qpn, qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); - if (qp->ibqp.send_cq != qp->ibqp.recv_cq) - mthca_cq_clean(dev, to_mcq(qp->ibqp.recv_cq), qp->qpn, - qp->ibqp.srq ? to_msrq(qp->ibqp.srq) : NULL); + if (send_cq != recv_cq) + mthca_cq_clean(dev, send_cq, qp->qpn, NULL); mthca_free_memfree(dev, qp); mthca_free_wqe_buf(dev, qp); diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h index 2594db2..fd55826 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib.h +++ b/drivers/infiniband/ulp/ipoib/ipoib.h @@ -219,7 +219,6 @@ struct ipoib_dev_priv { union ib_gid local_gid; u16 local_lid; - u8 local_rate; unsigned int admin_mtu; unsigned int mcast_mtu; diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c index 2d48387..0c4e59b 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c @@ -65,14 +65,14 @@ struct ipoib_cm_id { static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event); -static void ipoib_cm_dma_unmap_rx(struct ipoib_dev_priv *priv, +static void ipoib_cm_dma_unmap_rx(struct ipoib_dev_priv *priv, int frags, u64 mapping[IPOIB_CM_RX_SG]) { int i; ib_dma_unmap_single(priv->ca, mapping[0], IPOIB_CM_HEAD_SIZE, DMA_FROM_DEVICE); - for (i = 0; i < IPOIB_CM_RX_SG - 1; ++i) + for (i = 0; i < frags; ++i) ib_dma_unmap_single(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE); } @@ -90,7 +90,8 @@ static int ipoib_cm_post_receive(struct net_device *dev, int id) ret = ib_post_srq_recv(priv->cm.srq, &priv->cm.rx_wr, &bad_wr); if (unlikely(ret)) { ipoib_warn(priv, "post srq failed for buf %d (%d)\n", id, ret); - ipoib_cm_dma_unmap_rx(priv, priv->cm.srq_ring[id].mapping); + ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1, + priv->cm.srq_ring[id].mapping); dev_kfree_skb_any(priv->cm.srq_ring[id].skb); priv->cm.srq_ring[id].skb = NULL; } @@ -98,8 +99,8 @@ static int ipoib_cm_post_receive(struct net_device *dev, int id) return ret; } -static int ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, - u64 mapping[IPOIB_CM_RX_SG]) +static struct sk_buff *ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, int frags, + u64 mapping[IPOIB_CM_RX_SG]) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct sk_buff *skb; @@ -107,7 +108,7 @@ static int ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, skb = dev_alloc_skb(IPOIB_CM_HEAD_SIZE + 12); if (unlikely(!skb)) - return -ENOMEM; + return NULL; /* * IPoIB adds a 4 byte header. So we need 12 more bytes to align the @@ -119,10 +120,10 @@ static int ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, DMA_FROM_DEVICE); if (unlikely(ib_dma_mapping_error(priv->ca, mapping[0]))) { dev_kfree_skb_any(skb); - return -EIO; + return NULL; } - for (i = 0; i < IPOIB_CM_RX_SG - 1; i++) { + for (i = 0; i < frags; i++) { struct page *page = alloc_page(GFP_ATOMIC); if (!page) @@ -130,13 +131,13 @@ static int ipoib_cm_alloc_rx_skb(struct net_device *dev, int id, skb_fill_page_desc(skb, i, page, 0, PAGE_SIZE); mapping[i + 1] = ib_dma_map_page(priv->ca, skb_shinfo(skb)->frags[i].page, - 0, PAGE_SIZE, DMA_TO_DEVICE); + 0, PAGE_SIZE, DMA_FROM_DEVICE); if (unlikely(ib_dma_mapping_error(priv->ca, mapping[i + 1]))) goto partial_error; } priv->cm.srq_ring[id].skb = skb; - return 0; + return skb; partial_error: @@ -145,8 +146,8 @@ partial_error: for (; i >= 0; --i) ib_dma_unmap_single(priv->ca, mapping[i + 1], PAGE_SIZE, DMA_FROM_DEVICE); - kfree_skb(skb); - return -ENOMEM; + dev_kfree_skb_any(skb); + return NULL; } static struct ib_qp *ipoib_cm_create_rx_qp(struct net_device *dev, @@ -227,7 +228,6 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even struct net_device *dev = cm_id->context; struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_cm_rx *p; - unsigned long flags; unsigned psn; int ret; @@ -256,9 +256,9 @@ static int ipoib_cm_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even cm_id->context = p; p->jiffies = jiffies; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); list_add(&p->list, &priv->cm.passive_ids); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); queue_delayed_work(ipoib_workqueue, &priv->cm.stale_task, IPOIB_CM_RX_DELAY); return 0; @@ -276,7 +276,6 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id, { struct ipoib_cm_rx *p; struct ipoib_dev_priv *priv; - unsigned long flags; int ret; switch (event->event) { @@ -289,14 +288,14 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id, case IB_CM_REJ_RECEIVED: p = cm_id->context; priv = netdev_priv(p->dev); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); if (list_empty(&p->list)) ret = 0; /* Connection is going away already. */ else { list_del_init(&p->list); ret = -ECONNRESET; } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); if (ret) { ib_destroy_qp(p->qp); kfree(p); @@ -309,7 +308,7 @@ static int ipoib_cm_rx_handler(struct ib_cm_id *cm_id, } /* Adjust length of skb with fragments to match received data */ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space, - unsigned int length) + unsigned int length, struct sk_buff *toskb) { int i, num_frags; unsigned int size; @@ -326,7 +325,7 @@ static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space, if (length == 0) { /* don't need this page */ - __free_page(frag->page); + skb_fill_page_desc(toskb, i, frag->page, 0, PAGE_SIZE); --skb_shinfo(skb)->nr_frags; } else { size = min(length, (unsigned) PAGE_SIZE); @@ -344,13 +343,14 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) { struct ipoib_dev_priv *priv = netdev_priv(dev); unsigned int wr_id = wc->wr_id & ~IPOIB_CM_OP_SRQ; - struct sk_buff *skb; + struct sk_buff *skb, *newskb; struct ipoib_cm_rx *p; unsigned long flags; u64 mapping[IPOIB_CM_RX_SG]; + int frags; - ipoib_dbg_data(priv, "cm recv completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "cm recv completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_recvq_size)) { ipoib_warn(priv, "cm recv completion event with wrid %d (> %d)\n", @@ -383,7 +383,11 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) } } - if (unlikely(ipoib_cm_alloc_rx_skb(dev, wr_id, mapping))) { + frags = PAGE_ALIGN(wc->byte_len - min(wc->byte_len, + (unsigned)IPOIB_CM_HEAD_SIZE)) / PAGE_SIZE; + + newskb = ipoib_cm_alloc_rx_skb(dev, wr_id, frags, mapping); + if (unlikely(!newskb)) { /* * If we can't allocate a new RX buffer, dump * this packet and reuse the old buffer. @@ -393,16 +397,16 @@ void ipoib_cm_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) goto repost; } - ipoib_cm_dma_unmap_rx(priv, priv->cm.srq_ring[wr_id].mapping); - memcpy(priv->cm.srq_ring[wr_id].mapping, mapping, sizeof mapping); + ipoib_cm_dma_unmap_rx(priv, frags, priv->cm.srq_ring[wr_id].mapping); + memcpy(priv->cm.srq_ring[wr_id].mapping, mapping, (frags + 1) * sizeof *mapping); ipoib_dbg_data(priv, "received %d bytes, SLID 0x%04x\n", wc->byte_len, wc->slid); - skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len); + skb_put_frags(skb, IPOIB_CM_HEAD_SIZE, wc->byte_len, newskb); skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, IPOIB_ENCAP_LEN); dev->last_rx = jiffies; @@ -446,7 +450,7 @@ void ipoib_cm_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_cm_ skb->len, tx->mtu); ++priv->stats.tx_dropped; ++priv->stats.tx_errors; - ipoib_cm_skb_too_long(dev, skb, tx->mtu - INFINIBAND_ALEN); + ipoib_cm_skb_too_long(dev, skb, tx->mtu - IPOIB_ENCAP_LEN); return; } @@ -498,8 +502,8 @@ static void ipoib_cm_handle_tx_wc(struct net_device *dev, struct ipoib_cm_tx *tx struct ipoib_tx_buf *tx_req; unsigned long flags; - ipoib_dbg_data(priv, "cm send completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "cm send completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_sendq_size)) { ipoib_warn(priv, "cm send completion event with wrid %d (> %d)\n", @@ -606,23 +610,22 @@ void ipoib_cm_dev_stop(struct net_device *dev) { struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_cm_rx *p; - unsigned long flags; if (!IPOIB_CM_SUPPORTED(dev->dev_addr)) return; ib_destroy_cm_id(priv->cm.id); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); while (!list_empty(&priv->cm.passive_ids)) { p = list_entry(priv->cm.passive_ids.next, typeof(*p), list); list_del_init(&p->list); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); ib_destroy_cm_id(p->id); ib_destroy_qp(p->qp); kfree(p); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); cancel_delayed_work(&priv->cm.stale_task); } @@ -636,7 +639,6 @@ static int ipoib_cm_rep_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even struct ib_qp_attr qp_attr; int qp_attr_mask, ret; struct sk_buff *skb; - unsigned long flags; p->mtu = be32_to_cpu(data->mtu); @@ -674,12 +676,12 @@ static int ipoib_cm_rep_handler(struct ib_cm_id *cm_id, struct ib_cm_event *even skb_queue_head_init(&skqueue); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); set_bit(IPOIB_FLAG_OPER_UP, &p->flags); if (p->neigh) while ((skb = __skb_dequeue(&p->neigh->queue))) __skb_queue_tail(&skqueue, skb); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); while ((skb = __skb_dequeue(&skqueue))) { skb->dev = p->dev; @@ -889,7 +891,6 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, struct ipoib_dev_priv *priv = netdev_priv(tx->dev); struct net_device *dev = priv->dev; struct ipoib_neigh *neigh; - unsigned long flags; int ret; switch (event->event) { @@ -908,7 +909,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, case IB_CM_REJ_RECEIVED: case IB_CM_TIMEWAIT_EXIT: ipoib_dbg(priv, "CM error %d.\n", event->event); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); neigh = tx->neigh; @@ -928,7 +929,7 @@ static int ipoib_cm_tx_handler(struct ib_cm_id *cm_id, } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); break; default: break; @@ -1017,21 +1018,20 @@ static void ipoib_cm_tx_reap(struct work_struct *work) struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, cm.reap_task); struct ipoib_cm_tx *p; - unsigned long flags; - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); while (!list_empty(&priv->cm.reap_list)) { p = list_entry(priv->cm.reap_list.next, typeof(*p), list); list_del(&p->list); spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); ipoib_cm_tx_destroy(p); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); } static void ipoib_cm_skb_reap(struct work_struct *work) @@ -1040,15 +1040,14 @@ static void ipoib_cm_skb_reap(struct work_struct *work) cm.skb_task); struct net_device *dev = priv->dev; struct sk_buff *skb; - unsigned long flags; unsigned mtu = priv->mcast_mtu; - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); while ((skb = skb_dequeue(&priv->cm.skb_queue))) { spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); if (skb->protocol == htons(ETH_P_IP)) icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu)); #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -1056,11 +1055,11 @@ static void ipoib_cm_skb_reap(struct work_struct *work) icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); #endif dev_kfree_skb_any(skb); - spin_lock_irqsave(&priv->tx_lock, flags); + spin_lock_irq(&priv->tx_lock); spin_lock(&priv->lock); } spin_unlock(&priv->lock); - spin_unlock_irqrestore(&priv->tx_lock, flags); + spin_unlock_irq(&priv->tx_lock); } void ipoib_cm_skb_too_long(struct net_device* dev, struct sk_buff *skb, @@ -1082,23 +1081,22 @@ static void ipoib_cm_stale_task(struct work_struct *work) struct ipoib_dev_priv *priv = container_of(work, struct ipoib_dev_priv, cm.stale_task.work); struct ipoib_cm_rx *p; - unsigned long flags; - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); while (!list_empty(&priv->cm.passive_ids)) { /* List if sorted by LRU, start from tail, * stop when we see a recently used entry */ p = list_entry(priv->cm.passive_ids.prev, typeof(*p), list); - if (time_after_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT)) + if (time_before_eq(jiffies, p->jiffies + IPOIB_CM_RX_TIMEOUT)) break; list_del_init(&p->list); - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); ib_destroy_cm_id(p->id); ib_destroy_qp(p->qp); kfree(p); - spin_lock_irqsave(&priv->lock, flags); + spin_lock_irq(&priv->lock); } - spin_unlock_irqrestore(&priv->lock, flags); + spin_unlock_irq(&priv->lock); } @@ -1138,7 +1136,7 @@ static ssize_t set_mode(struct device *d, struct device_attribute *attr, return -EINVAL; } -static DEVICE_ATTR(mode, S_IWUGO | S_IRUGO, show_mode, set_mode); +static DEVICE_ATTR(mode, S_IWUSR | S_IRUGO, show_mode, set_mode); int ipoib_cm_add_mode_attr(struct net_device *dev) { @@ -1193,7 +1191,8 @@ int ipoib_cm_dev_init(struct net_device *dev) priv->cm.rx_wr.num_sge = IPOIB_CM_RX_SG; for (i = 0; i < ipoib_recvq_size; ++i) { - if (ipoib_cm_alloc_rx_skb(dev, i, priv->cm.srq_ring[i].mapping)) { + if (!ipoib_cm_alloc_rx_skb(dev, i, IPOIB_CM_RX_SG - 1, + priv->cm.srq_ring[i].mapping)) { ipoib_warn(priv, "failed to allocate receive buffer %d\n", i); ipoib_cm_dev_cleanup(dev); return -ENOMEM; @@ -1228,7 +1227,8 @@ void ipoib_cm_dev_cleanup(struct net_device *dev) return; for (i = 0; i < ipoib_recvq_size; ++i) if (priv->cm.srq_ring[i].skb) { - ipoib_cm_dma_unmap_rx(priv, priv->cm.srq_ring[i].mapping); + ipoib_cm_dma_unmap_rx(priv, IPOIB_CM_RX_SG - 1, + priv->cm.srq_ring[i].mapping); dev_kfree_skb_any(priv->cm.srq_ring[i].skb); priv->cm.srq_ring[i].skb = NULL; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index f2aa923..1bdb910 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -172,8 +172,8 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) struct sk_buff *skb; u64 addr; - ipoib_dbg_data(priv, "recv completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "recv completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_recvq_size)) { ipoib_warn(priv, "recv completion event with wrid %d (> %d)\n", @@ -216,7 +216,7 @@ static void ipoib_ib_handle_rx_wc(struct net_device *dev, struct ib_wc *wc) if (wc->slid != priv->local_lid || wc->src_qp != priv->qp->qp_num) { skb->protocol = ((struct ipoib_header *) skb->data)->proto; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, IPOIB_ENCAP_LEN); dev->last_rx = jiffies; @@ -245,8 +245,8 @@ static void ipoib_ib_handle_tx_wc(struct net_device *dev, struct ib_wc *wc) struct ipoib_tx_buf *tx_req; unsigned long flags; - ipoib_dbg_data(priv, "send completion: id %d, op %d, status: %d\n", - wr_id, wc->opcode, wc->status); + ipoib_dbg_data(priv, "send completion: id %d, status: %d\n", + wr_id, wc->status); if (unlikely(wr_id >= ipoib_sendq_size)) { ipoib_warn(priv, "send completion event with wrid %d (> %d)\n", @@ -328,9 +328,9 @@ void ipoib_send(struct net_device *dev, struct sk_buff *skb, struct ipoib_tx_buf *tx_req; u64 addr; - if (unlikely(skb->len > priv->mcast_mtu + INFINIBAND_ALEN)) { + if (unlikely(skb->len > priv->mcast_mtu + IPOIB_ENCAP_LEN)) { ipoib_warn(priv, "packet len %d (> %d) too long to send, dropping\n", - skb->len, priv->mcast_mtu + INFINIBAND_ALEN); + skb->len, priv->mcast_mtu + IPOIB_ENCAP_LEN); ++priv->stats.tx_dropped; ++priv->stats.tx_errors; ipoib_cm_skb_too_long(dev, skb, priv->mcast_mtu); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 18d27fd..b4c380c 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -380,12 +380,12 @@ static void path_rec_completion(int status, struct net_device *dev = path->dev; struct ipoib_dev_priv *priv = netdev_priv(dev); struct ipoib_ah *ah = NULL; - struct ipoib_neigh *neigh; + struct ipoib_neigh *neigh, *tn; struct sk_buff_head skqueue; struct sk_buff *skb; unsigned long flags; - if (pathrec) + if (!status) ipoib_dbg(priv, "PathRec LID 0x%04x for GID " IPOIB_GID_FMT "\n", be16_to_cpu(pathrec->dlid), IPOIB_GID_ARG(pathrec->dgid)); else @@ -395,14 +395,10 @@ static void path_rec_completion(int status, skb_queue_head_init(&skqueue); if (!status) { - struct ib_ah_attr av = { - .dlid = be16_to_cpu(pathrec->dlid), - .sl = pathrec->sl, - .port_num = priv->port, - .static_rate = pathrec->rate - }; - - ah = ipoib_create_ah(dev, priv->pd, &av); + struct ib_ah_attr av; + + if (!ib_init_ah_from_path(priv->ca, priv->port, pathrec, &av)) + ah = ipoib_create_ah(dev, priv->pd, &av); } spin_lock_irqsave(&priv->lock, flags); @@ -418,7 +414,7 @@ static void path_rec_completion(int status, while ((skb = __skb_dequeue(&path->queue))) __skb_queue_tail(&skqueue, skb); - list_for_each_entry(neigh, &path->neigh_list, list) { + list_for_each_entry_safe(neigh, tn, &path->neigh_list, list) { kref_get(&path->ah->ref); neigh->ah = path->ah; memcpy(&neigh->dgid.raw, &path->pathrec.dgid.raw, @@ -814,7 +810,7 @@ static void ipoib_set_mcast_list(struct net_device *dev) queue_work(ipoib_workqueue, &priv->restart_task); } -static void ipoib_neigh_destructor(struct neighbour *n) +static void ipoib_neigh_cleanup(struct neighbour *n) { struct ipoib_neigh *neigh; struct ipoib_dev_priv *priv = netdev_priv(n->dev); @@ -822,7 +818,7 @@ static void ipoib_neigh_destructor(struct neighbour *n) struct ipoib_ah *ah = NULL; ipoib_dbg(priv, - "neigh_destructor for %06x " IPOIB_GID_FMT "\n", + "neigh_cleanup for %06x " IPOIB_GID_FMT "\n", IPOIB_QPN(n->ha), IPOIB_GID_RAW_ARG(n->ha + 4)); @@ -874,7 +870,7 @@ void ipoib_neigh_free(struct net_device *dev, struct ipoib_neigh *neigh) static int ipoib_neigh_setup_dev(struct net_device *dev, struct neigh_parms *parms) { - parms->neigh_destructor = ipoib_neigh_destructor; + parms->neigh_cleanup = ipoib_neigh_cleanup; return 0; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c index fea737f..54fbead 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c @@ -60,14 +60,11 @@ static DEFINE_MUTEX(mcast_mutex); /* Used for all multicast joins (broadcast, IPv4 mcast and IPv6 mcast) */ struct ipoib_mcast { struct ib_sa_mcmember_rec mcmember; + struct ib_sa_multicast *mc; struct ipoib_ah *ah; struct rb_node rb_node; struct list_head list; - struct completion done; - - int query_id; - struct ib_sa_query *query; unsigned long created; unsigned long backoff; @@ -299,18 +296,22 @@ static int ipoib_mcast_join_finish(struct ipoib_mcast *mcast, return 0; } -static void +static int ipoib_mcast_sendonly_join_complete(int status, - struct ib_sa_mcmember_rec *mcmember, - void *mcast_ptr) + struct ib_sa_multicast *multicast) { - struct ipoib_mcast *mcast = mcast_ptr; + struct ipoib_mcast *mcast = multicast->context; struct net_device *dev = mcast->dev; struct ipoib_dev_priv *priv = netdev_priv(dev); + /* We trap for port events ourselves. */ + if (status == -ENETRESET) + return 0; + if (!status) - ipoib_mcast_join_finish(mcast, mcmember); - else { + status = ipoib_mcast_join_finish(mcast, &multicast->rec); + + if (status) { if (mcast->logcount++ < 20) ipoib_dbg_mcast(netdev_priv(dev), "multicast join failed for " IPOIB_GID_FMT ", status %d\n", @@ -325,11 +326,10 @@ ipoib_mcast_sendonly_join_complete(int status, spin_unlock_irq(&priv->tx_lock); /* Clear the busy flag so we try again */ - clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); - mcast->query = NULL; + status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, + &mcast->flags); } - - complete(&mcast->done); + return status; } static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast) @@ -359,35 +359,33 @@ static int ipoib_mcast_sendonly_join(struct ipoib_mcast *mcast) rec.port_gid = priv->local_gid; rec.pkey = cpu_to_be16(priv->pkey); - init_completion(&mcast->done); - - ret = ib_sa_mcmember_rec_set(&ipoib_sa_client, priv->ca, priv->port, &rec, - IB_SA_MCMEMBER_REC_MGID | - IB_SA_MCMEMBER_REC_PORT_GID | - IB_SA_MCMEMBER_REC_PKEY | - IB_SA_MCMEMBER_REC_JOIN_STATE, - 1000, GFP_ATOMIC, - ipoib_mcast_sendonly_join_complete, - mcast, &mcast->query); - if (ret < 0) { - ipoib_warn(priv, "ib_sa_mcmember_rec_set failed (ret = %d)\n", + mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, + priv->port, &rec, + IB_SA_MCMEMBER_REC_MGID | + IB_SA_MCMEMBER_REC_PORT_GID | + IB_SA_MCMEMBER_REC_PKEY | + IB_SA_MCMEMBER_REC_JOIN_STATE, + GFP_ATOMIC, + ipoib_mcast_sendonly_join_complete, + mcast); + if (IS_ERR(mcast->mc)) { + ret = PTR_ERR(mcast->mc); + clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + ipoib_warn(priv, "ib_sa_join_multicast failed (ret = %d)\n", ret); } else { ipoib_dbg_mcast(priv, "no multicast record for " IPOIB_GID_FMT ", starting join\n", IPOIB_GID_ARG(mcast->mcmember.mgid)); - - mcast->query_id = ret; } return ret; } -static void ipoib_mcast_join_complete(int status, - struct ib_sa_mcmember_rec *mcmember, - void *mcast_ptr) +static int ipoib_mcast_join_complete(int status, + struct ib_sa_multicast *multicast) { - struct ipoib_mcast *mcast = mcast_ptr; + struct ipoib_mcast *mcast = multicast->context; struct net_device *dev = mcast->dev; struct ipoib_dev_priv *priv = netdev_priv(dev); @@ -395,24 +393,29 @@ static void ipoib_mcast_join_complete(int status, " (status %d)\n", IPOIB_GID_ARG(mcast->mcmember.mgid), status); - if (!status && !ipoib_mcast_join_finish(mcast, mcmember)) { + /* We trap for port events ourselves. */ + if (status == -ENETRESET) + return 0; + + if (!status) + status = ipoib_mcast_join_finish(mcast, &multicast->rec); + + if (!status) { mcast->backoff = 1; mutex_lock(&mcast_mutex); if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) queue_delayed_work(ipoib_workqueue, &priv->mcast_task, 0); mutex_unlock(&mcast_mutex); - complete(&mcast->done); - return; - } - if (status == -EINTR) { - complete(&mcast->done); - return; + if (mcast == priv->broadcast) + netif_carrier_on(dev); + + return 0; } - if (status && mcast->logcount++ < 20) { - if (status == -ETIMEDOUT || status == -EINTR) { + if (mcast->logcount++ < 20) { + if (status == -ETIMEDOUT) { ipoib_dbg_mcast(priv, "multicast join failed for " IPOIB_GID_FMT ", status %d\n", IPOIB_GID_ARG(mcast->mcmember.mgid), @@ -429,24 +432,18 @@ static void ipoib_mcast_join_complete(int status, if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS) mcast->backoff = IPOIB_MAX_BACKOFF_SECONDS; - mutex_lock(&mcast_mutex); + /* Clear the busy flag so we try again */ + status = test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + mutex_lock(&mcast_mutex); spin_lock_irq(&priv->lock); - mcast->query = NULL; - - if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) { - if (status == -ETIMEDOUT) - queue_delayed_work(ipoib_workqueue, &priv->mcast_task, - 0); - else - queue_delayed_work(ipoib_workqueue, &priv->mcast_task, - mcast->backoff * HZ); - } else - complete(&mcast->done); + if (test_bit(IPOIB_MCAST_RUN, &priv->flags)) + queue_delayed_work(ipoib_workqueue, &priv->mcast_task, + mcast->backoff * HZ); spin_unlock_irq(&priv->lock); mutex_unlock(&mcast_mutex); - return; + return status; } static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, @@ -495,15 +492,14 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, rec.hop_limit = priv->broadcast->mcmember.hop_limit; } - init_completion(&mcast->done); - - ret = ib_sa_mcmember_rec_set(&ipoib_sa_client, priv->ca, priv->port, - &rec, comp_mask, mcast->backoff * 1000, - GFP_ATOMIC, ipoib_mcast_join_complete, - mcast, &mcast->query); - - if (ret < 0) { - ipoib_warn(priv, "ib_sa_mcmember_rec_set failed, status %d\n", ret); + set_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + mcast->mc = ib_sa_join_multicast(&ipoib_sa_client, priv->ca, priv->port, + &rec, comp_mask, GFP_KERNEL, + ipoib_mcast_join_complete, mcast); + if (IS_ERR(mcast->mc)) { + clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags); + ret = PTR_ERR(mcast->mc); + ipoib_warn(priv, "ib_sa_join_multicast failed, status %d\n", ret); mcast->backoff *= 2; if (mcast->backoff > IPOIB_MAX_BACKOFF_SECONDS) @@ -515,8 +511,7 @@ static void ipoib_mcast_join(struct net_device *dev, struct ipoib_mcast *mcast, &priv->mcast_task, mcast->backoff * HZ); mutex_unlock(&mcast_mutex); - } else - mcast->query_id = ret; + } } void ipoib_mcast_join_task(struct work_struct *work) @@ -536,12 +531,10 @@ void ipoib_mcast_join_task(struct work_struct *work) { struct ib_port_attr attr; - if (!ib_query_port(priv->ca, priv->port, &attr)) { - priv->local_lid = attr.lid; - priv->local_rate = attr.active_speed * - ib_width_enum_to_int(attr.active_width); - } else - ipoib_warn(priv, "ib_query_port failed\n"); + if (!ib_query_port(priv->ca, priv->port, &attr)) + priv->local_lid = attr.lid; + else + ipoib_warn(priv, "ib_query_port failed\n"); } if (!priv->broadcast) { @@ -568,7 +561,8 @@ void ipoib_mcast_join_task(struct work_struct *work) } if (!test_bit(IPOIB_MCAST_FLAG_ATTACHED, &priv->broadcast->flags)) { - ipoib_mcast_join(dev, priv->broadcast, 0); + if (!test_bit(IPOIB_MCAST_FLAG_BUSY, &priv->broadcast->flags)) + ipoib_mcast_join(dev, priv->broadcast, 0); return; } @@ -604,7 +598,6 @@ void ipoib_mcast_join_task(struct work_struct *work) ipoib_dbg_mcast(priv, "successfully joined all multicast groups\n"); clear_bit(IPOIB_MCAST_RUN, &priv->flags); - netif_carrier_on(dev); } int ipoib_mcast_start_thread(struct net_device *dev) @@ -625,26 +618,9 @@ int ipoib_mcast_start_thread(struct net_device *dev) return 0; } -static void wait_for_mcast_join(struct ipoib_dev_priv *priv, - struct ipoib_mcast *mcast) -{ - spin_lock_irq(&priv->lock); - if (mcast && mcast->query) { - ib_sa_cancel_query(mcast->query_id, mcast->query); - mcast->query = NULL; - spin_unlock_irq(&priv->lock); - ipoib_dbg_mcast(priv, "waiting for MGID " IPOIB_GID_FMT "\n", - IPOIB_GID_ARG(mcast->mcmember.mgid)); - wait_for_completion(&mcast->done); - } - else - spin_unlock_irq(&priv->lock); -} - int ipoib_mcast_stop_thread(struct net_device *dev, int flush) { struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ipoib_mcast *mcast; ipoib_dbg_mcast(priv, "stopping multicast thread\n"); @@ -660,52 +636,27 @@ int ipoib_mcast_stop_thread(struct net_device *dev, int flush) if (flush) flush_workqueue(ipoib_workqueue); - wait_for_mcast_join(priv, priv->broadcast); - - list_for_each_entry(mcast, &priv->multicast_list, list) - wait_for_mcast_join(priv, mcast); - return 0; } static int ipoib_mcast_leave(struct net_device *dev, struct ipoib_mcast *mcast) { struct ipoib_dev_priv *priv = netdev_priv(dev); - struct ib_sa_mcmember_rec rec = { - .join_state = 1 - }; int ret = 0; - if (!test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) - return 0; - - ipoib_dbg_mcast(priv, "leaving MGID " IPOIB_GID_FMT "\n", - IPOIB_GID_ARG(mcast->mcmember.mgid)); + if (test_and_clear_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) + ib_sa_free_multicast(mcast->mc); - rec.mgid = mcast->mcmember.mgid; - rec.port_gid = priv->local_gid; - rec.pkey = cpu_to_be16(priv->pkey); - - /* Remove ourselves from the multicast group */ - ret = ipoib_mcast_detach(dev, be16_to_cpu(mcast->mcmember.mlid), - &mcast->mcmember.mgid); - if (ret) - ipoib_warn(priv, "ipoib_mcast_detach failed (result = %d)\n", ret); + if (test_and_clear_bit(IPOIB_MCAST_FLAG_ATTACHED, &mcast->flags)) { + ipoib_dbg_mcast(priv, "leaving MGID " IPOIB_GID_FMT "\n", + IPOIB_GID_ARG(mcast->mcmember.mgid)); - /* - * Just make one shot at leaving and don't wait for a reply; - * if we fail, too bad. - */ - ret = ib_sa_mcmember_rec_delete(&ipoib_sa_client, priv->ca, priv->port, &rec, - IB_SA_MCMEMBER_REC_MGID | - IB_SA_MCMEMBER_REC_PORT_GID | - IB_SA_MCMEMBER_REC_PKEY | - IB_SA_MCMEMBER_REC_JOIN_STATE, - 0, GFP_ATOMIC, NULL, - mcast, &mcast->query); - if (ret < 0) - ipoib_warn(priv, "ib_sa_mcmember_rec_delete failed " - "for leave (result = %d)\n", ret); + /* Remove ourselves from the multicast group */ + ret = ipoib_mcast_detach(dev, be16_to_cpu(mcast->mcmember.mlid), + &mcast->mcmember.mgid); + if (ret) + ipoib_warn(priv, "ipoib_mcast_detach failed (result = %d)\n", ret); + } return 0; } @@ -758,7 +709,7 @@ void ipoib_mcast_send(struct net_device *dev, void *mgid, struct sk_buff *skb) dev_kfree_skb_any(skb); } - if (mcast->query) + if (test_bit(IPOIB_MCAST_FLAG_BUSY, &mcast->flags)) ipoib_dbg_mcast(priv, "no address vector, " "but multicast join already started\n"); else if (test_bit(IPOIB_MCAST_FLAG_SENDONLY, &mcast->flags)) @@ -916,7 +867,6 @@ void ipoib_mcast_restart_task(struct work_struct *work) /* We have to cancel outside of the spinlock */ list_for_each_entry_safe(mcast, tmcast, &remove_list, list) { - wait_for_mcast_join(priv, mcast); ipoib_mcast_leave(mcast->dev, mcast); ipoib_mcast_free(mcast); } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c index 3cb551b..7f3ec20 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_verbs.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_verbs.c @@ -259,12 +259,13 @@ void ipoib_event(struct ib_event_handler *handler, struct ipoib_dev_priv *priv = container_of(handler, struct ipoib_dev_priv, event_handler); - if (record->event == IB_EVENT_PORT_ERR || - record->event == IB_EVENT_PKEY_CHANGE || - record->event == IB_EVENT_PORT_ACTIVE || - record->event == IB_EVENT_LID_CHANGE || - record->event == IB_EVENT_SM_CHANGE || - record->event == IB_EVENT_CLIENT_REREGISTER) { + if ((record->event == IB_EVENT_PORT_ERR || + record->event == IB_EVENT_PKEY_CHANGE || + record->event == IB_EVENT_PORT_ACTIVE || + record->event == IB_EVENT_LID_CHANGE || + record->event == IB_EVENT_SM_CHANGE || + record->event == IB_EVENT_CLIENT_REREGISTER) && + record->element.port_num == priv->port) { ipoib_dbg(priv, "Port state change event\n"); queue_work(ipoib_workqueue, &priv->flush_task); } diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index cae8c96..8960196 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -245,7 +245,6 @@ struct iser_conn { wait_queue_head_t wait; /* waitq for conn/disconn */ atomic_t post_recv_buf_count; /* posted rx count */ atomic_t post_send_buf_count; /* posted tx count */ - struct work_struct comperror_work; /* conn term sleepable ctx*/ char name[ISER_OBJECT_NAME_SIZE]; struct iser_page_vec *page_vec; /* represents SG to fmr maps* * maps serialized as tx is*/ diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c index 89e3728..278fcbc 100644 --- a/drivers/infiniband/ulp/iser/iser_initiator.c +++ b/drivers/infiniband/ulp/iser/iser_initiator.c @@ -658,6 +658,7 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask) { int deferred; int is_rdma_aligned = 1; + struct iser_regd_buf *regd; /* if we were reading, copy back to unaligned sglist, * anyway dma_unmap and free the copy @@ -672,20 +673,20 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask) } if (iser_ctask->dir[ISER_DIR_IN]) { - deferred = iser_regd_buff_release - (&iser_ctask->rdma_regd[ISER_DIR_IN]); + regd = &iser_ctask->rdma_regd[ISER_DIR_IN]; + deferred = iser_regd_buff_release(regd); if (deferred) { - iser_err("References remain for BUF-IN rdma reg\n"); - BUG(); + iser_err("%d references remain for BUF-IN rdma reg\n", + atomic_read(®d->ref_count)); } } if (iser_ctask->dir[ISER_DIR_OUT]) { - deferred = iser_regd_buff_release - (&iser_ctask->rdma_regd[ISER_DIR_OUT]); + regd = &iser_ctask->rdma_regd[ISER_DIR_OUT]; + deferred = iser_regd_buff_release(regd); if (deferred) { - iser_err("References remain for BUF-OUT rdma reg\n"); - BUG(); + iser_err("%d references remain for BUF-OUT rdma reg\n", + atomic_read(®d->ref_count)); } } diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 693b770..1fc9674 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -48,7 +48,6 @@ static void iser_cq_tasklet_fn(unsigned long data); static void iser_cq_callback(struct ib_cq *cq, void *cq_context); -static void iser_comp_error_worker(struct work_struct *work); static void iser_cq_event_callback(struct ib_event *cause, void *context) { @@ -480,7 +479,6 @@ int iser_conn_init(struct iser_conn **ibconn) init_waitqueue_head(&ib_conn->wait); atomic_set(&ib_conn->post_recv_buf_count, 0); atomic_set(&ib_conn->post_send_buf_count, 0); - INIT_WORK(&ib_conn->comperror_work, iser_comp_error_worker); INIT_LIST_HEAD(&ib_conn->conn_list); spin_lock_init(&ib_conn->lock); @@ -753,26 +751,6 @@ int iser_post_send(struct iser_desc *tx_desc) return ret_val; } -static void iser_comp_error_worker(struct work_struct *work) -{ - struct iser_conn *ib_conn = - container_of(work, struct iser_conn, comperror_work); - - /* getting here when the state is UP means that the conn is being * - * terminated asynchronously from the iSCSI layer's perspective. */ - if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, - ISER_CONN_TERMINATING)) - iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn, - ISCSI_ERR_CONN_FAILED); - - /* complete the termination process if disconnect event was delivered * - * note there are no more non completed posts to the QP */ - if (ib_conn->disc_evt_flag) { - ib_conn->state = ISER_CONN_DOWN; - wake_up_interruptible(&ib_conn->wait); - } -} - static void iser_handle_comp_error(struct iser_desc *desc) { struct iser_dto *dto = &desc->dto; @@ -791,8 +769,22 @@ static void iser_handle_comp_error(struct iser_desc *desc) } if (atomic_read(&ib_conn->post_recv_buf_count) == 0 && - atomic_read(&ib_conn->post_send_buf_count) == 0) - schedule_work(&ib_conn->comperror_work); + atomic_read(&ib_conn->post_send_buf_count) == 0) { + /* getting here when the state is UP means that the conn is * + * being terminated asynchronously from the iSCSI layer's * + * perspective. */ + if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, + ISER_CONN_TERMINATING)) + iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn, + ISCSI_ERR_CONN_FAILED); + + /* complete the termination process if disconnect event was delivered * + * note there are no more non completed posts to the QP */ + if (ib_conn->disc_evt_flag) { + ib_conn->state = ISER_CONN_DOWN; + wake_up_interruptible(&ib_conn->wait); + } + } } static void iser_cq_tasklet_fn(unsigned long data) diff --git a/drivers/input/gameport/gameport.c b/drivers/input/gameport/gameport.c index a00fe47..bd686a2 100644 --- a/drivers/input/gameport/gameport.c +++ b/drivers/input/gameport/gameport.c @@ -190,16 +190,14 @@ static void gameport_run_poll_handler(unsigned long d) * Basic gameport -> driver core mappings */ -static void gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) +static int gameport_bind_driver(struct gameport *gameport, struct gameport_driver *drv) { int error; - down_write(&gameport_bus.subsys.rwsem); - gameport->dev.driver = &drv->driver; if (drv->connect(gameport, drv)) { gameport->dev.driver = NULL; - goto out; + return -ENODEV; } error = device_bind_driver(&gameport->dev); @@ -211,31 +209,21 @@ static void gameport_bind_driver(struct gameport *gameport, struct gameport_driv drv->description, error); drv->disconnect(gameport); gameport->dev.driver = NULL; - goto out; + return error; } - out: - up_write(&gameport_bus.subsys.rwsem); -} - -static void gameport_release_driver(struct gameport *gameport) -{ - down_write(&gameport_bus.subsys.rwsem); - device_release_driver(&gameport->dev); - up_write(&gameport_bus.subsys.rwsem); + return 0; } static void gameport_find_driver(struct gameport *gameport) { int error; - down_write(&gameport_bus.subsys.rwsem); error = device_attach(&gameport->dev); if (error < 0) printk(KERN_WARNING "gameport: device_attach() failed for %s (%s), error: %d\n", gameport->phys, gameport->name, error); - up_write(&gameport_bus.subsys.rwsem); } @@ -483,13 +471,12 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut { struct gameport *gameport = to_gameport_port(dev); struct device_driver *drv; - int retval; + int error; - retval = mutex_lock_interruptible(&gameport_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&gameport_mutex); + if (error) + return error; - retval = count; if (!strncmp(buf, "none", count)) { gameport_disconnect_port(gameport); } else if (!strncmp(buf, "reconnect", count)) { @@ -499,15 +486,15 @@ static ssize_t gameport_rebind_driver(struct device *dev, struct device_attribut gameport_find_driver(gameport); } else if ((drv = driver_find(buf, &gameport_bus)) != NULL) { gameport_disconnect_port(gameport); - gameport_bind_driver(gameport, to_gameport_driver(drv)); + error = gameport_bind_driver(gameport, to_gameport_driver(drv)); put_driver(drv); } else { - retval = -EINVAL; + error = -EINVAL; } mutex_unlock(&gameport_mutex); - return retval; + return error ? error : count; } static struct device_attribute gameport_device_attrs[] = { @@ -655,7 +642,7 @@ static void gameport_disconnect_port(struct gameport *gameport) do { parent = s->parent; - gameport_release_driver(s); + device_release_driver(&s->dev); gameport_destroy_port(s); } while ((s = parent) != gameport); } @@ -663,7 +650,7 @@ static void gameport_disconnect_port(struct gameport *gameport) /* * Ok, no children left, now disconnect this port */ - gameport_release_driver(gameport); + device_release_driver(&gameport->dev); } void gameport_rescan(struct gameport *gameport) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index be65e50..e285172 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -224,14 +224,16 @@ config KEYBOARD_AAED2000 module will be called aaed2000_kbd. config KEYBOARD_GPIO - tristate "Buttons on CPU GPIOs (PXA)" - depends on (ARCH_SA1100 || ARCH_PXA || ARCH_S3C2410) + tristate "GPIO Buttons" + depends on GENERIC_GPIO help This driver implements support for buttons connected - directly to GPIO pins of SA1100, PXA or S3C24xx CPUs. + to GPIO pins of various CPUs (and some other chips). Say Y here if your device has buttons connected - directly to GPIO pins of the CPU. + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. To compile this driver as a module, choose M here: the module will be called gpio-keys. diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 71771ba..0d2796c 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -23,11 +23,9 @@ #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> +#include <linux/gpio_keys.h> #include <asm/gpio.h> -#include <asm/arch/hardware.h> - -#include <asm/hardware/gpio_keys.h> static irqreturn_t gpio_keys_isr(int irq, void *dev_id) { diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index ff40b5c..7c17377 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -553,7 +553,8 @@ static int __devinit i8042_check_aux(void) */ param = 0x5a; - if (i8042_command(¶m, I8042_CMD_AUX_LOOP) || param != 0x5a) { + retval = i8042_command(¶m, I8042_CMD_AUX_LOOP); + if (retval || param != 0x5a) { /* * External connection test - filters out AT-soldered PS/2 i8042's @@ -567,7 +568,12 @@ static int __devinit i8042_check_aux(void) (param && param != 0xfa && param != 0xff)) return -1; - aux_loop_broken = 1; +/* + * If AUX_LOOP completed without error but returned unexpected data + * mark it as broken + */ + if (!retval) + aux_loop_broken = 1; } /* diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c index b3e84d3..10d9d74 100644 --- a/drivers/input/serio/libps2.c +++ b/drivers/input/serio/libps2.c @@ -97,7 +97,7 @@ EXPORT_SYMBOL(ps2_drain); int ps2_is_keyboard_id(char id_byte) { - const static char keyboard_ids[] = { + static const char keyboard_ids[] = { 0xab, /* Regular keyboards */ 0xac, /* NCD Sun keyboard */ 0x2b, /* Trust keyboard, translated */ diff --git a/drivers/input/serio/serio.c b/drivers/input/serio/serio.c index a15e531..5895202 100644 --- a/drivers/input/serio/serio.c +++ b/drivers/input/serio/serio.c @@ -115,18 +115,18 @@ static int serio_match_port(const struct serio_device_id *ids, struct serio *ser * Basic serio -> driver core mappings */ -static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) +static int serio_bind_driver(struct serio *serio, struct serio_driver *drv) { int error; - down_write(&serio_bus.subsys.rwsem); - if (serio_match_port(drv->id_table, serio)) { + serio->dev.driver = &drv->driver; if (serio_connect_driver(serio, drv)) { serio->dev.driver = NULL; - goto out; + return -ENODEV; } + error = device_bind_driver(&serio->dev); if (error) { printk(KERN_WARNING @@ -136,31 +136,21 @@ static void serio_bind_driver(struct serio *serio, struct serio_driver *drv) drv->description, error); serio_disconnect_driver(serio); serio->dev.driver = NULL; - goto out; + return error; } } - out: - up_write(&serio_bus.subsys.rwsem); -} - -static void serio_release_driver(struct serio *serio) -{ - down_write(&serio_bus.subsys.rwsem); - device_release_driver(&serio->dev); - up_write(&serio_bus.subsys.rwsem); + return 0; } static void serio_find_driver(struct serio *serio) { int error; - down_write(&serio_bus.subsys.rwsem); error = device_attach(&serio->dev); if (error < 0) printk(KERN_WARNING "serio: device_attach() failed for %s (%s), error: %d\n", serio->phys, serio->name, error); - up_write(&serio_bus.subsys.rwsem); } @@ -470,13 +460,12 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * { struct serio *serio = to_serio_port(dev); struct device_driver *drv; - int retval; + int error; - retval = mutex_lock_interruptible(&serio_mutex); - if (retval) - return retval; + error = mutex_lock_interruptible(&serio_mutex); + if (error) + return error; - retval = count; if (!strncmp(buf, "none", count)) { serio_disconnect_port(serio); } else if (!strncmp(buf, "reconnect", count)) { @@ -486,15 +475,15 @@ static ssize_t serio_rebind_driver(struct device *dev, struct device_attribute * serio_find_driver(serio); } else if ((drv = driver_find(buf, &serio_bus)) != NULL) { serio_disconnect_port(serio); - serio_bind_driver(serio, to_serio_driver(drv)); + error = serio_bind_driver(serio, to_serio_driver(drv)); put_driver(drv); } else { - retval = -EINVAL; + error = -EINVAL; } mutex_unlock(&serio_mutex); - return retval; + return error ? error : count; } static ssize_t serio_show_bind_mode(struct device *dev, struct device_attribute *attr, char *buf) @@ -665,7 +654,7 @@ static void serio_disconnect_port(struct serio *serio) do { parent = s->parent; - serio_release_driver(s); + device_release_driver(&s->dev); serio_destroy_port(s); } while ((s = parent) != serio); } @@ -673,7 +662,7 @@ static void serio_disconnect_port(struct serio *serio) /* * Ok, no children left, now disconnect this port */ - serio_release_driver(serio); + device_release_driver(&serio->dev); } void serio_rescan(struct serio *serio) diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index 24000f0..6582816 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -557,6 +557,7 @@ static int ucb1400_ts_remove(struct device *dev) } static struct device_driver ucb1400_ts_driver = { + .name = "ucb1400_ts", .owner = THIS_MODULE, .bus = &ac97_bus_type, .probe = ucb1400_ts_probe, diff --git a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c index e3e5c13..ee2b0b9 100644 --- a/drivers/isdn/act2000/module.c +++ b/drivers/isdn/act2000/module.c @@ -442,7 +442,7 @@ act2000_sendbuf(act2000_card *card, int channel, int ack, struct sk_buff *skb) return 0; } skb_reserve(xmit_skb, 19); - memcpy(skb_put(xmit_skb, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(xmit_skb, len), len); } else { xmit_skb = skb_clone(skb, GFP_ATOMIC); if (!xmit_skb) { diff --git a/drivers/isdn/capi/Kconfig b/drivers/isdn/capi/Kconfig index 8b6c9a4..c921d6c 100644 --- a/drivers/isdn/capi/Kconfig +++ b/drivers/isdn/capi/Kconfig @@ -2,13 +2,25 @@ # Config.in for the CAPI subsystem # config ISDN_DRV_AVMB1_VERBOSE_REASON - bool "Verbose reason code reporting (kernel size +=7K)" + bool "Verbose reason code reporting" depends on ISDN_CAPI + default y help - If you say Y here, the AVM B1 driver will give verbose reasons for + If you say Y here, the CAPI drivers will give verbose reasons for disconnecting. This will increase the size of the kernel by 7 KB. If unsure, say Y. +config CAPI_TRACE + bool "CAPI trace support" + depends on ISDN_CAPI + default y + help + If you say Y here, the kernelcapi driver can make verbose traces + of CAPI messages. This feature can be enabled/disabled via IOCTL for + every controler (default disabled). + This will increase the size of the kernelcapi module by 20 KB. + If unsure, say Y. + config ISDN_CAPI_MIDDLEWARE bool "CAPI2.0 Middleware support (EXPERIMENTAL)" depends on ISDN_CAPI && EXPERIMENTAL diff --git a/drivers/isdn/capi/capidrv.c b/drivers/isdn/capi/capidrv.c index 2a49cea..23b6f7b 100644 --- a/drivers/isdn/capi/capidrv.c +++ b/drivers/isdn/capi/capidrv.c @@ -990,6 +990,7 @@ static void handle_plci(_cmsg * cmsg) capidrv_contr *card = findcontrbynumber(cmsg->adr.adrController & 0x7f); capidrv_plci *plcip; isdn_ctrl cmd; + _cdebbuf *cdb; if (!card) { printk(KERN_ERR "capidrv: %s from unknown controller 0x%x\n", @@ -1122,8 +1123,15 @@ static void handle_plci(_cmsg * cmsg) break; } } - printk(KERN_ERR "capidrv-%d: %s\n", - card->contrnr, capi_cmsg2str(cmsg)); + cdb = capi_cmsg2str(cmsg); + if (cdb) { + printk(KERN_WARNING "capidrv-%d: %s\n", + card->contrnr, cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_WARNING "capidrv-%d: CAPI_INFO_IND InfoNumber %x not handled\n", + card->contrnr, cmsg->InfoNumber); + break; case CAPI_CONNECT_ACTIVE_CONF: /* plci */ @@ -1371,10 +1379,18 @@ static _cmsg s_cmsg; static void capidrv_recv_message(struct capi20_appl *ap, struct sk_buff *skb) { capi_message2cmsg(&s_cmsg, skb->data); - if (debugmode > 3) - printk(KERN_DEBUG "capidrv_signal: applid=%d %s\n", - ap->applid, capi_cmsg2str(&s_cmsg)); - + if (debugmode > 3) { + _cdebbuf *cdb = capi_cmsg2str(&s_cmsg); + + if (cdb) { + printk(KERN_DEBUG "%s: applid=%d %s\n", __FUNCTION__, + ap->applid, cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_DEBUG "%s: applid=%d %s not traced\n", + __FUNCTION__, ap->applid, + capi_cmd2str(s_cmsg.Command, s_cmsg.Subcommand)); + } if (s_cmsg.Command == CAPI_DATA_B3 && s_cmsg.Subcommand == CAPI_IND) { handle_data(&s_cmsg, skb); diff --git a/drivers/isdn/capi/capiutil.c b/drivers/isdn/capi/capiutil.c index c1b2155..ad1e270 100644 --- a/drivers/isdn/capi/capiutil.c +++ b/drivers/isdn/capi/capiutil.c @@ -648,6 +648,9 @@ char *capi_cmd2str(u8 cmd, u8 subcmd) /*-------------------------------------------------------*/ + +#ifdef CONFIG_CAPI_TRACE + /*-------------------------------------------------------*/ static char *pnames[] = @@ -703,44 +706,77 @@ static char *pnames[] = }; -static char buf[8192]; -static char *p = NULL; #include <stdarg.h> /*-------------------------------------------------------*/ -static void bufprint(char *fmt,...) +static _cdebbuf *bufprint(_cdebbuf *cdb, char *fmt,...) { va_list f; + size_t n,r; + + if (!cdb) + return NULL; va_start(f, fmt); - vsprintf(p, fmt, f); + r = cdb->size - cdb->pos; + n = vsnprintf(cdb->p, r, fmt, f); va_end(f); - p += strlen(p); + if (n >= r) { + /* truncated, need bigger buffer */ + size_t ns = 2 * cdb->size; + u_char *nb; + + while ((ns - cdb->pos) <= n) + ns *= 2; + nb = kmalloc(ns, GFP_ATOMIC); + if (!nb) { + cdebbuf_free(cdb); + return NULL; + } + memcpy(nb, cdb->buf, cdb->pos); + kfree(cdb->buf); + nb[cdb->pos] = 0; + cdb->buf = nb; + cdb->p = cdb->buf + cdb->pos; + cdb->size = ns; + va_start(f, fmt); + r = cdb->size - cdb->pos; + n = vsnprintf(cdb->p, r, fmt, f); + va_end(f); + } + cdb->p += n; + cdb->pos += n; + return cdb; } -static void printstructlen(u8 * m, unsigned len) +static _cdebbuf *printstructlen(_cdebbuf *cdb, u8 * m, unsigned len) { unsigned hex = 0; + + if (!cdb) + return NULL; for (; len; len--, m++) if (isalnum(*m) || *m == ' ') { if (hex) - bufprint(">"); - bufprint("%c", *m); + cdb = bufprint(cdb, ">"); + cdb = bufprint(cdb, "%c", *m); hex = 0; } else { if (!hex) - bufprint("<%02x", *m); + cdb = bufprint(cdb, "<%02x", *m); else - bufprint(" %02x", *m); + cdb = bufprint(cdb, " %02x", *m); hex = 1; } if (hex) - bufprint(">"); + cdb = bufprint(cdb, ">"); + return cdb; } -static void printstruct(u8 * m) +static _cdebbuf *printstruct(_cdebbuf *cdb, u8 * m) { unsigned len; + if (m[0] != 0xff) { len = m[0]; m += 1; @@ -748,42 +784,45 @@ static void printstruct(u8 * m) len = ((u16 *) (m + 1))[0]; m += 3; } - printstructlen(m, len); + cdb = printstructlen(cdb, m, len); + return cdb; } /*-------------------------------------------------------*/ #define NAME (pnames[cmsg->par[cmsg->p]]) -static void protocol_message_2_pars(_cmsg * cmsg, int level) +static _cdebbuf *protocol_message_2_pars(_cdebbuf *cdb, _cmsg *cmsg, int level) { for (; TYP != _CEND; cmsg->p++) { int slen = 29 + 3 - level; int i; - bufprint(" "); + if (!cdb) + return NULL; + cdb = bufprint(cdb, " "); for (i = 0; i < level - 1; i++) - bufprint(" "); + cdb = bufprint(cdb, " "); switch (TYP) { case _CBYTE: - bufprint("%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l)); + cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u8 *) (cmsg->m + cmsg->l)); cmsg->l++; break; case _CWORD: - bufprint("%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l)); + cdb = bufprint(cdb, "%-*s = 0x%x\n", slen, NAME, *(u16 *) (cmsg->m + cmsg->l)); cmsg->l += 2; break; case _CDWORD: - bufprint("%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l)); + cdb = bufprint(cdb, "%-*s = 0x%lx\n", slen, NAME, *(u32 *) (cmsg->m + cmsg->l)); cmsg->l += 4; break; case _CSTRUCT: - bufprint("%-*s = ", slen, NAME); + cdb = bufprint(cdb, "%-*s = ", slen, NAME); if (cmsg->m[cmsg->l] == '\0') - bufprint("default"); + cdb = bufprint(cdb, "default"); else - printstruct(cmsg->m + cmsg->l); - bufprint("\n"); + cdb = printstruct(cdb, cmsg->m + cmsg->l); + cdb = bufprint(cdb, "\n"); if (cmsg->m[cmsg->l] != 0xff) cmsg->l += 1 + cmsg->m[cmsg->l]; else @@ -794,61 +833,184 @@ static void protocol_message_2_pars(_cmsg * cmsg, int level) case _CMSTRUCT: /*----- Metastruktur 0 -----*/ if (cmsg->m[cmsg->l] == '\0') { - bufprint("%-*s = default\n", slen, NAME); + cdb = bufprint(cdb, "%-*s = default\n", slen, NAME); cmsg->l++; jumpcstruct(cmsg); } else { char *name = NAME; unsigned _l = cmsg->l; - bufprint("%-*s\n", slen, name); + cdb = bufprint(cdb, "%-*s\n", slen, name); cmsg->l = (cmsg->m + _l)[0] == 255 ? cmsg->l + 3 : cmsg->l + 1; cmsg->p++; - protocol_message_2_pars(cmsg, level + 1); + cdb = protocol_message_2_pars(cdb, cmsg, level + 1); } break; } } + return cdb; } /*-------------------------------------------------------*/ -char *capi_message2str(u8 * msg) + +static _cdebbuf *g_debbuf; +static u_long g_debbuf_lock; +static _cmsg *g_cmsg; + +_cdebbuf *cdebbuf_alloc(void) { + _cdebbuf *cdb; + + if (likely(!test_and_set_bit(1, &g_debbuf_lock))) { + cdb = g_debbuf; + goto init; + } else + cdb = kmalloc(sizeof(_cdebbuf), GFP_ATOMIC); + if (!cdb) + return NULL; + cdb->buf = kmalloc(CDEBUG_SIZE, GFP_ATOMIC); + if (!cdb->buf) { + kfree(cdb); + return NULL; + } + cdb->size = CDEBUG_SIZE; +init: + cdb->buf[0] = 0; + cdb->p = cdb->buf; + cdb->pos = 0; + return cdb; +} - _cmsg cmsg; - p = buf; - p[0] = 0; +void cdebbuf_free(_cdebbuf *cdb) +{ + if (likely(cdb == g_debbuf)) { + test_and_clear_bit(1, &g_debbuf_lock); + return; + } + if (likely(cdb)) + kfree(cdb->buf); + kfree(cdb); +} - cmsg.m = msg; - cmsg.l = 8; - cmsg.p = 0; - byteTRcpy(cmsg.m + 4, &cmsg.Command); - byteTRcpy(cmsg.m + 5, &cmsg.Subcommand); - cmsg.par = cpars[command_2_index(cmsg.Command, cmsg.Subcommand)]; - bufprint("%-26s ID=%03d #0x%04x LEN=%04d\n", - mnames[command_2_index(cmsg.Command, cmsg.Subcommand)], +_cdebbuf *capi_message2str(u8 * msg) +{ + _cdebbuf *cdb; + _cmsg *cmsg; + + cdb = cdebbuf_alloc(); + if (unlikely(!cdb)) + return NULL; + if (likely(cdb == g_debbuf)) + cmsg = g_cmsg; + else + cmsg = kmalloc(sizeof(_cmsg), GFP_ATOMIC); + if (unlikely(!cmsg)) { + cdebbuf_free(cdb); + return NULL; + } + cmsg->m = msg; + cmsg->l = 8; + cmsg->p = 0; + byteTRcpy(cmsg->m + 4, &cmsg->Command); + byteTRcpy(cmsg->m + 5, &cmsg->Subcommand); + cmsg->par = cpars[command_2_index(cmsg->Command, cmsg->Subcommand)]; + + cdb = bufprint(cdb, "%-26s ID=%03d #0x%04x LEN=%04d\n", + mnames[command_2_index(cmsg->Command, cmsg->Subcommand)], ((unsigned short *) msg)[1], ((unsigned short *) msg)[3], ((unsigned short *) msg)[0]); - protocol_message_2_pars(&cmsg, 1); - return buf; + cdb = protocol_message_2_pars(cdb, cmsg, 1); + if (unlikely(cmsg != g_cmsg)) + kfree(cmsg); + return cdb; } -char *capi_cmsg2str(_cmsg * cmsg) +_cdebbuf *capi_cmsg2str(_cmsg * cmsg) { - p = buf; - p[0] = 0; + _cdebbuf *cdb; + + cdb = cdebbuf_alloc(); + if (!cdb) + return NULL; cmsg->l = 8; cmsg->p = 0; - bufprint("%s ID=%03d #0x%04x LEN=%04d\n", + cdb = bufprint(cdb, "%s ID=%03d #0x%04x LEN=%04d\n", mnames[command_2_index(cmsg->Command, cmsg->Subcommand)], ((u16 *) cmsg->m)[1], ((u16 *) cmsg->m)[3], ((u16 *) cmsg->m)[0]); - protocol_message_2_pars(cmsg, 1); - return buf; + cdb = protocol_message_2_pars(cdb, cmsg, 1); + return cdb; } +int __init cdebug_init(void) +{ + g_cmsg= kmalloc(sizeof(_cmsg), GFP_KERNEL); + if (!g_cmsg) + return ENOMEM; + g_debbuf = kmalloc(sizeof(_cdebbuf), GFP_KERNEL); + if (!g_debbuf) { + kfree(g_cmsg); + return ENOMEM; + } + g_debbuf->buf = kmalloc(CDEBUG_GSIZE, GFP_KERNEL); + if (!g_debbuf->buf) { + kfree(g_cmsg); + kfree(g_debbuf); + return ENOMEM;; + } + g_debbuf->size = CDEBUG_GSIZE; + g_debbuf->buf[0] = 0; + g_debbuf->p = g_debbuf->buf; + g_debbuf->pos = 0; + return 0; +} + +void __exit cdebug_exit(void) +{ + if (g_debbuf) + kfree(g_debbuf->buf); + kfree(g_debbuf); + kfree(g_cmsg); +} + +#else /* !CONFIG_CAPI_TRACE */ + +static _cdebbuf g_debbuf = {"CONFIG_CAPI_TRACE not enabled", NULL, 0, 0}; + +_cdebbuf *capi_message2str(u8 * msg) +{ + return &g_debbuf; +} + +_cdebbuf *capi_cmsg2str(_cmsg * cmsg) +{ + return &g_debbuf; +} + +_cdebbuf *cdebbuf_alloc(void) +{ + return &g_debbuf; +} + +void cdebbuf_free(_cdebbuf *cdb) +{ +} + +int __init cdebug_init(void) +{ + return 0; +} + +void __exit cdebug_exit(void) +{ +} + +#endif + +EXPORT_SYMBOL(cdebbuf_alloc); +EXPORT_SYMBOL(cdebbuf_free); EXPORT_SYMBOL(capi_cmsg2message); EXPORT_SYMBOL(capi_message2cmsg); EXPORT_SYMBOL(capi_cmsg_header); diff --git a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c index 783a255..3ed34f7 100644 --- a/drivers/isdn/capi/kcapi.c +++ b/drivers/isdn/capi/kcapi.c @@ -276,10 +276,17 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s int showctl = 0; u8 cmd, subcmd; unsigned long flags; + _cdebbuf *cdb; if (card->cardstate != CARD_RUNNING) { - printk(KERN_INFO "kcapi: controller %d not active, got: %s", - card->cnr, capi_message2str(skb->data)); + cdb = capi_message2str(skb->data); + if (cdb) { + printk(KERN_INFO "kcapi: controller [%03d] not active, got: %s", + card->cnr, cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_INFO "kcapi: controller [%03d] not active, cannot trace\n", + card->cnr); goto error; } @@ -295,15 +302,21 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s showctl |= (card->traceflag & 1); if (showctl & 2) { if (showctl & 1) { - printk(KERN_DEBUG "kcapi: got [0x%lx] id#%d %s len=%u\n", - (unsigned long) card->cnr, - CAPIMSG_APPID(skb->data), + printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u\n", + card->cnr, CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), CAPIMSG_LEN(skb->data)); } else { - printk(KERN_DEBUG "kcapi: got [0x%lx] %s\n", - (unsigned long) card->cnr, - capi_message2str(skb->data)); + cdb = capi_message2str(skb->data); + if (cdb) { + printk(KERN_DEBUG "kcapi: got [%03d] %s\n", + card->cnr, cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_DEBUG "kcapi: got [%03d] id#%d %s len=%u, cannot trace\n", + card->cnr, CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); } } @@ -312,8 +325,15 @@ void capi_ctr_handle_message(struct capi_ctr * card, u16 appl, struct sk_buff *s ap = get_capi_appl_by_nr(CAPIMSG_APPID(skb->data)); if ((!ap) || (ap->release_in_progress)) { read_unlock_irqrestore(&application_lock, flags); - printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", - CAPIMSG_APPID(skb->data), capi_message2str(skb->data)); + cdb = capi_message2str(skb->data); + if (cdb) { + printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s)\n", + CAPIMSG_APPID(skb->data), cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_ERR "kcapi: handle_message: applid %d state released (%s) cannot trace\n", + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd)); goto error; } skb_queue_tail(&ap->recv_queue, skb); @@ -332,7 +352,7 @@ void capi_ctr_ready(struct capi_ctr * card) { card->cardstate = CARD_RUNNING; - printk(KERN_NOTICE "kcapi: card %d \"%s\" ready.\n", + printk(KERN_NOTICE "kcapi: card [%03d] \"%s\" ready.\n", card->cnr, card->name); notify_push(KCI_CONTRUP, card->cnr, 0, 0); @@ -364,7 +384,7 @@ void capi_ctr_reseted(struct capi_ctr * card) capi_ctr_put(card); } - printk(KERN_NOTICE "kcapi: card %d down.\n", card->cnr); + printk(KERN_NOTICE "kcapi: card [%03d] down.\n", card->cnr); notify_push(KCI_CONTRDOWN, card->cnr, 0, 0); } @@ -374,7 +394,7 @@ EXPORT_SYMBOL(capi_ctr_reseted); void capi_ctr_suspend_output(struct capi_ctr *card) { if (!card->blocked) { - printk(KERN_DEBUG "kcapi: card %d suspend\n", card->cnr); + printk(KERN_DEBUG "kcapi: card [%03d] suspend\n", card->cnr); card->blocked = 1; } } @@ -384,7 +404,7 @@ EXPORT_SYMBOL(capi_ctr_suspend_output); void capi_ctr_resume_output(struct capi_ctr *card) { if (card->blocked) { - printk(KERN_DEBUG "kcapi: card %d resume\n", card->cnr); + printk(KERN_DEBUG "kcapi: card [%03d] resume\n", card->cnr); card->blocked = 0; } } @@ -432,7 +452,7 @@ attach_capi_ctr(struct capi_ctr *card) } ncards++; - printk(KERN_NOTICE "kcapi: Controller %d: %s attached\n", + printk(KERN_NOTICE "kcapi: Controller [%03d]: %s attached\n", card->cnr, card->name); return 0; } @@ -451,7 +471,7 @@ int detach_capi_ctr(struct capi_ctr *card) card->procent = NULL; } capi_cards[card->cnr - 1] = NULL; - printk(KERN_NOTICE "kcapi: Controller %d: %s unregistered\n", + printk(KERN_NOTICE "kcapi: Controller [%03d]: %s unregistered\n", card->cnr, card->name); return 0; @@ -623,17 +643,25 @@ u16 capi20_put_message(struct capi20_appl *ap, struct sk_buff *skb) showctl |= (card->traceflag & 1); if (showctl & 2) { if (showctl & 1) { - printk(KERN_DEBUG "kcapi: put [%#x] id#%d %s len=%u\n", + printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u\n", CAPIMSG_CONTROLLER(skb->data), CAPIMSG_APPID(skb->data), capi_cmd2str(cmd, subcmd), CAPIMSG_LEN(skb->data)); } else { - printk(KERN_DEBUG "kcapi: put [%#x] %s\n", - CAPIMSG_CONTROLLER(skb->data), - capi_message2str(skb->data)); + _cdebbuf *cdb = capi_message2str(skb->data); + if (cdb) { + printk(KERN_DEBUG "kcapi: put [%03d] %s\n", + CAPIMSG_CONTROLLER(skb->data), + cdb->buf); + cdebbuf_free(cdb); + } else + printk(KERN_DEBUG "kcapi: put [%03d] id#%d %s len=%u cannot trace\n", + CAPIMSG_CONTROLLER(skb->data), + CAPIMSG_APPID(skb->data), + capi_cmd2str(cmd, subcmd), + CAPIMSG_LEN(skb->data)); } - } return card->send_message(card, skb); } @@ -894,7 +922,7 @@ int capi20_manufacturer(unsigned int cmd, void __user *data) return -ESRCH; card->traceflag = fdef.flag; - printk(KERN_INFO "kcapi: contr %d set trace=%d\n", + printk(KERN_INFO "kcapi: contr [%03d] set trace=%d\n", card->cnr, card->traceflag); return 0; } @@ -967,7 +995,11 @@ static int __init kcapi_init(void) { char *p; char rev[32]; + int ret; + ret = cdebug_init(); + if (ret) + return ret; kcapi_proc_init(); if ((p = strchr(revision, ':')) != 0 && p[1]) { @@ -988,6 +1020,7 @@ static void __exit kcapi_exit(void) /* make sure all notifiers are finished */ flush_scheduled_work(); + cdebug_exit(); } module_init(kcapi_init); diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile index 077e297..e9d3189 100644 --- a/drivers/isdn/gigaset/Makefile +++ b/drivers/isdn/gigaset/Makefile @@ -1,8 +1,9 @@ -gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o -usb_gigaset-y := usb-gigaset.o asyncdata.o +gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o asyncdata.o +usb_gigaset-y := usb-gigaset.o +ser_gigaset-y := ser-gigaset.o bas_gigaset-y := bas-gigaset.o isocdata.o -ser_gigaset-y := ser-gigaset.o asyncdata.o -obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o gigaset.o -obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o gigaset.o -obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o gigaset.o +obj-$(CONFIG_ISDN_DRV_GIGASET) += gigaset.o +obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o +obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o +obj-$(CONFIG_GIGASET_M101) += ser_gigaset.o diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index ddf5e92..00a3be5 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c @@ -13,11 +13,6 @@ * ===================================================================== */ -/* not set by Kbuild when building both ser_gigaset and usb_gigaset */ -#ifndef KBUILD_MODNAME -#define KBUILD_MODNAME "asy_gigaset" -#endif - #include "gigaset.h" #include <linux/crc-ccitt.h> #include <linux/bitrev.h> @@ -444,6 +439,7 @@ nextbyte: atomic_set(&inbuf->head, head); } } +EXPORT_SYMBOL_GPL(gigaset_m10x_input); /* == data output ========================================================== */ @@ -591,3 +587,4 @@ int gigaset_m10x_send_skb(struct bc_state *bcs, struct sk_buff *skb) return len; /* ok so far */ } +EXPORT_SYMBOL_GPL(gigaset_m10x_send_skb); diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c index 63e51dd..00e3160 100644 --- a/drivers/isdn/gigaset/bas-gigaset.c +++ b/drivers/isdn/gigaset/bas-gigaset.c @@ -54,7 +54,7 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode"); #define USB_SX353_PRODUCT_ID 0x0022 /* table of devices that work with this driver */ -static struct usb_device_id gigaset_table [] = { +static const struct usb_device_id gigaset_table [] = { { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3070_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_3075_PRODUCT_ID) }, { USB_DEVICE(USB_GIGA_VENDOR_ID, USB_SX303_PRODUCT_ID) }, @@ -2305,7 +2305,7 @@ static void gigaset_disconnect(struct usb_interface *interface) gigaset_unassign(cs); } -static struct gigaset_ops gigops = { +static const struct gigaset_ops gigops = { gigaset_write_cmd, gigaset_write_room, gigaset_chars_in_buffer, diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index b460a73..6df336b 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c @@ -944,8 +944,8 @@ static DEFINE_SPINLOCK(driver_lock); struct cardstate *gigaset_get_cs_by_id(int id) { unsigned long flags; - static struct cardstate *ret = NULL; - static struct cardstate *cs; + struct cardstate *ret = NULL; + struct cardstate *cs; struct gigaset_driver *drv; unsigned i; @@ -999,7 +999,7 @@ void gigaset_debugdrivers(void) static struct cardstate *gigaset_get_cs_by_minor(unsigned minor) { unsigned long flags; - static struct cardstate *ret = NULL; + struct cardstate *ret = NULL; struct gigaset_driver *drv; unsigned index; diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index 4661e2c..cec1ef3 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c @@ -409,7 +409,7 @@ static struct reply_t tab_cid[] = /* no dle mode */ //FIXME }; #endif -static struct resp_type_t resp_type[]= +static const struct resp_type_t resp_type[] = { /*{"", RSP_EMPTY, RT_NOTHING},*/ {"OK", RSP_OK, RT_NOTHING}, @@ -511,7 +511,7 @@ void gigaset_handle_modem_response(struct cardstate *cs) unsigned char *argv[MAX_REC_PARAMS + 1]; int params; int i, j; - struct resp_type_t *rt; + const struct resp_type_t *rt; int curarg; unsigned long flags; unsigned next, tail, head; diff --git a/drivers/isdn/gigaset/isocdata.c b/drivers/isdn/gigaset/isocdata.c index 8c0eb52..e0505f2 100644 --- a/drivers/isdn/gigaset/isocdata.c +++ b/drivers/isdn/gigaset/isocdata.c @@ -274,7 +274,7 @@ static inline void dump_bytes(enum debuglevel level, const char *tag, * bit 12..10 = number of trailing '1' bits in result * bit 14..13 = number of bits added by stuffing */ -static u16 stufftab[5 * 256] = { +static const u16 stufftab[5 * 256] = { // previous 1s = 0: 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x201f, @@ -629,7 +629,7 @@ static inline void hdlc_frag(struct bc_state *bcs, unsigned inbits) * (replacing 8 by 7 to make it fit; the algorithm won't care) * bit 7 set if there are 5 or more "interior" consecutive '1' bits */ -static unsigned char bitcounts[256] = { +static const unsigned char bitcounts[256] = { 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c index c8b7db6..ea44302 100644 --- a/drivers/isdn/gigaset/ser-gigaset.c +++ b/drivers/isdn/gigaset/ser-gigaset.c @@ -459,7 +459,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) return -EINVAL; } -static struct gigaset_ops ops = { +static const struct gigaset_ops ops = { gigaset_write_cmd, gigaset_write_room, gigaset_chars_in_buffer, diff --git a/drivers/isdn/gigaset/usb-gigaset.c b/drivers/isdn/gigaset/usb-gigaset.c index 04f2ad7..c8e1c35 100644 --- a/drivers/isdn/gigaset/usb-gigaset.c +++ b/drivers/isdn/gigaset/usb-gigaset.c @@ -50,7 +50,7 @@ MODULE_PARM_DESC(cidmode, "Call-ID mode"); #define USB_M105_PRODUCT_ID 0x0009 /* table of devices that work with this driver */ -static struct usb_device_id gigaset_table [] = { +static const struct usb_device_id gigaset_table [] = { { USB_DEVICE(USB_M105_VENDOR_ID, USB_M105_PRODUCT_ID) }, { } /* Terminating entry */ }; @@ -652,7 +652,7 @@ static int write_modem(struct cardstate *cs) * transmit data */ count = min(bcs->tx_skb->len, (unsigned) ucs->bulk_out_size); - memcpy(ucs->bulk_out_buffer, bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, ucs->bulk_out_buffer, count); skb_pull(bcs->tx_skb, count); atomic_set(&ucs->busy, 1); gig_dbg(DEBUG_OUTPUT, "write_modem: send %d bytes", count); @@ -860,7 +860,7 @@ static void gigaset_disconnect(struct usb_interface *interface) gigaset_unassign(cs); } -static struct gigaset_ops ops = { +static const struct gigaset_ops ops = { gigaset_write_cmd, gigaset_write_room, gigaset_chars_in_buffer, diff --git a/drivers/isdn/hardware/avm/b1dma.c b/drivers/isdn/hardware/avm/b1dma.c index 1e2d38e..428872b6 100644 --- a/drivers/isdn/hardware/avm/b1dma.c +++ b/drivers/isdn/hardware/avm/b1dma.c @@ -404,7 +404,8 @@ static void b1dma_dispatch_tx(avmcard *card) printk(KERN_DEBUG "tx: put 0x%x len=%d\n", skb->data[2], txlen); #endif - memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); } txlen = (txlen + 3) & ~3; diff --git a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c index 6f5efa8d..d58f927 100644 --- a/drivers/isdn/hardware/avm/c4.c +++ b/drivers/isdn/hardware/avm/c4.c @@ -457,7 +457,8 @@ static void c4_dispatch_tx(avmcard *card) printk(KERN_DEBUG "%s: tx put 0x%x len=%d\n", card->name, skb->data[2], txlen); #endif - memcpy(dma->sendbuf.dmabuf, skb->data+2, skb->len-2); + skb_copy_from_linear_data_offset(skb, 2, dma->sendbuf.dmabuf, + skb->len - 2); } txlen = (txlen + 3) & ~3; diff --git a/drivers/isdn/hisax/elsa_ser.c b/drivers/isdn/hisax/elsa_ser.c index ae377e8..1642dca 100644 --- a/drivers/isdn/hisax/elsa_ser.c +++ b/drivers/isdn/hisax/elsa_ser.c @@ -254,14 +254,16 @@ write_modem(struct BCState *bcs) { count = len; if (count > MAX_MODEM_BUF - fp) { count = MAX_MODEM_BUF - fp; - memcpy(cs->hw.elsa.transbuf + fp, bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, + cs->hw.elsa.transbuf + fp, count); skb_pull(bcs->tx_skb, count); cs->hw.elsa.transcnt += count; ret = count; count = len - count; fp = 0; } - memcpy((cs->hw.elsa.transbuf + fp), bcs->tx_skb->data, count); + skb_copy_from_linear_data(bcs->tx_skb, + cs->hw.elsa.transbuf + fp, count); skb_pull(bcs->tx_skb, count); cs->hw.elsa.transcnt += count; ret += count; diff --git a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c index 9df9e35..c547a66 100644 --- a/drivers/isdn/hisax/isar.c +++ b/drivers/isdn/hisax/isar.c @@ -440,7 +440,7 @@ isar_bh(struct work_struct *work) { struct BCState *bcs = container_of(work, struct BCState, tqueue); - BChannel_bh(bcs); + BChannel_bh(work); if (test_and_clear_bit(B_LL_NOCARRIER, &bcs->event)) ll_deliver_faxstat(bcs, ISDN_FAX_CLASS1_NOCARR); if (test_and_clear_bit(B_LL_CONNECT, &bcs->event)) diff --git a/drivers/isdn/hisax/isdnl2.c b/drivers/isdn/hisax/isdnl2.c index cd3b5ad..3446f24 100644 --- a/drivers/isdn/hisax/isdnl2.c +++ b/drivers/isdn/hisax/isdnl2.c @@ -1293,7 +1293,8 @@ l2_pull_iqueue(struct FsmInst *fi, int event, void *arg) oskb = skb; skb = alloc_skb(oskb->len + i, GFP_ATOMIC); memcpy(skb_put(skb, i), header, i); - memcpy(skb_put(skb, oskb->len), oskb->data, oskb->len); + skb_copy_from_linear_data(oskb, + skb_put(skb, oskb->len), oskb->len); dev_kfree_skb(oskb); } st->l2.l2l1(st, PH_PULL | INDICATION, skb); diff --git a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c index b2ae4ec..f854501 100644 --- a/drivers/isdn/hysdn/hycapi.c +++ b/drivers/isdn/hysdn/hycapi.c @@ -398,8 +398,9 @@ static u16 hycapi_send_message(struct capi_ctr *ctrl, struct sk_buff *skb) _len = CAPIMSG_LEN(skb->data); if (_len > 22) { _len2 = _len - 22; - memcpy(msghead, skb->data, 22); - memcpy(skb->data + _len2, msghead, 22); + skb_copy_from_linear_data(skb, msghead, 22); + skb_copy_to_linear_data_offset(skb, _len2, + msghead, 22); skb_pull(skb, _len2); CAPIMSG_SETLEN(skb->data, 22); retval = capilib_data_b3_req(&cinfo->ncci_head, diff --git a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c index 557d96c..cfa8fa5 100644 --- a/drivers/isdn/hysdn/hysdn_net.c +++ b/drivers/isdn/hysdn/hysdn_net.c @@ -214,8 +214,6 @@ hysdn_rx_netpkt(hysdn_card * card, unsigned char *buf, unsigned short len) lp->stats.rx_dropped++; return; } - skb->dev = &lp->netdev; - /* copy the data */ memcpy(skb_put(skb, len), buf, len); diff --git a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c index b7b5aa4..81db4a1 100644 --- a/drivers/isdn/hysdn/hysdn_sched.c +++ b/drivers/isdn/hysdn/hysdn_sched.c @@ -113,7 +113,8 @@ hysdn_sched_tx(hysdn_card *card, unsigned char *buf, (skb = hysdn_tx_netget(card)) != NULL) { if (skb->len <= maxlen) { - memcpy(buf, skb->data, skb->len); /* copy the packet to the buffer */ + /* copy the packet to the buffer */ + skb_copy_from_linear_data(skb, buf, skb->len); *len = skb->len; *chan = CHAN_NDIS_DATA; card->net_tx_busy = 1; /* we are busy sending network data */ @@ -126,7 +127,7 @@ hysdn_sched_tx(hysdn_card *card, unsigned char *buf, ((skb = hycapi_tx_capiget(card)) != NULL) ) { if (skb->len <= maxlen) { - memcpy(buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf, skb->len); *len = skb->len; *chan = CHAN_CAPI; hycapi_tx_capiack(card); diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c index 9c926e4..c97330b 100644 --- a/drivers/isdn/i4l/isdn_common.c +++ b/drivers/isdn/i4l/isdn_common.c @@ -829,7 +829,7 @@ isdn_readbchan(int di, int channel, u_char * buf, u_char * fp, int len, wait_que dflag = 0; } count_put = count_pull; - memcpy(cp, skb->data, count_put); + skb_copy_from_linear_data(skb, cp, count_put); cp += count_put; len -= count_put; #ifdef CONFIG_ISDN_AUDIO diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c index 838b373..aa83277 100644 --- a/drivers/isdn/i4l/isdn_net.c +++ b/drivers/isdn/i4l/isdn_net.c @@ -872,7 +872,8 @@ typedef struct { static void isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) { - u_char *p = skb->nh.raw; /* hopefully, this was set correctly */ + /* hopefully, this was set correctly */ + const u_char *p = skb_network_header(skb); unsigned short proto = ntohs(skb->protocol); int data_ofs; ip_ports *ipp; @@ -880,7 +881,7 @@ isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp) addinfo[0] = '\0'; /* This check stolen from 2.1.72 dev_queue_xmit_nit() */ - if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) { + if (p < skb->data || skb->network_header >= skb->tail) { /* fall back to old isdn_net_log_packet method() */ char * buf = skb->data; @@ -1121,7 +1122,7 @@ isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev) if (!skb) return; if (lp->p_encap == ISDN_NET_ENCAP_ETHER) { - int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN; + const int pullsize = skb_network_offset(skb) - ETH_HLEN; if (pullsize > 0) { printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize); skb_pull(skb, pullsize); @@ -1366,7 +1367,7 @@ isdn_net_type_trans(struct sk_buff *skb, struct net_device *dev) struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN); eth = eth_hdr(skb); @@ -1786,7 +1787,7 @@ isdn_net_receive(struct net_device *ndev, struct sk_buff *skb) } skb->dev = ndev; skb->pkt_type = PACKET_HOST; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); #ifdef ISDN_DEBUG_NET_DUMP isdn_dumppkt("R:", skb->data, skb->len, 40); #endif diff --git a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c index 4e3f127..387392c 100644 --- a/drivers/isdn/i4l/isdn_ppp.c +++ b/drivers/isdn/i4l/isdn_ppp.c @@ -1100,7 +1100,8 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff goto drop_packet; } skb_put(skb, skb_old->len + 128); - memcpy(skb->data, skb_old->data, skb_old->len); + skb_copy_from_linear_data(skb_old, skb->data, + skb_old->len); if (net_dev->local->ppp_slot < 0) { printk(KERN_ERR "%s: net_dev->local->ppp_slot(%d) out of range\n", __FUNCTION__, net_dev->local->ppp_slot); @@ -1167,7 +1168,7 @@ isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff mlp->huptimer = 0; #endif /* CONFIG_IPPP_FILTER */ skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); /* net_dev->local->stats.rx_packets++; done in isdn_net.c */ return; @@ -1680,7 +1681,7 @@ static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, * - we hit a gap in the sequence, so no reassembly/processing is * possible ('start' would be set to NULL) * - * algorightm for this code is derived from code in the book + * algorithm for this code is derived from code in the book * 'PPP Design And Debugging' by James Carlson (Addison-Wesley) */ while (start != NULL || newfrag != NULL) { @@ -1902,7 +1903,9 @@ void isdn_ppp_mp_reassembly( isdn_net_dev * net_dev, isdn_net_local * lp, while( from != to ) { unsigned int len = from->len - MP_HEADER_LEN; - memcpy(skb_put(skb,len), from->data+MP_HEADER_LEN, len); + skb_copy_from_linear_data_offset(from, MP_HEADER_LEN, + skb_put(skb,len), + len); frag = from->next; isdn_ppp_mp_free_skb(mp, from); from = frag; diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c index e3add27..e93ad59 100644 --- a/drivers/isdn/isdnloop/isdnloop.c +++ b/drivers/isdn/isdnloop/isdnloop.c @@ -415,7 +415,8 @@ isdnloop_sendbuf(int channel, struct sk_buff *skb, isdnloop_card * card) spin_lock_irqsave(&card->isdnloop_lock, flags); nskb = dev_alloc_skb(skb->len); if (nskb) { - memcpy(skb_put(nskb, len), skb->data, len); + skb_copy_from_linear_data(skb, + skb_put(nskb, len), len); skb_queue_tail(&card->bqueue[channel], nskb); dev_kfree_skb(skb); } else diff --git a/drivers/isdn/pcbit/capi.c b/drivers/isdn/pcbit/capi.c index 47c59e9..7b55e15 100644 --- a/drivers/isdn/pcbit/capi.c +++ b/drivers/isdn/pcbit/capi.c @@ -429,8 +429,9 @@ int capi_decode_conn_ind(struct pcbit_chan * chan, if (!(info->data.setup.CallingPN = kmalloc(len - count + 1, GFP_ATOMIC))) return -1; - memcpy(info->data.setup.CallingPN, skb->data + count + 1, - len - count); + skb_copy_from_linear_data_offset(skb, count + 1, + info->data.setup.CallingPN, + len - count); info->data.setup.CallingPN[len - count] = 0; } @@ -457,8 +458,9 @@ int capi_decode_conn_ind(struct pcbit_chan * chan, if (!(info->data.setup.CalledPN = kmalloc(len - count + 1, GFP_ATOMIC))) return -1; - memcpy(info->data.setup.CalledPN, skb->data + count + 1, - len - count); + skb_copy_from_linear_data_offset(skb, count + 1, + info->data.setup.CalledPN, + len - count); info->data.setup.CalledPN[len - count] = 0; } @@ -539,7 +541,7 @@ int capi_decode_conn_actv_ind(struct pcbit_chan * chan, struct sk_buff *skb) #ifdef DEBUG if (len > 1 && len < 31) { - memcpy(str, skb->data + 2, len - 1); + skb_copy_from_linear_data_offset(skb, 2, str, len - 1); str[len] = 0; printk(KERN_DEBUG "Connected Party Number: %s\n", str); } diff --git a/drivers/kvm/kvm.h b/drivers/kvm/kvm.h index 04574a9..0d122bf 100644 --- a/drivers/kvm/kvm.h +++ b/drivers/kvm/kvm.h @@ -14,6 +14,7 @@ #include "vmx.h" #include <linux/kvm.h> +#include <linux/kvm_para.h> #define CR0_PE_MASK (1ULL << 0) #define CR0_TS_MASK (1ULL << 3) @@ -237,6 +238,9 @@ struct kvm_vcpu { unsigned long cr0; unsigned long cr2; unsigned long cr3; + gpa_t para_state_gpa; + struct page *para_state_page; + gpa_t hypercall_gpa; unsigned long cr4; unsigned long cr8; u64 pdptrs[4]; /* pae */ @@ -305,6 +309,7 @@ struct kvm { int busy; unsigned long rmap_overflow; struct list_head vm_list; + struct file *filp; }; struct kvm_stat { @@ -339,7 +344,7 @@ struct kvm_arch_ops { int (*vcpu_create)(struct kvm_vcpu *vcpu); void (*vcpu_free)(struct kvm_vcpu *vcpu); - struct kvm_vcpu *(*vcpu_load)(struct kvm_vcpu *vcpu); + void (*vcpu_load)(struct kvm_vcpu *vcpu); void (*vcpu_put)(struct kvm_vcpu *vcpu); void (*vcpu_decache)(struct kvm_vcpu *vcpu); @@ -382,6 +387,8 @@ struct kvm_arch_ops { int (*run)(struct kvm_vcpu *vcpu, struct kvm_run *run); int (*vcpu_setup)(struct kvm_vcpu *vcpu); void (*skip_emulated_instruction)(struct kvm_vcpu *vcpu); + void (*patch_hypercall)(struct kvm_vcpu *vcpu, + unsigned char *hypercall_addr); }; extern struct kvm_stat kvm_stat; @@ -476,6 +483,8 @@ void kvm_mmu_post_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes); int kvm_mmu_unprotect_page_virt(struct kvm_vcpu *vcpu, gva_t gva); void kvm_mmu_free_some_pages(struct kvm_vcpu *vcpu); +int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run); + static inline int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u32 error_code) { @@ -523,7 +532,7 @@ static inline struct kvm_mmu_page *page_header(hpa_t shadow_page) { struct page *page = pfn_to_page(shadow_page >> PAGE_SHIFT); - return (struct kvm_mmu_page *)page->private; + return (struct kvm_mmu_page *)page_private(page); } static inline u16 read_fs(void) diff --git a/drivers/kvm/kvm_main.c b/drivers/kvm/kvm_main.c index af86614..dc7a8c7 100644 --- a/drivers/kvm/kvm_main.c +++ b/drivers/kvm/kvm_main.c @@ -20,6 +20,7 @@ #include <linux/kvm.h> #include <linux/module.h> #include <linux/errno.h> +#include <linux/magic.h> #include <asm/processor.h> #include <linux/percpu.h> #include <linux/gfp.h> @@ -36,6 +37,9 @@ #include <asm/desc.h> #include <linux/sysdev.h> #include <linux/cpu.h> +#include <linux/file.h> +#include <linux/fs.h> +#include <linux/mount.h> #include "x86_emulate.h" #include "segment_descriptor.h" @@ -72,6 +76,8 @@ static struct kvm_stats_debugfs_item { static struct dentry *debugfs_dir; +struct vfsmount *kvmfs_mnt; + #define MAX_IO_MSRS 256 #define CR0_RESEVED_BITS 0xffffffff1ffaffc0ULL @@ -90,6 +96,58 @@ struct segment_descriptor_64 { #endif +static long kvm_vcpu_ioctl(struct file *file, unsigned int ioctl, + unsigned long arg); + +static struct inode *kvmfs_inode(struct file_operations *fops) +{ + int error = -ENOMEM; + struct inode *inode = new_inode(kvmfs_mnt->mnt_sb); + + if (!inode) + goto eexit_1; + + inode->i_fop = fops; + + /* + * Mark the inode dirty from the very beginning, + * that way it will never be moved to the dirty + * list because mark_inode_dirty() will think + * that it already _is_ on the dirty list. + */ + inode->i_state = I_DIRTY; + inode->i_mode = S_IRUSR | S_IWUSR; + inode->i_uid = current->fsuid; + inode->i_gid = current->fsgid; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + return inode; + +eexit_1: + return ERR_PTR(error); +} + +static struct file *kvmfs_file(struct inode *inode, void *private_data) +{ + struct file *file = get_empty_filp(); + + if (!file) + return ERR_PTR(-ENFILE); + + file->f_path.mnt = mntget(kvmfs_mnt); + file->f_path.dentry = d_alloc_anon(inode); + if (!file->f_path.dentry) + return ERR_PTR(-ENOMEM); + file->f_mapping = inode->i_mapping; + + file->f_pos = 0; + file->f_flags = O_RDWR; + file->f_op = inode->i_fop; + file->f_mode = FMODE_READ | FMODE_WRITE; + file->f_version = 0; + file->private_data = private_data; + return file; +} + unsigned long segment_base(u16 selector) { struct descriptor_table gdt; @@ -126,10 +184,8 @@ static inline int valid_vcpu(int n) return likely(n >= 0 && n < KVM_MAX_VCPUS); } -int kvm_read_guest(struct kvm_vcpu *vcpu, - gva_t addr, - unsigned long size, - void *dest) +int kvm_read_guest(struct kvm_vcpu *vcpu, gva_t addr, unsigned long size, + void *dest) { unsigned char *host_buf = dest; unsigned long req_size = size; @@ -161,10 +217,8 @@ int kvm_read_guest(struct kvm_vcpu *vcpu, } EXPORT_SYMBOL_GPL(kvm_read_guest); -int kvm_write_guest(struct kvm_vcpu *vcpu, - gva_t addr, - unsigned long size, - void *data) +int kvm_write_guest(struct kvm_vcpu *vcpu, gva_t addr, unsigned long size, + void *data) { unsigned char *host_buf = data; unsigned long req_size = size; @@ -174,12 +228,15 @@ int kvm_write_guest(struct kvm_vcpu *vcpu, unsigned now; unsigned offset; hva_t guest_buf; + gfn_t gfn; paddr = gva_to_hpa(vcpu, addr); if (is_error_hpa(paddr)) break; + gfn = vcpu->mmu.gva_to_gpa(vcpu, addr) >> PAGE_SHIFT; + mark_page_dirty(vcpu->kvm, gfn); guest_buf = (hva_t)kmap_atomic( pfn_to_page(paddr >> PAGE_SHIFT), KM_USER0); offset = addr & ~PAGE_MASK; @@ -195,24 +252,30 @@ int kvm_write_guest(struct kvm_vcpu *vcpu, } EXPORT_SYMBOL_GPL(kvm_write_guest); -static int vcpu_slot(struct kvm_vcpu *vcpu) +/* + * Switches to specified vcpu, until a matching vcpu_put() + */ +static void vcpu_load(struct kvm_vcpu *vcpu) { - return vcpu - vcpu->kvm->vcpus; + mutex_lock(&vcpu->mutex); + kvm_arch_ops->vcpu_load(vcpu); } /* - * Switches to specified vcpu, until a matching vcpu_put() + * Switches to specified vcpu, until a matching vcpu_put(). Will return NULL + * if the slot is not populated. */ -static struct kvm_vcpu *vcpu_load(struct kvm *kvm, int vcpu_slot) +static struct kvm_vcpu *vcpu_load_slot(struct kvm *kvm, int slot) { - struct kvm_vcpu *vcpu = &kvm->vcpus[vcpu_slot]; + struct kvm_vcpu *vcpu = &kvm->vcpus[slot]; mutex_lock(&vcpu->mutex); - if (unlikely(!vcpu->vmcs)) { + if (!vcpu->vmcs) { mutex_unlock(&vcpu->mutex); return NULL; } - return kvm_arch_ops->vcpu_load(vcpu); + kvm_arch_ops->vcpu_load(vcpu); + return vcpu; } static void vcpu_put(struct kvm_vcpu *vcpu) @@ -221,13 +284,13 @@ static void vcpu_put(struct kvm_vcpu *vcpu) mutex_unlock(&vcpu->mutex); } -static int kvm_dev_open(struct inode *inode, struct file *filp) +static struct kvm *kvm_create_vm(void) { struct kvm *kvm = kzalloc(sizeof(struct kvm), GFP_KERNEL); int i; if (!kvm) - return -ENOMEM; + return ERR_PTR(-ENOMEM); spin_lock_init(&kvm->lock); INIT_LIST_HEAD(&kvm->active_mmu_pages); @@ -243,7 +306,11 @@ static int kvm_dev_open(struct inode *inode, struct file *filp) list_add(&kvm->vm_list, &vm_list); spin_unlock(&kvm_lock); } - filp->private_data = kvm; + return kvm; +} + +static int kvm_dev_open(struct inode *inode, struct file *filp) +{ return 0; } @@ -281,9 +348,10 @@ static void kvm_free_physmem(struct kvm *kvm) static void kvm_free_vcpu(struct kvm_vcpu *vcpu) { - if (!vcpu_load(vcpu->kvm, vcpu_slot(vcpu))) + if (!vcpu->vmcs) return; + vcpu_load(vcpu); kvm_mmu_destroy(vcpu); vcpu_put(vcpu); kvm_arch_ops->vcpu_free(vcpu); @@ -299,14 +367,24 @@ static void kvm_free_vcpus(struct kvm *kvm) static int kvm_dev_release(struct inode *inode, struct file *filp) { - struct kvm *kvm = filp->private_data; + return 0; +} +static void kvm_destroy_vm(struct kvm *kvm) +{ spin_lock(&kvm_lock); list_del(&kvm->vm_list); spin_unlock(&kvm_lock); kvm_free_vcpus(kvm); kvm_free_physmem(kvm); kfree(kvm); +} + +static int kvm_vm_release(struct inode *inode, struct file *filp) +{ + struct kvm *kvm = filp->private_data; + + kvm_destroy_vm(kvm); return 0; } @@ -457,7 +535,7 @@ EXPORT_SYMBOL_GPL(set_cr4); void set_cr3(struct kvm_vcpu *vcpu, unsigned long cr3) { if (is_long_mode(vcpu)) { - if ( cr3 & CR3_L_MODE_RESEVED_BITS) { + if (cr3 & CR3_L_MODE_RESEVED_BITS) { printk(KERN_DEBUG "set_cr3: #GP, reserved bits\n"); inject_gp(vcpu); return; @@ -533,55 +611,11 @@ void fx_init(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(fx_init); -/* - * Creates some virtual cpus. Good luck creating more than one. - */ -static int kvm_dev_ioctl_create_vcpu(struct kvm *kvm, int n) +static void do_remove_write_access(struct kvm_vcpu *vcpu, int slot) { - int r; - struct kvm_vcpu *vcpu; - - r = -EINVAL; - if (!valid_vcpu(n)) - goto out; - - vcpu = &kvm->vcpus[n]; - - mutex_lock(&vcpu->mutex); - - if (vcpu->vmcs) { - mutex_unlock(&vcpu->mutex); - return -EEXIST; - } - - vcpu->host_fx_image = (char*)ALIGN((hva_t)vcpu->fx_buf, - FX_IMAGE_ALIGN); - vcpu->guest_fx_image = vcpu->host_fx_image + FX_IMAGE_SIZE; - - r = kvm_arch_ops->vcpu_create(vcpu); - if (r < 0) - goto out_free_vcpus; - - r = kvm_mmu_create(vcpu); - if (r < 0) - goto out_free_vcpus; - - kvm_arch_ops->vcpu_load(vcpu); - r = kvm_mmu_setup(vcpu); - if (r >= 0) - r = kvm_arch_ops->vcpu_setup(vcpu); - vcpu_put(vcpu); - - if (r < 0) - goto out_free_vcpus; - - return 0; - -out_free_vcpus: - kvm_free_vcpu(vcpu); - mutex_unlock(&vcpu->mutex); -out: - return r; + spin_lock(&vcpu->kvm->lock); + kvm_mmu_slot_remove_write_access(vcpu, slot); + spin_unlock(&vcpu->kvm->lock); } /* @@ -590,8 +624,8 @@ out: * * Discontiguous memory is allowed, mostly for framebuffers. */ -static int kvm_dev_ioctl_set_memory_region(struct kvm *kvm, - struct kvm_memory_region *mem) +static int kvm_vm_ioctl_set_memory_region(struct kvm *kvm, + struct kvm_memory_region *mem) { int r; gfn_t base_gfn; @@ -674,7 +708,7 @@ raced: | __GFP_ZERO); if (!new.phys_mem[i]) goto out_free; - new.phys_mem[i]->private = 0; + set_page_private(new.phys_mem[i],0); } } @@ -711,9 +745,11 @@ raced: for (i = 0; i < KVM_MAX_VCPUS; ++i) { struct kvm_vcpu *vcpu; - vcpu = vcpu_load(kvm, i); + vcpu = vcpu_load_slot(kvm, i); if (!vcpu) continue; + if (new.flags & KVM_MEM_LOG_DIRTY_PAGES) + do_remove_write_access(vcpu, mem->slot); kvm_mmu_reset_context(vcpu); vcpu_put(vcpu); } @@ -729,18 +765,11 @@ out: return r; } -static void do_remove_write_access(struct kvm_vcpu *vcpu, int slot) -{ - spin_lock(&vcpu->kvm->lock); - kvm_mmu_slot_remove_write_access(vcpu, slot); - spin_unlock(&vcpu->kvm->lock); -} - /* * Get (and clear) the dirty memory log for a memory slot. */ -static int kvm_dev_ioctl_get_dirty_log(struct kvm *kvm, - struct kvm_dirty_log *log) +static int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, + struct kvm_dirty_log *log) { struct kvm_memory_slot *memslot; int r, i; @@ -765,21 +794,21 @@ static int kvm_dev_ioctl_get_dirty_log(struct kvm *kvm, if (!memslot->dirty_bitmap) goto out; - n = ALIGN(memslot->npages, 8) / 8; + n = ALIGN(memslot->npages, BITS_PER_LONG) / 8; - for (i = 0; !any && i < n; ++i) + for (i = 0; !any && i < n/sizeof(long); ++i) any = memslot->dirty_bitmap[i]; r = -EFAULT; if (copy_to_user(log->dirty_bitmap, memslot->dirty_bitmap, n)) goto out; - if (any) { cleared = 0; for (i = 0; i < KVM_MAX_VCPUS; ++i) { - struct kvm_vcpu *vcpu = vcpu_load(kvm, i); + struct kvm_vcpu *vcpu; + vcpu = vcpu_load_slot(kvm, i); if (!vcpu) continue; if (!cleared) { @@ -903,8 +932,9 @@ static int emulator_read_emulated(unsigned long addr, return X86EMUL_CONTINUE; else { gpa_t gpa = vcpu->mmu.gva_to_gpa(vcpu, addr); + if (gpa == UNMAPPED_GVA) - return vcpu_printf(vcpu, "not present\n"), X86EMUL_PROPAGATE_FAULT; + return X86EMUL_PROPAGATE_FAULT; vcpu->mmio_needed = 1; vcpu->mmio_phys_addr = gpa; vcpu->mmio_size = bytes; @@ -928,6 +958,7 @@ static int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa, return 0; page = gfn_to_page(m, gpa >> PAGE_SHIFT); kvm_mmu_pre_write(vcpu, gpa, bytes); + mark_page_dirty(vcpu->kvm, gpa >> PAGE_SHIFT); virt = kmap_atomic(page, KM_USER0); memcpy(virt + offset_in_page(gpa), &val, bytes); kunmap_atomic(virt, KM_USER0); @@ -1142,6 +1173,42 @@ int emulate_instruction(struct kvm_vcpu *vcpu, } EXPORT_SYMBOL_GPL(emulate_instruction); +int kvm_hypercall(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + unsigned long nr, a0, a1, a2, a3, a4, a5, ret; + + kvm_arch_ops->decache_regs(vcpu); + ret = -KVM_EINVAL; +#ifdef CONFIG_X86_64 + if (is_long_mode(vcpu)) { + nr = vcpu->regs[VCPU_REGS_RAX]; + a0 = vcpu->regs[VCPU_REGS_RDI]; + a1 = vcpu->regs[VCPU_REGS_RSI]; + a2 = vcpu->regs[VCPU_REGS_RDX]; + a3 = vcpu->regs[VCPU_REGS_RCX]; + a4 = vcpu->regs[VCPU_REGS_R8]; + a5 = vcpu->regs[VCPU_REGS_R9]; + } else +#endif + { + nr = vcpu->regs[VCPU_REGS_RBX] & -1u; + a0 = vcpu->regs[VCPU_REGS_RAX] & -1u; + a1 = vcpu->regs[VCPU_REGS_RCX] & -1u; + a2 = vcpu->regs[VCPU_REGS_RDX] & -1u; + a3 = vcpu->regs[VCPU_REGS_RSI] & -1u; + a4 = vcpu->regs[VCPU_REGS_RDI] & -1u; + a5 = vcpu->regs[VCPU_REGS_RBP] & -1u; + } + switch (nr) { + default: + ; + } + vcpu->regs[VCPU_REGS_RAX] = ret; + kvm_arch_ops->cache_regs(vcpu); + return 1; +} +EXPORT_SYMBOL_GPL(kvm_hypercall); + static u64 mk_cr_64(u64 curr_cr, u32 new_val) { return (curr_cr & ~((1ULL << 32) - 1)) | new_val; @@ -1208,6 +1275,75 @@ void realmode_set_cr(struct kvm_vcpu *vcpu, int cr, unsigned long val, } } +/* + * Register the para guest with the host: + */ +static int vcpu_register_para(struct kvm_vcpu *vcpu, gpa_t para_state_gpa) +{ + struct kvm_vcpu_para_state *para_state; + hpa_t para_state_hpa, hypercall_hpa; + struct page *para_state_page; + unsigned char *hypercall; + gpa_t hypercall_gpa; + + printk(KERN_DEBUG "kvm: guest trying to enter paravirtual mode\n"); + printk(KERN_DEBUG ".... para_state_gpa: %08Lx\n", para_state_gpa); + + /* + * Needs to be page aligned: + */ + if (para_state_gpa != PAGE_ALIGN(para_state_gpa)) + goto err_gp; + + para_state_hpa = gpa_to_hpa(vcpu, para_state_gpa); + printk(KERN_DEBUG ".... para_state_hpa: %08Lx\n", para_state_hpa); + if (is_error_hpa(para_state_hpa)) + goto err_gp; + + mark_page_dirty(vcpu->kvm, para_state_gpa >> PAGE_SHIFT); + para_state_page = pfn_to_page(para_state_hpa >> PAGE_SHIFT); + para_state = kmap_atomic(para_state_page, KM_USER0); + + printk(KERN_DEBUG ".... guest version: %d\n", para_state->guest_version); + printk(KERN_DEBUG ".... size: %d\n", para_state->size); + + para_state->host_version = KVM_PARA_API_VERSION; + /* + * We cannot support guests that try to register themselves + * with a newer API version than the host supports: + */ + if (para_state->guest_version > KVM_PARA_API_VERSION) { + para_state->ret = -KVM_EINVAL; + goto err_kunmap_skip; + } + + hypercall_gpa = para_state->hypercall_gpa; + hypercall_hpa = gpa_to_hpa(vcpu, hypercall_gpa); + printk(KERN_DEBUG ".... hypercall_hpa: %08Lx\n", hypercall_hpa); + if (is_error_hpa(hypercall_hpa)) { + para_state->ret = -KVM_EINVAL; + goto err_kunmap_skip; + } + + printk(KERN_DEBUG "kvm: para guest successfully registered.\n"); + vcpu->para_state_page = para_state_page; + vcpu->para_state_gpa = para_state_gpa; + vcpu->hypercall_gpa = hypercall_gpa; + + mark_page_dirty(vcpu->kvm, hypercall_gpa >> PAGE_SHIFT); + hypercall = kmap_atomic(pfn_to_page(hypercall_hpa >> PAGE_SHIFT), + KM_USER1) + (hypercall_hpa & ~PAGE_MASK); + kvm_arch_ops->patch_hypercall(vcpu, hypercall); + kunmap_atomic(hypercall, KM_USER1); + + para_state->ret = 0; +err_kunmap_skip: + kunmap_atomic(para_state, KM_USER0); + return 0; +err_gp: + return 1; +} + int kvm_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) { u64 data; @@ -1316,6 +1452,12 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data) case MSR_IA32_MISC_ENABLE: vcpu->ia32_misc_enable_msr = data; break; + /* + * This is the 'probe whether the host is KVM' logic: + */ + case MSR_KVM_API_MAGIC: + return vcpu_register_para(vcpu, data); + default: printk(KERN_ERR "kvm: unhandled wrmsr: 0x%x\n", msr); return 1; @@ -1338,8 +1480,7 @@ void kvm_resched(struct kvm_vcpu *vcpu) { vcpu_put(vcpu); cond_resched(); - /* Cannot fail - no vcpu unplug yet. */ - vcpu_load(vcpu->kvm, vcpu_slot(vcpu)); + vcpu_load(vcpu); } EXPORT_SYMBOL_GPL(kvm_resched); @@ -1361,17 +1502,11 @@ void save_msrs(struct vmx_msr_entry *e, int n) } EXPORT_SYMBOL_GPL(save_msrs); -static int kvm_dev_ioctl_run(struct kvm *kvm, struct kvm_run *kvm_run) +static int kvm_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { - struct kvm_vcpu *vcpu; int r; - if (!valid_vcpu(kvm_run->vcpu)) - return -EINVAL; - - vcpu = vcpu_load(kvm, kvm_run->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); /* re-sync apic's tpr */ vcpu->cr8 = kvm_run->cr8; @@ -1394,16 +1529,10 @@ static int kvm_dev_ioctl_run(struct kvm *kvm, struct kvm_run *kvm_run) return r; } -static int kvm_dev_ioctl_get_regs(struct kvm *kvm, struct kvm_regs *regs) +static int kvm_vcpu_ioctl_get_regs(struct kvm_vcpu *vcpu, + struct kvm_regs *regs) { - struct kvm_vcpu *vcpu; - - if (!valid_vcpu(regs->vcpu)) - return -EINVAL; - - vcpu = vcpu_load(kvm, regs->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); kvm_arch_ops->cache_regs(vcpu); @@ -1440,16 +1569,10 @@ static int kvm_dev_ioctl_get_regs(struct kvm *kvm, struct kvm_regs *regs) return 0; } -static int kvm_dev_ioctl_set_regs(struct kvm *kvm, struct kvm_regs *regs) +static int kvm_vcpu_ioctl_set_regs(struct kvm_vcpu *vcpu, + struct kvm_regs *regs) { - struct kvm_vcpu *vcpu; - - if (!valid_vcpu(regs->vcpu)) - return -EINVAL; - - vcpu = vcpu_load(kvm, regs->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); vcpu->regs[VCPU_REGS_RAX] = regs->rax; vcpu->regs[VCPU_REGS_RBX] = regs->rbx; @@ -1486,16 +1609,12 @@ static void get_segment(struct kvm_vcpu *vcpu, return kvm_arch_ops->get_segment(vcpu, var, seg); } -static int kvm_dev_ioctl_get_sregs(struct kvm *kvm, struct kvm_sregs *sregs) +static int kvm_vcpu_ioctl_get_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) { - struct kvm_vcpu *vcpu; struct descriptor_table dt; - if (!valid_vcpu(sregs->vcpu)) - return -EINVAL; - vcpu = vcpu_load(kvm, sregs->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); get_segment(vcpu, &sregs->cs, VCPU_SREG_CS); get_segment(vcpu, &sregs->ds, VCPU_SREG_DS); @@ -1537,18 +1656,14 @@ static void set_segment(struct kvm_vcpu *vcpu, return kvm_arch_ops->set_segment(vcpu, var, seg); } -static int kvm_dev_ioctl_set_sregs(struct kvm *kvm, struct kvm_sregs *sregs) +static int kvm_vcpu_ioctl_set_sregs(struct kvm_vcpu *vcpu, + struct kvm_sregs *sregs) { - struct kvm_vcpu *vcpu; int mmu_reset_needed = 0; int i; struct descriptor_table dt; - if (!valid_vcpu(sregs->vcpu)) - return -EINVAL; - vcpu = vcpu_load(kvm, sregs->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); set_segment(vcpu, &sregs->cs, VCPU_SREG_CS); set_segment(vcpu, &sregs->ds, VCPU_SREG_DS); @@ -1654,20 +1769,14 @@ static int do_set_msr(struct kvm_vcpu *vcpu, unsigned index, u64 *data) * * @return number of msrs set successfully. */ -static int __msr_io(struct kvm *kvm, struct kvm_msrs *msrs, +static int __msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs, struct kvm_msr_entry *entries, int (*do_msr)(struct kvm_vcpu *vcpu, unsigned index, u64 *data)) { - struct kvm_vcpu *vcpu; int i; - if (!valid_vcpu(msrs->vcpu)) - return -EINVAL; - - vcpu = vcpu_load(kvm, msrs->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); for (i = 0; i < msrs->nmsrs; ++i) if (do_msr(vcpu, entries[i].index, &entries[i].data)) @@ -1683,7 +1792,7 @@ static int __msr_io(struct kvm *kvm, struct kvm_msrs *msrs, * * @return number of msrs set successfully. */ -static int msr_io(struct kvm *kvm, struct kvm_msrs __user *user_msrs, +static int msr_io(struct kvm_vcpu *vcpu, struct kvm_msrs __user *user_msrs, int (*do_msr)(struct kvm_vcpu *vcpu, unsigned index, u64 *data), int writeback) @@ -1711,7 +1820,7 @@ static int msr_io(struct kvm *kvm, struct kvm_msrs __user *user_msrs, if (copy_from_user(entries, user_msrs->entries, size)) goto out_free; - r = n = __msr_io(kvm, &msrs, entries, do_msr); + r = n = __msr_io(vcpu, &msrs, entries, do_msr); if (r < 0) goto out_free; @@ -1730,38 +1839,31 @@ out: /* * Translate a guest virtual address to a guest physical address. */ -static int kvm_dev_ioctl_translate(struct kvm *kvm, struct kvm_translation *tr) +static int kvm_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) { unsigned long vaddr = tr->linear_address; - struct kvm_vcpu *vcpu; gpa_t gpa; - vcpu = vcpu_load(kvm, tr->vcpu); - if (!vcpu) - return -ENOENT; - spin_lock(&kvm->lock); + vcpu_load(vcpu); + spin_lock(&vcpu->kvm->lock); gpa = vcpu->mmu.gva_to_gpa(vcpu, vaddr); tr->physical_address = gpa; tr->valid = gpa != UNMAPPED_GVA; tr->writeable = 1; tr->usermode = 0; - spin_unlock(&kvm->lock); + spin_unlock(&vcpu->kvm->lock); vcpu_put(vcpu); return 0; } -static int kvm_dev_ioctl_interrupt(struct kvm *kvm, struct kvm_interrupt *irq) +static int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, + struct kvm_interrupt *irq) { - struct kvm_vcpu *vcpu; - - if (!valid_vcpu(irq->vcpu)) - return -EINVAL; if (irq->irq < 0 || irq->irq >= 256) return -EINVAL; - vcpu = vcpu_load(kvm, irq->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); set_bit(irq->irq, vcpu->irq_pending); set_bit(irq->irq / BITS_PER_LONG, &vcpu->irq_summary); @@ -1771,17 +1873,12 @@ static int kvm_dev_ioctl_interrupt(struct kvm *kvm, struct kvm_interrupt *irq) return 0; } -static int kvm_dev_ioctl_debug_guest(struct kvm *kvm, - struct kvm_debug_guest *dbg) +static int kvm_vcpu_ioctl_debug_guest(struct kvm_vcpu *vcpu, + struct kvm_debug_guest *dbg) { - struct kvm_vcpu *vcpu; int r; - if (!valid_vcpu(dbg->vcpu)) - return -EINVAL; - vcpu = vcpu_load(kvm, dbg->vcpu); - if (!vcpu) - return -ENOENT; + vcpu_load(vcpu); r = kvm_arch_ops->set_guest_debug(vcpu, dbg); @@ -1790,30 +1887,129 @@ static int kvm_dev_ioctl_debug_guest(struct kvm *kvm, return r; } -static long kvm_dev_ioctl(struct file *filp, - unsigned int ioctl, unsigned long arg) +static int kvm_vcpu_release(struct inode *inode, struct file *filp) { - struct kvm *kvm = filp->private_data; + struct kvm_vcpu *vcpu = filp->private_data; + + fput(vcpu->kvm->filp); + return 0; +} + +static struct file_operations kvm_vcpu_fops = { + .release = kvm_vcpu_release, + .unlocked_ioctl = kvm_vcpu_ioctl, + .compat_ioctl = kvm_vcpu_ioctl, +}; + +/* + * Allocates an inode for the vcpu. + */ +static int create_vcpu_fd(struct kvm_vcpu *vcpu) +{ + int fd, r; + struct inode *inode; + struct file *file; + + atomic_inc(&vcpu->kvm->filp->f_count); + inode = kvmfs_inode(&kvm_vcpu_fops); + if (IS_ERR(inode)) { + r = PTR_ERR(inode); + goto out1; + } + + file = kvmfs_file(inode, vcpu); + if (IS_ERR(file)) { + r = PTR_ERR(file); + goto out2; + } + + r = get_unused_fd(); + if (r < 0) + goto out3; + fd = r; + fd_install(fd, file); + + return fd; + +out3: + fput(file); +out2: + iput(inode); +out1: + fput(vcpu->kvm->filp); + return r; +} + +/* + * Creates some virtual cpus. Good luck creating more than one. + */ +static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, int n) +{ + int r; + struct kvm_vcpu *vcpu; + + r = -EINVAL; + if (!valid_vcpu(n)) + goto out; + + vcpu = &kvm->vcpus[n]; + + mutex_lock(&vcpu->mutex); + + if (vcpu->vmcs) { + mutex_unlock(&vcpu->mutex); + return -EEXIST; + } + + vcpu->host_fx_image = (char*)ALIGN((hva_t)vcpu->fx_buf, + FX_IMAGE_ALIGN); + vcpu->guest_fx_image = vcpu->host_fx_image + FX_IMAGE_SIZE; + + r = kvm_arch_ops->vcpu_create(vcpu); + if (r < 0) + goto out_free_vcpus; + + r = kvm_mmu_create(vcpu); + if (r < 0) + goto out_free_vcpus; + + kvm_arch_ops->vcpu_load(vcpu); + r = kvm_mmu_setup(vcpu); + if (r >= 0) + r = kvm_arch_ops->vcpu_setup(vcpu); + vcpu_put(vcpu); + + if (r < 0) + goto out_free_vcpus; + + r = create_vcpu_fd(vcpu); + if (r < 0) + goto out_free_vcpus; + + return r; + +out_free_vcpus: + kvm_free_vcpu(vcpu); + mutex_unlock(&vcpu->mutex); +out: + return r; +} + +static long kvm_vcpu_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + struct kvm_vcpu *vcpu = filp->private_data; void __user *argp = (void __user *)arg; int r = -EINVAL; switch (ioctl) { - case KVM_GET_API_VERSION: - r = KVM_API_VERSION; - break; - case KVM_CREATE_VCPU: { - r = kvm_dev_ioctl_create_vcpu(kvm, arg); - if (r) - goto out; - break; - } case KVM_RUN: { struct kvm_run kvm_run; r = -EFAULT; if (copy_from_user(&kvm_run, argp, sizeof kvm_run)) goto out; - r = kvm_dev_ioctl_run(kvm, &kvm_run); + r = kvm_vcpu_ioctl_run(vcpu, &kvm_run); if (r < 0 && r != -EINTR) goto out; if (copy_to_user(argp, &kvm_run, sizeof kvm_run)) { @@ -1825,10 +2021,8 @@ static long kvm_dev_ioctl(struct file *filp, case KVM_GET_REGS: { struct kvm_regs kvm_regs; - r = -EFAULT; - if (copy_from_user(&kvm_regs, argp, sizeof kvm_regs)) - goto out; - r = kvm_dev_ioctl_get_regs(kvm, &kvm_regs); + memset(&kvm_regs, 0, sizeof kvm_regs); + r = kvm_vcpu_ioctl_get_regs(vcpu, &kvm_regs); if (r) goto out; r = -EFAULT; @@ -1843,7 +2037,7 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&kvm_regs, argp, sizeof kvm_regs)) goto out; - r = kvm_dev_ioctl_set_regs(kvm, &kvm_regs); + r = kvm_vcpu_ioctl_set_regs(vcpu, &kvm_regs); if (r) goto out; r = 0; @@ -1852,10 +2046,8 @@ static long kvm_dev_ioctl(struct file *filp, case KVM_GET_SREGS: { struct kvm_sregs kvm_sregs; - r = -EFAULT; - if (copy_from_user(&kvm_sregs, argp, sizeof kvm_sregs)) - goto out; - r = kvm_dev_ioctl_get_sregs(kvm, &kvm_sregs); + memset(&kvm_sregs, 0, sizeof kvm_sregs); + r = kvm_vcpu_ioctl_get_sregs(vcpu, &kvm_sregs); if (r) goto out; r = -EFAULT; @@ -1870,7 +2062,7 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&kvm_sregs, argp, sizeof kvm_sregs)) goto out; - r = kvm_dev_ioctl_set_sregs(kvm, &kvm_sregs); + r = kvm_vcpu_ioctl_set_sregs(vcpu, &kvm_sregs); if (r) goto out; r = 0; @@ -1882,7 +2074,7 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&tr, argp, sizeof tr)) goto out; - r = kvm_dev_ioctl_translate(kvm, &tr); + r = kvm_vcpu_ioctl_translate(vcpu, &tr); if (r) goto out; r = -EFAULT; @@ -1897,7 +2089,7 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&irq, argp, sizeof irq)) goto out; - r = kvm_dev_ioctl_interrupt(kvm, &irq); + r = kvm_vcpu_ioctl_interrupt(vcpu, &irq); if (r) goto out; r = 0; @@ -1909,19 +2101,45 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&dbg, argp, sizeof dbg)) goto out; - r = kvm_dev_ioctl_debug_guest(kvm, &dbg); + r = kvm_vcpu_ioctl_debug_guest(vcpu, &dbg); if (r) goto out; r = 0; break; } + case KVM_GET_MSRS: + r = msr_io(vcpu, argp, get_msr, 1); + break; + case KVM_SET_MSRS: + r = msr_io(vcpu, argp, do_set_msr, 0); + break; + default: + ; + } +out: + return r; +} + +static long kvm_vm_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + struct kvm *kvm = filp->private_data; + void __user *argp = (void __user *)arg; + int r = -EINVAL; + + switch (ioctl) { + case KVM_CREATE_VCPU: + r = kvm_vm_ioctl_create_vcpu(kvm, arg); + if (r < 0) + goto out; + break; case KVM_SET_MEMORY_REGION: { struct kvm_memory_region kvm_mem; r = -EFAULT; if (copy_from_user(&kvm_mem, argp, sizeof kvm_mem)) goto out; - r = kvm_dev_ioctl_set_memory_region(kvm, &kvm_mem); + r = kvm_vm_ioctl_set_memory_region(kvm, &kvm_mem); if (r) goto out; break; @@ -1932,16 +2150,112 @@ static long kvm_dev_ioctl(struct file *filp, r = -EFAULT; if (copy_from_user(&log, argp, sizeof log)) goto out; - r = kvm_dev_ioctl_get_dirty_log(kvm, &log); + r = kvm_vm_ioctl_get_dirty_log(kvm, &log); if (r) goto out; break; } - case KVM_GET_MSRS: - r = msr_io(kvm, argp, get_msr, 1); + default: + ; + } +out: + return r; +} + +static struct page *kvm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int *type) +{ + struct kvm *kvm = vma->vm_file->private_data; + unsigned long pgoff; + struct kvm_memory_slot *slot; + struct page *page; + + *type = VM_FAULT_MINOR; + pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; + slot = gfn_to_memslot(kvm, pgoff); + if (!slot) + return NOPAGE_SIGBUS; + page = gfn_to_page(slot, pgoff); + if (!page) + return NOPAGE_SIGBUS; + get_page(page); + return page; +} + +static struct vm_operations_struct kvm_vm_vm_ops = { + .nopage = kvm_vm_nopage, +}; + +static int kvm_vm_mmap(struct file *file, struct vm_area_struct *vma) +{ + vma->vm_ops = &kvm_vm_vm_ops; + return 0; +} + +static struct file_operations kvm_vm_fops = { + .release = kvm_vm_release, + .unlocked_ioctl = kvm_vm_ioctl, + .compat_ioctl = kvm_vm_ioctl, + .mmap = kvm_vm_mmap, +}; + +static int kvm_dev_ioctl_create_vm(void) +{ + int fd, r; + struct inode *inode; + struct file *file; + struct kvm *kvm; + + inode = kvmfs_inode(&kvm_vm_fops); + if (IS_ERR(inode)) { + r = PTR_ERR(inode); + goto out1; + } + + kvm = kvm_create_vm(); + if (IS_ERR(kvm)) { + r = PTR_ERR(kvm); + goto out2; + } + + file = kvmfs_file(inode, kvm); + if (IS_ERR(file)) { + r = PTR_ERR(file); + goto out3; + } + kvm->filp = file; + + r = get_unused_fd(); + if (r < 0) + goto out4; + fd = r; + fd_install(fd, file); + + return fd; + +out4: + fput(file); +out3: + kvm_destroy_vm(kvm); +out2: + iput(inode); +out1: + return r; +} + +static long kvm_dev_ioctl(struct file *filp, + unsigned int ioctl, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int r = -EINVAL; + + switch (ioctl) { + case KVM_GET_API_VERSION: + r = KVM_API_VERSION; break; - case KVM_SET_MSRS: - r = msr_io(kvm, argp, do_set_msr, 0); + case KVM_CREATE_VM: + r = kvm_dev_ioctl_create_vm(); break; case KVM_GET_MSR_INDEX_LIST: { struct kvm_msr_list __user *user_msr_list = argp; @@ -1977,43 +2291,11 @@ out: return r; } -static struct page *kvm_dev_nopage(struct vm_area_struct *vma, - unsigned long address, - int *type) -{ - struct kvm *kvm = vma->vm_file->private_data; - unsigned long pgoff; - struct kvm_memory_slot *slot; - struct page *page; - - *type = VM_FAULT_MINOR; - pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; - slot = gfn_to_memslot(kvm, pgoff); - if (!slot) - return NOPAGE_SIGBUS; - page = gfn_to_page(slot, pgoff); - if (!page) - return NOPAGE_SIGBUS; - get_page(page); - return page; -} - -static struct vm_operations_struct kvm_dev_vm_ops = { - .nopage = kvm_dev_nopage, -}; - -static int kvm_dev_mmap(struct file *file, struct vm_area_struct *vma) -{ - vma->vm_ops = &kvm_dev_vm_ops; - return 0; -} - static struct file_operations kvm_chardev_ops = { .open = kvm_dev_open, .release = kvm_dev_release, .unlocked_ioctl = kvm_dev_ioctl, .compat_ioctl = kvm_dev_ioctl, - .mmap = kvm_dev_mmap, }; static struct miscdevice kvm_dev = { @@ -2080,13 +2362,17 @@ static int kvm_cpu_hotplug(struct notifier_block *notifier, unsigned long val, int cpu = (long)v; switch (val) { - case CPU_DEAD: + case CPU_DOWN_PREPARE: case CPU_UP_CANCELED: + printk(KERN_INFO "kvm: disabling virtualization on CPU%d\n", + cpu); decache_vcpus_on_cpu(cpu); smp_call_function_single(cpu, kvm_arch_ops->hardware_disable, NULL, 0, 1); break; - case CPU_UP_PREPARE: + case CPU_ONLINE: + printk(KERN_INFO "kvm: enabling virtualization on CPU%d\n", + cpu); smp_call_function_single(cpu, kvm_arch_ops->hardware_enable, NULL, 0, 1); break; @@ -2121,13 +2407,13 @@ static void kvm_exit_debug(void) static int kvm_suspend(struct sys_device *dev, pm_message_t state) { decache_vcpus_on_cpu(raw_smp_processor_id()); - on_each_cpu(kvm_arch_ops->hardware_disable, 0, 0, 1); + on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1); return 0; } static int kvm_resume(struct sys_device *dev) { - on_each_cpu(kvm_arch_ops->hardware_enable, 0, 0, 1); + on_each_cpu(kvm_arch_ops->hardware_enable, NULL, 0, 1); return 0; } @@ -2144,6 +2430,18 @@ static struct sys_device kvm_sysdev = { hpa_t bad_page_address; +static int kvmfs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "kvm:", NULL, KVMFS_SUPER_MAGIC, mnt); +} + +static struct file_system_type kvm_fs_type = { + .name = "kvmfs", + .get_sb = kvmfs_get_sb, + .kill_sb = kill_anon_super, +}; + int kvm_init_arch(struct kvm_arch_ops *ops, struct module *module) { int r; @@ -2166,7 +2464,7 @@ int kvm_init_arch(struct kvm_arch_ops *ops, struct module *module) r = kvm_arch_ops->hardware_setup(); if (r < 0) - return r; + goto out; on_each_cpu(kvm_arch_ops->hardware_enable, NULL, 0, 1); r = register_cpu_notifier(&kvm_cpu_notifier); @@ -2202,6 +2500,8 @@ out_free_2: out_free_1: on_each_cpu(kvm_arch_ops->hardware_disable, NULL, 0, 1); kvm_arch_ops->hardware_unsetup(); +out: + kvm_arch_ops = NULL; return r; } @@ -2220,8 +2520,16 @@ void kvm_exit_arch(void) static __init int kvm_init(void) { static struct page *bad_page; - int r = 0; + int r; + + r = register_filesystem(&kvm_fs_type); + if (r) + goto out3; + kvmfs_mnt = kern_mount(&kvm_fs_type); + r = PTR_ERR(kvmfs_mnt); + if (IS_ERR(kvmfs_mnt)) + goto out2; kvm_init_debug(); kvm_init_msr_list(); @@ -2234,10 +2542,14 @@ static __init int kvm_init(void) bad_page_address = page_to_pfn(bad_page) << PAGE_SHIFT; memset(__va(bad_page_address), 0, PAGE_SIZE); - return r; + return 0; out: kvm_exit_debug(); + mntput(kvmfs_mnt); +out2: + unregister_filesystem(&kvm_fs_type); +out3: return r; } @@ -2245,6 +2557,8 @@ static __exit void kvm_exit(void) { kvm_exit_debug(); __free_page(pfn_to_page(bad_page_address >> PAGE_SHIFT)); + mntput(kvmfs_mnt); + unregister_filesystem(&kvm_fs_type); } module_init(kvm_init) diff --git a/drivers/kvm/kvm_svm.h b/drivers/kvm/kvm_svm.h index 74cc862..624f1ca 100644 --- a/drivers/kvm/kvm_svm.h +++ b/drivers/kvm/kvm_svm.h @@ -1,6 +1,7 @@ #ifndef __KVM_SVM_H #define __KVM_SVM_H +#include <linux/kernel.h> #include <linux/types.h> #include <linux/list.h> #include <asm/msr.h> @@ -18,7 +19,7 @@ static const u32 host_save_msrs[] = { MSR_IA32_LASTBRANCHTOIP, MSR_IA32_LASTINTFROMIP,MSR_IA32_LASTINTTOIP,*/ }; -#define NR_HOST_SAVE_MSRS (sizeof(host_save_msrs) / sizeof(*host_save_msrs)) +#define NR_HOST_SAVE_MSRS ARRAY_SIZE(host_save_msrs) #define NUM_DB_REGS 4 struct vcpu_svm { diff --git a/drivers/kvm/mmu.c b/drivers/kvm/mmu.c index be79377..cab26f3 100644 --- a/drivers/kvm/mmu.c +++ b/drivers/kvm/mmu.c @@ -131,7 +131,7 @@ static int dbg = 1; (((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1)) -#define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & PAGE_MASK) +#define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) #define PT64_DIR_BASE_ADDR_MASK \ (PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + PT64_LEVEL_BITS)) - 1)) @@ -298,18 +298,18 @@ static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte) if (!is_rmap_pte(*spte)) return; page = pfn_to_page((*spte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT); - if (!page->private) { + if (!page_private(page)) { rmap_printk("rmap_add: %p %llx 0->1\n", spte, *spte); - page->private = (unsigned long)spte; - } else if (!(page->private & 1)) { + set_page_private(page,(unsigned long)spte); + } else if (!(page_private(page) & 1)) { rmap_printk("rmap_add: %p %llx 1->many\n", spte, *spte); desc = mmu_alloc_rmap_desc(vcpu); - desc->shadow_ptes[0] = (u64 *)page->private; + desc->shadow_ptes[0] = (u64 *)page_private(page); desc->shadow_ptes[1] = spte; - page->private = (unsigned long)desc | 1; + set_page_private(page,(unsigned long)desc | 1); } else { rmap_printk("rmap_add: %p %llx many->many\n", spte, *spte); - desc = (struct kvm_rmap_desc *)(page->private & ~1ul); + desc = (struct kvm_rmap_desc *)(page_private(page) & ~1ul); while (desc->shadow_ptes[RMAP_EXT-1] && desc->more) desc = desc->more; if (desc->shadow_ptes[RMAP_EXT-1]) { @@ -337,12 +337,12 @@ static void rmap_desc_remove_entry(struct kvm_vcpu *vcpu, if (j != 0) return; if (!prev_desc && !desc->more) - page->private = (unsigned long)desc->shadow_ptes[0]; + set_page_private(page,(unsigned long)desc->shadow_ptes[0]); else if (prev_desc) prev_desc->more = desc->more; else - page->private = (unsigned long)desc->more | 1; + set_page_private(page,(unsigned long)desc->more | 1); mmu_free_rmap_desc(vcpu, desc); } @@ -356,20 +356,20 @@ static void rmap_remove(struct kvm_vcpu *vcpu, u64 *spte) if (!is_rmap_pte(*spte)) return; page = pfn_to_page((*spte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT); - if (!page->private) { + if (!page_private(page)) { printk(KERN_ERR "rmap_remove: %p %llx 0->BUG\n", spte, *spte); BUG(); - } else if (!(page->private & 1)) { + } else if (!(page_private(page) & 1)) { rmap_printk("rmap_remove: %p %llx 1->0\n", spte, *spte); - if ((u64 *)page->private != spte) { + if ((u64 *)page_private(page) != spte) { printk(KERN_ERR "rmap_remove: %p %llx 1->BUG\n", spte, *spte); BUG(); } - page->private = 0; + set_page_private(page,0); } else { rmap_printk("rmap_remove: %p %llx many->many\n", spte, *spte); - desc = (struct kvm_rmap_desc *)(page->private & ~1ul); + desc = (struct kvm_rmap_desc *)(page_private(page) & ~1ul); prev_desc = NULL; while (desc) { for (i = 0; i < RMAP_EXT && desc->shadow_ptes[i]; ++i) @@ -398,16 +398,16 @@ static void rmap_write_protect(struct kvm_vcpu *vcpu, u64 gfn) BUG_ON(!slot); page = gfn_to_page(slot, gfn); - while (page->private) { - if (!(page->private & 1)) - spte = (u64 *)page->private; + while (page_private(page)) { + if (!(page_private(page) & 1)) + spte = (u64 *)page_private(page); else { - desc = (struct kvm_rmap_desc *)(page->private & ~1ul); + desc = (struct kvm_rmap_desc *)(page_private(page) & ~1ul); spte = desc->shadow_ptes[0]; } BUG_ON(!spte); - BUG_ON((*spte & PT64_BASE_ADDR_MASK) != - page_to_pfn(page) << PAGE_SHIFT); + BUG_ON((*spte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT + != page_to_pfn(page)); BUG_ON(!(*spte & PT_PRESENT_MASK)); BUG_ON(!(*spte & PT_WRITABLE_MASK)); rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); @@ -1093,22 +1093,40 @@ out: return r; } +static void mmu_pre_write_zap_pte(struct kvm_vcpu *vcpu, + struct kvm_mmu_page *page, + u64 *spte) +{ + u64 pte; + struct kvm_mmu_page *child; + + pte = *spte; + if (is_present_pte(pte)) { + if (page->role.level == PT_PAGE_TABLE_LEVEL) + rmap_remove(vcpu, spte); + else { + child = page_header(pte & PT64_BASE_ADDR_MASK); + mmu_page_remove_parent_pte(vcpu, child, spte); + } + } + *spte = 0; +} + void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes) { gfn_t gfn = gpa >> PAGE_SHIFT; struct kvm_mmu_page *page; - struct kvm_mmu_page *child; struct hlist_node *node, *n; struct hlist_head *bucket; unsigned index; u64 *spte; - u64 pte; unsigned offset = offset_in_page(gpa); unsigned pte_size; unsigned page_offset; unsigned misaligned; int level; int flooded = 0; + int npte; pgprintk("%s: gpa %llx bytes %d\n", __FUNCTION__, gpa, bytes); if (gfn == vcpu->last_pt_write_gfn) { @@ -1144,22 +1162,27 @@ void kvm_mmu_pre_write(struct kvm_vcpu *vcpu, gpa_t gpa, int bytes) } page_offset = offset; level = page->role.level; + npte = 1; if (page->role.glevels == PT32_ROOT_LEVEL) { - page_offset <<= 1; /* 32->64 */ + page_offset <<= 1; /* 32->64 */ + /* + * A 32-bit pde maps 4MB while the shadow pdes map + * only 2MB. So we need to double the offset again + * and zap two pdes instead of one. + */ + if (level == PT32_ROOT_LEVEL) { + page_offset &= ~7; /* kill rounding error */ + page_offset <<= 1; + npte = 2; + } page_offset &= ~PAGE_MASK; } spte = __va(page->page_hpa); spte += page_offset / sizeof(*spte); - pte = *spte; - if (is_present_pte(pte)) { - if (level == PT_PAGE_TABLE_LEVEL) - rmap_remove(vcpu, spte); - else { - child = page_header(pte & PT64_BASE_ADDR_MASK); - mmu_page_remove_parent_pte(vcpu, child, spte); - } + while (npte--) { + mmu_pre_write_zap_pte(vcpu, page, spte); + ++spte; } - *spte = 0; } } @@ -1218,7 +1241,7 @@ static int alloc_mmu_pages(struct kvm_vcpu *vcpu) INIT_LIST_HEAD(&page_header->link); if ((page = alloc_page(GFP_KERNEL)) == NULL) goto error_1; - page->private = (unsigned long)page_header; + set_page_private(page, (unsigned long)page_header); page_header->page_hpa = (hpa_t)page_to_pfn(page) << PAGE_SHIFT; memset(__va(page_header->page_hpa), 0, PAGE_SIZE); list_add(&page_header->link, &vcpu->free_pages); diff --git a/drivers/kvm/paging_tmpl.h b/drivers/kvm/paging_tmpl.h index b6b90e9..f3bcee9 100644 --- a/drivers/kvm/paging_tmpl.h +++ b/drivers/kvm/paging_tmpl.h @@ -128,8 +128,10 @@ static int FNAME(walk_addr)(struct guest_walker *walker, goto access_error; #endif - if (!(*ptep & PT_ACCESSED_MASK)) - *ptep |= PT_ACCESSED_MASK; /* avoid rmw */ + if (!(*ptep & PT_ACCESSED_MASK)) { + mark_page_dirty(vcpu->kvm, table_gfn); + *ptep |= PT_ACCESSED_MASK; + } if (walker->level == PT_PAGE_TABLE_LEVEL) { walker->gfn = (*ptep & PT_BASE_ADDR_MASK) @@ -185,6 +187,12 @@ static void FNAME(release_walker)(struct guest_walker *walker) kunmap_atomic(walker->table, KM_USER0); } +static void FNAME(mark_pagetable_dirty)(struct kvm *kvm, + struct guest_walker *walker) +{ + mark_page_dirty(kvm, walker->table_gfn[walker->level - 1]); +} + static void FNAME(set_pte)(struct kvm_vcpu *vcpu, u64 guest_pte, u64 *shadow_pte, u64 access_bits, gfn_t gfn) { @@ -348,12 +356,15 @@ static int FNAME(fix_write_pf)(struct kvm_vcpu *vcpu, } else if (kvm_mmu_lookup_page(vcpu, gfn)) { pgprintk("%s: found shadow page for %lx, marking ro\n", __FUNCTION__, gfn); + mark_page_dirty(vcpu->kvm, gfn); + FNAME(mark_pagetable_dirty)(vcpu->kvm, walker); *guest_ent |= PT_DIRTY_MASK; *write_pt = 1; return 0; } mark_page_dirty(vcpu->kvm, gfn); *shadow_ent |= PT_WRITABLE_MASK; + FNAME(mark_pagetable_dirty)(vcpu->kvm, walker); *guest_ent |= PT_DIRTY_MASK; rmap_add(vcpu, shadow_ent); @@ -430,9 +441,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, /* * mmio: emulate if accessible, otherwise its a guest fault. */ - if (is_io_pte(*shadow_pte)) { + if (is_io_pte(*shadow_pte)) return 1; - } ++kvm_stat.pf_fixed; kvm_mmu_audit(vcpu, "post page fault (fixed)"); diff --git a/drivers/kvm/svm.c b/drivers/kvm/svm.c index 83da4ea..3d8ea7a 100644 --- a/drivers/kvm/svm.c +++ b/drivers/kvm/svm.c @@ -15,6 +15,7 @@ */ #include <linux/module.h> +#include <linux/kernel.h> #include <linux/vmalloc.h> #include <linux/highmem.h> #include <linux/profile.h> @@ -75,7 +76,7 @@ struct svm_init_data { static u32 msrpm_ranges[] = {0, 0xc0000000, 0xc0010000}; -#define NUM_MSR_MAPS (sizeof(msrpm_ranges) / sizeof(*msrpm_ranges)) +#define NUM_MSR_MAPS ARRAY_SIZE(msrpm_ranges) #define MSRS_RANGE_SIZE 2048 #define MSRS_IN_RANGE (MSRS_RANGE_SIZE * 8 / 2) @@ -485,6 +486,7 @@ static void init_vmcb(struct vmcb *vmcb) control->intercept = (1ULL << INTERCEPT_INTR) | (1ULL << INTERCEPT_NMI) | + (1ULL << INTERCEPT_SMI) | /* * selective cr0 intercept bug? * 0: 0f 22 d8 mov %eax,%cr3 @@ -553,7 +555,7 @@ static void init_vmcb(struct vmcb *vmcb) * cr0 val on cpu init should be 0x60000010, we enable cpu * cache by default. the orderly way is to enable cache in bios. */ - save->cr0 = 0x00000010 | CR0_PG_MASK; + save->cr0 = 0x00000010 | CR0_PG_MASK | CR0_WP_MASK; save->cr4 = CR4_PAE_MASK; /* rdx = ?? */ } @@ -598,10 +600,9 @@ static void svm_free_vcpu(struct kvm_vcpu *vcpu) kfree(vcpu->svm); } -static struct kvm_vcpu *svm_vcpu_load(struct kvm_vcpu *vcpu) +static void svm_vcpu_load(struct kvm_vcpu *vcpu) { get_cpu(); - return vcpu; } static void svm_vcpu_put(struct kvm_vcpu *vcpu) @@ -1042,22 +1043,22 @@ static int io_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) addr_mask = io_adress(vcpu, _in, &kvm_run->io.address); if (!addr_mask) { - printk(KERN_DEBUG "%s: get io address failed\n", __FUNCTION__); + printk(KERN_DEBUG "%s: get io address failed\n", + __FUNCTION__); return 1; } if (kvm_run->io.rep) { - kvm_run->io.count = vcpu->regs[VCPU_REGS_RCX] & addr_mask; + kvm_run->io.count + = vcpu->regs[VCPU_REGS_RCX] & addr_mask; kvm_run->io.string_down = (vcpu->svm->vmcb->save.rflags & X86_EFLAGS_DF) != 0; } - } else { + } else kvm_run->io.value = vcpu->svm->vmcb->save.rax; - } return 0; } - static int nop_on_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { return 1; @@ -1075,6 +1076,12 @@ static int halt_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) return 0; } +static int vmmcall_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) +{ + vcpu->svm->vmcb->save.rip += 3; + return kvm_hypercall(vcpu, kvm_run); +} + static int invalid_op_interception(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { inject_ud(vcpu); @@ -1275,7 +1282,7 @@ static int (*svm_exit_handlers[])(struct kvm_vcpu *vcpu, [SVM_EXIT_TASK_SWITCH] = task_switch_interception, [SVM_EXIT_SHUTDOWN] = shutdown_interception, [SVM_EXIT_VMRUN] = invalid_op_interception, - [SVM_EXIT_VMMCALL] = invalid_op_interception, + [SVM_EXIT_VMMCALL] = vmmcall_interception, [SVM_EXIT_VMLOAD] = invalid_op_interception, [SVM_EXIT_VMSAVE] = invalid_op_interception, [SVM_EXIT_STGI] = invalid_op_interception, @@ -1297,7 +1304,7 @@ static int handle_exit(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) __FUNCTION__, vcpu->svm->vmcb->control.exit_int_info, exit_code); - if (exit_code >= sizeof(svm_exit_handlers) / sizeof(*svm_exit_handlers) + if (exit_code >= ARRAY_SIZE(svm_exit_handlers) || svm_exit_handlers[exit_code] == 0) { kvm_run->exit_reason = KVM_EXIT_UNKNOWN; printk(KERN_ERR "%s: 0x%x @ 0x%llx cr0 0x%lx rflags 0x%llx\n", @@ -1668,6 +1675,18 @@ static int is_disabled(void) return 0; } +static void +svm_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall) +{ + /* + * Patch in the VMMCALL instruction: + */ + hypercall[0] = 0x0f; + hypercall[1] = 0x01; + hypercall[2] = 0xd9; + hypercall[3] = 0xc3; +} + static struct kvm_arch_ops svm_arch_ops = { .cpu_has_kvm_support = has_svm, .disabled_by_bios = is_disabled, @@ -1716,6 +1735,7 @@ static struct kvm_arch_ops svm_arch_ops = { .run = svm_vcpu_run, .skip_emulated_instruction = skip_emulated_instruction, .vcpu_setup = svm_vcpu_setup, + .patch_hypercall = svm_patch_hypercall, }; static int __init svm_init(void) diff --git a/drivers/kvm/vmx.c b/drivers/kvm/vmx.c index fd4e9173..fbbf9d6 100644 --- a/drivers/kvm/vmx.c +++ b/drivers/kvm/vmx.c @@ -19,6 +19,7 @@ #include "vmx.h" #include "kvm_vmx.h" #include <linux/module.h> +#include <linux/kernel.h> #include <linux/mm.h> #include <linux/highmem.h> #include <linux/profile.h> @@ -27,7 +28,6 @@ #include "segment_descriptor.h" - MODULE_AUTHOR("Qumranet"); MODULE_LICENSE("GPL"); @@ -76,7 +76,7 @@ static const u32 vmx_msr_index[] = { #endif MSR_EFER, MSR_K6_STAR, }; -#define NR_VMX_MSR (sizeof(vmx_msr_index) / sizeof(*vmx_msr_index)) +#define NR_VMX_MSR ARRAY_SIZE(vmx_msr_index) static inline int is_page_fault(u32 intr_info) { @@ -204,7 +204,7 @@ static void vmcs_write64(unsigned long field, u64 value) * Switches to specified vcpu, until a matching vcpu_put(), but assumes * vcpu mutex is already taken. */ -static struct kvm_vcpu *vmx_vcpu_load(struct kvm_vcpu *vcpu) +static void vmx_vcpu_load(struct kvm_vcpu *vcpu) { u64 phys_addr = __pa(vcpu->vmcs); int cpu; @@ -242,7 +242,6 @@ static struct kvm_vcpu *vmx_vcpu_load(struct kvm_vcpu *vcpu) rdmsrl(MSR_IA32_SYSENTER_ESP, sysenter_esp); vmcs_writel(HOST_IA32_SYSENTER_ESP, sysenter_esp); /* 22.2.3 */ } - return vcpu; } static void vmx_vcpu_put(struct kvm_vcpu *vcpu) @@ -372,10 +371,10 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 *pdata) data = vmcs_read32(GUEST_SYSENTER_CS); break; case MSR_IA32_SYSENTER_EIP: - data = vmcs_read32(GUEST_SYSENTER_EIP); + data = vmcs_readl(GUEST_SYSENTER_EIP); break; case MSR_IA32_SYSENTER_ESP: - data = vmcs_read32(GUEST_SYSENTER_ESP); + data = vmcs_readl(GUEST_SYSENTER_ESP); break; default: msr = find_msr_entry(vcpu, msr_index); @@ -413,15 +412,14 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) vmcs_write32(GUEST_SYSENTER_CS, data); break; case MSR_IA32_SYSENTER_EIP: - vmcs_write32(GUEST_SYSENTER_EIP, data); + vmcs_writel(GUEST_SYSENTER_EIP, data); break; case MSR_IA32_SYSENTER_ESP: - vmcs_write32(GUEST_SYSENTER_ESP, data); + vmcs_writel(GUEST_SYSENTER_ESP, data); break; - case MSR_IA32_TIME_STAMP_COUNTER: { + case MSR_IA32_TIME_STAMP_COUNTER: guest_write_tsc(data); break; - } default: msr = find_msr_entry(vcpu, msr_index); if (msr) { @@ -620,7 +618,7 @@ static void fix_pmode_dataseg(int seg, struct kvm_save_segment *save) { struct kvm_vmx_segment_field *sf = &kvm_vmx_segment_fields[seg]; - if (vmcs_readl(sf->base) == save->base) { + if (vmcs_readl(sf->base) == save->base && (save->base & AR_S_MASK)) { vmcs_write16(sf->selector, save->selector); vmcs_writel(sf->base, save->base); vmcs_write32(sf->limit, save->limit); @@ -793,6 +791,9 @@ static void vmx_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) */ static void vmx_set_cr0_no_modeswitch(struct kvm_vcpu *vcpu, unsigned long cr0) { + if (!vcpu->rmode.active && !(cr0 & CR0_PE_MASK)) + enter_rmode(vcpu); + vcpu->rmode.active = ((cr0 & CR0_PE_MASK) == 0); update_exception_bitmap(vcpu); vmcs_writel(CR0_READ_SHADOW, cr0); @@ -1467,6 +1468,18 @@ static int handle_io(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) return 0; } +static void +vmx_patch_hypercall(struct kvm_vcpu *vcpu, unsigned char *hypercall) +{ + /* + * Patch in the VMCALL instruction: + */ + hypercall[0] = 0x0f; + hypercall[1] = 0x01; + hypercall[2] = 0xc1; + hypercall[3] = 0xc3; +} + static int handle_cr(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) { u64 exit_qualification; @@ -1643,6 +1656,12 @@ static int handle_halt(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) return 0; } +static int handle_vmcall(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) +{ + vmcs_writel(GUEST_RIP, vmcs_readl(GUEST_RIP)+3); + return kvm_hypercall(vcpu, kvm_run); +} + /* * The exit handlers return 1 if the exit was handled fully and guest execution * may resume. Otherwise they set the kvm_run parameter to indicate what needs @@ -1661,6 +1680,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu, [EXIT_REASON_MSR_WRITE] = handle_wrmsr, [EXIT_REASON_PENDING_INTERRUPT] = handle_interrupt_window, [EXIT_REASON_HLT] = handle_halt, + [EXIT_REASON_VMCALL] = handle_vmcall, }; static const int kvm_vmx_max_exit_handlers = @@ -1868,6 +1888,27 @@ again: [cr2]"i"(offsetof(struct kvm_vcpu, cr2)) : "cc", "memory" ); + /* + * Reload segment selectors ASAP. (it's needed for a functional + * kernel: x86 relies on having __KERNEL_PDA in %fs and x86_64 + * relies on having 0 in %gs for the CPU PDA to work.) + */ + if (fs_gs_ldt_reload_needed) { + load_ldt(ldt_sel); + load_fs(fs_sel); + /* + * If we have to reload gs, we must take care to + * preserve our gs base. + */ + local_irq_disable(); + load_gs(gs_sel); +#ifdef CONFIG_X86_64 + wrmsrl(MSR_GS_BASE, vmcs_readl(HOST_GS_BASE)); +#endif + local_irq_enable(); + + reload_tss(); + } ++kvm_stat.exits; save_msrs(vcpu->guest_msrs, NR_BAD_MSRS); @@ -1885,22 +1926,6 @@ again: kvm_run->exit_reason = vmcs_read32(VM_INSTRUCTION_ERROR); r = 0; } else { - if (fs_gs_ldt_reload_needed) { - load_ldt(ldt_sel); - load_fs(fs_sel); - /* - * If we have to reload gs, we must take care to - * preserve our gs base. - */ - local_irq_disable(); - load_gs(gs_sel); -#ifdef CONFIG_X86_64 - wrmsrl(MSR_GS_BASE, vmcs_readl(HOST_GS_BASE)); -#endif - local_irq_enable(); - - reload_tss(); - } /* * Profile KVM exit RIPs: */ @@ -2062,6 +2087,7 @@ static struct kvm_arch_ops vmx_arch_ops = { .run = vmx_vcpu_run, .skip_emulated_instruction = skip_emulated_instruction, .vcpu_setup = vmx_vcpu_setup, + .patch_hypercall = vmx_patch_hypercall, }; static int __init vmx_init(void) diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 7399ba7..80acd08 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -82,6 +82,18 @@ config LEDS_WRAP help This option enables support for the PCEngines WRAP programmable LEDs. +config LEDS_H1940 + tristate "LED Support for iPAQ H1940 device" + depends LEDS_CLASS && ARCH_H1940 + help + This option enables support for the LEDs on the h1940. + +config LEDS_COBALT + tristate "LED Support for Cobalt Server front LED" + depends on LEDS_CLASS && MIPS_COBALT + help + This option enables support for the front LED on Cobalt Server + comment "LED Triggers" config LEDS_TRIGGERS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index 500de3dc..aa2c18e 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -14,6 +14,8 @@ obj-$(CONFIG_LEDS_S3C24XX) += leds-s3c24xx.o obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o +obj-$(CONFIG_LEDS_H1940) += leds-h1940.o +obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff --git a/drivers/leds/leds-cobalt.c b/drivers/leds/leds-cobalt.c new file mode 100644 index 0000000..d16439c --- /dev/null +++ b/drivers/leds/leds-cobalt.c @@ -0,0 +1,43 @@ +/* + * Copyright 2006 - Florian Fainelli <florian@openwrt.org> + * + * Control the Cobalt Qube/RaQ front LED + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/leds.h> +#include <asm/mach-cobalt/cobalt.h> + +static void cobalt_led_set(struct led_classdev *led_cdev, enum led_brightness brightness) +{ + if (brightness) + COBALT_LED_PORT = COBALT_LED_BAR_LEFT | COBALT_LED_BAR_RIGHT; + else + COBALT_LED_PORT = 0; +} + +static struct led_classdev cobalt_led = { + .name = "cobalt-front-led", + .brightness_set = cobalt_led_set, + .default_trigger = "ide-disk", +}; + +static int __init cobalt_led_init(void) +{ + return led_classdev_register(NULL, &cobalt_led); +} + +static void __exit cobalt_led_exit(void) +{ + led_classdev_unregister(&cobalt_led); +} + +module_init(cobalt_led_init); +module_exit(cobalt_led_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Front LED support for Cobalt Server"); +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); diff --git a/drivers/leds/leds-h1940.c b/drivers/leds/leds-h1940.c new file mode 100644 index 0000000..1d49d2a --- /dev/null +++ b/drivers/leds/leds-h1940.c @@ -0,0 +1,163 @@ +/* + * drivers/leds/h1940-leds.c + * Copyright (c) Arnaud Patard <arnaud.patard@rtp-net.org> + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * H1940 leds driver + * + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/string.h> +#include <linux/ctype.h> +#include <linux/leds.h> +#include <asm/arch/regs-gpio.h> +#include <asm/hardware.h> +#include <asm/arch/h1940-latch.h> + +/* + * Green led. + */ +void h1940_greenled_set(struct led_classdev *led_dev, enum led_brightness value) +{ + switch (value) { + case LED_HALF: + h1940_latch_control(0,H1940_LATCH_LED_FLASH); + s3c2410_gpio_setpin(S3C2410_GPA7,1); + break; + case LED_FULL: + h1940_latch_control(0,H1940_LATCH_LED_GREEN); + s3c2410_gpio_setpin(S3C2410_GPA7,1); + break; + default: + case LED_OFF: + h1940_latch_control(H1940_LATCH_LED_FLASH,0); + h1940_latch_control(H1940_LATCH_LED_GREEN,0); + s3c2410_gpio_setpin(S3C2410_GPA7,0); + break; + } +} + +static struct led_classdev h1940_greenled = { + .name = "h1940:green", + .brightness_set = h1940_greenled_set, + .default_trigger = "h1940-charger", +}; + +/* + * Red led. + */ +void h1940_redled_set(struct led_classdev *led_dev, enum led_brightness value) +{ + switch (value) { + case LED_HALF: + h1940_latch_control(0,H1940_LATCH_LED_FLASH); + s3c2410_gpio_setpin(S3C2410_GPA1,1); + break; + case LED_FULL: + h1940_latch_control(0,H1940_LATCH_LED_RED); + s3c2410_gpio_setpin(S3C2410_GPA1,1); + break; + default: + case LED_OFF: + h1940_latch_control(H1940_LATCH_LED_FLASH,0); + h1940_latch_control(H1940_LATCH_LED_RED,0); + s3c2410_gpio_setpin(S3C2410_GPA1,0); + break; + } +} + +static struct led_classdev h1940_redled = { + .name = "h1940:red", + .brightness_set = h1940_redled_set, + .default_trigger = "h1940-charger", +}; + +/* + * Blue led. + * (it can only be blue flashing led) + */ +void h1940_blueled_set(struct led_classdev *led_dev, enum led_brightness value) +{ + if (value) { + /* flashing Blue */ + h1940_latch_control(0,H1940_LATCH_LED_FLASH); + s3c2410_gpio_setpin(S3C2410_GPA3,1); + } else { + h1940_latch_control(H1940_LATCH_LED_FLASH,0); + s3c2410_gpio_setpin(S3C2410_GPA3,0); + } + +} + +static struct led_classdev h1940_blueled = { + .name = "h1940:blue", + .brightness_set = h1940_blueled_set, + .default_trigger = "h1940-bluetooth", +}; + +static int __init h1940leds_probe(struct platform_device *pdev) +{ + int ret; + + ret = led_classdev_register(&pdev->dev, &h1940_greenled); + if (ret) + goto err_green; + + ret = led_classdev_register(&pdev->dev, &h1940_redled); + if (ret) + goto err_red; + + ret = led_classdev_register(&pdev->dev, &h1940_blueled); + if (ret) + goto err_blue; + + return 0; + +err_blue: + led_classdev_unregister(&h1940_redled); +err_red: + led_classdev_unregister(&h1940_greenled); +err_green: + return ret; +} + +static int h1940leds_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&h1940_greenled); + led_classdev_unregister(&h1940_redled); + led_classdev_unregister(&h1940_blueled); + return 0; +} + + +static struct platform_driver h1940leds_driver = { + .driver = { + .name = "h1940-leds", + }, + .probe = h1940leds_probe, + .remove = h1940leds_remove, +}; + + +static int __init h1940leds_init(void) +{ + return platform_driver_register(&h1940leds_driver); +} + +static void __exit h1940leds_exit(void) +{ + platform_driver_unregister(&h1940leds_driver); +} + +module_init(h1940leds_init); +module_exit(h1940leds_exit); + +MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); +MODULE_DESCRIPTION("LED driver for the iPAQ H1940"); +MODULE_LICENSE("GPL"); diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c index f729eeb..adfea3c 100644 --- a/drivers/macintosh/adb.c +++ b/drivers/macintosh/adb.c @@ -90,7 +90,7 @@ static int autopoll_devs; int __adb_probe_sync; #ifdef CONFIG_PM -static int adb_notify_sleep(struct pmu_sleep_notifier *self, int when); +static void adb_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier adb_sleep_notifier = { adb_notify_sleep, SLEEP_LEVEL_ADB, @@ -340,11 +340,9 @@ __initcall(adb_init); /* * notify clients before sleep and reset bus afterwards */ -int +void adb_notify_sleep(struct pmu_sleep_notifier *self, int when) { - int ret; - switch (when) { case PBOOK_SLEEP_REQUEST: adb_got_sleep = 1; @@ -353,22 +351,8 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when) /* Stop autopoll */ if (adb_controller->autopoll) adb_controller->autopoll(0); - ret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_POWERDOWN, NULL); - if (ret & NOTIFY_STOP_MASK) { - up(&adb_probe_mutex); - return PBOOK_SLEEP_REFUSE; - } - break; - case PBOOK_SLEEP_REJECT: - if (adb_got_sleep) { - adb_got_sleep = 0; - up(&adb_probe_mutex); - adb_reset_bus(); - } - break; - - case PBOOK_SLEEP_NOW: + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POWERDOWN, NULL); break; case PBOOK_WAKE: adb_got_sleep = 0; @@ -376,14 +360,13 @@ adb_notify_sleep(struct pmu_sleep_notifier *self, int when) adb_reset_bus(); break; } - return PBOOK_SLEEP_OK; } #endif /* CONFIG_PM */ static int do_adb_reset_bus(void) { - int ret, nret; + int ret; if (adb_controller == NULL) return -ENXIO; @@ -391,13 +374,8 @@ do_adb_reset_bus(void) if (adb_controller->autopoll) adb_controller->autopoll(0); - nret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_PRE_RESET, NULL); - if (nret & NOTIFY_STOP_MASK) { - if (adb_controller->autopoll) - adb_controller->autopoll(autopoll_devs); - return -EBUSY; - } + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_PRE_RESET, NULL); if (sleepy_trackpad) { /* Let the trackpad settle down */ @@ -427,10 +405,8 @@ do_adb_reset_bus(void) } up(&adb_handler_sem); - nret = blocking_notifier_call_chain(&adb_client_list, - ADB_MSG_POST_RESET, NULL); - if (nret & NOTIFY_STOP_MASK) - return -EBUSY; + blocking_notifier_call_chain(&adb_client_list, + ADB_MSG_POST_RESET, NULL); return ret; } diff --git a/drivers/macintosh/ans-lcd.c b/drivers/macintosh/ans-lcd.c index cdd5a0f..e54c4d9 100644 --- a/drivers/macintosh/ans-lcd.c +++ b/drivers/macintosh/ans-lcd.c @@ -145,11 +145,12 @@ anslcd_init(void) int retval; struct device_node* node; - node = find_devices("lcd"); - if (!node || !node->parent) - return -ENODEV; - if (strcmp(node->parent->name, "gc")) + node = of_find_node_by_name(NULL, "lcd"); + if (!node || !node->parent || strcmp(node->parent->name, "gc")) { + of_node_put(node); return -ENODEV; + } + of_node_put(node); anslcd_ptr = ioremap(ANSLCD_ADDR, 0x20); diff --git a/drivers/macintosh/apm_emu.c b/drivers/macintosh/apm_emu.c index c5e4d43..cdb0bea 100644 --- a/drivers/macintosh/apm_emu.c +++ b/drivers/macintosh/apm_emu.c @@ -96,7 +96,7 @@ static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); static struct apm_user * user_list; -static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when); +static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when); static struct pmu_sleep_notifier apm_sleep_notifier = { apm_notify_sleep, SLEEP_LEVEL_USERLAND, @@ -352,7 +352,7 @@ static int do_open(struct inode * inode, struct file * filp) * doesn't provide a way to NAK, but this could be added * here. */ -static int wait_all_suspend(void) +static void wait_all_suspend(void) { DECLARE_WAITQUEUE(wait, current); @@ -366,24 +366,19 @@ static int wait_all_suspend(void) remove_wait_queue(&apm_suspend_waitqueue, &wait); DBG("apm_emu: wait_all_suspend() - complete !\n"); - - return 1; } -static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when) +static void apm_notify_sleep(struct pmu_sleep_notifier *self, int when) { switch(when) { case PBOOK_SLEEP_REQUEST: queue_event(APM_SYS_SUSPEND, NULL); - if (!wait_all_suspend()) - return PBOOK_SLEEP_REFUSE; + wait_all_suspend(); break; - case PBOOK_SLEEP_REJECT: case PBOOK_WAKE: queue_event(APM_NORMAL_RESUME, NULL); break; } - return PBOOK_SLEEP_OK; } #define APM_CRITICAL 10 diff --git a/drivers/macintosh/mac_hid.c b/drivers/macintosh/mac_hid.c index c1fd816..1599dc3 100644 --- a/drivers/macintosh/mac_hid.c +++ b/drivers/macintosh/mac_hid.c @@ -102,8 +102,6 @@ int mac_hid_mouse_emulate_buttons(int caller, unsigned int keycode, int down) return 0; } -EXPORT_SYMBOL(mac_hid_mouse_emulate_buttons); - static int emumousebtn_input_register(void) { int ret; diff --git a/drivers/macintosh/macio-adb.c b/drivers/macintosh/macio-adb.c index 026b67f..79119f5 100644 --- a/drivers/macintosh/macio-adb.c +++ b/drivers/macintosh/macio-adb.c @@ -82,7 +82,14 @@ struct adb_driver macio_adb_driver = { int macio_probe(void) { - return find_compatible_devices("adb", "chrp,adb0")? 0: -ENODEV; + struct device_node *np; + + np = of_find_compatible_node(NULL, "adb", "chrp,adb0"); + if (np) { + of_node_put(np); + return 0; + } + return -ENODEV; } int macio_init(void) @@ -91,12 +98,14 @@ int macio_init(void) struct resource r; unsigned int irq; - adbs = find_compatible_devices("adb", "chrp,adb0"); + adbs = of_find_compatible_node(NULL, "adb", "chrp,adb0"); if (adbs == 0) return -ENXIO; - if (of_address_to_resource(adbs, 0, &r)) + if (of_address_to_resource(adbs, 0, &r)) { + of_node_put(adbs); return -ENXIO; + } adb = ioremap(r.start, sizeof(struct adb_regs)); out_8(&adb->ctrl.r, 0); @@ -107,6 +116,7 @@ int macio_init(void) out_8(&adb->autopoll.r, APE); irq = irq_of_parse_and_map(adbs, 0); + of_node_put(adbs); if (request_irq(irq, macio_adb_interrupt, 0, "ADB", (void *)0)) { printk(KERN_ERR "ADB: can't get irq %d\n", irq); return -EAGAIN; diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c index d562160..c96b7fe 100644 --- a/drivers/macintosh/macio_asic.c +++ b/drivers/macintosh/macio_asic.c @@ -134,108 +134,12 @@ static int macio_device_resume(struct device * dev) return 0; } -static int macio_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) -{ - struct macio_dev * macio_dev; - struct of_device * of; - char *scratch; - const char *compat, *compat2; - - int i = 0; - int length, cplen, cplen2, seen = 0; - - if (!dev) - return -ENODEV; - - macio_dev = to_macio_device(dev); - if (!macio_dev) - return -ENODEV; - - of = &macio_dev->ofdev; - - /* stuff we want to pass to /sbin/hotplug */ - envp[i++] = scratch = buffer; - length = scnprintf (scratch, buffer_size, "OF_NAME=%s", of->node->name); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_TYPE=%s", of->node->type); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - /* Since the compatible field can contain pretty much anything - * it's not really legal to split it out with commas. We split it - * up using a number of environment variables instead. */ - - compat = get_property(of->node, "compatible", &cplen); - compat2 = compat; - cplen2= cplen; - while (compat && cplen > 0) { - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, - "OF_COMPATIBLE_%d=%s", seen, compat); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - length = strlen (compat) + 1; - compat += length; - cplen -= length; - seen++; - } - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "OF_COMPATIBLE_N=%d", seen); - ++length; - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - envp[i++] = scratch; - length = scnprintf (scratch, buffer_size, "MODALIAS=of:N%sT%s", - of->node->name, of->node->type); - /* overwrite '\0' */ - buffer_size -= length; - if ((buffer_size <= 0) || (i >= num_envp)) - return -ENOMEM; - scratch += length; - - if (!compat2) { - compat2 = ""; - cplen2 = 1; - } - while (cplen2 > 0) { - length = snprintf (scratch, buffer_size, "C%s", compat2); - buffer_size -= length; - if (buffer_size <= 0) - return -ENOMEM; - scratch += length; - length = strlen (compat2) + 1; - compat2 += length; - cplen2 -= length; - } - - envp[i] = NULL; - - return 0; -} - extern struct device_attribute macio_dev_attrs[]; struct bus_type macio_bus_type = { .name = "macio", .match = macio_bus_match, - .uevent = macio_uevent, + .uevent = of_device_uevent, .probe = macio_device_probe, .remove = macio_device_remove, .shutdown = macio_device_shutdown, @@ -491,7 +395,7 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip, #endif MAX_NODE_NAME_SIZE, np->name); } else { - reg = get_property(np, "reg", NULL); + reg = of_get_property(np, "reg", NULL); sprintf(dev->ofdev.dev.bus_id, "%1d.%08x:%.*s", chip->lbus.index, reg ? *reg : 0, MAX_NODE_NAME_SIZE, np->name); diff --git a/drivers/macintosh/macio_sysfs.c b/drivers/macintosh/macio_sysfs.c index 8566bdf..cc82679 100644 --- a/drivers/macintosh/macio_sysfs.c +++ b/drivers/macintosh/macio_sysfs.c @@ -21,7 +21,7 @@ compatible_show (struct device *dev, struct device_attribute *attr, char *buf) int length = 0; of = &to_macio_device (dev)->ofdev; - compat = get_property(of->node, "compatible", &cplen); + compat = of_get_property(of->node, "compatible", &cplen); if (!compat) { *buf = '\0'; return 0; @@ -47,18 +47,20 @@ static ssize_t modalias_show (struct device *dev, struct device_attribute *attr, int length; of = &to_macio_device (dev)->ofdev; - compat = get_property(of->node, "compatible", &cplen); + compat = of_get_property(of->node, "compatible", &cplen); if (!compat) compat = "", cplen = 1; length = sprintf (buf, "of:N%sT%s", of->node->name, of->node->type); buf += length; while (cplen > 0) { int l; - length += sprintf (buf, "C%s", compat); - buf += length; + l = sprintf (buf, "C%s", compat); + length += l; + buf += l; l = strlen (compat) + 1; compat += l; cplen -= l; } + length += sprintf(buf, "\n"); return length; } diff --git a/drivers/macintosh/rack-meter.c b/drivers/macintosh/rack-meter.c index f83fad2..4177ff0 100644 --- a/drivers/macintosh/rack-meter.c +++ b/drivers/macintosh/rack-meter.c @@ -387,7 +387,7 @@ static int __devinit rackmeter_probe(struct macio_dev* mdev, if (strcmp(np->name, "lightshow") == 0) break; if ((strcmp(np->name, "sound") == 0) && - get_property(np, "virtual", NULL) != NULL) + of_get_property(np, "virtual", NULL) != NULL) break; } if (np == NULL) { diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c index 3096836..a98a328 100644 --- a/drivers/macintosh/smu.c +++ b/drivers/macintosh/smu.c @@ -491,7 +491,7 @@ int __init smu_init (void) printk(KERN_ERR "SMU: Can't find doorbell GPIO !\n"); goto fail; } - data = get_property(smu->db_node, "reg", NULL); + data = of_get_property(smu->db_node, "reg", NULL); if (data == NULL) { of_node_put(smu->db_node); smu->db_node = NULL; @@ -512,7 +512,7 @@ int __init smu_init (void) smu->msg_node = of_find_node_by_name(NULL, "smu-interrupt"); if (smu->msg_node == NULL) break; - data = get_property(smu->msg_node, "reg", NULL); + data = of_get_property(smu->msg_node, "reg", NULL); if (data == NULL) { of_node_put(smu->msg_node); smu->msg_node = NULL; @@ -952,7 +952,7 @@ static struct smu_sdbp_header *smu_create_sdb_partition(int id) prop->name = ((char *)prop) + tlen - 18; sprintf(prop->name, "sdb-partition-%02x", id); prop->length = len; - prop->value = (unsigned char *)hdr; + prop->value = hdr; prop->next = NULL; /* Read the datablock */ @@ -1004,7 +1004,7 @@ const struct smu_sdbp_header *__smu_get_sdb_partition(int id, } else mutex_lock(&smu_part_access); - part = get_property(smu->of_node, pname, size); + part = of_get_property(smu->of_node, pname, size); if (part == NULL) { DPRINTK("trying to extract from SMU ...\n"); part = smu_create_sdb_partition(id); @@ -1259,9 +1259,9 @@ static int smu_release(struct inode *inode, struct file *file) set_current_state(TASK_UNINTERRUPTIBLE); if (pp->cmd.status != 1) break; - spin_lock_irqsave(&pp->lock, flags); - schedule(); spin_unlock_irqrestore(&pp->lock, flags); + schedule(); + spin_lock_irqsave(&pp->lock, flags); } set_current_state(TASK_RUNNING); remove_wait_queue(&pp->wait, &wait); diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index a7ce5592..2289034 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c @@ -567,13 +567,13 @@ thermostat_init(void) else return -ENODEV; - prop = get_property(np, "hwsensor-params-version", NULL); + prop = of_get_property(np, "hwsensor-params-version", NULL); printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, (*prop == 1)?"":"un"); if (*prop != 1) return -ENODEV; - prop = get_property(np, "reg", NULL); + prop = of_get_property(np, "reg", NULL); if (!prop) return -ENODEV; @@ -591,9 +591,9 @@ thermostat_init(void) "limit_adjust: %d, fan_speed: %d\n", therm_bus, therm_address, limit_adjust, fan_speed); - if (get_property(np, "hwsensor-location", NULL)) { + if (of_get_property(np, "hwsensor-location", NULL)) { for (i = 0; i < 3; i++) { - sensor_location[i] = get_property(np, + sensor_location[i] = of_get_property(np, "hwsensor-location", NULL) + offset; if (sensor_location[i] == NULL) diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c index 2e4ad44..78ff186 100644 --- a/drivers/macintosh/therm_pm72.c +++ b/drivers/macintosh/therm_pm72.c @@ -674,7 +674,7 @@ static int read_eeprom(int cpu, struct mpu_data *out) printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n"); return -ENODEV; } - data = get_property(np, "cpuid", &len); + data = of_get_property(np, "cpuid", &len); if (data == NULL) { printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n"); of_node_put(np); @@ -1337,7 +1337,7 @@ static int init_backside_state(struct backside_pid_state *state) */ u3 = of_find_node_by_path("/u3@0,f8000000"); if (u3 != NULL) { - const u32 *vers = get_property(u3, "device-rev", NULL); + const u32 *vers = of_get_property(u3, "device-rev", NULL); if (vers) if (((*vers) & 0x3f) < 0x34) u3h = 0; @@ -2129,8 +2129,8 @@ static void fcu_lookup_fans(struct device_node *fcu_node) continue; /* Lookup for a matching location */ - loc = get_property(np, "location", NULL); - reg = get_property(np, "reg", NULL); + loc = of_get_property(np, "location", NULL); + reg = of_get_property(np, "reg", NULL); if (loc == NULL || reg == NULL) continue; DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg); diff --git a/drivers/macintosh/therm_windtunnel.c b/drivers/macintosh/therm_windtunnel.c index a1d3a98..35233de 100644 --- a/drivers/macintosh/therm_windtunnel.c +++ b/drivers/macintosh/therm_windtunnel.c @@ -492,7 +492,7 @@ g4fan_init( void ) if( !(np=of_find_node_by_name(NULL, "power-mgt")) ) return -ENODEV; - info = get_property(np, "thermal-info", NULL); + info = of_get_property(np, "thermal-info", NULL); of_node_put(np); if( !info || !machine_is_compatible("PowerMac3,6") ) diff --git a/drivers/macintosh/via-cuda.c b/drivers/macintosh/via-cuda.c index d58fcf6..76d2177 100644 --- a/drivers/macintosh/via-cuda.c +++ b/drivers/macintosh/via-cuda.c @@ -131,7 +131,7 @@ int __init find_via_cuda(void) if (vias == 0) return 0; - reg = get_property(vias, "reg", NULL); + reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-cuda: No \"reg\" property !\n"); goto fail; diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c index 801a974..7e27071 100644 --- a/drivers/macintosh/via-pmu-backlight.c +++ b/drivers/macintosh/via-pmu-backlight.c @@ -15,7 +15,7 @@ #define MAX_PMU_LEVEL 0xFF -static struct backlight_properties pmu_backlight_data; +static struct backlight_ops pmu_backlight_data; static DEFINE_SPINLOCK(pmu_backlight_lock); static int sleeping; static u8 bl_curve[FB_BACKLIGHT_LEVELS]; @@ -72,7 +72,7 @@ static int pmu_backlight_update_status(struct backlight_device *bd) { struct adb_request req; unsigned long flags; - int level = bd->props->brightness; + int level = bd->props.brightness; spin_lock_irqsave(&pmu_backlight_lock, flags); @@ -80,8 +80,8 @@ static int pmu_backlight_update_status(struct backlight_device *bd) if (sleeping) goto out; - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; if (level > 0) { @@ -107,14 +107,13 @@ out: static int pmu_backlight_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties pmu_backlight_data = { - .owner = THIS_MODULE, +static struct backlight_ops pmu_backlight_data = { .get_brightness = pmu_backlight_get_brightness, .update_status = pmu_backlight_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), + }; #ifdef CONFIG_PM @@ -152,9 +151,10 @@ void __init pmu_backlight_init() printk("pmubl: Backlight registration failed\n"); goto error; } + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; pmu_backlight_init_curve(0x7F, 0x46, 0x0E); - level = pmu_backlight_data.max_brightness; + level = bd->props.max_brightness; if (autosave) { /* read autosaved value if available */ @@ -164,19 +164,12 @@ void __init pmu_backlight_init() level = pmu_backlight_curve_lookup( (req.reply[0] >> 4) * - pmu_backlight_data.max_brightness / 15); + bd->props.max_brightness / 15); } - down(&bd->sem); - bd->props->brightness = level; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); - - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); + bd->props.brightness = level; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("pmubl: Backlight initialized (%s)\n", name); diff --git a/drivers/macintosh/via-pmu-led.c b/drivers/macintosh/via-pmu-led.c index 179af10..fc89a70 100644 --- a/drivers/macintosh/via-pmu-led.c +++ b/drivers/macintosh/via-pmu-led.c @@ -81,7 +81,7 @@ static struct led_classdev pmu_led = { }; #ifdef CONFIG_PM -static int pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) +static void pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) { unsigned long flags; @@ -99,8 +99,6 @@ static int pmu_led_sleep_call(struct pmu_sleep_notifier *self, int when) break; } spin_unlock_irqrestore(&pmu_blink_lock, flags); - - return PBOOK_SLEEP_OK; } static struct pmu_sleep_notifier via_pmu_led_sleep_notif = { @@ -120,11 +118,13 @@ static int __init via_pmu_led_init(void) dt = of_find_node_by_path("/"); if (dt == NULL) return -ENODEV; - model = get_property(dt, "model", NULL); + model = of_get_property(dt, "model", NULL); if (model == NULL) return -ENODEV; if (strncmp(model, "PowerBook", strlen("PowerBook")) != 0 && - strncmp(model, "iBook", strlen("iBook")) != 0) { + strncmp(model, "iBook", strlen("iBook")) != 0 && + strcmp(model, "PowerMac7,2") != 0 && + strcmp(model, "PowerMac7,3") != 0) { of_node_put(dt); /* ignore */ return -ENODEV; diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c index b6073bd..1729d3f 100644 --- a/drivers/macintosh/via-pmu.c +++ b/drivers/macintosh/via-pmu.c @@ -289,7 +289,7 @@ int __init find_via_pmu(void) if (vias == NULL) return 0; - reg = get_property(vias, "reg", NULL); + reg = of_get_property(vias, "reg", NULL); if (reg == NULL) { printk(KERN_ERR "via-pmu: No \"reg\" property !\n"); goto fail; @@ -319,10 +319,13 @@ int __init find_via_pmu(void) else if (device_is_compatible(vias->parent, "Keylargo") || device_is_compatible(vias->parent, "K2-Keylargo")) { struct device_node *gpiop; + struct device_node *adbp; u64 gaddr = OF_BAD_ADDR; pmu_kind = PMU_KEYLARGO_BASED; - pmu_has_adb = (find_type_devices("adb") != NULL); + adbp = of_find_node_by_type(NULL, "adb"); + pmu_has_adb = (adbp != NULL); + of_node_put(adbp); pmu_intr_mask = PMU_INT_PCEJECT | PMU_INT_SNDBRT | PMU_INT_ADB | @@ -331,7 +334,7 @@ int __init find_via_pmu(void) gpiop = of_find_node_by_name(NULL, "gpio"); if (gpiop) { - reg = get_property(gpiop, "reg", NULL); + reg = of_get_property(gpiop, "reg", NULL); if (reg) gaddr = of_translate_address(gpiop, reg); if (gaddr != OF_BAD_ADDR) @@ -484,10 +487,11 @@ static int __init via_pmu_dev_init(void) pmu_batteries[0].flags |= PMU_BATT_TYPE_SMART; pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } else { - struct device_node* prim = find_devices("power-mgt"); + struct device_node* prim = + of_find_node_by_name(NULL, "power-mgt"); const u32 *prim_info = NULL; if (prim) - prim_info = get_property(prim, "prim-info", NULL); + prim_info = of_get_property(prim, "prim-info", NULL); if (prim_info) { /* Other stuffs here yet unknown */ pmu_battery_count = (prim_info[6] >> 16) & 0xff; @@ -495,6 +499,7 @@ static int __init via_pmu_dev_init(void) if (pmu_battery_count > 1) pmu_batteries[1].flags |= PMU_BATT_TYPE_SMART; } + of_node_put(prim); } #endif /* CONFIG_PPC32 */ @@ -1769,35 +1774,21 @@ EXPORT_SYMBOL(pmu_unregister_sleep_notifier); #if defined(CONFIG_PM) && defined(CONFIG_PPC32) /* Sleep is broadcast last-to-first */ -static int -broadcast_sleep(int when, int fallback) +static void broadcast_sleep(int when) { - int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; for (list = sleep_notifiers.prev; list != &sleep_notifiers; list = list->prev) { notifier = list_entry(list, struct pmu_sleep_notifier, list); - ret = notifier->notifier_call(notifier, when); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_DEBUG "sleep %d rejected by %p (%p)\n", - when, notifier, notifier->notifier_call); - for (; list != &sleep_notifiers; list = list->next) { - notifier = list_entry(list, struct pmu_sleep_notifier, list); - notifier->notifier_call(notifier, fallback); - } - return ret; - } + notifier->notifier_call(notifier, when); } - return ret; } /* Wake is broadcast first-to-last */ -static int -broadcast_wake(void) +static void broadcast_wake(void) { - int ret = PBOOK_SLEEP_OK; struct list_head *list; struct pmu_sleep_notifier *notifier; @@ -1806,7 +1797,6 @@ broadcast_wake(void) notifier = list_entry(list, struct pmu_sleep_notifier, list); notifier->notifier_call(notifier, PBOOK_WAKE); } - return ret; } /* @@ -2013,12 +2003,8 @@ pmac_suspend_devices(void) pm_prepare_console(); - /* Notify old-style device drivers & userland */ - ret = broadcast_sleep(PBOOK_SLEEP_REQUEST, PBOOK_SLEEP_REJECT); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_ERR "Sleep rejected by drivers\n"); - return -EBUSY; - } + /* Notify old-style device drivers */ + broadcast_sleep(PBOOK_SLEEP_REQUEST); /* Sync the disks. */ /* XXX It would be nice to have some way to ensure that @@ -2028,12 +2014,7 @@ pmac_suspend_devices(void) */ sys_sync(); - /* Sleep can fail now. May not be very robust but useful for debugging */ - ret = broadcast_sleep(PBOOK_SLEEP_NOW, PBOOK_WAKE); - if (ret != PBOOK_SLEEP_OK) { - printk(KERN_ERR "Driver sleep failed\n"); - return -EBUSY; - } + broadcast_sleep(PBOOK_SLEEP_NOW); /* Send suspend call to devices, hold the device core's dpm_sem */ ret = device_suspend(PMSG_SUSPEND); @@ -2154,7 +2135,7 @@ static int powerbook_sleep_grackle(void) int ret; struct pci_dev *grackle; - grackle = pci_find_slot(0, 0); + grackle = pci_get_bus_and_slot(0, 0); if (!grackle) return -ENODEV; @@ -2202,6 +2183,8 @@ static int powerbook_sleep_grackle(void) pmcr1 &= ~(GRACKLE_PM|GRACKLE_DOZE|GRACKLE_SLEEP|GRACKLE_NAP); pci_write_config_word(grackle, 0x70, pmcr1); + pci_dev_put(grackle); + /* Make sure the PMU is idle */ pmac_call_feature(PMAC_FTR_SLEEP_STATE,NULL,0,0); restore_via_state(); diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c index 3f7967f..ab4d1b6 100644 --- a/drivers/macintosh/windfarm_lm75_sensor.c +++ b/drivers/macintosh/windfarm_lm75_sensor.c @@ -176,7 +176,7 @@ static int wf_lm75_attach(struct i2c_adapter *adapter) for (dev = NULL; (dev = of_get_next_child(busnode, dev)) != NULL;) { const char *loc = - get_property(dev, "hwsensor-location", NULL); + of_get_property(dev, "hwsensor-location", NULL); u8 addr; /* We must re-match the adapter in order to properly check diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c index eae1189..eaa74af 100644 --- a/drivers/macintosh/windfarm_max6690_sensor.c +++ b/drivers/macintosh/windfarm_max6690_sensor.c @@ -134,7 +134,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter) if (!device_is_compatible(dev, "max6690")) continue; addr = pmac_i2c_get_dev_addr(dev); - loc = get_property(dev, "hwsensor-location", NULL); + loc = of_get_property(dev, "hwsensor-location", NULL); if (loc == NULL || addr == 0) continue; printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 31b750d..ff398ad 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c @@ -167,7 +167,7 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, if (fct == NULL) return NULL; fct->ctrl.ops = &smu_fan_ops; - l = get_property(node, "location", NULL); + l = of_get_property(node, "location", NULL); if (l == NULL) goto fail; @@ -224,17 +224,17 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, goto fail; /* Get min & max values*/ - v = get_property(node, "min-value", NULL); + v = of_get_property(node, "min-value", NULL); if (v == NULL) goto fail; fct->min = *v; - v = get_property(node, "max-value", NULL); + v = of_get_property(node, "max-value", NULL); if (v == NULL) goto fail; fct->max = *v; /* Get "reg" value */ - reg = get_property(node, "reg", NULL); + reg = of_get_property(node, "reg", NULL); if (reg == NULL) goto fail; fct->reg = *reg; diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c index 83f79de..9a6c2cf 100644 --- a/drivers/macintosh/windfarm_smu_sat.c +++ b/drivers/macintosh/windfarm_smu_sat.c @@ -241,7 +241,7 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) char *name; int vsens[2], isens[2]; - reg = get_property(dev, "reg", NULL); + reg = of_get_property(dev, "reg", NULL); if (reg == NULL) return; addr = *reg; @@ -268,9 +268,9 @@ static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) isens[0] = isens[1] = -1; child = NULL; while ((child = of_get_next_child(dev, child)) != NULL) { - reg = get_property(child, "reg", NULL); - type = get_property(child, "device_type", NULL); - loc = get_property(child, "location", NULL); + reg = of_get_property(child, "reg", NULL); + type = of_get_property(child, "device_type", NULL); + loc = of_get_property(child, "location", NULL); if (reg == NULL || loc == NULL) continue; diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index 01b4c50..9c567b9 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c @@ -204,8 +204,8 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node) ads = kmalloc(sizeof(struct smu_ad_sensor), GFP_KERNEL); if (ads == NULL) return NULL; - c = get_property(node, "device_type", NULL); - l = get_property(node, "location", NULL); + c = of_get_property(node, "device_type", NULL); + l = of_get_property(node, "location", NULL); if (c == NULL || l == NULL) goto fail; @@ -255,7 +255,7 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node) } else goto fail; - v = get_property(node, "reg", NULL); + v = of_get_property(node, "reg", NULL); if (v == NULL) goto fail; ads->reg = *v; diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 5554ada..e61e0ef 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -863,9 +863,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start) /* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */ bitmap->filemap_attr = kzalloc( - (((num_pages*4/8)+sizeof(unsigned long)-1) - /sizeof(unsigned long)) - *sizeof(unsigned long), + roundup( DIV_ROUND_UP(num_pages*4, 8), sizeof(unsigned long)), GFP_KERNEL); if (!bitmap->filemap_attr) goto out; diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c index 4c2471e..d812123 100644 --- a/drivers/md/dm-crypt.c +++ b/drivers/md/dm-crypt.c @@ -867,7 +867,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv) goto bad4; } - cc->bs = bioset_create(MIN_IOS, MIN_IOS, 4); + cc->bs = bioset_create(MIN_IOS, MIN_IOS); if (!cc->bs) { ti->error = "Cannot allocate crypt bioset"; goto bad_bs; diff --git a/drivers/md/dm-io.c b/drivers/md/dm-io.c index 4eb73d3..8bdc8a8 100644 --- a/drivers/md/dm-io.c +++ b/drivers/md/dm-io.c @@ -60,7 +60,7 @@ static int resize_pool(unsigned int new_ios) if (!_io_pool) return -ENOMEM; - _bios = bioset_create(16, 16, 4); + _bios = bioset_create(16, 16); if (!_bios) { mempool_destroy(_io_pool); _io_pool = NULL; diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 3668b17..11a98df 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1012,7 +1012,7 @@ static struct mapped_device *alloc_dev(int minor) if (!md->tio_pool) goto bad3; - md->bs = bioset_create(16, 16, 4); + md->bs = bioset_create(16, 16); if (!md->bs) goto bad_no_bioset; diff --git a/drivers/md/linear.c b/drivers/md/linear.c index c625ddb..d5ecd2d 100644 --- a/drivers/md/linear.c +++ b/drivers/md/linear.c @@ -188,7 +188,7 @@ static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks) for (i=0; i < cnt-1 ; i++) { sector_t sz = 0; int j; - for (j=i; i<cnt-1 && sz < min_spacing ; j++) + for (j = i; j < cnt - 1 && sz < min_spacing; j++) sz += conf->disks[j].size; if (sz >= min_spacing && sz < conf->hash_spacing) conf->hash_spacing = sz; diff --git a/drivers/md/md.c b/drivers/md/md.c index 05febfd..509171c 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -1296,27 +1296,17 @@ static struct super_type super_types[] = { .sync_super = super_1_sync, }, }; - -static mdk_rdev_t * match_dev_unit(mddev_t *mddev, mdk_rdev_t *dev) -{ - struct list_head *tmp; - mdk_rdev_t *rdev; - - ITERATE_RDEV(mddev,rdev,tmp) - if (rdev->bdev->bd_contains == dev->bdev->bd_contains) - return rdev; - - return NULL; -} static int match_mddev_units(mddev_t *mddev1, mddev_t *mddev2) { - struct list_head *tmp; - mdk_rdev_t *rdev; + struct list_head *tmp, *tmp2; + mdk_rdev_t *rdev, *rdev2; ITERATE_RDEV(mddev1,rdev,tmp) - if (match_dev_unit(mddev2, rdev)) - return 1; + ITERATE_RDEV(mddev2, rdev2, tmp2) + if (rdev->bdev->bd_contains == + rdev2->bdev->bd_contains) + return 1; return 0; } @@ -1325,10 +1315,10 @@ static LIST_HEAD(pending_raid_disks); static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) { - mdk_rdev_t *same_pdev; - char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; + char b[BDEVNAME_SIZE]; struct kobject *ko; char *s; + int err; if (rdev->mddev) { MD_BUG(); @@ -1342,14 +1332,6 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) else mddev->size = rdev->size; } - same_pdev = match_dev_unit(mddev, rdev); - if (same_pdev) - printk(KERN_WARNING - "%s: WARNING: %s appears to be on the same physical" - " disk as %s. True\n protection against single-disk" - " failure might be compromised.\n", - mdname(mddev), bdevname(rdev->bdev,b), - bdevname(same_pdev->bdev,b2)); /* Verify rdev->desc_nr is unique. * If it is -1, assign a free number, else @@ -1371,20 +1353,35 @@ static int bind_rdev_to_array(mdk_rdev_t * rdev, mddev_t * mddev) while ( (s=strchr(rdev->kobj.k_name, '/')) != NULL) *s = '!'; - list_add(&rdev->same_set, &mddev->disks); rdev->mddev = mddev; printk(KERN_INFO "md: bind<%s>\n", b); rdev->kobj.parent = &mddev->kobj; - kobject_add(&rdev->kobj); + if ((err = kobject_add(&rdev->kobj))) + goto fail; if (rdev->bdev->bd_part) ko = &rdev->bdev->bd_part->kobj; else ko = &rdev->bdev->bd_disk->kobj; - sysfs_create_link(&rdev->kobj, ko, "block"); + if ((err = sysfs_create_link(&rdev->kobj, ko, "block"))) { + kobject_del(&rdev->kobj); + goto fail; + } + list_add(&rdev->same_set, &mddev->disks); bd_claim_by_disk(rdev->bdev, rdev, mddev->gendisk); return 0; + + fail: + printk(KERN_WARNING "md: failed to register dev-%s for %s\n", + b, mdname(mddev)); + return err; +} + +static void delayed_delete(struct work_struct *ws) +{ + mdk_rdev_t *rdev = container_of(ws, mdk_rdev_t, del_work); + kobject_del(&rdev->kobj); } static void unbind_rdev_from_array(mdk_rdev_t * rdev) @@ -1399,7 +1396,12 @@ static void unbind_rdev_from_array(mdk_rdev_t * rdev) printk(KERN_INFO "md: unbind<%s>\n", bdevname(rdev->bdev,b)); rdev->mddev = NULL; sysfs_remove_link(&rdev->kobj, "block"); - kobject_del(&rdev->kobj); + + /* We need to delay this, otherwise we can deadlock when + * writing to 'remove' to "dev/state" + */ + INIT_WORK(&rdev->del_work, delayed_delete); + schedule_work(&rdev->del_work); } /* @@ -2985,7 +2987,9 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data) mddev->kobj.k_name = NULL; snprintf(mddev->kobj.name, KOBJ_NAME_LEN, "%s", "md"); mddev->kobj.ktype = &md_ktype; - kobject_register(&mddev->kobj); + if (kobject_register(&mddev->kobj)) + printk(KERN_WARNING "md: cannot register %s/md - name in use\n", + disk->disk_name); return NULL; } @@ -3109,6 +3113,36 @@ static int do_md_run(mddev_t * mddev) return -EINVAL; } + if (pers->sync_request) { + /* Warn if this is a potentially silly + * configuration. + */ + char b[BDEVNAME_SIZE], b2[BDEVNAME_SIZE]; + mdk_rdev_t *rdev2; + struct list_head *tmp2; + int warned = 0; + ITERATE_RDEV(mddev, rdev, tmp) { + ITERATE_RDEV(mddev, rdev2, tmp2) { + if (rdev < rdev2 && + rdev->bdev->bd_contains == + rdev2->bdev->bd_contains) { + printk(KERN_WARNING + "%s: WARNING: %s appears to be" + " on the same physical disk as" + " %s.\n", + mdname(mddev), + bdevname(rdev->bdev,b), + bdevname(rdev2->bdev,b2)); + warned = 1; + } + } + } + if (warned) + printk(KERN_WARNING + "True protection against single-disk" + " failure might be compromised.\n"); + } + mddev->recovery = 0; mddev->resync_max_sectors = mddev->size << 1; /* may be over-ridden by personality */ mddev->barriers_work = 1; @@ -3133,9 +3167,12 @@ static int do_md_run(mddev_t * mddev) bitmap_destroy(mddev); return err; } - if (mddev->pers->sync_request) - sysfs_create_group(&mddev->kobj, &md_redundancy_group); - else if (mddev->ro == 2) /* auto-readonly not meaningful */ + if (mddev->pers->sync_request) { + if (sysfs_create_group(&mddev->kobj, &md_redundancy_group)) + printk(KERN_WARNING + "md: cannot register extra attributes for %s\n", + mdname(mddev)); + } else if (mddev->ro == 2) /* auto-readonly not meaningful */ mddev->ro = 0; atomic_set(&mddev->writes_pending,0); @@ -3149,7 +3186,9 @@ static int do_md_run(mddev_t * mddev) if (rdev->raid_disk >= 0) { char nm[20]; sprintf(nm, "rd%d", rdev->raid_disk); - sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); + if (sysfs_create_link(&mddev->kobj, &rdev->kobj, nm)) + printk("md: cannot register %s for %s\n", + nm, mdname(mddev)); } set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); @@ -3311,6 +3350,10 @@ static int do_md_stop(mddev_t * mddev, int mode) set_disk_ro(disk, 0); blk_queue_make_request(mddev->queue, md_fail_request); mddev->pers->stop(mddev); + mddev->queue->merge_bvec_fn = NULL; + mddev->queue->unplug_fn = NULL; + mddev->queue->issue_flush_fn = NULL; + mddev->queue->backing_dev_info.congested_fn = NULL; if (mddev->pers->sync_request) sysfs_remove_group(&mddev->kobj, &md_redundancy_group); @@ -3357,6 +3400,9 @@ static int do_md_stop(mddev_t * mddev, int mode) sysfs_remove_link(&mddev->kobj, nm); } + /* make sure all delayed_delete calls have finished */ + flush_scheduled_work(); + export_array(mddev); mddev->array_size = 0; @@ -5343,6 +5389,48 @@ void md_do_sync(mddev_t *mddev) EXPORT_SYMBOL_GPL(md_do_sync); +static int remove_and_add_spares(mddev_t *mddev) +{ + mdk_rdev_t *rdev; + struct list_head *rtmp; + int spares = 0; + + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk >= 0 && + (test_bit(Faulty, &rdev->flags) || + ! test_bit(In_sync, &rdev->flags)) && + atomic_read(&rdev->nr_pending)==0) { + if (mddev->pers->hot_remove_disk( + mddev, rdev->raid_disk)==0) { + char nm[20]; + sprintf(nm,"rd%d", rdev->raid_disk); + sysfs_remove_link(&mddev->kobj, nm); + rdev->raid_disk = -1; + } + } + + if (mddev->degraded) { + ITERATE_RDEV(mddev,rdev,rtmp) + if (rdev->raid_disk < 0 + && !test_bit(Faulty, &rdev->flags)) { + rdev->recovery_offset = 0; + if (mddev->pers->hot_add_disk(mddev,rdev)) { + char nm[20]; + sprintf(nm, "rd%d", rdev->raid_disk); + if (sysfs_create_link(&mddev->kobj, + &rdev->kobj, nm)) + printk(KERN_WARNING + "md: cannot register " + "%s for %s\n", + nm, mdname(mddev)); + spares++; + md_new_event(mddev); + } else + break; + } + } + return spares; +} /* * This routine is regularly called by all per-raid-array threads to * deal with generic issues like resync and super-block update. @@ -5397,7 +5485,7 @@ void md_check_recovery(mddev_t *mddev) return; if (mddev_trylock(mddev)) { - int spares =0; + int spares = 0; spin_lock_irq(&mddev->write_lock); if (mddev->safemode && !atomic_read(&mddev->writes_pending) && @@ -5460,35 +5548,13 @@ void md_check_recovery(mddev_t *mddev) * Spare are also removed and re-added, to allow * the personality to fail the re-add. */ - ITERATE_RDEV(mddev,rdev,rtmp) - if (rdev->raid_disk >= 0 && - (test_bit(Faulty, &rdev->flags) || ! test_bit(In_sync, &rdev->flags)) && - atomic_read(&rdev->nr_pending)==0) { - if (mddev->pers->hot_remove_disk(mddev, rdev->raid_disk)==0) { - char nm[20]; - sprintf(nm,"rd%d", rdev->raid_disk); - sysfs_remove_link(&mddev->kobj, nm); - rdev->raid_disk = -1; - } - } - - if (mddev->degraded) { - ITERATE_RDEV(mddev,rdev,rtmp) - if (rdev->raid_disk < 0 - && !test_bit(Faulty, &rdev->flags)) { - rdev->recovery_offset = 0; - if (mddev->pers->hot_add_disk(mddev,rdev)) { - char nm[20]; - sprintf(nm, "rd%d", rdev->raid_disk); - sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); - spares++; - md_new_event(mddev); - } else - break; - } - } - if (spares) { + if (mddev->reshape_position != MaxSector) { + if (mddev->pers->check_reshape(mddev) != 0) + /* Cannot proceed */ + goto unlock; + set_bit(MD_RECOVERY_RESHAPE, &mddev->recovery); + } else if ((spares = remove_and_add_spares(mddev))) { clear_bit(MD_RECOVERY_SYNC, &mddev->recovery); clear_bit(MD_RECOVERY_CHECK, &mddev->recovery); } else if (mddev->recovery_cp < MaxSector) { diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index a9401c0..82249a6 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -429,7 +429,7 @@ static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev) if (dev < 0) dev += conf->raid_disks; } else { - while (sector > conf->stride) { + while (sector >= conf->stride) { sector -= conf->stride; if (dev < conf->near_copies) dev += conf->raid_disks - conf->near_copies; @@ -1801,6 +1801,7 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i for (k=0; k<conf->copies; k++) if (r10_bio->devs[k].devnum == i) break; + BUG_ON(k == conf->copies); bio = r10_bio->devs[1].bio; bio->bi_next = biolist; biolist = bio; @@ -2021,19 +2022,30 @@ static int run(mddev_t *mddev) if (!conf->tmppage) goto out_free_conf; + conf->mddev = mddev; + conf->raid_disks = mddev->raid_disks; conf->near_copies = nc; conf->far_copies = fc; conf->copies = nc*fc; conf->far_offset = fo; conf->chunk_mask = (sector_t)(mddev->chunk_size>>9)-1; conf->chunk_shift = ffz(~mddev->chunk_size) - 9; + size = mddev->size >> (conf->chunk_shift-1); + sector_div(size, fc); + size = size * conf->raid_disks; + sector_div(size, nc); + /* 'size' is now the number of chunks in the array */ + /* calculate "used chunks per device" in 'stride' */ + stride = size * conf->copies; + sector_div(stride, conf->raid_disks); + mddev->size = stride << (conf->chunk_shift-1); + if (fo) - conf->stride = 1 << conf->chunk_shift; - else { - stride = mddev->size >> (conf->chunk_shift-1); + stride = 1; + else sector_div(stride, fc); - conf->stride = stride << conf->chunk_shift; - } + conf->stride = stride << conf->chunk_shift; + conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc, r10bio_pool_free, conf); if (!conf->r10bio_pool) { @@ -2063,8 +2075,6 @@ static int run(mddev_t *mddev) disk->head_position = 0; } - conf->raid_disks = mddev->raid_disks; - conf->mddev = mddev; spin_lock_init(&conf->device_lock); INIT_LIST_HEAD(&conf->retry_list); @@ -2106,16 +2116,8 @@ static int run(mddev_t *mddev) /* * Ok, everything is just fine now */ - if (conf->far_offset) { - size = mddev->size >> (conf->chunk_shift-1); - size *= conf->raid_disks; - size <<= conf->chunk_shift; - sector_div(size, conf->far_copies); - } else - size = conf->stride * conf->raid_disks; - sector_div(size, conf->near_copies); - mddev->array_size = size/2; - mddev->resync_max_sectors = size; + mddev->array_size = size << (conf->chunk_shift-1); + mddev->resync_max_sectors = size << conf->chunk_shift; mddev->queue->unplug_fn = raid10_unplug; mddev->queue->issue_flush_fn = raid10_issue_flush; diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 11c3d7b..8d59914 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -1050,7 +1050,7 @@ static void compute_parity5(struct stripe_head *sh, int method) static void compute_parity6(struct stripe_head *sh, int method) { raid6_conf_t *conf = sh->raid_conf; - int i, pd_idx = sh->pd_idx, qd_idx, d0_idx, disks = conf->raid_disks, count; + int i, pd_idx = sh->pd_idx, qd_idx, d0_idx, disks = sh->disks, count; struct bio *chosen; /**** FIX THIS: This could be very bad if disks is close to 256 ****/ void *ptrs[disks]; @@ -1131,8 +1131,7 @@ static void compute_parity6(struct stripe_head *sh, int method) /* Compute one missing block */ static void compute_block_1(struct stripe_head *sh, int dd_idx, int nozero) { - raid6_conf_t *conf = sh->raid_conf; - int i, count, disks = conf->raid_disks; + int i, count, disks = sh->disks; void *ptr[MAX_XOR_BLOCKS], *p; int pd_idx = sh->pd_idx; int qd_idx = raid6_next_disk(pd_idx, disks); @@ -1170,8 +1169,7 @@ static void compute_block_1(struct stripe_head *sh, int dd_idx, int nozero) /* Compute two missing blocks */ static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2) { - raid6_conf_t *conf = sh->raid_conf; - int i, count, disks = conf->raid_disks; + int i, count, disks = sh->disks; int pd_idx = sh->pd_idx; int qd_idx = raid6_next_disk(pd_idx, disks); int d0_idx = raid6_next_disk(qd_idx, disks); @@ -1887,11 +1885,11 @@ static void handle_stripe5(struct stripe_head *sh) static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) { raid6_conf_t *conf = sh->raid_conf; - int disks = conf->raid_disks; + int disks = sh->disks; struct bio *return_bi= NULL; struct bio *bi; int i; - int syncing; + int syncing, expanding, expanded; int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0; int non_overwrite = 0; int failed_num[2] = {0, 0}; @@ -1909,6 +1907,8 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) clear_bit(STRIPE_DELAYED, &sh->state); syncing = test_bit(STRIPE_SYNCING, &sh->state); + expanding = test_bit(STRIPE_EXPAND_SOURCE, &sh->state); + expanded = test_bit(STRIPE_EXPAND_READY, &sh->state); /* Now to look around and see what can be done */ rcu_read_lock(); @@ -2114,13 +2114,15 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) * parity, or to satisfy requests * or to load a block that is being partially written. */ - if (to_read || non_overwrite || (to_write && failed) || (syncing && (uptodate < disks))) { + if (to_read || non_overwrite || (to_write && failed) || + (syncing && (uptodate < disks)) || expanding) { for (i=disks; i--;) { dev = &sh->dev[i]; if (!test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) && (dev->toread || (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) || syncing || + expanding || (failed >= 1 && (sh->dev[failed_num[0]].toread || to_write)) || (failed >= 2 && (sh->dev[failed_num[1]].toread || to_write)) ) @@ -2355,6 +2357,79 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) } } } + + if (expanded && test_bit(STRIPE_EXPANDING, &sh->state)) { + /* Need to write out all blocks after computing P&Q */ + sh->disks = conf->raid_disks; + sh->pd_idx = stripe_to_pdidx(sh->sector, conf, + conf->raid_disks); + compute_parity6(sh, RECONSTRUCT_WRITE); + for (i = conf->raid_disks ; i-- ; ) { + set_bit(R5_LOCKED, &sh->dev[i].flags); + locked++; + set_bit(R5_Wantwrite, &sh->dev[i].flags); + } + clear_bit(STRIPE_EXPANDING, &sh->state); + } else if (expanded) { + clear_bit(STRIPE_EXPAND_READY, &sh->state); + atomic_dec(&conf->reshape_stripes); + wake_up(&conf->wait_for_overlap); + md_done_sync(conf->mddev, STRIPE_SECTORS, 1); + } + + if (expanding && locked == 0) { + /* We have read all the blocks in this stripe and now we need to + * copy some of them into a target stripe for expand. + */ + clear_bit(STRIPE_EXPAND_SOURCE, &sh->state); + for (i = 0; i < sh->disks ; i++) + if (i != pd_idx && i != qd_idx) { + int dd_idx2, pd_idx2, j; + struct stripe_head *sh2; + + sector_t bn = compute_blocknr(sh, i); + sector_t s = raid5_compute_sector( + bn, conf->raid_disks, + conf->raid_disks - conf->max_degraded, + &dd_idx2, &pd_idx2, conf); + sh2 = get_active_stripe(conf, s, + conf->raid_disks, + pd_idx2, 1); + if (sh2 == NULL) + /* so for only the early blocks of + * this stripe have been requests. + * When later blocks get requests, we + * will try again + */ + continue; + if (!test_bit(STRIPE_EXPANDING, &sh2->state) || + test_bit(R5_Expanded, + &sh2->dev[dd_idx2].flags)) { + /* must have already done this block */ + release_stripe(sh2); + continue; + } + memcpy(page_address(sh2->dev[dd_idx2].page), + page_address(sh->dev[i].page), + STRIPE_SIZE); + set_bit(R5_Expanded, &sh2->dev[dd_idx2].flags); + set_bit(R5_UPTODATE, &sh2->dev[dd_idx2].flags); + for (j = 0 ; j < conf->raid_disks ; j++) + if (j != sh2->pd_idx && + j != raid6_next_disk(sh2->pd_idx, + sh2->disks) && + !test_bit(R5_Expanded, + &sh2->dev[j].flags)) + break; + if (j == conf->raid_disks) { + set_bit(STRIPE_EXPAND_READY, + &sh2->state); + set_bit(STRIPE_HANDLE, &sh2->state); + } + release_stripe(sh2); + } + } + spin_unlock(&sh->lock); while ((bi=return_bi)) { @@ -2395,7 +2470,7 @@ static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page) rcu_read_unlock(); if (rdev) { - if (syncing) + if (syncing || expanding || expanded) md_sync_acct(rdev->bdev, STRIPE_SECTORS); bi->bi_bdev = rdev->bdev; @@ -2915,8 +2990,9 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped struct stripe_head *sh; int pd_idx; sector_t first_sector, last_sector; - int raid_disks; - int data_disks; + int raid_disks = conf->previous_raid_disks; + int data_disks = raid_disks - conf->max_degraded; + int new_data_disks = conf->raid_disks - conf->max_degraded; int i; int dd_idx; sector_t writepos, safepos, gap; @@ -2925,7 +3001,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped conf->expand_progress != 0) { /* restarting in the middle, skip the initial sectors */ sector_nr = conf->expand_progress; - sector_div(sector_nr, conf->raid_disks-1); + sector_div(sector_nr, new_data_disks); *skipped = 1; return sector_nr; } @@ -2939,14 +3015,14 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped * to after where expand_lo old_maps to */ writepos = conf->expand_progress + - conf->chunk_size/512*(conf->raid_disks-1); - sector_div(writepos, conf->raid_disks-1); + conf->chunk_size/512*(new_data_disks); + sector_div(writepos, new_data_disks); safepos = conf->expand_lo; - sector_div(safepos, conf->previous_raid_disks-1); + sector_div(safepos, data_disks); gap = conf->expand_progress - conf->expand_lo; if (writepos >= safepos || - gap > (conf->raid_disks-1)*3000*2 /*3Meg*/) { + gap > (new_data_disks)*3000*2 /*3Meg*/) { /* Cannot proceed until we've updated the superblock... */ wait_event(conf->wait_for_overlap, atomic_read(&conf->reshape_stripes)==0); @@ -2976,6 +3052,9 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped sector_t s; if (j == sh->pd_idx) continue; + if (conf->level == 6 && + j == raid6_next_disk(sh->pd_idx, sh->disks)) + continue; s = compute_blocknr(sh, j); if (s < (mddev->array_size<<1)) { skipped = 1; @@ -2992,28 +3071,27 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped release_stripe(sh); } spin_lock_irq(&conf->device_lock); - conf->expand_progress = (sector_nr + i)*(conf->raid_disks-1); + conf->expand_progress = (sector_nr + i) * new_data_disks; spin_unlock_irq(&conf->device_lock); /* Ok, those stripe are ready. We can start scheduling * reads on the source stripes. * The source stripes are determined by mapping the first and last * block on the destination stripes. */ - raid_disks = conf->previous_raid_disks; - data_disks = raid_disks - 1; first_sector = - raid5_compute_sector(sector_nr*(conf->raid_disks-1), + raid5_compute_sector(sector_nr*(new_data_disks), raid_disks, data_disks, &dd_idx, &pd_idx, conf); last_sector = raid5_compute_sector((sector_nr+conf->chunk_size/512) - *(conf->raid_disks-1) -1, + *(new_data_disks) -1, raid_disks, data_disks, &dd_idx, &pd_idx, conf); if (last_sector >= (mddev->size<<1)) last_sector = (mddev->size<<1)-1; while (first_sector <= last_sector) { - pd_idx = stripe_to_pdidx(first_sector, conf, conf->previous_raid_disks); + pd_idx = stripe_to_pdidx(first_sector, conf, + conf->previous_raid_disks); sh = get_active_stripe(conf, first_sector, conf->previous_raid_disks, pd_idx, 0); set_bit(STRIPE_EXPAND_SOURCE, &sh->state); @@ -3348,35 +3426,44 @@ static int run(mddev_t *mddev) */ sector_t here_new, here_old; int old_disks; + int max_degraded = (mddev->level == 5 ? 1 : 2); if (mddev->new_level != mddev->level || mddev->new_layout != mddev->layout || mddev->new_chunk != mddev->chunk_size) { - printk(KERN_ERR "raid5: %s: unsupported reshape required - aborting.\n", + printk(KERN_ERR "raid5: %s: unsupported reshape " + "required - aborting.\n", mdname(mddev)); return -EINVAL; } if (mddev->delta_disks <= 0) { - printk(KERN_ERR "raid5: %s: unsupported reshape (reduce disks) required - aborting.\n", + printk(KERN_ERR "raid5: %s: unsupported reshape " + "(reduce disks) required - aborting.\n", mdname(mddev)); return -EINVAL; } old_disks = mddev->raid_disks - mddev->delta_disks; /* reshape_position must be on a new-stripe boundary, and one - * further up in new geometry must map after here in old geometry. + * further up in new geometry must map after here in old + * geometry. */ here_new = mddev->reshape_position; - if (sector_div(here_new, (mddev->chunk_size>>9)*(mddev->raid_disks-1))) { - printk(KERN_ERR "raid5: reshape_position not on a stripe boundary\n"); + if (sector_div(here_new, (mddev->chunk_size>>9)* + (mddev->raid_disks - max_degraded))) { + printk(KERN_ERR "raid5: reshape_position not " + "on a stripe boundary\n"); return -EINVAL; } /* here_new is the stripe we will write to */ here_old = mddev->reshape_position; - sector_div(here_old, (mddev->chunk_size>>9)*(old_disks-1)); - /* here_old is the first stripe that we might need to read from */ + sector_div(here_old, (mddev->chunk_size>>9)* + (old_disks-max_degraded)); + /* here_old is the first stripe that we might need to read + * from */ if (here_new >= here_old) { /* Reading from the same stripe as writing to - bad */ - printk(KERN_ERR "raid5: reshape_position too early for auto-recovery - aborting.\n"); + printk(KERN_ERR "raid5: reshape_position too early for " + "auto-recovery - aborting.\n"); return -EINVAL; } printk(KERN_INFO "raid5: reshape will continue\n"); @@ -3555,12 +3642,15 @@ static int run(mddev_t *mddev) } /* Ok, everything is just fine now */ - sysfs_create_group(&mddev->kobj, &raid5_attrs_group); + if (sysfs_create_group(&mddev->kobj, &raid5_attrs_group)) + printk(KERN_WARNING + "raid5: failed to create sysfs attributes for %s\n", + mdname(mddev)); mddev->queue->unplug_fn = raid5_unplug_device; mddev->queue->issue_flush_fn = raid5_issue_flush; - mddev->queue->backing_dev_info.congested_fn = raid5_congested; 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 - conf->max_degraded); @@ -3591,6 +3681,7 @@ static int stop(mddev_t *mddev) mddev->thread = NULL; shrink_stripes(conf); kfree(conf->stripe_hashtbl); + mddev->queue->backing_dev_info.congested_fn = NULL; blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/ sysfs_remove_group(&mddev->kobj, &raid5_attrs_group); kfree(conf->disks); @@ -3814,6 +3905,8 @@ static int raid5_check_reshape(mddev_t *mddev) if (err) return err; + if (mddev->degraded > conf->max_degraded) + return -EINVAL; /* looks like we might be able to manage this */ return 0; } @@ -3827,8 +3920,7 @@ static int raid5_start_reshape(mddev_t *mddev) int added_devices = 0; unsigned long flags; - if (mddev->degraded || - test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) + if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) return -EBUSY; ITERATE_RDEV(mddev, rdev, rtmp) @@ -3836,7 +3928,7 @@ static int raid5_start_reshape(mddev_t *mddev) !test_bit(Faulty, &rdev->flags)) spares++; - if (spares < mddev->delta_disks-1) + if (spares - mddev->degraded < mddev->delta_disks - conf->max_degraded) /* Not enough devices even to make a degraded array * of that size */ @@ -3862,7 +3954,12 @@ static int raid5_start_reshape(mddev_t *mddev) added_devices++; rdev->recovery_offset = 0; sprintf(nm, "rd%d", rdev->raid_disk); - sysfs_create_link(&mddev->kobj, &rdev->kobj, nm); + if (sysfs_create_link(&mddev->kobj, + &rdev->kobj, nm)) + printk(KERN_WARNING + "raid5: failed to create " + " link %s for %s\n", + nm, mdname(mddev)); } else break; } @@ -3899,7 +3996,8 @@ 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->raid_disks-1); + conf->mddev->array_size = conf->mddev->size * + (conf->raid_disks - conf->max_degraded); set_capacity(conf->mddev->gendisk, conf->mddev->array_size << 1); conf->mddev->changed = 1; @@ -3972,6 +4070,10 @@ static struct mdk_personality raid6_personality = .spare_active = raid5_spare_active, .sync_request = sync_request, .resize = raid5_resize, +#ifdef CONFIG_MD_RAID5_RESHAPE + .check_reshape = raid5_check_reshape, + .start_reshape = raid5_start_reshape, +#endif .quiesce = raid5_quiesce, }; static struct mdk_personality raid5_personality = @@ -4011,6 +4113,10 @@ static struct mdk_personality raid4_personality = .spare_active = raid5_spare_active, .sync_request = sync_request, .resize = raid5_resize, +#ifdef CONFIG_MD_RAID5_RESHAPE + .check_reshape = raid5_check_reshape, + .start_reshape = raid5_start_reshape, +#endif .quiesce = raid5_quiesce, }; diff --git a/drivers/md/raid6mmx.c b/drivers/md/raid6mmx.c index 359157a..6181a5a 100644 --- a/drivers/md/raid6mmx.c +++ b/drivers/md/raid6mmx.c @@ -30,14 +30,8 @@ const struct raid6_mmx_constants { static int raid6_have_mmx(void) { -#ifdef __KERNEL__ /* Not really "boot_cpu" but "all_cpus" */ return boot_cpu_has(X86_FEATURE_MMX); -#else - /* User space test code */ - u32 features = cpuid_features(); - return ( (features & (1<<23)) == (1<<23) ); -#endif } /* @@ -48,13 +42,12 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_mmx_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_mmx(&sa); + kernel_fpu_begin(); asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d)); asm volatile("pxor %mm5,%mm5"); /* Zero temp */ @@ -78,7 +71,7 @@ static void raid6_mmx1_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("pxor %mm4,%mm4"); } - raid6_after_mmx(&sa); + kernel_fpu_end(); } const struct raid6_calls raid6_mmxx1 = { @@ -96,13 +89,12 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_mmx_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_mmx(&sa); + kernel_fpu_begin(); asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d)); asm volatile("pxor %mm5,%mm5"); /* Zero temp */ @@ -137,7 +129,7 @@ static void raid6_mmx2_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("movq %%mm6,%0" : "=m" (q[d+8])); } - raid6_after_mmx(&sa); + kernel_fpu_end(); } const struct raid6_calls raid6_mmxx2 = { diff --git a/drivers/md/raid6sse1.c b/drivers/md/raid6sse1.c index f7e7859..f0a1ba8 100644 --- a/drivers/md/raid6sse1.c +++ b/drivers/md/raid6sse1.c @@ -33,16 +33,10 @@ extern const struct raid6_mmx_constants { static int raid6_have_sse1_or_mmxext(void) { -#ifdef __KERNEL__ /* Not really boot_cpu but "all_cpus" */ return boot_cpu_has(X86_FEATURE_MMX) && (boot_cpu_has(X86_FEATURE_XMM) || boot_cpu_has(X86_FEATURE_MMXEXT)); -#else - /* User space test code - this incorrectly breaks on some Athlons */ - u32 features = cpuid_features(); - return ( (features & (5<<23)) == (5<<23) ); -#endif } /* @@ -53,14 +47,12 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_mmx_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - /* This is really MMX code, not SSE */ - raid6_before_mmx(&sa); + kernel_fpu_begin(); asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d)); asm volatile("pxor %mm5,%mm5"); /* Zero temp */ @@ -94,8 +86,8 @@ static void raid6_sse11_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("movntq %%mm4,%0" : "=m" (q[d])); } - raid6_after_mmx(&sa); asm volatile("sfence" : : : "memory"); + kernel_fpu_end(); } const struct raid6_calls raid6_sse1x1 = { @@ -113,13 +105,12 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_mmx_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_mmx(&sa); + kernel_fpu_begin(); asm volatile("movq %0,%%mm0" : : "m" (raid6_mmx_constants.x1d)); asm volatile("pxor %mm5,%mm5"); /* Zero temp */ @@ -157,8 +148,8 @@ static void raid6_sse12_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("movntq %%mm6,%0" : "=m" (q[d+8])); } - raid6_after_mmx(&sa); asm volatile("sfence" : :: "memory"); + kernel_fpu_end(); } const struct raid6_calls raid6_sse1x2 = { diff --git a/drivers/md/raid6sse2.c b/drivers/md/raid6sse2.c index b3aa7fe..0f01976 100644 --- a/drivers/md/raid6sse2.c +++ b/drivers/md/raid6sse2.c @@ -30,17 +30,11 @@ static const struct raid6_sse_constants { static int raid6_have_sse2(void) { -#ifdef __KERNEL__ /* Not really boot_cpu but "all_cpus" */ return boot_cpu_has(X86_FEATURE_MMX) && boot_cpu_has(X86_FEATURE_FXSR) && boot_cpu_has(X86_FEATURE_XMM) && boot_cpu_has(X86_FEATURE_XMM2); -#else - /* User space test code */ - u32 features = cpuid_features(); - return ( (features & (15<<23)) == (15<<23) ); -#endif } /* @@ -51,13 +45,12 @@ static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_sse_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_sse2(&sa); + kernel_fpu_begin(); asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0])); asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */ @@ -93,8 +86,8 @@ static void raid6_sse21_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("pxor %xmm4,%xmm4"); } - raid6_after_sse2(&sa); asm volatile("sfence" : : : "memory"); + kernel_fpu_end(); } const struct raid6_calls raid6_sse2x1 = { @@ -112,13 +105,12 @@ static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_sse_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_sse2(&sa); + kernel_fpu_begin(); asm volatile("movdqa %0,%%xmm0" : : "m" (raid6_sse_constants.x1d[0])); asm volatile("pxor %xmm5,%xmm5"); /* Zero temp */ @@ -156,8 +148,8 @@ static void raid6_sse22_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("movntdq %%xmm6,%0" : "=m" (q[d+16])); } - raid6_after_sse2(&sa); asm volatile("sfence" : : : "memory"); + kernel_fpu_end(); } const struct raid6_calls raid6_sse2x2 = { @@ -179,13 +171,12 @@ static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs) u8 **dptr = (u8 **)ptrs; u8 *p, *q; int d, z, z0; - raid6_sse16_save_t sa; z0 = disks - 3; /* Highest data disk */ p = dptr[z0+1]; /* XOR parity */ q = dptr[z0+2]; /* RS syndrome */ - raid6_before_sse16(&sa); + kernel_fpu_begin(); asm volatile("movdqa %0,%%xmm0" :: "m" (raid6_sse_constants.x1d[0])); asm volatile("pxor %xmm2,%xmm2"); /* P[0] */ @@ -256,8 +247,9 @@ static void raid6_sse24_gen_syndrome(int disks, size_t bytes, void **ptrs) asm volatile("movntdq %%xmm14,%0" : "=m" (q[d+48])); asm volatile("pxor %xmm14,%xmm14"); } + asm volatile("sfence" : : : "memory"); - raid6_after_sse16(&sa); + kernel_fpu_end(); } const struct raid6_calls raid6_sse2x4 = { diff --git a/drivers/md/raid6x86.h b/drivers/md/raid6x86.h index 4cf2053..9111950 100644 --- a/drivers/md/raid6x86.h +++ b/drivers/md/raid6x86.h @@ -21,224 +21,40 @@ #if defined(__i386__) || defined(__x86_64__) -#ifdef __x86_64__ - -typedef struct { - unsigned int fsave[27]; - unsigned long cr0; -} raid6_mmx_save_t __attribute__((aligned(16))); - -/* N.B.: For SSE we only save %xmm0-%xmm7 even for x86-64, since - the code doesn't know about the additional x86-64 registers */ -typedef struct { - unsigned int sarea[8*4+2]; - unsigned long cr0; -} raid6_sse_save_t __attribute__((aligned(16))); - -/* This is for x86-64-specific code which uses all 16 XMM registers */ -typedef struct { - unsigned int sarea[16*4+2]; - unsigned long cr0; -} raid6_sse16_save_t __attribute__((aligned(16))); - -/* On x86-64 the stack *SHOULD* be 16-byte aligned, but currently this - is buggy in the kernel and it's only 8-byte aligned in places, so - we need to do this anyway. Sigh. */ -#define SAREA(x) ((unsigned int *)((((unsigned long)&(x)->sarea)+15) & ~15)) - -#else /* __i386__ */ - -typedef struct { - unsigned int fsave[27]; - unsigned long cr0; -} raid6_mmx_save_t; - -/* On i386, the stack is only 8-byte aligned, but SSE requires 16-byte - alignment. The +3 is so we have the slack space to manually align - a properly-sized area correctly. */ -typedef struct { - unsigned int sarea[8*4+3]; - unsigned long cr0; -} raid6_sse_save_t; - -/* Find the 16-byte aligned save area */ -#define SAREA(x) ((unsigned int *)((((unsigned long)&(x)->sarea)+15) & ~15)) - -#endif - #ifdef __KERNEL__ /* Real code */ -/* Note: %cr0 is 32 bits on i386 and 64 bits on x86-64 */ - -static inline unsigned long raid6_get_fpu(void) -{ - unsigned long cr0; - - preempt_disable(); - asm volatile("mov %%cr0,%0 ; clts" : "=r" (cr0)); - return cr0; -} - -static inline void raid6_put_fpu(unsigned long cr0) -{ - asm volatile("mov %0,%%cr0" : : "r" (cr0)); - preempt_enable(); -} +#include <asm/i387.h> #else /* Dummy code for user space testing */ -static inline unsigned long raid6_get_fpu(void) -{ - return 0xf00ba6; -} - -static inline void raid6_put_fpu(unsigned long cr0) -{ - (void)cr0; -} - -#endif - -static inline void raid6_before_mmx(raid6_mmx_save_t *s) -{ - s->cr0 = raid6_get_fpu(); - asm volatile("fsave %0 ; fwait" : "=m" (s->fsave[0])); -} - -static inline void raid6_after_mmx(raid6_mmx_save_t *s) -{ - asm volatile("frstor %0" : : "m" (s->fsave[0])); - raid6_put_fpu(s->cr0); -} - -static inline void raid6_before_sse(raid6_sse_save_t *s) -{ - unsigned int *rsa = SAREA(s); - - s->cr0 = raid6_get_fpu(); - - asm volatile("movaps %%xmm0,%0" : "=m" (rsa[0])); - asm volatile("movaps %%xmm1,%0" : "=m" (rsa[4])); - asm volatile("movaps %%xmm2,%0" : "=m" (rsa[8])); - asm volatile("movaps %%xmm3,%0" : "=m" (rsa[12])); - asm volatile("movaps %%xmm4,%0" : "=m" (rsa[16])); - asm volatile("movaps %%xmm5,%0" : "=m" (rsa[20])); - asm volatile("movaps %%xmm6,%0" : "=m" (rsa[24])); - asm volatile("movaps %%xmm7,%0" : "=m" (rsa[28])); -} - -static inline void raid6_after_sse(raid6_sse_save_t *s) -{ - unsigned int *rsa = SAREA(s); - - asm volatile("movaps %0,%%xmm0" : : "m" (rsa[0])); - asm volatile("movaps %0,%%xmm1" : : "m" (rsa[4])); - asm volatile("movaps %0,%%xmm2" : : "m" (rsa[8])); - asm volatile("movaps %0,%%xmm3" : : "m" (rsa[12])); - asm volatile("movaps %0,%%xmm4" : : "m" (rsa[16])); - asm volatile("movaps %0,%%xmm5" : : "m" (rsa[20])); - asm volatile("movaps %0,%%xmm6" : : "m" (rsa[24])); - asm volatile("movaps %0,%%xmm7" : : "m" (rsa[28])); - - raid6_put_fpu(s->cr0); -} - -static inline void raid6_before_sse2(raid6_sse_save_t *s) +static inline void kernel_fpu_begin(void) { - unsigned int *rsa = SAREA(s); - - s->cr0 = raid6_get_fpu(); - - asm volatile("movdqa %%xmm0,%0" : "=m" (rsa[0])); - asm volatile("movdqa %%xmm1,%0" : "=m" (rsa[4])); - asm volatile("movdqa %%xmm2,%0" : "=m" (rsa[8])); - asm volatile("movdqa %%xmm3,%0" : "=m" (rsa[12])); - asm volatile("movdqa %%xmm4,%0" : "=m" (rsa[16])); - asm volatile("movdqa %%xmm5,%0" : "=m" (rsa[20])); - asm volatile("movdqa %%xmm6,%0" : "=m" (rsa[24])); - asm volatile("movdqa %%xmm7,%0" : "=m" (rsa[28])); } -static inline void raid6_after_sse2(raid6_sse_save_t *s) +static inline void kernel_fpu_end(void) { - unsigned int *rsa = SAREA(s); - - asm volatile("movdqa %0,%%xmm0" : : "m" (rsa[0])); - asm volatile("movdqa %0,%%xmm1" : : "m" (rsa[4])); - asm volatile("movdqa %0,%%xmm2" : : "m" (rsa[8])); - asm volatile("movdqa %0,%%xmm3" : : "m" (rsa[12])); - asm volatile("movdqa %0,%%xmm4" : : "m" (rsa[16])); - asm volatile("movdqa %0,%%xmm5" : : "m" (rsa[20])); - asm volatile("movdqa %0,%%xmm6" : : "m" (rsa[24])); - asm volatile("movdqa %0,%%xmm7" : : "m" (rsa[28])); - - raid6_put_fpu(s->cr0); } -#ifdef __x86_64__ - -static inline void raid6_before_sse16(raid6_sse16_save_t *s) -{ - unsigned int *rsa = SAREA(s); - - s->cr0 = raid6_get_fpu(); +#define X86_FEATURE_MMX (0*32+23) /* Multimedia Extensions */ +#define X86_FEATURE_FXSR (0*32+24) /* FXSAVE and FXRSTOR instructions + * (fast save and restore) */ +#define X86_FEATURE_XMM (0*32+25) /* Streaming SIMD Extensions */ +#define X86_FEATURE_XMM2 (0*32+26) /* Streaming SIMD Extensions-2 */ +#define X86_FEATURE_MMXEXT (1*32+22) /* AMD MMX extensions */ - asm volatile("movdqa %%xmm0,%0" : "=m" (rsa[0])); - asm volatile("movdqa %%xmm1,%0" : "=m" (rsa[4])); - asm volatile("movdqa %%xmm2,%0" : "=m" (rsa[8])); - asm volatile("movdqa %%xmm3,%0" : "=m" (rsa[12])); - asm volatile("movdqa %%xmm4,%0" : "=m" (rsa[16])); - asm volatile("movdqa %%xmm5,%0" : "=m" (rsa[20])); - asm volatile("movdqa %%xmm6,%0" : "=m" (rsa[24])); - asm volatile("movdqa %%xmm7,%0" : "=m" (rsa[28])); - asm volatile("movdqa %%xmm8,%0" : "=m" (rsa[32])); - asm volatile("movdqa %%xmm9,%0" : "=m" (rsa[36])); - asm volatile("movdqa %%xmm10,%0" : "=m" (rsa[40])); - asm volatile("movdqa %%xmm11,%0" : "=m" (rsa[44])); - asm volatile("movdqa %%xmm12,%0" : "=m" (rsa[48])); - asm volatile("movdqa %%xmm13,%0" : "=m" (rsa[52])); - asm volatile("movdqa %%xmm14,%0" : "=m" (rsa[56])); - asm volatile("movdqa %%xmm15,%0" : "=m" (rsa[60])); -} - -static inline void raid6_after_sse16(raid6_sse16_save_t *s) +/* Should work well enough on modern CPUs for testing */ +static inline int boot_cpu_has(int flag) { - unsigned int *rsa = SAREA(s); + u32 eax = (flag >> 5) ? 0x80000001 : 1; + u32 edx; - asm volatile("movdqa %0,%%xmm0" : : "m" (rsa[0])); - asm volatile("movdqa %0,%%xmm1" : : "m" (rsa[4])); - asm volatile("movdqa %0,%%xmm2" : : "m" (rsa[8])); - asm volatile("movdqa %0,%%xmm3" : : "m" (rsa[12])); - asm volatile("movdqa %0,%%xmm4" : : "m" (rsa[16])); - asm volatile("movdqa %0,%%xmm5" : : "m" (rsa[20])); - asm volatile("movdqa %0,%%xmm6" : : "m" (rsa[24])); - asm volatile("movdqa %0,%%xmm7" : : "m" (rsa[28])); - asm volatile("movdqa %0,%%xmm8" : : "m" (rsa[32])); - asm volatile("movdqa %0,%%xmm9" : : "m" (rsa[36])); - asm volatile("movdqa %0,%%xmm10" : : "m" (rsa[40])); - asm volatile("movdqa %0,%%xmm11" : : "m" (rsa[44])); - asm volatile("movdqa %0,%%xmm12" : : "m" (rsa[48])); - asm volatile("movdqa %0,%%xmm13" : : "m" (rsa[52])); - asm volatile("movdqa %0,%%xmm14" : : "m" (rsa[56])); - asm volatile("movdqa %0,%%xmm15" : : "m" (rsa[60])); + asm volatile("cpuid" + : "+a" (eax), "=d" (edx) + : : "ecx", "ebx"); - raid6_put_fpu(s->cr0); + return (edx >> (flag & 31)) & 1; } -#endif /* __x86_64__ */ - -/* User space test hack */ -#ifndef __KERNEL__ -static inline int cpuid_features(void) -{ - u32 eax = 1; - u32 ebx, ecx, edx; - - asm volatile("cpuid" : - "+a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)); - - return edx; -} #endif /* ndef __KERNEL__ */ #endif diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 87410db..91d25798 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -70,6 +70,7 @@ config VIDEO_TUNER depends on I2C config VIDEO_BUF + depends on PCI tristate config VIDEO_BUF_DVB diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig index f33e5d9..c120114 100644 --- a/drivers/media/common/Kconfig +++ b/drivers/media/common/Kconfig @@ -5,8 +5,4 @@ config VIDEO_SAA7146 config VIDEO_SAA7146_VV tristate select VIDEO_BUF - select VIDEO_VIDEOBUF select VIDEO_SAA7146 - -config VIDEO_VIDEOBUF - tristate diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index 9a8dd87..fcb1941 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -256,6 +256,114 @@ int ir_decode_biphase(u32 *samples, int count, int low, int high) return value; } +/* RC5 decoding stuff, moved from bttv-input.c to share it with + * saa7134 */ + +/* decode raw bit pattern to RC5 code */ +u32 ir_rc5_decode(unsigned int code) +{ + unsigned int org_code = code; + unsigned int pair; + unsigned int rc5 = 0; + int i; + + for (i = 0; i < 14; ++i) { + pair = code & 0x3; + code >>= 2; + + rc5 <<= 1; + switch (pair) { + case 0: + case 2: + break; + case 1: + rc5 |= 1; + break; + case 3: + dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); + return 0; + } + } + dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + "instr=%x\n", rc5, org_code, RC5_START(rc5), + RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); + return rc5; +} + +void ir_rc5_timer_end(unsigned long data) +{ + struct card_ir *ir = (struct card_ir *)data; + struct timeval tv; + unsigned long current_jiffies, timeout; + u32 gap; + u32 rc5 = 0; + + /* get time */ + current_jiffies = jiffies; + do_gettimeofday(&tv); + + /* avoid overflow with gap >1s */ + if (tv.tv_sec - ir->base_time.tv_sec > 1) { + gap = 200000; + } else { + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + } + + /* signal we're ready to start a new code */ + ir->active = 0; + + /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ + if (gap < 28000) { + dprintk(1, "ir-common: spurious timer_end\n"); + return; + } + + if (ir->last_bit < 20) { + /* ignore spurious codes (caused by light/other remotes) */ + dprintk(1, "ir-common: short code: %x\n", ir->code); + } else { + ir->code = (ir->code << ir->shift_by) | 1; + rc5 = ir_rc5_decode(ir->code); + + /* two start bits? */ + if (RC5_START(rc5) != ir->start) { + dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); + + /* right address? */ + } else if (RC5_ADDR(rc5) == ir->addr) { + u32 toggle = RC5_TOGGLE(rc5); + u32 instr = RC5_INSTR(rc5); + + /* Good code, decide if repeat/repress */ + if (toggle != RC5_TOGGLE(ir->last_rc5) || + instr != RC5_INSTR(ir->last_rc5)) { + dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, + toggle); + ir_input_nokey(ir->dev, &ir->ir); + ir_input_keydown(ir->dev, &ir->ir, instr, + instr); + } + + /* Set/reset key-up timer */ + timeout = current_jiffies + (500 + ir->rc5_key_timeout + * HZ) / 1000; + mod_timer(&ir->timer_keyup, timeout); + + /* Save code for repeat test */ + ir->last_rc5 = rc5; + } + } +} + +void ir_rc5_timer_keyup(unsigned long data) +{ + struct card_ir *ir = (struct card_ir *)data; + + dprintk(1, "ir-common: key released\n"); + ir_input_nokey(ir->dev, &ir->ir); +} + EXPORT_SYMBOL_GPL(ir_input_init); EXPORT_SYMBOL_GPL(ir_input_nokey); EXPORT_SYMBOL_GPL(ir_input_keydown); @@ -265,6 +373,10 @@ EXPORT_SYMBOL_GPL(ir_dump_samples); EXPORT_SYMBOL_GPL(ir_decode_biphase); EXPORT_SYMBOL_GPL(ir_decode_pulsedistance); +EXPORT_SYMBOL_GPL(ir_rc5_decode); +EXPORT_SYMBOL_GPL(ir_rc5_timer_end); +EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup); + /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 0e948a5..cbd1184 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -667,7 +667,7 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = { [ 0x1f ] = KEY_L, [ 0x2b ] = KEY_I, - [ 0x2d ] = KEY_ZOOM, + [ 0x2d ] = KEY_SCREEN, [ 0x1e ] = KEY_ZOOM, [ 0x1b ] = KEY_VOLUMEUP, [ 0x0f ] = KEY_VOLUMEDOWN, @@ -682,12 +682,12 @@ IR_KEYTAB_TYPE ir_codes_pinnacle_grey[IR_KEYTAB_SIZE] = { [ 0x3f ] = KEY_UP, [ 0x3e ] = KEY_DOWN, - [ 0x1a ] = KEY_PAUSE, + [ 0x1a ] = KEY_ENTER, [ 0x1d ] = KEY_MENU, - [ 0x19 ] = KEY_PLAY, - [ 0x16 ] = KEY_REWIND, - [ 0x13 ] = KEY_FORWARD, + [ 0x19 ] = KEY_AGAIN, + [ 0x16 ] = KEY_PREVIOUSSONG, + [ 0x13 ] = KEY_NEXTSONG, [ 0x15 ] = KEY_PAUSE, [ 0x0e ] = KEY_REWIND, [ 0x0d ] = KEY_PLAY, @@ -1606,3 +1606,180 @@ IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_budget_ci_old); + +/* + * Marc Fargas <telenieko@telenieko.com> + * this is the remote control that comes with the asus p7131 + * which has a label saying is "Model PC-39" + */ +IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE] = { + /* Keys 0 to 9 */ + [ 0x15 ] = KEY_0, + [ 0x29 ] = KEY_1, + [ 0x2d ] = KEY_2, + [ 0x2b ] = KEY_3, + [ 0x09 ] = KEY_4, + [ 0x0d ] = KEY_5, + [ 0x0b ] = KEY_6, + [ 0x31 ] = KEY_7, + [ 0x35 ] = KEY_8, + [ 0x33 ] = KEY_9, + + [ 0x3e ] = KEY_RADIO, /* radio */ + [ 0x03 ] = KEY_MENU, /* dvd/menu */ + [ 0x2a ] = KEY_VOLUMEUP, + [ 0x19 ] = KEY_VOLUMEDOWN, + [ 0x37 ] = KEY_UP, + [ 0x3b ] = KEY_DOWN, + [ 0x27 ] = KEY_LEFT, + [ 0x2f ] = KEY_RIGHT, + [ 0x25 ] = KEY_VIDEO, /* video */ + [ 0x39 ] = KEY_AUDIO, /* music */ + + [ 0x21 ] = KEY_TV, /* tv */ + [ 0x1d ] = KEY_EXIT, /* back */ + [ 0x0a ] = KEY_CHANNELUP, /* channel / program + */ + [ 0x1b ] = KEY_CHANNELDOWN, /* channel / program - */ + [ 0x1a ] = KEY_ENTER, /* enter */ + + [ 0x06 ] = KEY_PAUSE, /* play/pause */ + [ 0x1e ] = KEY_PREVIOUS, /* rew */ + [ 0x26 ] = KEY_NEXT, /* forward */ + [ 0x0e ] = KEY_REWIND, /* backward << */ + [ 0x3a ] = KEY_FASTFORWARD, /* forward >> */ + [ 0x36 ] = KEY_STOP, + [ 0x2e ] = KEY_RECORD, /* recording */ + [ 0x16 ] = KEY_POWER, /* the button that reads "close" */ + + [ 0x11 ] = KEY_ZOOM, /* full screen */ + [ 0x13 ] = KEY_MACRO, /* recall */ + [ 0x23 ] = KEY_HOME, /* home */ + [ 0x05 ] = KEY_PVR, /* picture */ + [ 0x3d ] = KEY_MUTE, /* mute */ + [ 0x01 ] = KEY_DVD, /* dvd */ +}; + +EXPORT_SYMBOL_GPL(ir_codes_asus_pc39); + + +/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons + Juan Pablo Sormani <sorman@gmail.com> */ +IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = { + + /* Power button does nothing, neither in Windows app, + although it sends data (used for BIOS wakeup?) */ + [ 0x0d ] = KEY_MUTE, + + [ 0x1e ] = KEY_TV, + [ 0x00 ] = KEY_VIDEO, + [ 0x01 ] = KEY_AUDIO, /* music */ + [ 0x02 ] = KEY_MHP, /* picture */ + + [ 0x1f ] = KEY_1, + [ 0x03 ] = KEY_2, + [ 0x04 ] = KEY_3, + [ 0x05 ] = KEY_4, + [ 0x1c ] = KEY_5, + [ 0x06 ] = KEY_6, + [ 0x07 ] = KEY_7, + [ 0x08 ] = KEY_8, + [ 0x1d ] = KEY_9, + [ 0x0a ] = KEY_0, + + [ 0x09 ] = KEY_LIST, /* -/-- */ + [ 0x0b ] = KEY_LAST, /* recall */ + + [ 0x14 ] = KEY_HOME, /* win start menu */ + [ 0x15 ] = KEY_EXIT, /* exit */ + [ 0x16 ] = KEY_UP, + [ 0x12 ] = KEY_DOWN, + [ 0x0c ] = KEY_RIGHT, + [ 0x17 ] = KEY_LEFT, + + [ 0x18 ] = KEY_ENTER, /* OK */ + + [ 0x0e ] = KEY_ESC, + [ 0x13 ] = KEY_D, /* desktop */ + [ 0x11 ] = KEY_TAB, + [ 0x19 ] = KEY_SWITCHVIDEOMODE, /* switch */ + + [ 0x1a ] = KEY_MENU, + [ 0x1b ] = KEY_ZOOM, /* fullscreen */ + [ 0x44 ] = KEY_TIME, /* time shift */ + [ 0x40 ] = KEY_MODE, /* source */ + + [ 0x5a ] = KEY_RECORD, + [ 0x42 ] = KEY_PLAY, /* play/pause */ + [ 0x45 ] = KEY_STOP, + [ 0x43 ] = KEY_CAMERA, /* camera icon */ + + [ 0x48 ] = KEY_REWIND, + [ 0x4a ] = KEY_FASTFORWARD, + [ 0x49 ] = KEY_PREVIOUS, + [ 0x4b ] = KEY_NEXT, + + [ 0x4c ] = KEY_FAVORITES, /* tv wall */ + [ 0x4d ] = KEY_SOUND, /* DVD sound */ + [ 0x4e ] = KEY_LANGUAGE, /* DVD lang */ + [ 0x4f ] = KEY_TEXT, /* DVD text */ + + [ 0x50 ] = KEY_SLEEP, /* shutdown */ + [ 0x51 ] = KEY_MODE, /* stereo > main */ + [ 0x52 ] = KEY_SELECT, /* stereo > sap */ + [ 0x53 ] = KEY_PROG1, /* teletext */ + + + [ 0x59 ] = KEY_RED, /* AP1 */ + [ 0x41 ] = KEY_GREEN, /* AP2 */ + [ 0x47 ] = KEY_YELLOW, /* AP3 */ + [ 0x57 ] = KEY_BLUE, /* AP4 */ + + +}; + +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); + +/* for the Technotrend 1500 bundled remotes (grey and black): */ +IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = { + [ 0x01 ] = KEY_POWER, + [ 0x02 ] = KEY_SHUFFLE, /* ? double-arrow key */ + [ 0x03 ] = KEY_1, + [ 0x04 ] = KEY_2, + [ 0x05 ] = KEY_3, + [ 0x06 ] = KEY_4, + [ 0x07 ] = KEY_5, + [ 0x08 ] = KEY_6, + [ 0x09 ] = KEY_7, + [ 0x0a ] = KEY_8, + [ 0x0b ] = KEY_9, + [ 0x0c ] = KEY_0, + [ 0x0d ] = KEY_UP, + [ 0x0e ] = KEY_LEFT, + [ 0x0f ] = KEY_OK, + [ 0x10 ] = KEY_RIGHT, + [ 0x11 ] = KEY_DOWN, + [ 0x12 ] = KEY_INFO, + [ 0x13 ] = KEY_EXIT, + [ 0x14 ] = KEY_RED, + [ 0x15 ] = KEY_GREEN, + [ 0x16 ] = KEY_YELLOW, + [ 0x17 ] = KEY_BLUE, + [ 0x18 ] = KEY_MUTE, + [ 0x19 ] = KEY_TEXT, + [ 0x1a ] = KEY_MODE, /* ? TV/Radio */ + [ 0x21 ] = KEY_OPTION, + [ 0x22 ] = KEY_EPG, + [ 0x23 ] = KEY_CHANNELUP, + [ 0x24 ] = KEY_CHANNELDOWN, + [ 0x25 ] = KEY_VOLUMEUP, + [ 0x26 ] = KEY_VOLUMEDOWN, + [ 0x27 ] = KEY_SETUP, + [ 0x3a ] = KEY_RECORD, /* these keys are only in the black remote */ + [ 0x3b ] = KEY_PLAY, + [ 0x3c ] = KEY_STOP, + [ 0x3d ] = KEY_REWIND, + [ 0x3e ] = KEY_PAUSE, + [ 0x3f ] = KEY_FORWARD, +}; + +EXPORT_SYMBOL_GPL(ir_codes_tt_1500); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index b8dcfa1..c18a5da 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -508,7 +508,7 @@ int saa7146_vv_release(struct saa7146_dev* dev) DEB_EE(("dev:%p\n",dev)); - pci_free_consistent(dev->pci, SAA7146_RPS_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); kfree(vv); dev->vv_data = NULL; dev->vv_callback = NULL; diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 7e0cedc5..e3d04a4 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1428,6 +1428,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data; struct saa7146_vv *vv = dev->vv_data; + struct videobuf_queue *q = &fh->video_q; int err; if (IS_CAPTURE_ACTIVE(fh) != 0) { @@ -1436,6 +1437,11 @@ static void video_close(struct saa7146_dev *dev, struct file *file) err = saa7146_stop_preview(fh); } + // release all capture buffers + mutex_lock(&q->lock); + videobuf_read_stop(q); + mutex_unlock(&q->lock); + /* hmm, why is this function declared void? */ /* return err */ } diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig index 7987595..a0dcd59 100644 --- a/drivers/media/dvb/b2c2/Kconfig +++ b/drivers/media/dvb/b2c2/Kconfig @@ -9,7 +9,6 @@ config DVB_B2C2_FLEXCOP select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE help Support for the digital TV receiver chip made by B2C2 Inc. included in Technisats PCI cards and USB boxes. diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c index c2b35e3..b02c2fd 100644 --- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c +++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c @@ -14,7 +14,6 @@ #include "stv0297.h" #include "mt312.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "dvb-pll.h" /* lnb control */ @@ -385,9 +384,9 @@ static int alps_tdee4_stv0297_tuner_set_params(struct dvb_frontend* fe, else buf[3] = 0x88; if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.i2c_gate_ctrl(fe, 0); deb_tuner("tuner buffer for %d Hz: %x %x %x %x\n",fep->frequency, buf[0],buf[1],buf[2],buf[3]); - ret = fc->i2c_request(fc,FC_WRITE,FC_I2C_PORT_TUNER,0x61,buf[0],&buf[1],3); + ret = fc->i2c_request(fc, FC_WRITE, FC_I2C_PORT_TUNER, 0x61, buf[0], &buf[1], 3); deb_tuner("tuner write returned: %d\n",ret); return 0; @@ -398,91 +397,71 @@ static u8 alps_tdee4_stv0297_inittab[] = { 0x80, 0x00, 0x81, 0x01, 0x81, 0x00, - 0x00, 0x09, - 0x01, 0x69, + 0x00, 0x48, + 0x01, 0x58, 0x03, 0x00, 0x04, 0x00, 0x07, 0x00, 0x08, 0x00, - 0x20, 0x00, - 0x21, 0x40, - 0x22, 0x00, - 0x23, 0x00, - 0x24, 0x40, - 0x25, 0x88, 0x30, 0xff, - 0x31, 0x00, + 0x31, 0x9d, 0x32, 0xff, 0x33, 0x00, - 0x34, 0x50, - 0x35, 0x7f, - 0x36, 0x00, - 0x37, 0x20, - 0x38, 0x00, - 0x40, 0x1c, - 0x41, 0xff, - 0x42, 0x29, + 0x34, 0x29, + 0x35, 0x55, + 0x36, 0x80, + 0x37, 0x6e, + 0x38, 0x9c, + 0x40, 0x1a, + 0x41, 0xfe, + 0x42, 0x33, 0x43, 0x00, 0x44, 0xff, 0x45, 0x00, 0x46, 0x00, 0x49, 0x04, - 0x4a, 0x00, + 0x4a, 0x51, 0x4b, 0xf8, 0x52, 0x30, - 0x55, 0xae, - 0x56, 0x47, - 0x57, 0xe1, - 0x58, 0x3a, - 0x5a, 0x1e, - 0x5b, 0x34, - 0x60, 0x00, - 0x63, 0x00, - 0x64, 0x00, - 0x65, 0x00, - 0x66, 0x00, - 0x67, 0x00, - 0x68, 0x00, - 0x69, 0x00, - 0x6a, 0x02, - 0x6b, 0x00, + 0x53, 0x06, + 0x59, 0x06, + 0x5a, 0x5e, + 0x5b, 0x04, + 0x61, 0x49, + 0x62, 0x0a, 0x70, 0xff, - 0x71, 0x00, + 0x71, 0x04, 0x72, 0x00, 0x73, 0x00, 0x74, 0x0c, - 0x80, 0x00, + 0x80, 0x20, 0x81, 0x00, - 0x82, 0x00, + 0x82, 0x30, 0x83, 0x00, 0x84, 0x04, - 0x85, 0x80, - 0x86, 0x24, - 0x87, 0x78, - 0x88, 0x10, + 0x85, 0x22, + 0x86, 0x08, + 0x87, 0x1b, + 0x88, 0x00, 0x89, 0x00, - 0x90, 0x01, - 0x91, 0x01, - 0xa0, 0x04, + 0x90, 0x00, + 0x91, 0x04, + 0xa0, 0x86, 0xa1, 0x00, 0xa2, 0x00, 0xb0, 0x91, 0xb1, 0x0b, - 0xc0, 0x53, - 0xc1, 0x70, + 0xc0, 0x5b, + 0xc1, 0x10, 0xc2, 0x12, - 0xd0, 0x00, + 0xd0, 0x02, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, - 0xd4, 0x00, + 0xd4, 0x02, 0xd5, 0x00, 0xde, 0x00, - 0xdf, 0x00, - 0x61, 0x49, - 0x62, 0x0b, - 0x53, 0x08, - 0x59, 0x08, + 0xdf, 0x01, 0xff, 0xff, }; @@ -527,7 +506,7 @@ int flexcop_frontend_init(struct flexcop_device *fc) /* try the air atsc 3nd generation (lgdt3303) */ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) { fc->dev_type = FC_AIR_ATSC3; - dvb_attach(lgh06xf_attach, fc->fe, &fc->i2c_adap); + dvb_attach(dvb_pll_attach, fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_lg_tdvs_h06xf); info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address); } else /* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */ diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c index 6e16680..01af4d2 100644 --- a/drivers/media/dvb/b2c2/flexcop-pci.c +++ b/drivers/media/dvb/b2c2/flexcop-pci.c @@ -127,10 +127,11 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) { struct flexcop_pci *fc_pci = dev_id; struct flexcop_device *fc = fc_pci->fc_dev; + unsigned long flags; flexcop_ibi_value v; irqreturn_t ret = IRQ_HANDLED; - spin_lock_irq(&fc_pci->irq_lock); + spin_lock_irqsave(&fc_pci->irq_lock,flags); v = fc->read_ibi_reg(fc,irq_20c); @@ -194,7 +195,7 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id) ret = IRQ_NONE; } - spin_unlock_irq(&fc_pci->irq_lock); + spin_unlock_irqrestore(&fc_pci->irq_lock,flags); return ret; } @@ -293,12 +294,12 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci) } pci_set_drvdata(fc_pci->pdev, fc_pci); - + spin_lock_init(&fc_pci->irq_lock); if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr, IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0) goto err_pci_iounmap; - spin_lock_init(&fc_pci->irq_lock); + fc_pci->init_state |= FC_PCI_INIT; return ret; diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig index dd66b60..cfd6fb7 100644 --- a/drivers/media/dvb/bt8xx/Kconfig +++ b/drivers/media/dvb/bt8xx/Kconfig @@ -7,7 +7,7 @@ config DVB_BT8XX select DVB_CX24110 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE + select DVB_PLL select DVB_ZL10353 if !DVB_FE_CUSTOMISE select FW_LOADER help diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 329a51c..df72b4b 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -390,11 +390,10 @@ static struct cards card_list[] __devinitdata = { { 0xfc00270f, BTTV_BOARD_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" }, { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" }, { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" }, + { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x20007063, BTTV_BOARD_PC_HDTV, "pcHDTV HD-2000 TV" }, - { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" }, - - { 0, -1, NULL } + { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini" } }; diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index 9f72b70..0393a3d 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -1161,7 +1161,7 @@ static int dst_get_device_id(struct dst_state *state) } } - if (i >= sizeof (dst_tlist) / sizeof (dst_tlist [0])) { + if (i >= ARRAY_SIZE(dst_tlist)) { dprintk(verbose, DST_ERROR, 1, "Unable to recognize %s or %s", &state->rxbuffer[0], &state->rxbuffer[1]); dprintk(verbose, DST_ERROR, 1, "please email linux-dvb@linuxtv.org with this type in"); use_dst_type = DST_TYPE_IS_SAT; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 3e35931..4f1c09b 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -23,6 +23,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/kernel.h> #include <linux/device.h> #include <linux/delay.h> #include <linux/slab.h> @@ -213,7 +214,7 @@ static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend freq = 2150000; /* satellite IF is 950..2150MHz */ /* decide which VCO to use for the input frequency */ - for(i=1;(i<sizeof(osci)/sizeof(osci[0]))&&(osci[i]<freq);i++); + for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++); printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq); band=bandsel[i]; /* the gain values must be set by SetSymbolrate */ @@ -609,7 +610,8 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type) lgdt330x_reset(card); card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter); if (card->fe != NULL) { - dvb_attach(lgh06xf_attach, card->fe, card->i2c_adapter); + dvb_attach(dvb_pll_attach, card->fe, 0x61, + card->i2c_adapter, &dvb_pll_lg_tdvs_h06xf); dprintk ("dvb_bt8xx: lgdt330x detected\n"); } break; diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.h b/drivers/media/dvb/bt8xx/dvb-bt8xx.h index e75f417..436880e 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.h +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.h @@ -37,8 +37,8 @@ #include "cx24110.h" #include "or51211.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "zl10353.h" +#include "dvb-pll.h" struct dvb_bt8xx_card { struct mutex lock; diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c index d64b96c..a6cbbdd 100644 --- a/drivers/media/dvb/cinergyT2/cinergyT2.c +++ b/drivers/media/dvb/cinergyT2/cinergyT2.c @@ -819,6 +819,11 @@ static int cinergyt2_register_rc(struct cinergyt2 *cinergyt2) set_bit(rc_keys[i + 2], input_dev->keybit); input_dev->keycodesize = 0; input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = cinergyt2->udev->descriptor.idVendor; + input_dev->id.product = cinergyt2->udev->descriptor.idProduct; + input_dev->id.version = 1; + input_dev->cdev.dev = &cinergyt2->udev->dev; err = input_register_device(input_dev); if (err) { diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c index fc77de4..275df65 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.c +++ b/drivers/media/dvb/dvb-core/dmxdev.c @@ -132,6 +132,11 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) if (mutex_lock_interruptible(&dmxdev->mutex)) return -ERESTARTSYS; + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } + if ((file->f_flags & O_ACCMODE) == O_RDWR) { if (!(dmxdev->capabilities & DMXDEV_CAP_DUPLEX)) { mutex_unlock(&dmxdev->mutex); @@ -171,6 +176,7 @@ static int dvb_dvr_open(struct inode *inode, struct file *file) dmxdev->demux->disconnect_frontend(dmxdev->demux); dmxdev->demux->connect_frontend(dmxdev->demux, front); } + dvbdev->users++; mutex_unlock(&dmxdev->mutex); return 0; } @@ -180,8 +186,7 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct dmxdev *dmxdev = dvbdev->priv; - if (mutex_lock_interruptible(&dmxdev->mutex)) - return -ERESTARTSYS; + mutex_lock(&dmxdev->mutex); if ((file->f_flags & O_ACCMODE) == O_WRONLY) { dmxdev->demux->disconnect_frontend(dmxdev->demux); @@ -199,7 +204,16 @@ static int dvb_dvr_release(struct inode *inode, struct file *file) vfree(mem); } } - mutex_unlock(&dmxdev->mutex); + /* TODO */ + dvbdev->users--; + if(dvbdev->users==-1 && dmxdev->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + mutex_unlock(&dmxdev->mutex); + wake_up(&dvbdev->wait_queue); + } else + mutex_unlock(&dmxdev->mutex); + return 0; } @@ -216,6 +230,11 @@ static ssize_t dvb_dvr_write(struct file *file, const char __user *buf, return -EINVAL; if (mutex_lock_interruptible(&dmxdev->mutex)) return -ERESTARTSYS; + + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } ret = dmxdev->demux->write(dmxdev->demux, buf, count); mutex_unlock(&dmxdev->mutex); return ret; @@ -228,6 +247,11 @@ static ssize_t dvb_dvr_read(struct file *file, char __user *buf, size_t count, struct dmxdev *dmxdev = dvbdev->priv; int ret; + if (dmxdev->exit) { + mutex_unlock(&dmxdev->mutex); + return -ENODEV; + } + //mutex_lock(&dmxdev->mutex); ret = dvb_dmxdev_buffer_read(&dmxdev->dvr_buffer, file->f_flags & O_NONBLOCK, @@ -666,6 +690,8 @@ static int dvb_demux_open(struct inode *inode, struct file *file) dmxdevfilter->feed.ts = NULL; init_timer(&dmxdevfilter->timer); + dvbdev->users++; + mutex_unlock(&dmxdev->mutex); return 0; } @@ -673,13 +699,8 @@ static int dvb_demux_open(struct inode *inode, struct file *file) static int dvb_dmxdev_filter_free(struct dmxdev *dmxdev, struct dmxdev_filter *dmxdevfilter) { - if (mutex_lock_interruptible(&dmxdev->mutex)) - return -ERESTARTSYS; - - if (mutex_lock_interruptible(&dmxdevfilter->mutex)) { - mutex_unlock(&dmxdev->mutex); - return -ERESTARTSYS; - } + mutex_lock(&dmxdev->mutex); + mutex_lock(&dmxdevfilter->mutex); dvb_dmxdev_filter_stop(dmxdevfilter); dvb_dmxdev_filter_reset(dmxdevfilter); @@ -949,7 +970,21 @@ static int dvb_demux_release(struct inode *inode, struct file *file) struct dmxdev_filter *dmxdevfilter = file->private_data; struct dmxdev *dmxdev = dmxdevfilter->dev; - return dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); + int ret; + + ret = dvb_dmxdev_filter_free(dmxdev, dmxdevfilter); + + mutex_lock(&dmxdev->mutex); + dmxdev->dvbdev->users--; + if(dmxdev->dvbdev->users==1 && dmxdev->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + mutex_unlock(&dmxdev->mutex); + wake_up(&dmxdev->dvbdev->wait_queue); + } else + mutex_unlock(&dmxdev->mutex); + + return ret; } static struct file_operations dvb_demux_fops = { @@ -1033,6 +1068,7 @@ static struct file_operations dvb_dvr_fops = { static struct dvb_device dvbdev_dvr = { .priv = NULL, .readers = 1, + .users = 1, .fops = &dvb_dvr_fops }; @@ -1070,6 +1106,16 @@ EXPORT_SYMBOL(dvb_dmxdev_init); void dvb_dmxdev_release(struct dmxdev *dmxdev) { + dmxdev->exit=1; + if (dmxdev->dvbdev->users > 1) { + wait_event(dmxdev->dvbdev->wait_queue, + dmxdev->dvbdev->users==1); + } + if (dmxdev->dvr_dvbdev->users > 1) { + wait_event(dmxdev->dvr_dvbdev->wait_queue, + dmxdev->dvr_dvbdev->users==1); + } + dvb_unregister_device(dmxdev->dvbdev); dvb_unregister_device(dmxdev->dvr_dvbdev); diff --git a/drivers/media/dvb/dvb-core/dmxdev.h b/drivers/media/dvb/dvb-core/dmxdev.h index d2bee9f..29746e7 100644 --- a/drivers/media/dvb/dvb-core/dmxdev.h +++ b/drivers/media/dvb/dvb-core/dmxdev.h @@ -91,6 +91,8 @@ struct dmxdev { int filternum; int capabilities; + + unsigned int exit:1; #define DMXDEV_CAP_DUPLEX 1 struct dmx_frontend *dvr_orig_fe; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index fcff5ea..6d8d1c3d 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -673,8 +673,7 @@ static int dmx_ts_feed_stop_filtering(struct dmx_ts_feed *ts_feed) struct dvb_demux *demux = feed->demux; int ret; - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; + mutex_lock(&demux->mutex); if (feed->state < DMX_STATE_GO) { mutex_unlock(&demux->mutex); @@ -748,8 +747,7 @@ static int dvbdmx_release_ts_feed(struct dmx_demux *dmx, struct dvb_demux *demux = (struct dvb_demux *)dmx; struct dvb_demux_feed *feed = (struct dvb_demux_feed *)ts_feed; - if (mutex_lock_interruptible(&demux->mutex)) - return -ERESTARTSYS; + mutex_lock(&demux->mutex); if (feed->state == DMX_STATE_FREE) { mutex_unlock(&demux->mutex); @@ -916,8 +914,7 @@ static int dmx_section_feed_stop_filtering(struct dmx_section_feed *feed) struct dvb_demux *dvbdmx = dvbdmxfeed->demux; int ret; - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; + mutex_lock(&dvbdmx->mutex); if (!dvbdmx->stop_feed) { mutex_unlock(&dvbdmx->mutex); @@ -942,8 +939,7 @@ static int dmx_section_feed_release_filter(struct dmx_section_feed *feed, struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; + mutex_lock(&dvbdmx->mutex); if (dvbdmxfilter->feed != dvbdmxfeed) { mutex_unlock(&dvbdmx->mutex); @@ -1016,8 +1012,7 @@ static int dvbdmx_release_section_feed(struct dmx_demux *demux, struct dvb_demux_feed *dvbdmxfeed = (struct dvb_demux_feed *)feed; struct dvb_demux *dvbdmx = (struct dvb_demux *)demux; - if (mutex_lock_interruptible(&dvbdmx->mutex)) - return -ERESTARTSYS; + mutex_lock(&dvbdmx->mutex); if (dvbdmxfeed->state == DMX_STATE_FREE) { mutex_unlock(&dvbdmx->mutex); @@ -1126,8 +1121,7 @@ static int dvbdmx_connect_frontend(struct dmx_demux *demux, if (demux->frontend) return -EINVAL; - if (mutex_lock_interruptible(&dvbdemux->mutex)) - return -ERESTARTSYS; + mutex_lock(&dvbdemux->mutex); demux->frontend = frontend; mutex_unlock(&dvbdemux->mutex); @@ -1138,8 +1132,7 @@ static int dvbdmx_disconnect_frontend(struct dmx_demux *demux) { struct dvb_demux *dvbdemux = (struct dvb_demux *)demux; - if (mutex_lock_interruptible(&dvbdemux->mutex)) - return -ERESTARTSYS; + mutex_lock(&dvbdemux->mutex); demux->frontend = NULL; mutex_unlock(&dvbdemux->mutex); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index e859722..f4e4ca2 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -36,6 +36,7 @@ #include <linux/list.h> #include <linux/freezer.h> #include <linux/jiffies.h> +#include <linux/kthread.h> #include <asm/processor.h> #include "dvb_frontend.h" @@ -100,7 +101,7 @@ struct dvb_frontend_private { struct semaphore sem; struct list_head list_head; wait_queue_head_t wait_queue; - pid_t thread_pid; + struct task_struct *thread; unsigned long release_jiffies; unsigned int exit; unsigned int wakeup; @@ -508,19 +509,11 @@ static int dvb_frontend_thread(void *data) struct dvb_frontend *fe = data; struct dvb_frontend_private *fepriv = fe->frontend_priv; unsigned long timeout; - char name [15]; fe_status_t s; struct dvb_frontend_parameters *params; dprintk("%s\n", __FUNCTION__); - snprintf (name, sizeof(name), "kdvb-fe-%i", fe->dvb->num); - - lock_kernel(); - daemonize(name); - sigfillset(¤t->blocked); - unlock_kernel(); - fepriv->check_wrapped = 0; fepriv->quality = 0; fepriv->delay = 3*HZ; @@ -532,16 +525,18 @@ static int dvb_frontend_thread(void *data) while (1) { up(&fepriv->sem); /* is locked when we enter the thread... */ - +restart: timeout = wait_event_interruptible_timeout(fepriv->wait_queue, - dvb_frontend_should_wakeup(fe), - fepriv->delay); - if (0 != dvb_frontend_is_exiting(fe)) { + dvb_frontend_should_wakeup(fe) || kthread_should_stop(), + fepriv->delay); + + if (kthread_should_stop() || dvb_frontend_is_exiting(fe)) { /* got signal or quitting */ break; } - try_to_freeze(); + if (try_to_freeze()) + goto restart; if (down_interruptible(&fepriv->sem)) break; @@ -591,7 +586,7 @@ static int dvb_frontend_thread(void *data) fe->ops.sleep(fe); } - fepriv->thread_pid = 0; + fepriv->thread = NULL; mb(); dvb_frontend_wakeup(fe); @@ -600,7 +595,6 @@ static int dvb_frontend_thread(void *data) static void dvb_frontend_stop(struct dvb_frontend *fe) { - unsigned long ret; struct dvb_frontend_private *fepriv = fe->frontend_priv; dprintk ("%s\n", __FUNCTION__); @@ -608,33 +602,18 @@ static void dvb_frontend_stop(struct dvb_frontend *fe) fepriv->exit = 1; mb(); - if (!fepriv->thread_pid) - return; - - /* check if the thread is really alive */ - if (kill_proc(fepriv->thread_pid, 0, 1) == -ESRCH) { - printk("dvb_frontend_stop: thread PID %d already died\n", - fepriv->thread_pid); - /* make sure the mutex was not held by the thread */ - init_MUTEX (&fepriv->sem); + if (!fepriv->thread) return; - } - /* wake up the frontend thread, so it notices that fe->exit == 1 */ - dvb_frontend_wakeup(fe); + kthread_stop(fepriv->thread); - /* wait until the frontend thread has exited */ - ret = wait_event_interruptible(fepriv->wait_queue,0 == fepriv->thread_pid); - if (-ERESTARTSYS != ret) { - fepriv->state = FESTATE_IDLE; - return; - } + init_MUTEX (&fepriv->sem); fepriv->state = FESTATE_IDLE; /* paranoia check in case a signal arrived */ - if (fepriv->thread_pid) - printk("dvb_frontend_stop: warning: thread PID %d won't exit\n", - fepriv->thread_pid); + if (fepriv->thread) + printk("dvb_frontend_stop: warning: thread %p won't exit\n", + fepriv->thread); } s32 timeval_usec_diff(struct timeval lasttime, struct timeval curtime) @@ -684,10 +663,11 @@ static int dvb_frontend_start(struct dvb_frontend *fe) { int ret; struct dvb_frontend_private *fepriv = fe->frontend_priv; + struct task_struct *fe_thread; dprintk ("%s\n", __FUNCTION__); - if (fepriv->thread_pid) { + if (fepriv->thread) { if (!fepriv->exit) return 0; else @@ -701,18 +681,18 @@ static int dvb_frontend_start(struct dvb_frontend *fe) fepriv->state = FESTATE_IDLE; fepriv->exit = 0; - fepriv->thread_pid = 0; + fepriv->thread = NULL; mb(); - ret = kernel_thread (dvb_frontend_thread, fe, 0); - - if (ret < 0) { - printk("dvb_frontend_start: failed to start kernel_thread (%d)\n", ret); + fe_thread = kthread_run(dvb_frontend_thread, fe, + "kdvb-fe-%i", fe->dvb->num); + if (IS_ERR(fe_thread)) { + ret = PTR_ERR(fe_thread); + printk("dvb_frontend_start: failed to start kthread (%d)\n", ret); up(&fepriv->sem); return ret; } - fepriv->thread_pid = ret; - + fepriv->thread = fe_thread; return 0; } @@ -915,7 +895,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, fetunesettings.parameters.inversion = INVERSION_AUTO; } if (fe->ops.info.type == FE_OFDM) { - /* without hierachical coding code_rate_LP is irrelevant, + /* without hierarchical coding code_rate_LP is irrelevant, * so we tolerate the otherwise invalid FEC_NONE setting */ if (fepriv->parameters.u.ofdm.hierarchy_information == HIERARCHY_NONE && fepriv->parameters.u.ofdm.code_rate_LP == FEC_NONE) @@ -1044,6 +1024,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; + int ret; dprintk ("%s\n", __FUNCTION__); @@ -1053,7 +1034,14 @@ static int dvb_frontend_release(struct inode *inode, struct file *file) if (fe->ops.ts_bus_ctrl) fe->ops.ts_bus_ctrl (fe, 0); - return dvb_generic_release (inode, file); + ret = dvb_generic_release (inode, file); + + if (dvbdev->users==-1 && fepriv->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + wake_up(&dvbdev->wait_queue); + } + return ret; } static struct file_operations dvb_frontend_fops = { @@ -1113,8 +1101,15 @@ int dvb_unregister_frontend(struct dvb_frontend* fe) dprintk ("%s\n", __FUNCTION__); mutex_lock(&frontend_mutex); - dvb_unregister_device (fepriv->dvbdev); dvb_frontend_stop (fe); + mutex_unlock(&frontend_mutex); + + if (fepriv->dvbdev->users < -1) + wait_event(fepriv->dvbdev->wait_queue, + fepriv->dvbdev->users==-1); + + mutex_lock(&frontend_mutex); + dvb_unregister_device (fepriv->dvbdev); /* fe is invalid now */ kfree(fepriv); diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 76e9c36..4ebf33a5 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -174,7 +174,7 @@ static unsigned short dvb_net_eth_type_trans(struct sk_buff *skb, struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb_pull(skb,dev->hard_header_len); eth = eth_hdr(skb); @@ -600,6 +600,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) /* Check CRC32, we've got it in our skb already. */ unsigned short ulen = htons(priv->ule_sndu_len); unsigned short utype = htons(priv->ule_sndu_type); + const u8 *tail; struct kvec iov[3] = { { &ulen, sizeof ulen }, { &utype, sizeof utype }, @@ -613,10 +614,11 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } ule_crc = iov_crc32(ule_crc, iov, 3); - expected_crc = *((u8 *)priv->ule_skb->tail - 4) << 24 | - *((u8 *)priv->ule_skb->tail - 3) << 16 | - *((u8 *)priv->ule_skb->tail - 2) << 8 | - *((u8 *)priv->ule_skb->tail - 1); + tail = skb_tail_pointer(priv->ule_skb); + expected_crc = *(tail - 4) << 24 | + *(tail - 3) << 16 | + *(tail - 2) << 8 | + *(tail - 1); if (ule_crc != expected_crc) { printk(KERN_WARNING "%lu: CRC32 check FAILED: %08x / %08x, SNDU len %d type %#x, ts_remain %d, next 2: %x.\n", priv->ts_count, ule_crc, expected_crc, priv->ule_sndu_len, priv->ule_sndu_type, ts_remain, ts_remain > 2 ? *(unsigned short *)from_where : 0); @@ -695,7 +697,9 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) } else { - memcpy(dest_addr, priv->ule_skb->data, ETH_ALEN); + skb_copy_from_linear_data(priv->ule_skb, + dest_addr, + ETH_ALEN); skb_pull(priv->ule_skb, ETH_ALEN); } } @@ -1435,11 +1439,36 @@ static int dvb_net_ioctl(struct inode *inode, struct file *file, return dvb_usercopy(inode, file, cmd, arg, dvb_net_do_ioctl); } +static int dvb_net_close(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct dvb_net *dvbnet = dvbdev->priv; + + if (!dvbdev) + return -ENODEV; + + if ((file->f_flags & O_ACCMODE) == O_RDONLY) { + dvbdev->readers++; + } else { + dvbdev->writers++; + } + + dvbdev->users++; + + if(dvbdev->users == 1 && dvbnet->exit==1) { + fops_put(file->f_op); + file->f_op = NULL; + wake_up(&dvbdev->wait_queue); + } + return 0; +} + + static struct file_operations dvb_net_fops = { .owner = THIS_MODULE, .ioctl = dvb_net_ioctl, .open = dvb_generic_open, - .release = dvb_generic_release, + .release = dvb_net_close, }; static struct dvb_device dvbdev_net = { @@ -1454,6 +1483,11 @@ void dvb_net_release (struct dvb_net *dvbnet) { int i; + dvbnet->exit = 1; + if (dvbnet->dvbdev->users < 1) + wait_event(dvbnet->dvbdev->wait_queue, + dvbnet->dvbdev->users==1); + dvb_unregister_device(dvbnet->dvbdev); for (i=0; i<DVB_NET_DEVICES_MAX; i++) { diff --git a/drivers/media/dvb/dvb-core/dvb_net.h b/drivers/media/dvb/dvb-core/dvb_net.h index f14e4ca..3a3126ca 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.h +++ b/drivers/media/dvb/dvb-core/dvb_net.h @@ -36,6 +36,7 @@ struct dvb_net { struct dvb_device *dvbdev; struct net_device *device[DVB_NET_DEVICES_MAX]; int state[DVB_NET_DEVICES_MAX]; + unsigned int exit:1; struct dmx_demux *demux; }; diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c index 826b47f..e23d8a0 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.c +++ b/drivers/media/dvb/dvb-core/dvbdev.c @@ -199,12 +199,13 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, const struct dvb_device *template, void *priv, int type) { struct dvb_device *dvbdev; + struct file_operations *dvbdevfops; + int id; - if (mutex_lock_interruptible(&dvbdev_register_lock)) - return -ERESTARTSYS; + mutex_lock(&dvbdev_register_lock); - if ((id = dvbdev_get_free_id (adap, type)) < 0) { + if ((id = dvbdev_get_free_id (adap, type)) < 0){ mutex_unlock(&dvbdev_register_lock); *pdvbdev = NULL; printk ("%s: could get find free device id...\n", __FUNCTION__); @@ -213,7 +214,15 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, *pdvbdev = dvbdev = kmalloc(sizeof(struct dvb_device), GFP_KERNEL); - if (!dvbdev) { + if (!dvbdev){ + mutex_unlock(&dvbdev_register_lock); + return -ENOMEM; + } + + dvbdevfops = kzalloc(sizeof(struct file_operations), GFP_KERNEL); + + if (!dvbdevfops){ + kfree (dvbdev); mutex_unlock(&dvbdev_register_lock); return -ENOMEM; } @@ -223,7 +232,10 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev, dvbdev->id = id; dvbdev->adapter = adap; dvbdev->priv = priv; + dvbdev->fops = dvbdevfops; + init_waitqueue_head (&dvbdev->wait_queue); + memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations)); dvbdev->fops->owner = adap->module; list_add_tail (&dvbdev->list_head, &adap->device_list); @@ -251,6 +263,7 @@ void dvb_unregister_device(struct dvb_device *dvbdev) dvbdev->type, dvbdev->id))); list_del (&dvbdev->list_head); + kfree (dvbdev->fops); kfree (dvbdev); } EXPORT_SYMBOL(dvb_unregister_device); @@ -281,8 +294,7 @@ int dvb_register_adapter(struct dvb_adapter *adap, const char *name, struct modu { int num; - if (mutex_lock_interruptible(&dvbdev_register_lock)) - return -ERESTARTSYS; + mutex_lock(&dvbdev_register_lock); if ((num = dvbdev_get_free_adapter_num ()) < 0) { mutex_unlock(&dvbdev_register_lock); @@ -310,8 +322,7 @@ EXPORT_SYMBOL(dvb_register_adapter); int dvb_unregister_adapter(struct dvb_adapter *adap) { - if (mutex_lock_interruptible(&dvbdev_register_lock)) - return -ERESTARTSYS; + mutex_lock(&dvbdev_register_lock); list_del (&adap->list_head); mutex_unlock(&dvbdev_register_lock); return 0; diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 620e788..6dff10e 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -69,6 +69,7 @@ struct dvb_device { int writers; int users; + wait_queue_head_t wait_queue; /* don't really need those !? -- FIXME: use video_usercopy */ int (*kernel_ioctl)(struct inode *inode, struct file *file, unsigned int cmd, void *arg); diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index ad52143..5448873 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -33,6 +33,7 @@ config DVB_USB_A800 config DVB_USB_DIBUSB_MB tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)" depends on DVB_USB + select DVB_PLL select DVB_DIB3000MB select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -88,6 +89,7 @@ config DVB_USB_DIB0700 config DVB_USB_UMT_010 tristate "HanfTek UMT-010 DVB-T USB2.0 support" depends on DVB_USB + select DVB_PLL select DVB_DIB3000MC select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE help @@ -96,9 +98,9 @@ config DVB_USB_UMT_010 config DVB_USB_CXUSB tristate "Conexant USB2.0 hybrid reference design support" depends on DVB_USB + select DVB_PLL select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE help @@ -109,9 +111,38 @@ config DVB_USB_CXUSB Medion MD95700 hybrid USB2.0 device. DViCO FusionHDTV (Bluebird) USB2.0 devices +config DVB_USB_M920X + tristate "Uli m920x DVB-T USB2.0 support" + depends on DVB_USB + select DVB_MT352 if !DVB_FE_CUSTOMISE + select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + help + Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. + Currently, only devices with a product id of + "DTV USB MINI" (in cold state) are supported. + Firmware required. + +config DVB_USB_GL861 + tristate "Genesys Logic GL861 USB2.0 support" + depends on DVB_USB + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + help + Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 + receiver with USB ID 0db0:5581. + +config DVB_USB_AU6610 + tristate "Alcor Micro AU6610 USB2.0 support" + depends on DVB_USB + select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. + config DVB_USB_DIGITV tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support" depends on DVB_USB + select DVB_PLL select DVB_NXT6000 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE help @@ -180,3 +211,10 @@ config DVB_USB_DTT200U The receivers are also known as DTT200U (Yakumo) and UB300 (Yuan). The WT-220U and its clones are pen-sized. + +config DVB_USB_OPERA1 + tristate "Opera1 DVB-S USB2.0 receiver" + depends on DVB_USB + select DVB_STV0299 if !DVB_FE_CUSTOMISE + help + Say Y here to support the Opera DVB-S USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 154d593..976f840 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -30,6 +30,15 @@ obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2 dvb-usb-umt-010-objs = umt-010.o obj-$(CONFIG_DVB_USB_UMT_010) += dvb-usb-dibusb-common.o dvb-usb-umt-010.o +dvb-usb-m920x-objs = m920x.o +obj-$(CONFIG_DVB_USB_M920X) += dvb-usb-m920x.o + +dvb-usb-gl861-objs = gl861.o +obj-$(CONFIG_DVB_USB_GL861) += dvb-usb-gl861.o + +dvb-usb-au6610-objs = au6610.o +obj-$(CONFIG_DVB_USB_AU6610) += dvb-usb-au6610.o + dvb-usb-digitv-objs = digitv.o obj-$(CONFIG_DVB_USB_DIGITV) += dvb-usb-digitv.o @@ -42,4 +51,8 @@ obj-$(CONFIG_DVB_USB_TTUSB2) += dvb-usb-ttusb2.o dvb-usb-dib0700-objs = dib0700_core.o dib0700_devices.o obj-$(CONFIG_DVB_USB_DIB0700) += dvb-usb-dib0700.o +dvb-usb-opera-objs = opera1.o +obj-$(CONFIG_DVB_USB_OPERA1) += dvb-usb-opera.o + + EXTRA_CFLAGS = -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ diff --git a/drivers/media/dvb/dvb-usb/au6610.c b/drivers/media/dvb/dvb-usb/au6610.c new file mode 100644 index 0000000..18e0b16 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/au6610.c @@ -0,0 +1,255 @@ +/* DVB USB compliant linux driver for Sigmatek DVB-110 DVB-T USB2.0 receiver + * + * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include "au6610.h" + +#include "zl10353.h" +#include "qt1010.h" + +/* debug */ +static int dvb_usb_au6610_debug; +module_param_named(debug, dvb_usb_au6610_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +static int au6610_usb_msg(struct dvb_usb_device *d, u8 operation, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + int ret; + u16 index; + u8 usb_buf[6]; /* enough for all known requests, + read returns 5 and write 6 bytes */ + switch (wlen) { + case 1: + index = wbuf[0] << 8; + break; + case 2: + index = wbuf[0] << 8; + index += wbuf[1]; + break; + default: + warn("wlen = %x, aborting.", wlen); + return -EINVAL; + } + + ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), operation, + USB_TYPE_VENDOR|USB_DIR_IN, addr << 1, index, usb_buf, + sizeof(usb_buf), AU6610_USB_TIMEOUT); + + if (ret < 0) + return ret; + + switch (operation) { + case AU6610_REQ_I2C_READ: + case AU6610_REQ_USB_READ: + /* requested value is always 5th byte in buffer */ + rbuf[0] = usb_buf[4]; + } + + return ret; +} + +static int au6610_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 request; + u8 wo = (rbuf == NULL || rlen == 0); /* write-only */ + + if (wo) { + request = AU6610_REQ_I2C_WRITE; + } else { /* rw */ + request = AU6610_REQ_I2C_READ; + } + + return au6610_usb_msg(d, request, addr, wbuf, wlen, rbuf, rlen); +} + + +/* I2C */ +static int au6610_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + return -EINVAL; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, + msg[i+1].len) < 0) + break; + i++; + } else if (au6610_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + + +static u32 au6610_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm au6610_i2c_algo = { + .master_xfer = au6610_i2c_xfer, + .functionality = au6610_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int au6610_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = 0; + return 0; +} + +static struct zl10353_config au6610_zl10353_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int au6610_zl10353_frontend_attach(struct dvb_usb_adapter *adap) +{ + if ((adap->fe = dvb_attach(zl10353_attach, &au6610_zl10353_config, + &adap->dev->i2c_adap)) != NULL) { + return 0; + } + + return -EIO; +} + +static struct qt1010_config au6610_qt1010_config = { + .i2c_address = 0x62 +}; + +static int au6610_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, + adap->fe, &adap->dev->i2c_adap, + &au6610_qt1010_config) == NULL ? -ENODEV : 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties au6610_properties; + +static int au6610_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + if (intf->num_altsetting < AU6610_ALTSETTING_COUNT) + return -ENODEV; + + if ((ret = dvb_usb_device_init(intf, &au6610_properties, THIS_MODULE, &d)) == 0) { + alt = usb_altnum_to_altsetting(intf, AU6610_ALTSETTING); + + if (alt == NULL) { + deb_rc("no alt found!\n"); + return -ENODEV; + } + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + } + + return ret; +} + + +static struct usb_device_id au6610_table [] = { + { USB_DEVICE(USB_VID_ALCOR_MICRO, USB_PID_SIGMATEK_DVB_110) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, au6610_table); + +static struct dvb_usb_device_properties au6610_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + .size_of_priv = 0, + .identify_state = au6610_identify_state, + .num_adapters = 1, + .adapter = { + { + .frontend_attach = au6610_zl10353_frontend_attach, + .tuner_attach = au6610_qt1010_tuner_attach, + + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x82, + .u = { + .isoc = { + .framesperurb = 40, + .framesize = 942, /* maximum packet size */ + .interval = 1.25, /* 125 us */ + } + } + }, + } + }, + .i2c_algo = &au6610_i2c_algo, + .num_device_descs = 1, + .devices = { + { + "Sigmatek DVB-110 DVB-T USB2.0", + { &au6610_table[0], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver au6610_driver = { + .name = "dvb_usb_au6610", + .probe = au6610_probe, + .disconnect = dvb_usb_device_exit, + .id_table = au6610_table, +}; + +/* module stuff */ +static int __init au6610_module_init(void) +{ + int ret; + + if ((ret = usb_register(&au6610_driver))) { + err("usb_register failed. Error number %d", ret); + return ret; + } + + return 0; +} + +static void __exit au6610_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&au6610_driver); +} + +module_init (au6610_module_init); +module_exit (au6610_module_exit); + +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_DESCRIPTION("Driver Sigmatek DVB-110 DVB-T USB2.0 / AU6610"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/au6610.h b/drivers/media/dvb/dvb-usb/au6610.h new file mode 100644 index 0000000..4161b05 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/au6610.h @@ -0,0 +1,19 @@ +#ifndef _DVB_USB_AU6610_H_ +#define _DVB_USB_AU6610_H_ + +#define DVB_USB_LOG_PREFIX "au6610" +#include "dvb-usb.h" + +#define deb_rc(args...) dprintk(dvb_usb_au6610_debug,0x01,args) + +#define AU6610_REQ_I2C_WRITE 0x14 +#define AU6610_REQ_I2C_READ 0x13 +#define AU6610_REQ_USB_WRITE 0x16 +#define AU6610_REQ_USB_READ 0x15 + +#define AU6610_USB_TIMEOUT 1000 + +#define AU6610_ALTSETTING_COUNT 6 +#define AU6610_ALTSETTING 5 + +#endif diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 15d12fc..bac2ae3 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -27,7 +27,6 @@ #include "cx22702.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "mt352.h" #include "mt352_priv.h" #include "zl10353.h" @@ -388,7 +387,8 @@ static int cxusb_dtt7579_tuner_attach(struct dvb_usb_adapter *adap) static int cxusb_lgh064f_tuner_attach(struct dvb_usb_adapter *adap) { - dvb_attach(lgh06xf_attach, adap->fe, &adap->dev->i2c_adap); + dvb_attach(dvb_pll_attach, adap->fe, 0x61, &adap->dev->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); return 0; } @@ -469,9 +469,9 @@ static int bluebird_patch_dvico_firmware_download(struct usb_device *udev, fw->data[BLUEBIRD_01_ID_OFFSET + 1] == USB_VID_DVICO >> 8) { fw->data[BLUEBIRD_01_ID_OFFSET + 2] = - udev->descriptor.idProduct + 1; + le16_to_cpu(udev->descriptor.idProduct) + 1; fw->data[BLUEBIRD_01_ID_OFFSET + 3] = - udev->descriptor.idProduct >> 8; + le16_to_cpu(udev->descriptor.idProduct) >> 8; return usb_cypress_load_firmware(udev, fw, CYPRESS_FX2); } diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 6a4d150..dddf164 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -56,10 +56,6 @@ static int dib0700_ctrl_rd(struct dvb_usb_device *d, u8 *tx, u8 txlen, u8 *rx, u if (txlen > 3) index |= tx[3]; - /* think about swapping here */ - value = le16_to_cpu(value); - index = le16_to_cpu(index); - status = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev,0), tx[0], USB_TYPE_VENDOR | USB_DIR_IN, value, index, rx, rxlen, USB_CTRL_GET_TIMEOUT); diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c index 4a198d4..b5acb11 100644 --- a/drivers/media/dvb/dvb-usb/digitv.c +++ b/drivers/media/dvb/dvb-usb/digitv.c @@ -119,6 +119,8 @@ static int digitv_nxt6000_tuner_set_params(struct dvb_frontend *fe, struct dvb_f struct dvb_usb_adapter *adap = fe->dvb->priv; u8 b[5]; dvb_usb_tuner_calc_regs(fe,fep,b, 5); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); return digitv_ctrl_msg(adap->dev, USB_WRITE_TUNER, 0, &b[1], 4, NULL, 0); } diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 299382d..97715f7 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -11,7 +11,9 @@ /* Vendor IDs */ #define USB_VID_ADSTECH 0x06e1 +#define USB_VID_ALCOR_MICRO 0x058f #define USB_VID_ANCHOR 0x0547 +#define USB_VID_ANUBIS_ELECTRONIC 0x10fd #define USB_VID_AVERMEDIA 0x07ca #define USB_VID_COMPRO 0x185b #define USB_VID_COMPRO_UNK 0x145f @@ -29,6 +31,8 @@ #define USB_VID_LEADTEK 0x0413 #define USB_VID_LITEON 0x04ca #define USB_VID_MEDION 0x1660 +#define USB_VID_MSI 0x0db0 +#define USB_VID_OPERA1 0x695c #define USB_VID_PINNACLE 0x2304 #define USB_VID_VISIONPLUS 0x13d3 #define USB_VID_TWINHAN 0x1822 @@ -119,10 +123,13 @@ #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_COLD 0xdb54 #define USB_PID_DIGITALNOW_BLUEBIRD_DUAL_1_WARM 0xdb55 #define USB_PID_MEDION_MD95700 0x0932 +#define USB_PID_MSI_MEGASKY580 0x5580 +#define USB_PID_MSI_MEGASKY580_55801 0x5581 #define USB_PID_KYE_DVB_T_COLD 0x701e #define USB_PID_KYE_DVB_T_WARM 0x701f #define USB_PID_PCTV_200E 0x020e #define USB_PID_PCTV_400E 0x020f +#define USB_PID_PCTV_450E 0x0222 #define USB_PID_LITEON_DVB_T_COLD 0xf000 #define USB_PID_LITEON_DVB_T_WARM 0xf001 #define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360 @@ -134,6 +141,10 @@ #define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 #define USB_PID_GENPIX_8PSK_COLD 0x0200 #define USB_PID_GENPIX_8PSK_WARM 0x0201 +#define USB_PID_SIGMATEK_DVB_110 0x6610 +#define USB_PID_MSI_DIGI_VOX_MINI_II 0x1513 +#define USB_PID_OPERA1_COLD 0x2830 +#define USB_PID_OPERA1_WARM 0x3829 #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 19ff597..68ed3a7 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -107,8 +107,6 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) return -ENOMEM; input_dev->evbit[0] = BIT(EV_KEY); - input_dev->keycodesize = sizeof(unsigned char); - input_dev->keycodemax = KEY_MAX; input_dev->name = "IR-receiver inside an USB DVB receiver"; input_dev->phys = d->rc_phys; usb_to_input_id(d->udev, &input_dev->id); @@ -151,7 +149,7 @@ int dvb_usb_remote_init(struct dvb_usb_device *d) int dvb_usb_remote_exit(struct dvb_usb_device *d) { if (d->state & DVB_USB_STATE_REMOTE) { - cancel_delayed_work(&d->rc_query_work); + cancel_rearming_delayed_work(&d->rc_query_work); flush_scheduled_work(); input_unregister_device(d->rc_input_dev); } diff --git a/drivers/media/dvb/dvb-usb/gl861.c b/drivers/media/dvb/dvb-usb/gl861.c new file mode 100644 index 0000000..e0587e6 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/gl861.c @@ -0,0 +1,231 @@ +/* DVB USB compliant linux driver for GL861 USB2.0 devices. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "gl861.h" + +#include "zl10353.h" +#include "qt1010.h" + +/* debug */ +static int dvb_usb_gl861_debug; +module_param_named(debug,dvb_usb_gl861_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +static int gl861_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u16 index; + u16 value = addr << (8 + 1); + int wo = (rbuf == NULL || rlen == 0); /* write-only */ + u8 req, type; + + if (wo) { + req = GL861_REQ_I2C_WRITE; + type = GL861_WRITE; + } else { /* rw */ + req = GL861_REQ_I2C_READ; + type = GL861_READ; + } + + switch (wlen) { + case 1: + index = wbuf[0]; + break; + case 2: + index = wbuf[0]; + value = value + wbuf[1]; + break; + default: + warn("wlen = %x, aborting.", wlen); + return -EINVAL; + } + + return usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), req, type, + value, index, rbuf, rlen, 2000); +} + +/* I2C */ +static int gl861_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + return -EINVAL; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, msg[i+1].buf, msg[i+1].len) < 0) + break; + i++; + } else + if (gl861_i2c_msg(d, msg[i].addr, msg[i].buf, + msg[i].len, NULL, 0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 gl861_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm gl861_i2c_algo = { + .master_xfer = gl861_i2c_xfer, + .functionality = gl861_i2c_func, +}; + +/* Callbacks for DVB USB */ +static int gl861_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + *cold = 0; + + return 0; +} + +static struct zl10353_config gl861_zl10353_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .parallel_ts = 1, +}; + +static int gl861_frontend_attach(struct dvb_usb_adapter *adap) +{ + if ((adap->fe = dvb_attach(zl10353_attach, &gl861_zl10353_config, + &adap->dev->i2c_adap)) != NULL) { + return 0; + } + + return -EIO; +} + +static struct qt1010_config gl861_qt1010_config = { + .i2c_address = 0x62 +}; + +static int gl861_tuner_attach(struct dvb_usb_adapter *adap) +{ + return dvb_attach(qt1010_attach, + adap->fe, &adap->dev->i2c_adap, + &gl861_qt1010_config) == NULL ? -ENODEV : 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties gl861_properties; + +static int gl861_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + if (intf->num_altsetting < 2) + return -ENODEV; + + if ((ret = dvb_usb_device_init(intf, &gl861_properties, THIS_MODULE, &d)) == 0) { + alt = usb_altnum_to_altsetting(intf, 0); + + if (alt == NULL) { + deb_rc("not alt found!\n"); + return -ENODEV; + } + + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + } + + return ret; +} + +static struct usb_device_id gl861_table [] = { + { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580_55801) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, gl861_table); + +static struct dvb_usb_device_properties gl861_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = DEVICE_SPECIFIC, + + .size_of_priv = 0, + + .identify_state = gl861_identify_state, + .num_adapters = 1, + .adapter = {{ + + .frontend_attach = gl861_frontend_attach, + .tuner_attach = gl861_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + .i2c_algo = &gl861_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI Mega Sky 55801 DVB-T USB2.0", + { &gl861_table[0], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver gl861_driver = { + .name = "dvb_usb_gl861", + .probe = gl861_probe, + .disconnect = dvb_usb_device_exit, + .id_table = gl861_table, +}; + +/* module stuff */ +static int __init gl861_module_init(void) +{ + int ret; + + if ((ret = usb_register(&gl861_driver))) { + err("usb_register failed. Error number %d", ret); + return ret; + } + + return 0; +} + +static void __exit gl861_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&gl861_driver); +} + +module_init (gl861_module_init); +module_exit (gl861_module_exit); + +MODULE_AUTHOR("Carl Lundqvist <comabug@gmail.com>"); +MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / GL861"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/gl861.h b/drivers/media/dvb/dvb-usb/gl861.h new file mode 100644 index 0000000..72a51af --- /dev/null +++ b/drivers/media/dvb/dvb-usb/gl861.h @@ -0,0 +1,15 @@ +#ifndef _DVB_USB_GL861_H_ +#define _DVB_USB_GL861_H_ + +#define DVB_USB_LOG_PREFIX "gl861" +#include "dvb-usb.h" + +#define deb_rc(args...) dprintk(dvb_usb_gl861_debug,0x01,args) + +#define GL861_WRITE 0x40 +#define GL861_READ 0xc0 + +#define GL861_REQ_I2C_WRITE 0x01 +#define GL861_REQ_I2C_READ 0x02 + +#endif diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c new file mode 100644 index 0000000..45d7bc2 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -0,0 +1,620 @@ +/* DVB USB compliant linux driver for MSI Mega Sky 580 DVB-T USB2.0 receiver + * + * Copyright (C) 2006 Aapo Tahkola (aet@rasterburn.org) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ + +#include "m920x.h" + +#include "mt352.h" +#include "mt352_priv.h" +#include "qt1010.h" +#include "tda1004x.h" +#include "tda827x.h" + +/* debug */ +static int dvb_usb_m920x_debug; +module_param_named(debug,dvb_usb_m920x_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=rc (or-able))." DVB_USB_DEBUG_STATUS); + +static struct dvb_usb_rc_key megasky_rc_keys [] = { + { 0x0, 0x12, KEY_POWER }, + { 0x0, 0x1e, KEY_CYCLEWINDOWS }, /* min/max */ + { 0x0, 0x02, KEY_CHANNELUP }, + { 0x0, 0x05, KEY_CHANNELDOWN }, + { 0x0, 0x03, KEY_VOLUMEUP }, + { 0x0, 0x06, KEY_VOLUMEDOWN }, + { 0x0, 0x04, KEY_MUTE }, + { 0x0, 0x07, KEY_OK }, /* TS */ + { 0x0, 0x08, KEY_STOP }, + { 0x0, 0x09, KEY_MENU }, /* swap */ + { 0x0, 0x0a, KEY_REWIND }, + { 0x0, 0x1b, KEY_PAUSE }, + { 0x0, 0x1f, KEY_FASTFORWARD }, + { 0x0, 0x0c, KEY_RECORD }, + { 0x0, 0x0d, KEY_CAMERA }, /* screenshot */ + { 0x0, 0x0e, KEY_COFFEE }, /* "MTS" */ +}; + +static inline int m9206_read(struct usb_device *udev, u8 request, u16 value,\ + u16 index, void *data, int size) +{ + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + request, USB_TYPE_VENDOR | USB_DIR_IN, + value, index, data, size, 2000); + if (ret < 0) { + printk(KERN_INFO "m920x_read = error: %d\n", ret); + return ret; + } + + if (ret != size) { + deb_rc("m920x_read = no data\n"); + return -EIO; + } + + return 0; +} + +static inline int m9206_write(struct usb_device *udev, u8 request, + u16 value, u16 index) +{ + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + request, USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, NULL, 0, 2000); + + return ret; +} + +static int m9206_init(struct dvb_usb_device *d) +{ + int ret = 0; + + /* Remote controller init. */ + if (d->props.rc_query) { + if ((ret = m9206_write(d->udev, M9206_CORE, 0xa8, M9206_RC_INIT2)) != 0) + return ret; + + if ((ret = m9206_write(d->udev, M9206_CORE, 0x51, M9206_RC_INIT1)) != 0) + return ret; + } + + return ret; +} + +static int m9206_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + struct m9206_state *m = d->priv; + int i, ret = 0; + u8 rc_state[2]; + + if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_STATE, rc_state, 1)) != 0) + goto unlock; + + if ((ret = m9206_read(d->udev, M9206_CORE, 0x0, M9206_RC_KEY, rc_state + 1, 1)) != 0) + goto unlock; + + for (i = 0; i < d->props.rc_key_map_size; i++) + if (d->props.rc_key_map[i].data == rc_state[1]) { + *event = d->props.rc_key_map[i].event; + + switch(rc_state[0]) { + case 0x80: + *state = REMOTE_NO_KEY_PRESSED; + goto unlock; + + case 0x93: + case 0x92: + m->rep_count = 0; + *state = REMOTE_KEY_PRESSED; + goto unlock; + + case 0x91: + /* For comfort. */ + if (++m->rep_count > 2) + *state = REMOTE_KEY_REPEAT; + goto unlock; + + default: + deb_rc("Unexpected rc response %x\n", rc_state[0]); + *state = REMOTE_NO_KEY_PRESSED; + goto unlock; + } + } + + if (rc_state[1] != 0) + deb_rc("Unknown rc key %x\n", rc_state[1]); + + *state = REMOTE_NO_KEY_PRESSED; + + unlock: + + return ret; +} + +/* I2C */ +static int m9206_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i, j; + int ret = 0; + + if (!num) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if (msg[i].flags & (I2C_M_NO_RD_ACK|I2C_M_IGNORE_NAK|I2C_M_TEN) || + msg[i].len == 0) { + /* For a 0 byte message, I think sending the address to index 0x80|0x40 + * would be the correct thing to do. However, zero byte messages are + * only used for probing, and since we don't know how to get the slave's + * ack, we can't probe. */ + ret = -ENOTSUPP; + goto unlock; + } + /* Send START & address/RW bit */ + if (!(msg[i].flags & I2C_M_NOSTART)) { + if ((ret = m9206_write(d->udev, M9206_I2C, (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), 0x80)) != 0) + goto unlock; + /* Should check for ack here, if we knew how. */ + } + if (msg[i].flags & I2C_M_RD) { + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? Send STOP, otherwise send ACK. */ + int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x01; + if ((ret = m9206_read(d->udev, M9206_I2C, 0x0, 0x20|stop, &msg[i].buf[j], 1)) != 0) + goto unlock; + } + } else { + for (j = 0; j < msg[i].len; j++) { + /* Last byte of transaction? Then send STOP. */ + int stop = (i+1 == num && j+1 == msg[i].len)?0x40:0x00; + if ((ret = m9206_write(d->udev, M9206_I2C, msg[i].buf[j], stop)) != 0) + goto unlock; + /* Should check for ack here too. */ + } + } + } + ret = num; + +unlock: + mutex_unlock(&d->i2c_mutex); + + return ret; +} + +static u32 m9206_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm m9206_i2c_algo = { + .master_xfer = m9206_i2c_xfer, + .functionality = m9206_i2c_func, +}; + + +static int m9206_set_filter(struct dvb_usb_adapter *adap, int type, int idx, + int pid) +{ + int ret = 0; + + if (pid >= 0x8000) + return -EINVAL; + + pid |= 0x8000; + + if ((ret = m9206_write(adap->dev->udev, M9206_FILTER, pid, (type << 8) | (idx * 4) )) != 0) + return ret; + + if ((ret = m9206_write(adap->dev->udev, M9206_FILTER, 0, (type << 8) | (idx * 4) )) != 0) + return ret; + + return ret; +} + +static int m9206_update_filters(struct dvb_usb_adapter *adap) +{ + struct m9206_state *m = adap->dev->priv; + int enabled = m->filtering_enabled; + int i, ret = 0, filter = 0; + + for (i = 0; i < M9206_MAX_FILTERS; i++) + if (m->filters[i] == 8192) + enabled = 0; + + /* Disable all filters */ + if ((ret = m9206_set_filter(adap, 0x81, 1, enabled)) != 0) + return ret; + + for (i = 0; i < M9206_MAX_FILTERS; i++) + if ((ret = m9206_set_filter(adap, 0x81, i + 2, 0)) != 0) + return ret; + + if ((ret = m9206_set_filter(adap, 0x82, 0, 0x0)) != 0) + return ret; + + /* Set */ + if (enabled) { + for (i = 0; i < M9206_MAX_FILTERS; i++) { + if (m->filters[i] == 0) + continue; + + if ((ret = m9206_set_filter(adap, 0x81, filter + 2, m->filters[i])) != 0) + return ret; + + filter++; + } + } + + if ((ret = m9206_set_filter(adap, 0x82, 0, 0x02f5)) != 0) + return ret; + + return ret; +} + +static int m9206_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct m9206_state *m = adap->dev->priv; + + m->filtering_enabled = onoff ? 1 : 0; + + return m9206_update_filters(adap); +} + +static int m9206_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + struct m9206_state *m = adap->dev->priv; + + m->filters[index] = onoff ? pid : 0; + + return m9206_update_filters(adap); +} + +static int m9206_firmware_download(struct usb_device *udev, + const struct firmware *fw) +{ + u16 value, index, size; + u8 read[4], *buff; + int i, pass, ret = 0; + + buff = kmalloc(65536, GFP_KERNEL); + + if ((ret = m9206_read(udev, M9206_FILTER, 0x0, 0x8000, read, 4)) != 0) + goto done; + deb_rc("%x %x %x %x\n", read[0], read[1], read[2], read[3]); + + if ((ret = m9206_read(udev, M9206_FW, 0x0, 0x0, read, 1)) != 0) + goto done; + deb_rc("%x\n", read[0]); + + for (pass = 0; pass < 2; pass++) { + for (i = 0; i + (sizeof(u16) * 3) < fw->size;) { + value = le16_to_cpu(*(u16 *)(fw->data + i)); + i += sizeof(u16); + + index = le16_to_cpu(*(u16 *)(fw->data + i)); + i += sizeof(u16); + + size = le16_to_cpu(*(u16 *)(fw->data + i)); + i += sizeof(u16); + + if (pass == 1) { + /* Will stall if using fw->data ... */ + memcpy(buff, fw->data + i, size); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev,0), + M9206_FW, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, index, buff, size, 20); + if (ret != size) { + deb_rc("error while uploading fw!\n"); + ret = -EIO; + goto done; + } + msleep(3); + } + i += size; + } + if (i != fw->size) { + deb_rc("bad firmware file!\n"); + ret = -EINVAL; + goto done; + } + } + + msleep(36); + + /* m9206 will disconnect itself from the bus after this. */ + (void) m9206_write(udev, M9206_CORE, 0x01, M9206_FW_GO); + deb_rc("firmware uploaded!\n"); + + done: + kfree(buff); + + return ret; +} + +/* Callbacks for DVB USB */ +static int m920x_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + struct usb_host_interface *alt; + + alt = usb_altnum_to_altsetting(usb_ifnum_to_if(udev, 0), 1); + *cold = (alt == NULL) ? 1 : 0; + + return 0; +} + +static int megasky_mt352_demod_init(struct dvb_frontend *fe) +{ + u8 config[] = { CONFIG, 0x3d }; + u8 clock[] = { CLOCK_CTL, 0x30 }; + u8 reset[] = { RESET, 0x80 }; + u8 adc_ctl[] = { ADC_CTL_1, 0x40 }; + u8 agc[] = { AGC_TARGET, 0x1c, 0x20 }; + u8 sec_agc[] = { 0x69, 0x00, 0xff, 0xff, 0x40, 0xff, 0x00, 0x40, 0x40 }; + u8 unk1[] = { 0x93, 0x1a }; + u8 unk2[] = { 0xb5, 0x7a }; + + mt352_write(fe, config, ARRAY_SIZE(config)); + mt352_write(fe, clock, ARRAY_SIZE(clock)); + mt352_write(fe, reset, ARRAY_SIZE(reset)); + mt352_write(fe, adc_ctl, ARRAY_SIZE(adc_ctl)); + mt352_write(fe, agc, ARRAY_SIZE(agc)); + mt352_write(fe, sec_agc, ARRAY_SIZE(sec_agc)); + mt352_write(fe, unk1, ARRAY_SIZE(unk1)); + mt352_write(fe, unk2, ARRAY_SIZE(unk2)); + + deb_rc("Demod init!\n"); + + return 0; +} + +static struct mt352_config megasky_mt352_config = { + .demod_address = 0x0f, + .no_tuner = 1, + .demod_init = megasky_mt352_demod_init, +}; + +static int megasky_mt352_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb_rc("megasky_frontend_attach!\n"); + + if ((adap->fe = dvb_attach(mt352_attach, &megasky_mt352_config, &adap->dev->i2c_adap)) == NULL) + return -EIO; + + return 0; +} + +static struct qt1010_config megasky_qt1010_config = { + .i2c_address = 0x62 +}; + +static int megasky_qt1010_tuner_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(qt1010_attach, adap->fe, &adap->dev->i2c_adap, + &megasky_qt1010_config) == NULL) + return -ENODEV; + + return 0; +} + +static struct tda1004x_config digivox_tda10046_config = { + .demod_address = 0x08, + .invert = 0, + .invert_oclk = 0, + .ts_mode = TDA10046_TS_SERIAL, + .xtal_freq = TDA10046_XTAL_16M, + .if_freq = TDA10046_FREQ_045, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GPTRI, + .request_firmware = NULL, +}; + +static int digivox_tda10046_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb_rc("digivox_tda10046_frontend_attach!\n"); + + if ((adap->fe = dvb_attach(tda10046_attach, &digivox_tda10046_config, + &adap->dev->i2c_adap)) == NULL) + return -EIO; + + return 0; +} + +static int digivox_tda8275_tuner_attach(struct dvb_usb_adapter *adap) +{ + if (dvb_attach(tda827x_attach, adap->fe, 0x60, &adap->dev->i2c_adap, + NULL) == NULL) + return -ENODEV; + return 0; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties megasky_properties; +static struct dvb_usb_device_properties digivox_mini_ii_properties; + +static int m920x_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_host_interface *alt; + int ret; + + deb_rc("Probed!\n"); + + if (((ret = dvb_usb_device_init(intf, &megasky_properties, THIS_MODULE, &d)) == 0) || + ((ret = dvb_usb_device_init(intf, &digivox_mini_ii_properties, THIS_MODULE, &d)) == 0)) + goto found; + + return ret; + +found: + alt = usb_altnum_to_altsetting(intf, 1); + if (alt == NULL) { + deb_rc("No alt found!\n"); + return -ENODEV; + } + + ret = usb_set_interface(d->udev, alt->desc.bInterfaceNumber, + alt->desc.bAlternateSetting); + if (ret < 0) + return ret; + + if ((ret = m9206_init(d)) != 0) + return ret; + + return ret; +} + +static struct usb_device_id m920x_table [] = { + { USB_DEVICE(USB_VID_MSI, USB_PID_MSI_MEGASKY580) }, + { USB_DEVICE(USB_VID_ANUBIS_ELECTRONIC, + USB_PID_MSI_DIGI_VOX_MINI_II) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, m920x_table); + +static struct dvb_usb_device_properties megasky_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-megasky-02.fw", + .download_firmware = m9206_firmware_download, + + .rc_interval = 100, + .rc_key_map = megasky_rc_keys, + .rc_key_map_size = ARRAY_SIZE(megasky_rc_keys), + .rc_query = m9206_rc_query, + + .size_of_priv = sizeof(struct m9206_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m9206_pid_filter, + .pid_filter_ctrl = m9206_pid_filter_ctrl, + + .frontend_attach = megasky_mt352_frontend_attach, + .tuner_attach = megasky_qt1010_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 512, + } + } + }, + }}, + .i2c_algo = &m9206_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI Mega Sky 580 DVB-T USB2.0", + { &m920x_table[0], NULL }, + { NULL }, + } + } +}; + +static struct dvb_usb_device_properties digivox_mini_ii_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .firmware = "dvb-usb-digivox-02.fw", + .download_firmware = m9206_firmware_download, + + .size_of_priv = sizeof(struct m9206_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m9206_pid_filter, + .pid_filter_ctrl = m9206_pid_filter_ctrl, + + .frontend_attach = digivox_tda10046_frontend_attach, + .tuner_attach = digivox_tda8275_tuner_attach, + + .stream = { + .type = USB_BULK, + .count = 8, + .endpoint = 0x81, + .u = { + .bulk = { + .buffersize = 0x4000, + } + } + }, + }}, + .i2c_algo = &m9206_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "MSI DIGI VOX mini II DVB-T USB2.0", + { &m920x_table[1], NULL }, + { NULL }, + }, + } +}; + +static struct usb_driver m920x_driver = { + .name = "dvb_usb_m920x", + .probe = m920x_probe, + .disconnect = dvb_usb_device_exit, + .id_table = m920x_table, +}; + +/* module stuff */ +static int __init m920x_module_init(void) +{ + int ret; + + if ((ret = usb_register(&m920x_driver))) { + err("usb_register failed. Error number %d", ret); + return ret; + } + + return 0; +} + +static void __exit m920x_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&m920x_driver); +} + +module_init (m920x_module_init); +module_exit (m920x_module_exit); + +MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); +MODULE_DESCRIPTION("Driver MSI Mega Sky 580 DVB-T USB2.0 / Uli m920x"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h new file mode 100644 index 0000000..7dd3db6 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/m920x.h @@ -0,0 +1,67 @@ +#ifndef _DVB_USB_M920X_H_ +#define _DVB_USB_M920X_H_ + +#define DVB_USB_LOG_PREFIX "m920x" +#include "dvb-usb.h" + +#define deb_rc(args...) dprintk(dvb_usb_m920x_debug,0x01,args) + +#define M9206_CORE 0x22 +#define M9206_RC_STATE 0xff51 +#define M9206_RC_KEY 0xff52 +#define M9206_RC_INIT1 0xff54 +#define M9206_RC_INIT2 0xff55 +#define M9206_FW_GO 0xff69 + +#define M9206_I2C 0x23 +#define M9206_FILTER 0x25 +#define M9206_FW 0x30 + +#define M9206_MAX_FILTERS 8 + +/* +sequences found in logs: +[index value] +0x80 write addr +(0x00 out byte)* +0x40 out byte + +0x80 write addr +(0x00 out byte)* +0x80 read addr +(0x21 in byte)* +0x60 in byte + +this sequence works: +0x80 read addr +(0x21 in byte)* +0x60 in byte + +Guess at API of the I2C function: +I2C operation is done one byte at a time with USB control messages. The +index the messages is sent to is made up of a set of flags that control +the I2C bus state: +0x80: Send START condition. After a START condition, one would normally + always send the 7-bit slave I2C address as the 7 MSB, followed by + the read/write bit as the LSB. +0x40: Send STOP condition. This should be set on the last byte of an + I2C transaction. +0x20: Read a byte from the slave. As opposed to writing a byte to the + slave. The slave will normally not produce any data unless you + set the R/W bit to 1 when sending the slave's address after the + START condition. +0x01: Respond with ACK, as opposed to a NACK. For a multi-byte read, + the master should send an ACK, that is pull SDA low during the 9th + clock cycle, after every byte but the last. This flags only makes + sense when bit 0x20 is set, indicating a read. + +What any other bits might mean, or how to get the slave's ACK/NACK +response to a write, is unknown. +*/ + +struct m9206_state { + u16 filters[M9206_MAX_FILTERS]; + int filtering_enabled; + int rep_count; +}; +#endif diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c new file mode 100644 index 0000000..518d7ad --- /dev/null +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -0,0 +1,581 @@ +/* DVB USB framework compliant Linux driver for the Opera1 DVB-S Card +* +* Copyright (C) 2006 Mario Hlawitschka (dh1pa@amsat.org) +* Copyright (C) 2006 Marco Gittler (g.marco@freenet.de) +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the Free +* Software Foundation, version 2. +* +* see Documentation/dvb/README.dvb-usb for more information +*/ + +#include "opera1.h" +#include "stv0299.h" + +#define OPERA_READ_MSG 0 +#define OPERA_WRITE_MSG 1 +#define OPERA_I2C_TUNER 0xd1 + +#define READ_FX2_REG_REQ 0xba +#define READ_MAC_ADDR 0x08 +#define OPERA_WRITE_FX2 0xbb +#define OPERA_TUNER_REQ 0xb1 +#define REG_1F_SYMBOLRATE_BYTE0 0x1f +#define REG_20_SYMBOLRATE_BYTE1 0x20 +#define REG_21_SYMBOLRATE_BYTE2 0x21 + +#define ADDR_B600_VOLTAGE_13V (0x02) +#define ADDR_B601_VOLTAGE_18V (0x03) +#define ADDR_B1A6_STREAM_CTRL (0x04) +#define ADDR_B880_READ_REMOTE (0x05) + +struct opera1_state { + u32 last_key_pressed; +}; +struct opera_rc_keys { + u32 keycode; + u32 event; +}; + +int dvb_usb_opera1_debug; +module_param_named(debug, dvb_usb_opera1_debug, int, 0644); +MODULE_PARM_DESC(debug, + "set debugging level (1=info,xfer=2,pll=4,ts=8,err=16,rc=32,fw=64 (or-able))." + DVB_USB_DEBUG_STATUS); + +static int opera1_xilinx_rw(struct usb_device *dev, u8 request, u16 value, + u8 * data, u16 len, int flags) +{ + int ret; + u8 r; + u8 u8buf[len]; + + unsigned int pipe = (flags == OPERA_READ_MSG) ? + usb_rcvctrlpipe(dev,0) : usb_sndctrlpipe(dev, 0); + u8 request_type = (flags == OPERA_READ_MSG) ? USB_DIR_IN : USB_DIR_OUT; + + if (flags == OPERA_WRITE_MSG) + memcpy(u8buf, data, len); + ret = + usb_control_msg(dev, pipe, request, request_type | USB_TYPE_VENDOR, + value, 0x0, u8buf, len, 2000); + + if (request == OPERA_TUNER_REQ) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + OPERA_TUNER_REQ, USB_DIR_IN | USB_TYPE_VENDOR, + 0x01, 0x0, &r, 1, 2000)<1 || r!=0x08) + return 0; + } + if (flags == OPERA_READ_MSG) + memcpy(data, u8buf, len); + return ret; +} + +/* I2C */ + +static int opera1_usb_i2c_msgxfer(struct dvb_usb_device *dev, u16 addr, + u8 * buf, u16 len) +{ + int ret = 0; + u8 request; + u16 value; + + if (!dev) { + info("no usb_device"); + return -EINVAL; + } + if (mutex_lock_interruptible(&dev->usb_mutex) < 0) + return -EAGAIN; + + switch (addr>>1){ + case ADDR_B600_VOLTAGE_13V: + request=0xb6; + value=0x00; + break; + case ADDR_B601_VOLTAGE_18V: + request=0xb6; + value=0x01; + break; + case ADDR_B1A6_STREAM_CTRL: + request=0xb1; + value=0xa6; + break; + case ADDR_B880_READ_REMOTE: + request=0xb8; + value=0x80; + break; + default: + request=0xb1; + value=addr; + } + ret = opera1_xilinx_rw(dev->udev, request, + value, buf, len, + addr&0x01?OPERA_READ_MSG:OPERA_WRITE_MSG); + + mutex_unlock(&dev->usb_mutex); + return ret; +} + +static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, tmp = 0; + + if (!d) + return -ENODEV; + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + if ((tmp = opera1_usb_i2c_msgxfer(d, + (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), + msg[i].buf, + msg[i].len + )!= msg[i].len)) { + break; + } + if (dvb_usb_opera1_debug & 0x10) + info("sending i2c mesage %d %d", tmp, msg[i].len); + } + mutex_unlock(&d->i2c_mutex); + return num; +} + +static u32 opera1_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm opera1_i2c_algo = { + .master_xfer = opera1_i2c_xfer, + .functionality = opera1_i2c_func, +}; + +static int opera1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + static u8 command_13v[1]={0x00}; + static u8 command_18v[1]={0x01}; + struct i2c_msg msg[] = { + {.addr = ADDR_B600_VOLTAGE_13V,.flags = 0,.buf = command_13v,.len = 1}, + }; + struct dvb_usb_adapter *udev_adap = + (struct dvb_usb_adapter *)(fe->dvb->priv); + if (voltage == SEC_VOLTAGE_18) { + msg[0].addr = ADDR_B601_VOLTAGE_18V; + msg[0].buf = command_18v; + } + i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_stv0299_set_symbol_rate(struct dvb_frontend *fe, u32 srate, + u32 ratio) +{ + stv0299_writereg(fe, 0x13, 0x98); + stv0299_writereg(fe, 0x14, 0x95); + stv0299_writereg(fe, REG_1F_SYMBOLRATE_BYTE0, (ratio >> 16) & 0xff); + stv0299_writereg(fe, REG_20_SYMBOLRATE_BYTE1, (ratio >> 8) & 0xff); + stv0299_writereg(fe, REG_21_SYMBOLRATE_BYTE2, (ratio) & 0xf0); + return 0; + +} +static u8 opera1_inittab[] = { + 0x00, 0xa1, + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7d, + 0x05, 0x05, + 0x06, 0x02, + 0x07, 0x00, + 0x0b, 0x00, + 0x0c, 0x01, + 0x0d, 0x81, + 0x0e, 0x44, + 0x0f, 0x19, + 0x10, 0x3f, + 0x11, 0x84, + 0x12, 0xda, + 0x13, 0x98, + 0x14, 0x95, + 0x15, 0xc9, + 0x16, 0xeb, + 0x17, 0x00, + 0x18, 0x19, + 0x19, 0x8b, + 0x1a, 0x00, + 0x1b, 0x82, + 0x1c, 0x7f, + 0x1d, 0x00, + 0x1e, 0x00, + REG_1F_SYMBOLRATE_BYTE0, 0x06, + REG_20_SYMBOLRATE_BYTE1, 0x50, + REG_21_SYMBOLRATE_BYTE2, 0x10, + 0x22, 0x00, + 0x23, 0x00, + 0x24, 0x37, + 0x25, 0xbc, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x1e, + 0x2a, 0x14, + 0x2b, 0x1f, + 0x2c, 0x09, + 0x2d, 0x0a, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0x00, + 0x31, 0x1f, + 0x32, 0x19, + 0x33, 0xfc, + 0x34, 0x13, + 0xff, 0xff, +}; + +static struct stv0299_config opera1_stv0299_config = { + .demod_address = 0xd0>>1, + .min_delay_ms = 100, + .mclk = 88000000UL, + .invert = 1, + .skip_reinit = 0, + .lock_output = STV0229_LOCKOUTPUT_0, + .volt13_op0_op1 = STV0299_VOLT13_OP0, + .inittab = opera1_inittab, + .set_symbol_rate = opera1_stv0299_set_symbol_rate, +}; + +static int opera1_frontend_attach(struct dvb_usb_adapter *d) +{ + if ((d->fe = + dvb_attach(stv0299_attach, &opera1_stv0299_config, + &d->dev->i2c_adap)) != NULL) { + d->fe->ops.set_voltage = opera1_set_voltage; + return 0; + } + info("not attached stv0299"); + return -EIO; +} + +static int opera1_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach( + dvb_pll_attach, adap->fe, 0xc0>>1, + &adap->dev->i2c_adap, &dvb_pll_opera1 + ); + return 0; +} + +static int opera1_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 val = onoff ? 0x01 : 0x00; + + if (dvb_usb_opera1_debug) + info("power %s", onoff ? "on" : "off"); + return opera1_xilinx_rw(d->udev, 0xb7, val, + &val, 1, OPERA_WRITE_MSG); +} + +static int opera1_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + static u8 buf_start[2] = { 0xff, 0x03 }; + static u8 buf_stop[2] = { 0xff, 0x00 }; + struct i2c_msg start_tuner[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = onoff ? buf_start : buf_stop,.len = 2}, + }; + if (dvb_usb_opera1_debug) + info("streaming %s", onoff ? "on" : "off"); + i2c_transfer(&adap->dev->i2c_adap, start_tuner, 1); + return 0; +} + +static int opera1_pid_filter(struct dvb_usb_adapter *adap, int index, u16 pid, + int onoff) +{ + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("pidfilter index: %d pid: %d %s", index, pid, + onoff ? "on" : "off"); + b_pid[0] = (2 * index) + 4; + b_pid[1] = onoff ? (pid & 0xff) : (0x00); + b_pid[2] = onoff ? ((pid >> 8) & 0xff) : (0x00); + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + return 0; +} + +static int opera1_pid_filter_control(struct dvb_usb_adapter *adap, int onoff) +{ + int u = 0x04; + u8 b_pid[3]; + struct i2c_msg msg[] = { + {.addr = ADDR_B1A6_STREAM_CTRL,.buf = b_pid,.len = 3}, + }; + if (dvb_usb_opera1_debug) + info("%s hw-pidfilter", onoff ? "enable" : "disable"); + for (; u < 0x7e; u += 2) { + b_pid[0] = u; + b_pid[1] = 0; + b_pid[2] = 0x80; + i2c_transfer(&adap->dev->i2c_adap, msg, 1); + } + return 0; +} + +static struct dvb_usb_rc_key opera1_rc_keys[] = { + {0x5f, 0xa0, KEY_1}, + {0x51, 0xaf, KEY_2}, + {0x5d, 0xa2, KEY_3}, + {0x41, 0xbe, KEY_4}, + {0x0b, 0xf5, KEY_5}, + {0x43, 0xbd, KEY_6}, + {0x47, 0xb8, KEY_7}, + {0x49, 0xb6, KEY_8}, + {0x05, 0xfa, KEY_9}, + {0x45, 0xba, KEY_0}, + {0x09, 0xf6, KEY_UP}, /*chanup */ + {0x1b, 0xe5, KEY_DOWN}, /*chandown */ + {0x5d, 0xa3, KEY_LEFT}, /*voldown */ + {0x5f, 0xa1, KEY_RIGHT}, /*volup */ + {0x07, 0xf8, KEY_SPACE}, /*tab */ + {0x1f, 0xe1, KEY_ENTER}, /*play ok */ + {0x1b, 0xe4, KEY_Z}, /*zoom */ + {0x59, 0xa6, KEY_M}, /*mute */ + {0x5b, 0xa5, KEY_F}, /*tv/f */ + {0x19, 0xe7, KEY_R}, /*rec */ + {0x01, 0xfe, KEY_S}, /*Stop */ + {0x03, 0xfd, KEY_P}, /*pause */ + {0x03, 0xfc, KEY_W}, /*<- -> */ + {0x07, 0xf9, KEY_C}, /*capture */ + {0x47, 0xb9, KEY_Q}, /*exit */ + {0x43, 0xbc, KEY_O}, /*power */ + +}; + +static int opera1_rc_query(struct dvb_usb_device *dev, u32 * event, int *state) +{ + struct opera1_state *opst = dev->priv; + u8 rcbuffer[32]; + const u16 startmarker1 = 0x10ed; + const u16 startmarker2 = 0x11ec; + struct i2c_msg read_remote[] = { + {.addr = ADDR_B880_READ_REMOTE,.buf = rcbuffer,.flags = I2C_M_RD,.len = 32}, + }; + int i = 0; + u32 send_key = 0; + + if (i2c_transfer(&dev->i2c_adap, read_remote, 1) == 1) { + for (i = 0; i < 32; i++) { + if (rcbuffer[i]) + send_key |= 1; + if (i < 31) + send_key = send_key << 1; + } + if (send_key & 0x8000) + send_key = (send_key << 1) | (send_key >> 15 & 0x01); + + if (send_key == 0xffff && opst->last_key_pressed != 0) { + *state = REMOTE_KEY_REPEAT; + *event = opst->last_key_pressed; + return 0; + } + for (; send_key != 0;) { + if (send_key >> 16 == startmarker2) { + break; + } else if (send_key >> 16 == startmarker1) { + send_key = + (send_key & 0xfffeffff) | (startmarker1 << 16); + break; + } else + send_key >>= 1; + } + + if (send_key == 0) + return 0; + + send_key = (send_key & 0xffff) | 0x0100; + + for (i = 0; i < ARRAY_SIZE(opera1_rc_keys); i++) { + if ((opera1_rc_keys[i].custom * 256 + + opera1_rc_keys[i].data) == (send_key & 0xffff)) { + *state = REMOTE_KEY_PRESSED; + *event = opera1_rc_keys[i].event; + opst->last_key_pressed = + opera1_rc_keys[i].event; + break; + } + opst->last_key_pressed = 0; + } + } else + *state = REMOTE_NO_KEY_PRESSED; + return 0; +} + +static struct usb_device_id opera1_table[] = { + {USB_DEVICE(USB_VID_CYPRESS, USB_PID_OPERA1_COLD)}, + {USB_DEVICE(USB_VID_OPERA1, USB_PID_OPERA1_WARM)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, opera1_table); + +static int opera1_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + u8 command[] = { READ_MAC_ADDR }; + opera1_xilinx_rw(d->udev, 0xb1, 0xa0, command, 1, OPERA_WRITE_MSG); + opera1_xilinx_rw(d->udev, 0xb1, 0xa1, mac, 6, OPERA_READ_MSG); + return 0; +} +static int opera1_xilinx_load_firmware(struct usb_device *dev, + const char *filename) +{ + const struct firmware *fw = NULL; + u8 *b, *p; + int ret = 0, i; + u8 testval; + info("start downloading fpga firmware"); + + if ((ret = request_firmware(&fw, filename, &dev->dev)) != 0) { + err("did not find the firmware file. (%s) " + "Please see linux/Documentation/dvb/ for more details on firmware-problems.", + filename); + return ret; + } else { + p = kmalloc(fw->size, GFP_KERNEL); + opera1_xilinx_rw(dev, 0xbc, 0x00, &testval, 1, OPERA_READ_MSG); + if (p != NULL && testval != 0x67) { + + u8 reset = 0, fpga_command = 0; + memcpy(p, fw->data, fw->size); + /* clear fpga ? */ + opera1_xilinx_rw(dev, 0xbc, 0xaa, &fpga_command, 1, + OPERA_WRITE_MSG); + for (i = 0; p[i] != 0 && i < fw->size;) { + b = (u8 *) p + i; + if (opera1_xilinx_rw + (dev, OPERA_WRITE_FX2, 0x0, b + 1, b[0], + OPERA_WRITE_MSG) != b[0] + ) { + err("error while transferring firmware"); + ret = -EINVAL; + break; + } + i = i + 1 + b[0]; + } + /* restart the CPU */ + if (ret || opera1_xilinx_rw + (dev, 0xa0, 0xe600, &reset, 1, + OPERA_WRITE_MSG) != 1) { + err("could not restart the USB controller CPU."); + ret = -EINVAL; + } + kfree(p); + } + } + if (fw) { + release_firmware(fw); + } + return ret; +} + +static struct dvb_usb_device_properties opera1_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-opera-01.fw", + .size_of_priv = sizeof(struct opera1_state), + + .power_ctrl = opera1_power_ctrl, + .i2c_algo = &opera1_i2c_algo, + + .rc_key_map = opera1_rc_keys, + .rc_key_map_size = ARRAY_SIZE(opera1_rc_keys), + .rc_interval = 200, + .rc_query = opera1_rc_query, + .read_mac_address = opera1_read_mac_address, + .generic_bulk_ctrl_endpoint = 0x00, + /* parameter for the MPEG2-data transfer */ + .num_adapters = 1, + .adapter = { + { + .frontend_attach = opera1_frontend_attach, + .streaming_ctrl = opera1_streaming_ctrl, + .tuner_attach = opera1_tuner_attach, + .caps = + DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter = opera1_pid_filter, + .pid_filter_ctrl = opera1_pid_filter_control, + .pid_filter_count = 252, + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, + .num_device_descs = 1, + .devices = { + {"Opera1 DVB-S USB2.0", + {&opera1_table[0], NULL}, + {&opera1_table[1], NULL}, + }, + } +}; + +static int opera1_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct dvb_usb_device *d; + struct usb_device *udev = interface_to_usbdev(intf); + + if (udev->descriptor.idProduct == USB_PID_OPERA1_WARM && + udev->descriptor.idVendor == USB_VID_OPERA1 && + (d == NULL + || opera1_xilinx_load_firmware(udev, "dvb-usb-opera1-fpga.fw") != 0) + ) { + return -EINVAL; + } + + if (dvb_usb_device_init(intf, &opera1_properties, THIS_MODULE, &d) != 0) + return -EINVAL; + return 0; +} + +static struct usb_driver opera1_driver = { + .name = "opera1", + .probe = opera1_probe, + .disconnect = dvb_usb_device_exit, + .id_table = opera1_table, +}; + +static int __init opera1_module_init(void) +{ + int result = 0; + if ((result = usb_register(&opera1_driver))) { + err("usb_register failed. Error number %d", result); + } + return result; +} + +static void __exit opera1_module_exit(void) +{ + usb_deregister(&opera1_driver); +} + +module_init(opera1_module_init); +module_exit(opera1_module_exit); + +MODULE_AUTHOR("Mario Hlawitschka (c) dh1pa@amsat.org"); +MODULE_AUTHOR("Marco Gittler (c) g.marco@freenet.de"); +MODULE_DESCRIPTION("Driver for Opera1 DVB-S device"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/opera1.h b/drivers/media/dvb/dvb-usb/opera1.h new file mode 100644 index 0000000..5317442 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/opera1.h @@ -0,0 +1,9 @@ +#ifndef _OPERA1_H_ +#define _OPERA1_H_ + +#define DVB_USB_LOG_PREFIX "opera" +#include "dvb-usb.h" + +extern int dvb_usb_opera1_debug; +#define deb_xfer(args...) dprintk(dvb_usb_opera1_debug,0x02,args) +#endif diff --git a/drivers/media/dvb/dvb-usb/ttusb2.c b/drivers/media/dvb/dvb-usb/ttusb2.c index 95d2997..88dc436 100644 --- a/drivers/media/dvb/dvb-usb/ttusb2.c +++ b/drivers/media/dvb/dvb-usb/ttusb2.c @@ -184,6 +184,7 @@ static int ttusb2_probe(struct usb_interface *intf, static struct usb_device_id ttusb2_table [] = { { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_400E) }, + { USB_DEVICE(USB_VID_PINNACLE, USB_PID_PCTV_450E) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, ttusb2_table); @@ -227,12 +228,16 @@ static struct dvb_usb_device_properties ttusb2_properties = { .generic_bulk_ctrl_endpoint = 0x01, - .num_device_descs = 1, + .num_device_descs = 2, .devices = { { "Pinnacle 400e DVB-S USB2.0", { &ttusb2_table[0], NULL }, { NULL }, }, + { "Pinnacle 450e DVB-S USB2.0", + { &ttusb2_table[1], NULL }, + { NULL }, + }, } }; diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index af314bb..ff44876 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -205,6 +205,13 @@ config DVB_TDA10021 help A DVB-C tuner module. Say Y when you want to support this frontend. +config DVB_TDA10023 + tristate "Philips TDA10023 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-C tuner module. Say Y when you want to support this frontend. + config DVB_STV0297 tristate "ST STV0297 based" depends on DVB_CORE && I2C @@ -280,8 +287,12 @@ comment "Tuners/PLL support" depends on DVB_CORE config DVB_PLL - tristate + tristate "Generic I2C PLL based tuners" depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + This module driver a number of tuners based on PLL chips with a + common I2C interface. Say Y when you want to support these tuners. config DVB_TDA826X tristate "Philips TDA826X silicon tuner" @@ -290,20 +301,26 @@ config DVB_TDA826X help A DVB-S silicon tuner module. Say Y when you want to support this tuner. -config DVB_TUNER_MT2060 - tristate "Microtune MT2060 silicon IF tuner" - depends on I2C +config DVB_TDA827X + tristate "Philips TDA827X silicon tuner" + depends on DVB_CORE && I2C default m if DVB_FE_CUSTOMISE help - A driver for the silicon IF tuner MT2060 from Microtune. + A DVB-T silicon tuner module. Say Y when you want to support this tuner. -config DVB_TUNER_LGH06XF - tristate "LG TDVS-H06xF ATSC tuner" +config DVB_TUNER_QT1010 + tristate "Quantek QT1010 silicon tuner" depends on DVB_CORE && I2C - select DVB_PLL default m if DVB_FE_CUSTOMISE help - A driver for the LG TDVS-H06xF ATSC tuner family. + A driver for the silicon tuner QT1010 from Quantek. + +config DVB_TUNER_MT2060 + tristate "Microtune MT2060 silicon IF tuner" + depends on I2C + default m if DVB_FE_CUSTOMISE + help + A driver for the silicon IF tuner MT2060 from Microtune. comment "Miscellaneous devices" depends on DVB_CORE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 3fa6e5d..27f3865 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_DVB_MT352) += mt352.o obj-$(CONFIG_DVB_ZL10353) += zl10353.o obj-$(CONFIG_DVB_CX22702) += cx22702.o obj-$(CONFIG_DVB_TDA10021) += tda10021.o +obj-$(CONFIG_DVB_TDA10023) += tda10023.o obj-$(CONFIG_DVB_STV0297) += stv0297.o obj-$(CONFIG_DVB_NXT200X) += nxt200x.o obj-$(CONFIG_DVB_OR51211) += or51211.o @@ -37,6 +38,7 @@ obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_ISL6421) += isl6421.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA826X) += tda826x.o +obj-$(CONFIG_DVB_TDA827X) += tda827x.o obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o +obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o -obj-$(CONFIG_DVB_TUNER_LGH06XF) += lgh06xf.o diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index ae96395..10fc0c8 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -254,7 +254,7 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate) if (srate<500000) srate=500000; - for(i=0;(i<sizeof(bands)/sizeof(bands[0]))&&(srate>bands[i]);i++) + for(i = 0; (i < ARRAY_SIZE(bands)) && (srate>bands[i]); i++) ; /* first, check which sample rate is appropriate: 45, 60 80 or 90 MHz, and set the PLL accordingly (R07[1:0] Fclk, R06[7:4] PLLmult, @@ -361,7 +361,7 @@ static int cx24110_initfe(struct dvb_frontend* fe) dprintk("%s: init chip\n", __FUNCTION__); - for(i=0;i<sizeof(cx24110_regdata)/sizeof(cx24110_regdata[0]);i++) { + for(i = 0; i < ARRAY_SIZE(cx24110_regdata); i++) { cx24110_writereg(state, cx24110_regdata[i].reg, cx24110_regdata[i].data); }; diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c index a356d28..732e94a 100644 --- a/drivers/media/dvb/frontends/cx24123.c +++ b/drivers/media/dvb/frontends/cx24123.c @@ -507,7 +507,7 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa int i = 0; int pump = 2; int band = 0; - int num_bands = sizeof(cx24123_bandselect_vals) / sizeof(cx24123_bandselect_vals[0]); + int num_bands = ARRAY_SIZE(cx24123_bandselect_vals); /* Defaults for low freq, low rate */ state->VCAarg = cx24123_AGC_vals[0].VCAprogdata; @@ -516,7 +516,7 @@ static int cx24123_pll_calculate(struct dvb_frontend* fe, struct dvb_frontend_pa vco_div = cx24123_bandselect_vals[0].VCOdivider; /* For the given symbol rate, determine the VCA, VGA and FILTUNE programming bits */ - for (i = 0; i < sizeof(cx24123_AGC_vals) / sizeof(cx24123_AGC_vals[0]); i++) + for (i = 0; i < ARRAY_SIZE(cx24123_AGC_vals); i++) { if ((cx24123_AGC_vals[i].symbolrate_low <= p->u.qpsk.symbol_rate) && (cx24123_AGC_vals[i].symbolrate_high >= p->u.qpsk.symbol_rate) ) { @@ -658,7 +658,7 @@ static int cx24123_initfe(struct dvb_frontend* fe) dprintk("%s: init frontend\n",__FUNCTION__); /* Configure the demod to a good set of defaults */ - for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++) + for (i = 0; i < ARRAY_SIZE(cx24123_regdata); i++) cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data); /* Set the LNB polarity */ diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c index adbabfd..b6adea5 100644 --- a/drivers/media/dvb/frontends/dib3000mb.c +++ b/drivers/media/dvb/frontends/dib3000mb.c @@ -239,7 +239,7 @@ static int dib3000mb_set_frontend(struct dvb_frontend* fe, default: return -EINVAL; } - deb_setf("hierachy: "); + deb_setf("hierarchy: "); switch (ofdm->hierarchy_information) { case HIERARCHY_NONE: deb_setf("none "); diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c index 23aa75a..054d7e6 100644 --- a/drivers/media/dvb/frontends/dib3000mc.c +++ b/drivers/media/dvb/frontends/dib3000mc.c @@ -475,7 +475,7 @@ static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, struct dibx tmp = ((chan->nfft & 0x1) << 7) | (chan->guard << 5) | (chan->nqam << 3) | chan->vit_alpha; dib3000mc_write_word(state, 0, tmp); - dib3000mc_write_word(state, 5, seq); + dib3000mc_write_word(state, 5, (1 << 8) | ((seq & 0xf) << 4)); tmp = (chan->vit_hrch << 4) | (chan->vit_select_hp); if (!chan->vit_hrch || (chan->vit_hrch && chan->vit_select_hp)) diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c index 62de760..5f96ffd 100644 --- a/drivers/media/dvb/frontends/dvb-pll.c +++ b/drivers/media/dvb/frontends/dvb-pll.c @@ -27,17 +27,29 @@ /* ----------------------------------------------------------- */ /* descriptions */ +/* Set AGC TOP value to 103 dBuV: + 0x80 = Control Byte + 0x40 = 250 uA charge pump (irrelevant) + 0x18 = Aux Byte to follow + 0x06 = 64.5 kHz divider (irrelevant) + 0x01 = Disable Vt (aka sleep) + + 0x00 = AGC Time constant 2s Iagc = 300 nA (vs 0x80 = 9 nA) + 0x50 = AGC Take over point = 103 dBuV */ +static u8 tua603x_agc103[] = { 2, 0x80|0x40|0x18|0x06|0x01, 0x00|0x50 }; + struct dvb_pll_desc dvb_pll_thomson_dtt7579 = { .name = "Thomson dtt7579", .min = 177000000, .max = 858000000, - .count = 5, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xb4, 0x03 }, + .count = 4, .entries = { - { 0, 36166667, 166666, 0xb4, 0x03 }, /* go sleep */ - { 443250000, 36166667, 166666, 0xb4, 0x02 }, - { 542000000, 36166667, 166666, 0xb4, 0x08 }, - { 771000000, 36166667, 166666, 0xbc, 0x08 }, - { 999999999, 36166667, 166666, 0xf4, 0x08 }, + { 443250000, 166667, 0xb4, 0x02 }, + { 542000000, 166667, 0xb4, 0x08 }, + { 771000000, 166667, 0xbc, 0x08 }, + { 999999999, 166667, 0xf4, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt7579); @@ -46,11 +58,12 @@ struct dvb_pll_desc dvb_pll_thomson_dtt7610 = { .name = "Thomson dtt7610", .min = 44000000, .max = 958000000, + .iffreq= 44000000, .count = 3, .entries = { - { 157250000, 44000000, 62500, 0x8e, 0x39 }, - { 454000000, 44000000, 62500, 0x8e, 0x3a }, - { 999999999, 44000000, 62500, 0x8e, 0x3c }, + { 157250000, 62500, 0x8e, 0x39 }, + { 454000000, 62500, 0x8e, 0x3a }, + { 999999999, 62500, 0x8e, 0x3c }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt7610); @@ -66,14 +79,15 @@ struct dvb_pll_desc dvb_pll_thomson_dtt759x = { .min = 177000000, .max = 896000000, .setbw = thomson_dtt759x_bw, - .count = 6, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0x84, 0x03 }, + .count = 5, .entries = { - { 0, 36166667, 166666, 0x84, 0x03 }, - { 264000000, 36166667, 166666, 0xb4, 0x02 }, - { 470000000, 36166667, 166666, 0xbc, 0x02 }, - { 735000000, 36166667, 166666, 0xbc, 0x08 }, - { 835000000, 36166667, 166666, 0xf4, 0x08 }, - { 999999999, 36166667, 166666, 0xfc, 0x08 }, + { 264000000, 166667, 0xb4, 0x02 }, + { 470000000, 166667, 0xbc, 0x02 }, + { 735000000, 166667, 0xbc, 0x08 }, + { 835000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt759x); @@ -82,14 +96,15 @@ struct dvb_pll_desc dvb_pll_lg_z201 = { .name = "LG z201", .min = 174000000, .max = 862000000, - .count = 6, + .iffreq= 36166667, + .sleepdata = (u8[]){ 2, 0xbc, 0x03 }, + .count = 5, .entries = { - { 0, 36166667, 166666, 0xbc, 0x03 }, - { 157500000, 36166667, 166666, 0xbc, 0x01 }, - { 443250000, 36166667, 166666, 0xbc, 0x02 }, - { 542000000, 36166667, 166666, 0xbc, 0x04 }, - { 830000000, 36166667, 166666, 0xf4, 0x04 }, - { 999999999, 36166667, 166666, 0xfc, 0x04 }, + { 157500000, 166667, 0xbc, 0x01 }, + { 443250000, 166667, 0xbc, 0x02 }, + { 542000000, 166667, 0xbc, 0x04 }, + { 830000000, 166667, 0xf4, 0x04 }, + { 999999999, 166667, 0xfc, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_lg_z201); @@ -98,11 +113,12 @@ struct dvb_pll_desc dvb_pll_microtune_4042 = { .name = "Microtune 4042 FI5", .min = 57000000, .max = 858000000, + .iffreq= 44000000, .count = 3, .entries = { - { 162000000, 44000000, 62500, 0x8e, 0xa1 }, - { 457000000, 44000000, 62500, 0x8e, 0x91 }, - { 999999999, 44000000, 62500, 0x8e, 0x31 }, + { 162000000, 62500, 0x8e, 0xa1 }, + { 457000000, 62500, 0x8e, 0x91 }, + { 999999999, 62500, 0x8e, 0x31 }, }, }; EXPORT_SYMBOL(dvb_pll_microtune_4042); @@ -112,11 +128,13 @@ struct dvb_pll_desc dvb_pll_thomson_dtt761x = { .name = "Thomson dtt761x", .min = 57000000, .max = 863000000, + .iffreq= 44000000, .count = 3, + .initdata = tua603x_agc103, .entries = { - { 147000000, 44000000, 62500, 0x8e, 0x39 }, - { 417000000, 44000000, 62500, 0x8e, 0x3a }, - { 999999999, 44000000, 62500, 0x8e, 0x3c }, + { 147000000, 62500, 0x8e, 0x39 }, + { 417000000, 62500, 0x8e, 0x3a }, + { 999999999, 62500, 0x8e, 0x3c }, }, }; EXPORT_SYMBOL(dvb_pll_thomson_dtt761x); @@ -125,17 +143,18 @@ struct dvb_pll_desc dvb_pll_unknown_1 = { .name = "unknown 1", /* used by dntv live dvb-t */ .min = 174000000, .max = 862000000, + .iffreq= 36166667, .count = 9, .entries = { - { 150000000, 36166667, 166666, 0xb4, 0x01 }, - { 173000000, 36166667, 166666, 0xbc, 0x01 }, - { 250000000, 36166667, 166666, 0xb4, 0x02 }, - { 400000000, 36166667, 166666, 0xbc, 0x02 }, - { 420000000, 36166667, 166666, 0xf4, 0x02 }, - { 470000000, 36166667, 166666, 0xfc, 0x02 }, - { 600000000, 36166667, 166666, 0xbc, 0x08 }, - { 730000000, 36166667, 166666, 0xf4, 0x08 }, - { 999999999, 36166667, 166666, 0xfc, 0x08 }, + { 150000000, 166667, 0xb4, 0x01 }, + { 173000000, 166667, 0xbc, 0x01 }, + { 250000000, 166667, 0xb4, 0x02 }, + { 400000000, 166667, 0xbc, 0x02 }, + { 420000000, 166667, 0xf4, 0x02 }, + { 470000000, 166667, 0xfc, 0x02 }, + { 600000000, 166667, 0xbc, 0x08 }, + { 730000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x08 }, }, }; EXPORT_SYMBOL(dvb_pll_unknown_1); @@ -147,11 +166,12 @@ struct dvb_pll_desc dvb_pll_tua6010xs = { .name = "Infineon TUA6010XS", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 3, .entries = { - { 115750000, 36125000, 62500, 0x8e, 0x03 }, - { 403250000, 36125000, 62500, 0x8e, 0x06 }, - { 999999999, 36125000, 62500, 0x8e, 0x85 }, + { 115750000, 62500, 0x8e, 0x03 }, + { 403250000, 62500, 0x8e, 0x06 }, + { 999999999, 62500, 0x8e, 0x85 }, }, }; EXPORT_SYMBOL(dvb_pll_tua6010xs); @@ -161,12 +181,13 @@ struct dvb_pll_desc dvb_pll_env57h1xd5 = { .name = "Panasonic ENV57H1XD5", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 4, .entries = { - { 153000000, 36291666, 166666, 0xc2, 0x41 }, - { 470000000, 36291666, 166666, 0xc2, 0x42 }, - { 526000000, 36291666, 166666, 0xc2, 0x84 }, - { 999999999, 36291666, 166666, 0xc2, 0xa4 }, + { 153000000, 166667, 0xc2, 0x41 }, + { 470000000, 166667, 0xc2, 0x42 }, + { 526000000, 166667, 0xc2, 0x84 }, + { 999999999, 166667, 0xc2, 0xa4 }, }, }; EXPORT_SYMBOL(dvb_pll_env57h1xd5); @@ -185,20 +206,21 @@ struct dvb_pll_desc dvb_pll_tda665x = { .min = 44250000, .max = 858000000, .setbw = tda665x_bw, + .iffreq= 36166667, .count = 12, .entries = { - { 93834000, 36249333, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, - { 123834000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, - { 161000000, 36249333, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, - { 163834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, - { 253834000, 36249333, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, - { 383834000, 36249333, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, - { 443834000, 36249333, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, - { 444000000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, - { 583834000, 36249333, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, - { 793834000, 36249333, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, - { 444834000, 36249333, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, - { 861000000, 36249333, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, + { 93834000, 166667, 0xca, 0x61 /* 011 0 0 0 01 */ }, + { 123834000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 161000000, 166667, 0xca, 0xa1 /* 101 0 0 0 01 */ }, + { 163834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 253834000, 166667, 0xca, 0x62 /* 011 0 0 0 10 */ }, + { 383834000, 166667, 0xca, 0xa2 /* 101 0 0 0 10 */ }, + { 443834000, 166667, 0xca, 0xc2 /* 110 0 0 0 10 */ }, + { 444000000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 583834000, 166667, 0xca, 0x64 /* 011 0 0 1 00 */ }, + { 793834000, 166667, 0xca, 0xa4 /* 101 0 0 1 00 */ }, + { 444834000, 166667, 0xca, 0xc4 /* 110 0 0 1 00 */ }, + { 861000000, 166667, 0xca, 0xe4 /* 111 0 0 1 00 */ }, } }; EXPORT_SYMBOL(dvb_pll_tda665x); @@ -216,12 +238,13 @@ struct dvb_pll_desc dvb_pll_tua6034 = { .name = "Infineon TUA6034", .min = 44250000, .max = 858000000, + .iffreq= 36166667, .count = 3, .setbw = tua6034_bw, .entries = { - { 174500000, 36166667, 62500, 0xce, 0x01 }, - { 230000000, 36166667, 62500, 0xce, 0x02 }, - { 999999999, 36166667, 62500, 0xce, 0x04 }, + { 174500000, 62500, 0xce, 0x01 }, + { 230000000, 62500, 0xce, 0x02 }, + { 999999999, 62500, 0xce, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_tua6034); @@ -233,11 +256,13 @@ struct dvb_pll_desc dvb_pll_lg_tdvs_h06xf = { .name = "LG TDVS-H06xF", .min = 54000000, .max = 863000000, + .iffreq= 44000000, + .initdata = tua603x_agc103, .count = 3, .entries = { - { 165000000, 44000000, 62500, 0xce, 0x01 }, - { 450000000, 44000000, 62500, 0xce, 0x02 }, - { 999999999, 44000000, 62500, 0xce, 0x04 }, + { 165000000, 62500, 0xce, 0x01 }, + { 450000000, 62500, 0xce, 0x02 }, + { 999999999, 62500, 0xce, 0x04 }, }, }; EXPORT_SYMBOL(dvb_pll_lg_tdvs_h06xf); @@ -255,16 +280,17 @@ struct dvb_pll_desc dvb_pll_fmd1216me = { .name = "Philips FMD1216ME", .min = 50870000, .max = 858000000, + .iffreq= 36125000, .setbw = fmd1216me_bw, .count = 7, .entries = { - { 143870000, 36213333, 166667, 0xbc, 0x41 }, - { 158870000, 36213333, 166667, 0xf4, 0x41 }, - { 329870000, 36213333, 166667, 0xbc, 0x42 }, - { 441870000, 36213333, 166667, 0xf4, 0x42 }, - { 625870000, 36213333, 166667, 0xbc, 0x44 }, - { 803870000, 36213333, 166667, 0xf4, 0x44 }, - { 999999999, 36213333, 166667, 0xfc, 0x44 }, + { 143870000, 166667, 0xbc, 0x41 }, + { 158870000, 166667, 0xf4, 0x41 }, + { 329870000, 166667, 0xbc, 0x42 }, + { 441870000, 166667, 0xf4, 0x42 }, + { 625870000, 166667, 0xbc, 0x44 }, + { 803870000, 166667, 0xf4, 0x44 }, + { 999999999, 166667, 0xfc, 0x44 }, } }; EXPORT_SYMBOL(dvb_pll_fmd1216me); @@ -282,13 +308,14 @@ struct dvb_pll_desc dvb_pll_tded4 = { .name = "ALPS TDED4", .min = 47000000, .max = 863000000, + .iffreq= 36166667, .setbw = tded4_bw, .count = 4, .entries = { - { 153000000, 36166667, 166667, 0x85, 0x01 }, - { 470000000, 36166667, 166667, 0x85, 0x02 }, - { 823000000, 36166667, 166667, 0x85, 0x08 }, - { 999999999, 36166667, 166667, 0x85, 0x88 }, + { 153000000, 166667, 0x85, 0x01 }, + { 470000000, 166667, 0x85, 0x02 }, + { 823000000, 166667, 0x85, 0x08 }, + { 999999999, 166667, 0x85, 0x88 }, } }; EXPORT_SYMBOL(dvb_pll_tded4); @@ -300,12 +327,13 @@ struct dvb_pll_desc dvb_pll_tdhu2 = { .name = "ALPS TDHU2", .min = 54000000, .max = 864000000, + .iffreq= 44000000, .count = 4, .entries = { - { 162000000, 44000000, 62500, 0x85, 0x01 }, - { 426000000, 44000000, 62500, 0x85, 0x02 }, - { 782000000, 44000000, 62500, 0x85, 0x08 }, - { 999999999, 44000000, 62500, 0x85, 0x88 }, + { 162000000, 62500, 0x85, 0x01 }, + { 426000000, 62500, 0x85, 0x02 }, + { 782000000, 62500, 0x85, 0x08 }, + { 999999999, 62500, 0x85, 0x88 }, } }; EXPORT_SYMBOL(dvb_pll_tdhu2); @@ -317,11 +345,12 @@ struct dvb_pll_desc dvb_pll_tuv1236d = { .name = "Philips TUV1236D", .min = 54000000, .max = 864000000, + .iffreq= 44000000, .count = 3, .entries = { - { 157250000, 44000000, 62500, 0xc6, 0x41 }, - { 454000000, 44000000, 62500, 0xc6, 0x42 }, - { 999999999, 44000000, 62500, 0xc6, 0x44 }, + { 157250000, 62500, 0xc6, 0x41 }, + { 454000000, 62500, 0xc6, 0x42 }, + { 999999999, 62500, 0xc6, 0x44 }, }, }; EXPORT_SYMBOL(dvb_pll_tuv1236d); @@ -333,14 +362,15 @@ struct dvb_pll_desc dvb_pll_samsung_tbmv = { .name = "Samsung TBMV30111IN / TBMV30712IN1", .min = 54000000, .max = 860000000, + .iffreq= 44000000, .count = 6, .entries = { - { 172000000, 44000000, 166666, 0xb4, 0x01 }, - { 214000000, 44000000, 166666, 0xb4, 0x02 }, - { 467000000, 44000000, 166666, 0xbc, 0x02 }, - { 721000000, 44000000, 166666, 0xbc, 0x08 }, - { 841000000, 44000000, 166666, 0xf4, 0x08 }, - { 999999999, 44000000, 166666, 0xfc, 0x02 }, + { 172000000, 166667, 0xb4, 0x01 }, + { 214000000, 166667, 0xb4, 0x02 }, + { 467000000, 166667, 0xbc, 0x02 }, + { 721000000, 166667, 0xbc, 0x08 }, + { 841000000, 166667, 0xf4, 0x08 }, + { 999999999, 166667, 0xfc, 0x02 }, } }; EXPORT_SYMBOL(dvb_pll_samsung_tbmv); @@ -352,12 +382,13 @@ struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261 = { .name = "Philips SD1878", .min = 950000, .max = 2150000, + .iffreq= 249, /* zero-IF, offset 249 is to round up */ .count = 4, .entries = { - { 1250000, 499, 500, 0xc4, 0x00}, - { 1550000, 499, 500, 0xc4, 0x40}, - { 2050000, 499, 500, 0xc4, 0x80}, - { 2150000, 499, 500, 0xc4, 0xc0}, + { 1250000, 500, 0xc4, 0x00}, + { 1550000, 500, 0xc4, 0x40}, + { 2050000, 500, 0xc4, 0x80}, + { 2150000, 500, 0xc4, 0xc0}, }, }; EXPORT_SYMBOL(dvb_pll_philips_sd1878_tda8261); @@ -388,18 +419,19 @@ struct dvb_pll_desc dvb_pll_philips_td1316 = { .name = "Philips TD1316", .min = 87000000, .max = 895000000, + .iffreq= 36166667, .setbw = td1316_bw, .count = 9, .entries = { - { 93834000, 36166000, 166666, 0xca, 0x60}, - { 123834000, 36166000, 166666, 0xca, 0xa0}, - { 163834000, 36166000, 166666, 0xca, 0xc0}, - { 253834000, 36166000, 166666, 0xca, 0x60}, - { 383834000, 36166000, 166666, 0xca, 0xa0}, - { 443834000, 36166000, 166666, 0xca, 0xc0}, - { 583834000, 36166000, 166666, 0xca, 0x60}, - { 793834000, 36166000, 166666, 0xca, 0xa0}, - { 858834000, 36166000, 166666, 0xca, 0xe0}, + { 93834000, 166667, 0xca, 0x60}, + { 123834000, 166667, 0xca, 0xa0}, + { 163834000, 166667, 0xca, 0xc0}, + { 253834000, 166667, 0xca, 0x60}, + { 383834000, 166667, 0xca, 0xa0}, + { 443834000, 166667, 0xca, 0xc0}, + { 583834000, 166667, 0xca, 0x60}, + { 793834000, 166667, 0xca, 0xa0}, + { 858834000, 166667, 0xca, 0xe0}, }, }; EXPORT_SYMBOL(dvb_pll_philips_td1316); @@ -409,15 +441,41 @@ struct dvb_pll_desc dvb_pll_thomson_fe6600 = { .name = "Thomson FE6600", .min = 44250000, .max = 858000000, + .iffreq= 36125000, .count = 4, .entries = { - { 250000000, 36213333, 166667, 0xb4, 0x12 }, - { 455000000, 36213333, 166667, 0xfe, 0x11 }, - { 775500000, 36213333, 166667, 0xbc, 0x18 }, - { 999999999, 36213333, 166667, 0xf4, 0x18 }, + { 250000000, 166667, 0xb4, 0x12 }, + { 455000000, 166667, 0xfe, 0x11 }, + { 775500000, 166667, 0xbc, 0x18 }, + { 999999999, 166667, 0xf4, 0x18 }, } }; EXPORT_SYMBOL(dvb_pll_thomson_fe6600); +static void opera1_bw(u8 *buf, u32 freq, int bandwidth) +{ + if (bandwidth == BANDWIDTH_8_MHZ) + buf[2] |= 0x08; +} + +struct dvb_pll_desc dvb_pll_opera1 = { + .name = "Opera Tuner", + .min = 900000, + .max = 2250000, + .iffreq= 0, + .setbw = opera1_bw, + .count = 8, + .entries = { + { 1064000, 500, 0xe5, 0xc6 }, + { 1169000, 500, 0xe5, 0xe6 }, + { 1299000, 500, 0xe5, 0x24 }, + { 1444000, 500, 0xe5, 0x44 }, + { 1606000, 500, 0xe5, 0x64 }, + { 1777000, 500, 0xe5, 0x84 }, + { 1941000, 500, 0xe5, 0xa4 }, + { 2250000, 500, 0xe5, 0xc4 }, + } +}; +EXPORT_SYMBOL(dvb_pll_opera1); struct dvb_pll_priv { /* i2c details */ @@ -459,7 +517,8 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, if (i == desc->count) return -EINVAL; - div = (freq + desc->entries[i].offset) / desc->entries[i].stepsize; + div = (freq + desc->iffreq + desc->entries[i].stepsize/2) / + desc->entries[i].stepsize; buf[0] = div >> 8; buf[1] = div & 0xff; buf[2] = desc->entries[i].config; @@ -473,7 +532,7 @@ int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, desc->name, div, buf[0], buf[1], buf[2], buf[3]); // calculate the frequency we set it to - return (div * desc->entries[i].stepsize) - desc->entries[i].offset; + return (div * desc->entries[i].stepsize) - desc->iffreq; } EXPORT_SYMBOL(dvb_pll_configure); @@ -487,35 +546,27 @@ static int dvb_pll_release(struct dvb_frontend *fe) static int dvb_pll_sleep(struct dvb_frontend *fe) { struct dvb_pll_priv *priv = fe->tuner_priv; - u8 buf[4]; - struct i2c_msg msg = - { .addr = priv->pll_i2c_address, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - int i; - int result; if (priv->i2c == NULL) return -EINVAL; - for (i = 0; i < priv->pll_desc->count; i++) { - if (priv->pll_desc->entries[i].limit == 0) - break; - } - if (i == priv->pll_desc->count) - return 0; + if (priv->pll_desc->sleepdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->sleepdata + 1, + .len = priv->pll_desc->sleepdata[0] }; - buf[0] = 0; - buf[1] = 0; - buf[2] = priv->pll_desc->entries[i].config; - buf[3] = priv->pll_desc->entries[i].cb; + int result; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - return result; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + return 0; } - - return 0; + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; } static int dvb_pll_set_params(struct dvb_frontend *fe, @@ -599,9 +650,35 @@ static int dvb_pll_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) return 0; } +static int dvb_pll_init(struct dvb_frontend *fe) +{ + struct dvb_pll_priv *priv = fe->tuner_priv; + + if (priv->i2c == NULL) + return -EINVAL; + + if (priv->pll_desc->initdata) { + struct i2c_msg msg = { .flags = 0, + .addr = priv->pll_i2c_address, + .buf = priv->pll_desc->initdata + 1, + .len = priv->pll_desc->initdata[0] }; + + int result; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { + return result; + } + return 0; + } + /* Shouldn't be called when initdata is NULL, maybe BUG()? */ + return -EINVAL; +} + static struct dvb_tuner_ops dvb_pll_tuner_ops = { .release = dvb_pll_release, .sleep = dvb_pll_sleep, + .init = dvb_pll_init, .set_params = dvb_pll_set_params, .calc_regs = dvb_pll_calc_regs, .get_frequency = dvb_pll_get_frequency, @@ -640,9 +717,14 @@ struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, memcpy(&fe->ops.tuner_ops, &dvb_pll_tuner_ops, sizeof(struct dvb_tuner_ops)); - strncpy(fe->ops.tuner_ops.info.name, desc->name, 128); + strncpy(fe->ops.tuner_ops.info.name, desc->name, + sizeof(fe->ops.tuner_ops.info.name)); fe->ops.tuner_ops.info.frequency_min = desc->min; fe->ops.tuner_ops.info.frequency_min = desc->max; + if (!desc->initdata) + fe->ops.tuner_ops.init = NULL; + if (!desc->sleepdata) + fe->ops.tuner_ops.sleep = NULL; fe->tuner_priv = priv; return fe; diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h index 681186a..5209f46 100644 --- a/drivers/media/dvb/frontends/dvb-pll.h +++ b/drivers/media/dvb/frontends/dvb-pll.h @@ -12,11 +12,13 @@ struct dvb_pll_desc { char *name; u32 min; u32 max; + u32 iffreq; void (*setbw)(u8 *buf, u32 freq, int bandwidth); + u8 *initdata; + u8 *sleepdata; int count; struct { u32 limit; - u32 offset; u32 stepsize; u8 config; u8 cb; @@ -46,6 +48,7 @@ extern struct dvb_pll_desc dvb_pll_philips_sd1878_tda8261; extern struct dvb_pll_desc dvb_pll_philips_td1316; extern struct dvb_pll_desc dvb_pll_thomson_fe6600; +extern struct dvb_pll_desc dvb_pll_opera1; extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, u32 freq, int bandwidth); @@ -59,9 +62,20 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf, * @param desc dvb_pll_desc to use. * @return Frontend pointer on success, NULL on failure */ +#if defined(CONFIG_DVB_PLL) || (defined(CONFIG_DVB_PLL_MODULE) && defined(MODULE)) extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc); +#else +static inline struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, + int pll_addr, + struct i2c_adapter *i2c, + struct dvb_pll_desc *desc) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif #endif diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb/frontends/isl6421.c index ef31936..c967148 100644 --- a/drivers/media/dvb/frontends/isl6421.c +++ b/drivers/media/dvb/frontends/isl6421.c @@ -122,6 +122,7 @@ struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter /* detect if it is present or not */ if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) { kfree(isl6421); + fe->sec_priv = NULL; return NULL; } diff --git a/drivers/media/dvb/frontends/lgdt330x.c b/drivers/media/dvb/frontends/lgdt330x.c index 68aad0f..e25286e 100644 --- a/drivers/media/dvb/frontends/lgdt330x.c +++ b/drivers/media/dvb/frontends/lgdt330x.c @@ -475,7 +475,7 @@ static int lgdt3302_read_status(struct dvb_frontend* fe, fe_status_t* status) *status |= FE_HAS_CARRIER; break; default: - printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); + printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); } return 0; @@ -534,7 +534,7 @@ static int lgdt3303_read_status(struct dvb_frontend* fe, fe_status_t* status) } break; default: - printk("KERN_WARNING lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); + printk(KERN_WARNING "lgdt330x: %s: Modulation set to unsupported value\n", __FUNCTION__); } return 0; } diff --git a/drivers/media/dvb/frontends/lgh06xf.c b/drivers/media/dvb/frontends/lgh06xf.c deleted file mode 100644 index 2202d0c..0000000 --- a/drivers/media/dvb/frontends/lgh06xf.c +++ /dev/null @@ -1,134 +0,0 @@ -/* - * lgh06xf.c - ATSC Tuner support for LG TDVS-H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include "dvb-pll.h" -#include "lgh06xf.h" - -#define LG_H06XF_PLL_I2C_ADDR 0x61 - -struct lgh06xf_priv { - struct i2c_adapter *i2c; - u32 frequency; -}; - -static int lgh06xf_release(struct dvb_frontend *fe) -{ - kfree(fe->tuner_priv); - fe->tuner_priv = NULL; - return 0; -} - -static int lgh06xf_set_params(struct dvb_frontend* fe, - struct dvb_frontend_parameters* params) -{ - struct lgh06xf_priv *priv = fe->tuner_priv; - u8 buf[4]; - struct i2c_msg msg = { .addr = LG_H06XF_PLL_I2C_ADDR, .flags = 0, - .buf = buf, .len = sizeof(buf) }; - u32 frequency; - int result; - - if ((result = dvb_pll_configure(&dvb_pll_lg_tdvs_h06xf, buf, - params->frequency, 0)) < 0) - return result; - else - frequency = result; - - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, result = %i)\n", - __FUNCTION__, buf[0], buf[1], result); - if (result < 0) - return result; - else - return -EREMOTEIO; - } - - /* Set the Auxiliary Byte. */ - buf[0] = buf[2]; - buf[0] &= ~0x20; - buf[0] |= 0x18; - buf[1] = 0x50; - msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if ((result = i2c_transfer(priv->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "lgh06xf: %s error " - "(addr %02x <- %02x, result = %i)\n", - __FUNCTION__, buf[0], buf[1], result); - if (result < 0) - return result; - else - return -EREMOTEIO; - } - - priv->frequency = frequency; - - return 0; -} - -static int lgh06xf_get_frequency(struct dvb_frontend *fe, u32 *frequency) -{ - struct lgh06xf_priv *priv = fe->tuner_priv; - *frequency = priv->frequency; - return 0; -} - -static struct dvb_tuner_ops lgh06xf_tuner_ops = { - .release = lgh06xf_release, - .set_params = lgh06xf_set_params, - .get_frequency = lgh06xf_get_frequency, -}; - -struct dvb_frontend* lgh06xf_attach(struct dvb_frontend *fe, - struct i2c_adapter *i2c) -{ - struct lgh06xf_priv *priv = NULL; - - priv = kzalloc(sizeof(struct lgh06xf_priv), GFP_KERNEL); - if (priv == NULL) - return NULL; - - priv->i2c = i2c; - - memcpy(&fe->ops.tuner_ops, &lgh06xf_tuner_ops, - sizeof(struct dvb_tuner_ops)); - - strlcpy(fe->ops.tuner_ops.info.name, dvb_pll_lg_tdvs_h06xf.name, - sizeof(fe->ops.tuner_ops.info.name)); - - fe->ops.tuner_ops.info.frequency_min = dvb_pll_lg_tdvs_h06xf.min; - fe->ops.tuner_ops.info.frequency_max = dvb_pll_lg_tdvs_h06xf.max; - - fe->tuner_priv = priv; - return fe; -} - -EXPORT_SYMBOL(lgh06xf_attach); - -MODULE_DESCRIPTION("LG TDVS-H06xF ATSC Tuner support"); -MODULE_AUTHOR("Michael Krufky"); -MODULE_LICENSE("GPL"); - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/media/dvb/frontends/lgh06xf.h b/drivers/media/dvb/frontends/lgh06xf.h deleted file mode 100644 index 510b4be..0000000 --- a/drivers/media/dvb/frontends/lgh06xf.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * lgh06xf.h - ATSC Tuner support for LG TDVS-H06xF - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#ifndef _LGH06XF_H_ -#define _LGH06XF_H_ -#include "dvb_frontend.h" - -#if defined(CONFIG_DVB_TUNER_LGH06XF) || (defined(CONFIG_DVB_TUNER_LGH06XF_MODULE) && defined(MODULE)) -extern struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, - struct i2c_adapter *i2c); -#else -static inline struct dvb_frontend* lgh06xf_attach(struct dvb_frontend* fe, - struct i2c_adapter *i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); - return NULL; -} -#endif /* CONFIG_DVB_TUNER_LGH06XF */ - -#endif /* _LGH06XF_H_ */ diff --git a/drivers/media/dvb/frontends/nxt200x.c b/drivers/media/dvb/frontends/nxt200x.c index 87c286e..b809f83 100644 --- a/drivers/media/dvb/frontends/nxt200x.c +++ b/drivers/media/dvb/frontends/nxt200x.c @@ -562,7 +562,7 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, /* set input */ if (state->config->set_pll_input) - state->config->set_pll_input(buf, 1); + state->config->set_pll_input(buf+1, 1); break; case VSB_8: /* Set non-punctured clock for VSB */ @@ -571,7 +571,7 @@ static int nxt200x_setup_frontend_parameters (struct dvb_frontend* fe, /* set input */ if (state->config->set_pll_input) - state->config->set_pll_input(buf, 0); + state->config->set_pll_input(buf+1, 0); break; default: return -EINVAL; diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index 5a3a6e5..4e0aca7 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -1,6 +1,9 @@ /* * Support for OR51132 (pcHDTV HD-3000) - VSB/QAM * + * + * Copyright (C) 2007 Trent Piepho <xyzzy@speakeasy.org> + * * Copyright (C) 2005 Kirk Lapray <kirk_lapray@bigfoot.com> * * Based on code from Jack Kelliher (kelliher@xmission.com) @@ -69,46 +72,70 @@ struct or51132_state u32 current_frequency; }; -static int i2c_writebytes (struct or51132_state* state, u8 reg, u8 *buf, int len) + +/* Write buffer to demod */ +static int or51132_writebuf(struct or51132_state *state, const u8 *buf, int len) { int err; - struct i2c_msg msg; - msg.addr = reg; - msg.flags = 0; - msg.len = len; - msg.buf = buf; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = (u8*)buf, .len = len }; + /* msleep(20); */ /* doesn't appear to be necessary */ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51132: i2c_writebytes error (addr %02x, err == %i)\n", reg, err); + printk(KERN_WARNING "or51132: I2C write (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); return -EREMOTEIO; } - return 0; } -static u8 i2c_readbytes (struct or51132_state* state, u8 reg, u8* buf, int len) +/* Write constant bytes, e.g. or51132_writebytes(state, 0x04, 0x42, 0x00); + Less code and more efficient that loading a buffer on the stack with + the bytes to send and then calling or51132_writebuf() on that. */ +#define or51132_writebytes(state, data...) \ + ({ const static u8 _data[] = {data}; \ + or51132_writebuf(state, _data, sizeof(_data)); }) + +/* Read data from demod into buffer. Returns 0 on success. */ +static int or51132_readbuf(struct or51132_state *state, u8 *buf, int len) { int err; - struct i2c_msg msg; - msg.addr = reg; - msg.flags = I2C_M_RD; - msg.len = len; - msg.buf = buf; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = I2C_M_RD, .buf = buf, .len = len }; + /* msleep(20); */ /* doesn't appear to be necessary */ if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) { - printk(KERN_WARNING "or51132: i2c_readbytes error (addr %02x, err == %i)\n", reg, err); + printk(KERN_WARNING "or51132: I2C read (addr 0x%02x len %d) error: %d\n", + msg.addr, msg.len, err); return -EREMOTEIO; } - return 0; } +/* Reads a 16-bit demod register. Returns <0 on error. */ +static int or51132_readreg(struct or51132_state *state, u8 reg) +{ + u8 buf[2] = { 0x04, reg }; + struct i2c_msg msg[2] = { + {.addr = state->config->demod_address, .flags = 0, + .buf = buf, .len = 2 }, + {.addr = state->config->demod_address, .flags = I2C_M_RD, + .buf = buf, .len = 2 }}; + int err; + + if ((err = i2c_transfer(state->i2c, msg, 2)) != 2) { + printk(KERN_WARNING "or51132: I2C error reading register %d: %d\n", + reg, err); + return -EREMOTEIO; + } + return le16_to_cpup((u16*)buf); +} + static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware *fw) { struct or51132_state* state = fe->demodulator_priv; - static u8 run_buf[] = {0x7F,0x01}; + const static u8 run_buf[] = {0x7F,0x01}; u8 rec_buf[8]; - u8 cmd_buf[3]; u32 firmwareAsize, firmwareBsize; int i,ret; @@ -121,30 +148,21 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware dprintk("FirmwareB is %i bytes\n",firmwareBsize); /* Upload firmware */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - &fw->data[8],firmwareAsize))) { + if ((ret = or51132_writebuf(state, &fw->data[8], firmwareAsize))) { printk(KERN_WARNING "or51132: load_firmware error 1\n"); return ret; } - msleep(1); /* 1ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - &fw->data[8+firmwareAsize],firmwareBsize))) { + if ((ret = or51132_writebuf(state, &fw->data[8+firmwareAsize], + firmwareBsize))) { printk(KERN_WARNING "or51132: load_firmware error 2\n"); return ret; } - msleep(1); /* 1ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - run_buf,2))) { + if ((ret = or51132_writebuf(state, run_buf, 2))) { printk(KERN_WARNING "or51132: load_firmware error 3\n"); return ret; } - - /* Wait at least 5 msec */ - msleep(20); /* 10ms */ - - if ((ret = i2c_writebytes(state,state->config->demod_address, - run_buf,2))) { + if ((ret = or51132_writebuf(state, run_buf, 2))) { printk(KERN_WARNING "or51132: load_firmware error 4\n"); return ret; } @@ -154,43 +172,25 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware /* Read back ucode version to besure we loaded correctly and are really up and running */ /* Get uCode version */ - cmd_buf[0] = 0x10; - cmd_buf[1] = 0x10; - cmd_buf[2] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,3))) { + if ((ret = or51132_writebytes(state, 0x10, 0x10, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error a\n"); return ret; } - - cmd_buf[0] = 0x04; - cmd_buf[1] = 0x17; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,2))) { + if ((ret = or51132_writebytes(state, 0x04, 0x17))) { printk(KERN_WARNING "or51132: load_firmware error b\n"); return ret; } - - cmd_buf[0] = 0x00; - cmd_buf[1] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,2))) { + if ((ret = or51132_writebytes(state, 0x00, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error c\n"); return ret; } - - for(i=0;i<4;i++) { - msleep(20); /* 20ms */ + for (i=0;i<4;i++) { /* Once upon a time, this command might have had something to do with getting the firmware version, but it's not used anymore: {0x04,0x00,0x30,0x00,i+1} */ /* Read 8 bytes, two bytes at a time */ - if ((ret = i2c_readbytes(state,state->config->demod_address, - &rec_buf[i*2],2))) { + if ((ret = or51132_readbuf(state, &rec_buf[i*2], 2))) { printk(KERN_WARNING "or51132: load_firmware error d - %d\n",i); return ret; @@ -204,12 +204,7 @@ static int or51132_load_firmware (struct dvb_frontend* fe, const struct firmware rec_buf[3],rec_buf[2]>>4,rec_buf[2]&0x0f, rec_buf[5],rec_buf[4]>>4,rec_buf[4]&0x0f); - cmd_buf[0] = 0x10; - cmd_buf[1] = 0x00; - cmd_buf[2] = 0x00; - msleep(20); /* 20ms */ - if ((ret = i2c_writebytes(state,state->config->demod_address, - cmd_buf,3))) { + if ((ret = or51132_writebytes(state, 0x10, 0x00, 0x00))) { printk(KERN_WARNING "or51132: load_firmware error e\n"); return ret; } @@ -241,70 +236,55 @@ static int or51132_sleep(struct dvb_frontend* fe) static int or51132_setmode(struct dvb_frontend* fe) { struct or51132_state* state = fe->demodulator_priv; - unsigned char cmd_buf[3]; + u8 cmd_buf1[3] = {0x04, 0x01, 0x5f}; + u8 cmd_buf2[3] = {0x1c, 0x00, 0 }; dprintk("setmode %d\n",(int)state->current_modulation); - /* set operation mode in Receiver 1 register; */ - cmd_buf[0] = 0x04; - cmd_buf[1] = 0x01; + switch (state->current_modulation) { - case QAM_256: - case QAM_64: - case QAM_AUTO: - /* Auto-deinterleave; MPEG ser, MPEG2tr, phase noise-high*/ - cmd_buf[2] = 0x5F; - break; case VSB_8: - /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high*/ - cmd_buf[2] = 0x50; + /* Auto CH, Auto NTSC rej, MPEGser, MPEG2tr, phase noise-high */ + cmd_buf1[2] = 0x50; + /* REC MODE inv IF spectrum, Normal */ + cmd_buf2[1] = 0x03; + /* Channel MODE ATSC/VSB8 */ + cmd_buf2[2] = 0x06; break; - default: - printk("setmode:Modulation set to unsupported value\n"); - }; - if (i2c_writebytes(state,state->config->demod_address, - cmd_buf,3)) { - printk(KERN_WARNING "or51132: set_mode error 1\n"); - return -1; - } - dprintk("or51132: set #1 to %02x\n", cmd_buf[2]); - - /* Set operation mode in Receiver 6 register */ - cmd_buf[0] = 0x1C; - switch (state->current_modulation) { + /* All QAM modes are: + Auto-deinterleave; MPEGser, MPEG2tr, phase noise-high + REC MODE Normal Carrier Lock */ case QAM_AUTO: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE Auto QAM64/256 */ - cmd_buf[2] = 0x4f; + cmd_buf2[2] = 0x4f; break; case QAM_256: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE QAM256 */ - cmd_buf[2] = 0x45; + cmd_buf2[2] = 0x45; break; case QAM_64: - /* REC MODE Normal Carrier Lock */ - cmd_buf[1] = 0x00; /* Channel MODE QAM64 */ - cmd_buf[2] = 0x43; - break; - case VSB_8: - /* REC MODE inv IF spectrum, Normal */ - cmd_buf[1] = 0x03; - /* Channel MODE ATSC/VSB8 */ - cmd_buf[2] = 0x06; + cmd_buf2[2] = 0x43; break; default: - printk("setmode: Modulation set to unsupported value\n"); - }; - msleep(20); /* 20ms */ - if (i2c_writebytes(state,state->config->demod_address, - cmd_buf,3)) { + printk(KERN_WARNING + "or51132: setmode: Modulation set to unsupported value (%d)\n", + state->current_modulation); + return -EINVAL; + } + + /* Set Receiver 1 register */ + if (or51132_writebuf(state, cmd_buf1, 3)) { + printk(KERN_WARNING "or51132: set_mode error 1\n"); + return -EREMOTEIO; + } + dprintk("set #1 to %02x\n", cmd_buf1[2]); + + /* Set operation mode in Receiver 6 register */ + if (or51132_writebuf(state, cmd_buf2, 3)) { printk(KERN_WARNING "or51132: set_mode error 2\n"); - return -1; + return -EREMOTEIO; } - dprintk("or51132: set #6 to 0x%02x%02x\n", cmd_buf[1], cmd_buf[2]); + dprintk("set #6 to 0x%02x%02x\n", cmd_buf2[1], cmd_buf2[2]); return 0; } @@ -401,28 +381,23 @@ static int or51132_get_parameters(struct dvb_frontend* fe, struct dvb_frontend_parameters *param) { struct or51132_state* state = fe->demodulator_priv; - u8 buf[2]; + int status; + int retry = 1; +start: /* Receiver Status */ - buf[0]=0x04; - buf[1]=0x00; - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,buf,2)) { - printk(KERN_WARNING "or51132: get_parameters write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,buf,2)) { - printk(KERN_WARNING "or51132: get_parameters read error\n"); + if ((status = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: get_parameters: error reading receiver status\n"); return -EREMOTEIO; } - switch(buf[0]) { + switch(status&0xff) { case 0x06: param->u.vsb.modulation = VSB_8; break; case 0x43: param->u.vsb.modulation = QAM_64; break; case 0x45: param->u.vsb.modulation = QAM_256; break; default: + if (retry--) goto start; printk(KERN_WARNING "or51132: unknown status 0x%02x\n", - buf[0]); + status&0xff); return -EREMOTEIO; } @@ -438,32 +413,21 @@ static int or51132_get_parameters(struct dvb_frontend* fe, static int or51132_read_status(struct dvb_frontend* fe, fe_status_t* status) { struct or51132_state* state = fe->demodulator_priv; - unsigned char rec_buf[2]; - unsigned char snd_buf[2]; - *status = 0; + int reg; /* Receiver Status */ - snd_buf[0]=0x04; - snd_buf[1]=0x00; - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: read_status write error\n"); - return -1; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: read_status read error\n"); - return -1; - } - dprintk("read_status %x %x\n",rec_buf[0],rec_buf[1]); - - if (rec_buf[1] & 0x01) { /* Receiver Lock */ - *status |= FE_HAS_SIGNAL; - *status |= FE_HAS_CARRIER; - *status |= FE_HAS_VITERBI; - *status |= FE_HAS_SYNC; - *status |= FE_HAS_LOCK; + if ((reg = or51132_readreg(state, 0x00)) < 0) { + printk(KERN_WARNING "or51132: read_status: error reading receiver status: %d\n", reg); + *status = 0; + return -EREMOTEIO; } + dprintk("%s: read_status %04x\n", __FUNCTION__, reg); + + if (reg & 0x0100) /* Receiver Lock */ + *status = FE_HAS_SIGNAL|FE_HAS_CARRIER|FE_HAS_VITERBI| + FE_HAS_SYNC|FE_HAS_LOCK; + else + *status = 0; return 0; } @@ -506,47 +470,30 @@ static u32 calculate_snr(u32 mse, u32 c) static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) { struct or51132_state* state = fe->demodulator_priv; - u8 rec_buf[2]; - u8 snd_buf[2]; - u32 noise; - u32 c; - u32 usK; - - /* Register is same for VSB or QAM firmware */ - snd_buf[0]=0x04; - snd_buf[1]=0x02; /* SNR after Equalizer */ - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: snr write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: snr read error\n"); + int noise, reg; + u32 c, usK = 0; + int retry = 1; + +start: + /* SNR after Equalizer */ + noise = or51132_readreg(state, 0x02); + if (noise < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading equalizer\n"); return -EREMOTEIO; } - noise = rec_buf[0] | (rec_buf[1] << 8); - dprintk("read_snr noise %x %x (%i)\n",rec_buf[0],rec_buf[1],noise); + dprintk("read_snr noise (%d)\n", noise); /* Read status, contains modulation type for QAM_AUTO and NTSC filter for VSB */ - snd_buf[0]=0x04; - snd_buf[1]=0x00; /* Status register */ - msleep(30); /* 30ms */ - if (i2c_writebytes(state,state->config->demod_address,snd_buf,2)) { - printk(KERN_WARNING "or51132: status write error\n"); - return -EREMOTEIO; - } - msleep(30); /* 30ms */ - if (i2c_readbytes(state,state->config->demod_address,rec_buf,2)) { - printk(KERN_WARNING "or51132: status read error\n"); + reg = or51132_readreg(state, 0x00); + if (reg < 0) { + printk(KERN_WARNING "or51132: read_snr: error reading receiver status\n"); return -EREMOTEIO; } - usK = 0; - switch (rec_buf[0]) { + switch (reg&0xff) { case 0x06: - usK = (rec_buf[1] & 0x10) ? 0x03000000 : 0; + if (reg & 0x1000) usK = 3 << 24; /* Fall through to QAM64 case */ case 0x43: c = 150204167; @@ -555,11 +502,12 @@ static int or51132_read_snr(struct dvb_frontend* fe, u16* snr) c = 150290396; break; default: - printk(KERN_ERR "or51132: unknown status 0x%02x\n", rec_buf[0]); + printk(KERN_WARNING "or51132: unknown status 0x%02x\n", reg&0xff); + if (retry--) goto start; return -EREMOTEIO; } dprintk("%s: modulation %02x, NTSC rej O%s\n", __FUNCTION__, - rec_buf[0], rec_buf[1]&0x10?"n":"ff"); + reg&0xff, reg&0x1000?"n":"ff"); /* Calculate SNR using noise, c, and NTSC rejection correction */ state->snr = calculate_snr(noise, c) - usK; @@ -671,6 +619,7 @@ MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); MODULE_DESCRIPTION("OR51132 ATSC [pcHDTV HD-3000] (8VSB & ITU J83 AnnexB FEC QAM64/256) Demodulator Driver"); MODULE_AUTHOR("Kirk Lapray"); +MODULE_AUTHOR("Trent Piepho"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(or51132_attach); diff --git a/drivers/media/dvb/frontends/qt1010.c b/drivers/media/dvb/frontends/qt1010.c new file mode 100644 index 0000000..825aa14 --- /dev/null +++ b/drivers/media/dvb/frontends/qt1010.c @@ -0,0 +1,485 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> + * Aapo Tahkola <aet@rasterburn.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "qt1010.h" +#include "qt1010_priv.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "QT1010: " args); \ + } while (0) + +/* read single register */ +static int qt1010_readreg(struct qt1010_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + printk(KERN_WARNING "qt1010 I2C read failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* write single register */ +static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->cfg->i2c_address, + .flags = 0, .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "qt1010 I2C write failed\n"); + return -EREMOTEIO; + } + return 0; +} + +/* dump all registers */ +static void qt1010_dump_regs(struct qt1010_priv *priv) +{ + char buf[52], buf2[4]; + u8 reg, val; + + for (reg = 0; ; reg++) { + if (reg % 16 == 0) { + if (reg) + printk("%s\n", buf); + sprintf(buf, "%02x: ", reg); + } + if (qt1010_readreg(priv, reg, &val) == 0) + sprintf(buf2, "%02x ", val); + else + strcpy(buf2, "-- "); + strcat(buf, buf2); + if (reg == 0x2f) + break; + } + printk("%s\n", buf); +} + +static int qt1010_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct qt1010_priv *priv; + int err; + u32 freq, div, mod1, mod2; + u8 i, tmpval, reg05; + qt1010_i2c_oper_t rd[48] = { + { QT1010_WR, 0x01, 0x80 }, + { QT1010_WR, 0x02, 0x3f }, + { QT1010_WR, 0x05, 0xff }, /* 02 c write */ + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x07, 0xff }, /* 04 c write */ + { QT1010_WR, 0x08, 0x08 }, + { QT1010_WR, 0x09, 0xff }, /* 06 c write */ + { QT1010_WR, 0x0a, 0xff }, /* 07 c write */ + { QT1010_WR, 0x0b, 0xff }, /* 08 c write */ + { QT1010_WR, 0x0c, 0xe1 }, + { QT1010_WR, 0x1a, 0xff }, /* 10 c write */ + { QT1010_WR, 0x1b, 0x00 }, + { QT1010_WR, 0x1c, 0x89 }, + { QT1010_WR, 0x11, 0xff }, /* 13 c write */ + { QT1010_WR, 0x12, 0xff }, /* 14 c write */ + { QT1010_WR, 0x22, 0xff }, /* 15 c write */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xd0 }, + { QT1010_RD, 0x22, 0xff }, /* 16 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_RD, 0x05, 0xff }, /* 20 c read */ + { QT1010_RD, 0x22, 0xff }, /* 21 c read */ + { QT1010_WR, 0x23, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xe0 }, + { QT1010_RD, 0x23, 0xff }, /* 25 c read */ + { QT1010_RD, 0x23, 0xff }, /* 26 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x24, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xf0 }, + { QT1010_RD, 0x24, 0xff }, /* 31 c read */ + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x14, 0x7f }, + { QT1010_WR, 0x15, 0x7f }, + { QT1010_WR, 0x05, 0xff }, /* 35 c write */ + { QT1010_WR, 0x06, 0x00 }, + { QT1010_WR, 0x15, 0x1f }, + { QT1010_WR, 0x16, 0xff }, + { QT1010_WR, 0x18, 0xff }, + { QT1010_WR, 0x1f, 0xff }, /* 40 c write */ + { QT1010_WR, 0x20, 0xff }, /* 41 c write */ + { QT1010_WR, 0x21, 0x53 }, + { QT1010_WR, 0x25, 0xff }, /* 43 c write */ + { QT1010_WR, 0x26, 0x15 }, + { QT1010_WR, 0x00, 0xff }, /* 45 c write */ + { QT1010_WR, 0x02, 0x00 }, + { QT1010_WR, 0x01, 0x00 } + }; + +#define FREQ1 32000000 /* 32 MHz */ +#define FREQ2 4000000 /* 4 MHz Quartz oscillator in the stick? */ + + priv = fe->tuner_priv; + freq = params->frequency; + div = (freq + QT1010_OFFSET) / QT1010_STEP; + freq = (div * QT1010_STEP) - QT1010_OFFSET; + mod1 = (freq + QT1010_OFFSET) % FREQ1; + mod2 = (freq + QT1010_OFFSET) % FREQ2; + priv->bandwidth = + (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + priv->frequency = freq; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + /* reg 05 base value */ + if (freq < 290000000) reg05 = 0x14; /* 290 MHz */ + else if (freq < 610000000) reg05 = 0x34; /* 610 MHz */ + else if (freq < 802000000) reg05 = 0x54; /* 802 MHz */ + else reg05 = 0x74; + + /* 0x5 */ + rd[2].val = reg05; + + /* 07 - set frequency: 32 MHz scale */ + rd[4].val = (freq + QT1010_OFFSET) / FREQ1; + + /* 09 - changes every 8/24 MHz */ + if (mod1 < 8000000) rd[6].val = 0x1d; + else rd[6].val = 0x1c; + + /* 0a - set frequency: 4 MHz scale (max 28 MHz) */ + if (mod1 < 1*FREQ2) rd[7].val = 0x09; /* +0 MHz */ + else if (mod1 < 2*FREQ2) rd[7].val = 0x08; /* +4 MHz */ + else if (mod1 < 3*FREQ2) rd[7].val = 0x0f; /* +8 MHz */ + else if (mod1 < 4*FREQ2) rd[7].val = 0x0e; /* +12 MHz */ + else if (mod1 < 5*FREQ2) rd[7].val = 0x0d; /* +16 MHz */ + else if (mod1 < 6*FREQ2) rd[7].val = 0x0c; /* +20 MHz */ + else if (mod1 < 7*FREQ2) rd[7].val = 0x0b; /* +24 MHz */ + else rd[7].val = 0x0a; /* +28 MHz */ + + /* 0b - changes every 2/2 MHz */ + if (mod2 < 2000000) rd[8].val = 0x45; + else rd[8].val = 0x44; + + /* 1a - set frequency: 125 kHz scale (max 3875 kHz)*/ + tmpval = 0x78; /* byte, overflows intentionally */ + rd[10].val = tmpval-((mod2/QT1010_STEP)*0x08); + + /* 11 */ + rd[13].val = 0xfd; /* TODO: correct value calculation */ + + /* 12 */ + rd[14].val = 0x91; /* TODO: correct value calculation */ + + /* 22 */ + if (freq < 450000000) rd[15].val = 0xd0; /* 450 MHz */ + else if (freq < 482000000) rd[15].val = 0xd1; /* 482 MHz */ + else if (freq < 514000000) rd[15].val = 0xd4; /* 514 MHz */ + else if (freq < 546000000) rd[15].val = 0xd7; /* 546 MHz */ + else if (freq < 610000000) rd[15].val = 0xda; /* 610 MHz */ + else rd[15].val = 0xd0; + + /* 05 */ + rd[35].val = (reg05 & 0xf0); + + /* 1f */ + if (mod1 < 8000000) tmpval = 0x00; + else if (mod1 < 12000000) tmpval = 0x01; + else if (mod1 < 16000000) tmpval = 0x02; + else if (mod1 < 24000000) tmpval = 0x03; + else if (mod1 < 28000000) tmpval = 0x04; + else tmpval = 0x05; + rd[40].val = (priv->reg1f_init_val + 0x0e + tmpval); + + /* 20 */ + if (mod1 < 8000000) tmpval = 0x00; + else if (mod1 < 12000000) tmpval = 0x01; + else if (mod1 < 20000000) tmpval = 0x02; + else if (mod1 < 24000000) tmpval = 0x03; + else if (mod1 < 28000000) tmpval = 0x04; + else tmpval = 0x05; + rd[41].val = (priv->reg20_init_val + 0x0d + tmpval); + + /* 25 */ + rd[43].val = priv->reg25_init_val; + + /* 00 */ + rd[45].val = 0x92; /* TODO: correct value calculation */ + + dprintk("freq:%u 05:%02x 07:%02x 09:%02x 0a:%02x 0b:%02x " \ + "1a:%02x 11:%02x 12:%02x 22:%02x 05:%02x 1f:%02x " \ + "20:%02x 25:%02x 00:%02x", \ + freq, rd[2].val, rd[4].val, rd[6].val, rd[7].val, rd[8].val, \ + rd[10].val, rd[13].val, rd[14].val, rd[15].val, rd[35].val, \ + rd[40].val, rd[41].val, rd[43].val, rd[45].val); + + for (i = 0; i < ARRAY_SIZE(rd); i++) { + if (rd[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, rd[i].reg, rd[i].val); + } else { /* read is required to proper locking */ + err = qt1010_readreg(priv, rd[i].reg, &tmpval); + } + if (err) return err; + } + + if (debug) + qt1010_dump_regs(priv); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return 0; +} + +static int qt1010_init_meas1(struct qt1010_priv *priv, + u8 oper, u8 reg, u8 reg_init_val, u8 *retval) +{ + u8 i, val1, val2; + int err; + + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, reg, reg_init_val }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, oper }, + { QT1010_RD, reg, 0xff } + }; + + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + if (i2c_data[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + } else { + err = qt1010_readreg(priv, i2c_data[i].reg, &val2); + } + if (err) return err; + } + + do { + val1 = val2; + err = qt1010_readreg(priv, reg, &val2); + if (err) return err; + dprintk("compare reg:%02x %02x %02x", reg, val1, val2); + } while (val1 != val2); + *retval = val1; + + return qt1010_writereg(priv, 0x1e, 0x00); +} + +static u8 qt1010_init_meas2(struct qt1010_priv *priv, + u8 reg_init_val, u8 *retval) +{ + u8 i, val; + int err; + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, 0x07, reg_init_val }, + { QT1010_WR, 0x22, 0xd0 }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x1e, 0xd0 }, + { QT1010_RD, 0x22, 0xff }, + { QT1010_WR, 0x1e, 0x00 }, + { QT1010_WR, 0x22, 0xff } + }; + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + if (i2c_data[i].oper == QT1010_WR) { + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + } else { + err = qt1010_readreg(priv, i2c_data[i].reg, &val); + } + if (err) return err; + } + *retval = val; + return 0; +} + +static int qt1010_init(struct dvb_frontend *fe) +{ + struct qt1010_priv *priv = fe->tuner_priv; + struct dvb_frontend_parameters params; + int err = 0; + u8 i, tmpval, *valptr = NULL; + + qt1010_i2c_oper_t i2c_data[] = { + { QT1010_WR, 0x01, 0x80 }, + { QT1010_WR, 0x0d, 0x84 }, + { QT1010_WR, 0x0e, 0xb7 }, + { QT1010_WR, 0x2a, 0x23 }, + { QT1010_WR, 0x2c, 0xdc }, + { QT1010_M1, 0x25, 0x40 }, /* get reg 25 init value */ + { QT1010_M1, 0x81, 0xff }, /* get reg 25 init value */ + { QT1010_WR, 0x2b, 0x70 }, + { QT1010_WR, 0x2a, 0x23 }, + { QT1010_M1, 0x26, 0x08 }, + { QT1010_M1, 0x82, 0xff }, + { QT1010_WR, 0x05, 0x14 }, + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x07, 0x28 }, + { QT1010_WR, 0x08, 0x0b }, + { QT1010_WR, 0x11, 0xfd }, + { QT1010_M1, 0x22, 0x0d }, + { QT1010_M1, 0xd0, 0xff }, + { QT1010_WR, 0x06, 0x40 }, + { QT1010_WR, 0x16, 0xf0 }, + { QT1010_WR, 0x02, 0x38 }, + { QT1010_WR, 0x03, 0x18 }, + { QT1010_WR, 0x20, 0xe0 }, + { QT1010_M1, 0x1f, 0x20 }, /* get reg 1f init value */ + { QT1010_M1, 0x84, 0xff }, /* get reg 1f init value */ + { QT1010_RD, 0x20, 0x20 }, /* get reg 20 init value */ + { QT1010_WR, 0x03, 0x19 }, + { QT1010_WR, 0x02, 0x3f }, + { QT1010_WR, 0x21, 0x53 }, + { QT1010_RD, 0x21, 0xff }, + { QT1010_WR, 0x11, 0xfd }, + { QT1010_WR, 0x05, 0x34 }, + { QT1010_WR, 0x06, 0x44 }, + { QT1010_WR, 0x08, 0x08 } + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + for (i = 0; i < ARRAY_SIZE(i2c_data); i++) { + switch (i2c_data[i].oper) { + case QT1010_WR: + err = qt1010_writereg(priv, i2c_data[i].reg, + i2c_data[i].val); + break; + case QT1010_RD: + if (i2c_data[i].val == 0x20) + valptr = &priv->reg20_init_val; + else + valptr = &tmpval; + err = qt1010_readreg(priv, i2c_data[i].reg, valptr); + break; + case QT1010_M1: + if (i2c_data[i].val == 0x25) + valptr = &priv->reg25_init_val; + else if (i2c_data[i].val == 0x1f) + valptr = &priv->reg1f_init_val; + else + valptr = &tmpval; + err = qt1010_init_meas1(priv, i2c_data[i+1].reg, + i2c_data[i].reg, + i2c_data[i].val, valptr); + i++; + break; + } + if (err) return err; + } + + for (i = 0x31; i < 0x3a; i++) /* 0x31 - 0x39 */ + if ((err = qt1010_init_meas2(priv, i, &tmpval))) + return err; + + params.frequency = 545000000; /* Sigmatek DVB-110 545000000 */ + /* MSI Megasky 580 GL861 533000000 */ + return qt1010_set_params(fe, ¶ms); +} + +static int qt1010_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int qt1010_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct qt1010_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int qt1010_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct qt1010_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static const struct dvb_tuner_ops qt1010_tuner_ops = { + .info = { + .name = "Quantek QT1010", + .frequency_min = QT1010_MIN_FREQ, + .frequency_max = QT1010_MAX_FREQ, + .frequency_step = QT1010_STEP, + }, + + .release = qt1010_release, + .init = qt1010_init, + /* TODO: implement sleep */ + + .set_params = qt1010_set_params, + .get_frequency = qt1010_get_frequency, + .get_bandwidth = qt1010_get_bandwidth +}; + +struct dvb_frontend * qt1010_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct qt1010_config *cfg) +{ + struct qt1010_priv *priv = NULL; + u8 id; + + priv = kzalloc(sizeof(struct qt1010_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + + /* Try to detect tuner chip. Probably this is not correct register. */ + if (qt1010_readreg(priv, 0x29, &id) != 0 || (id != 0x39)) { + kfree(priv); + return NULL; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + printk(KERN_INFO "Quantek QT1010 successfully identified.\n"); + memcpy(&fe->ops.tuner_ops, &qt1010_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(qt1010_attach); + +MODULE_DESCRIPTION("Quantek QT1010 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>"); +MODULE_AUTHOR("Aapo Tahkola <aet@rasterburn.org>"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/qt1010.h b/drivers/media/dvb/frontends/qt1010.h new file mode 100644 index 0000000..3ab4aa0 --- /dev/null +++ b/drivers/media/dvb/frontends/qt1010.h @@ -0,0 +1,53 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> + * Aapo Tahkola <aet@rasterburn.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef QT1010_H +#define QT1010_H + +#include "dvb_frontend.h" + +struct qt1010_config { + u8 i2c_address; +}; + +/** + * Attach a qt1010 tuner to the supplied frontend structure. + * + * @param fe frontend to attach to + * @param i2c i2c adapter to use + * @param cfg tuner hw based configuration + * @return fe pointer on success, NULL on failure + */ +#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE)) +extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct qt1010_config *cfg); +#else +static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct qt1010_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TUNER_QT1010 + +#endif diff --git a/drivers/media/dvb/frontends/qt1010_priv.h b/drivers/media/dvb/frontends/qt1010_priv.h new file mode 100644 index 0000000..090cf47 --- /dev/null +++ b/drivers/media/dvb/frontends/qt1010_priv.h @@ -0,0 +1,105 @@ +/* + * Driver for Quantek QT1010 silicon tuner + * + * Copyright (C) 2006 Antti Palosaari <crope@iki.fi> + * Aapo Tahkola <aet@rasterburn.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef QT1010_PRIV_H +#define QT1010_PRIV_H + +/* +reg def meaning +=== === ======= +00 00 ? +01 a0 ? operation start/stop; start=80, stop=00 +02 00 ? +03 19 ? +04 00 ? +05 00 ? maybe band selection +06 00 ? +07 2b set frequency: 32 MHz scale, n*32 MHz +08 0b ? +09 10 ? changes every 8/24 MHz; values 1d/1c +0a 08 set frequency: 4 MHz scale, n*4 MHz +0b 41 ? changes every 2/2 MHz; values 45/45 +0c e1 ? +0d 94 ? +0e b6 ? +0f 2c ? +10 10 ? +11 f1 ? maybe device specified adjustment +12 11 ? maybe device specified adjustment +13 3f ? +14 1f ? +15 3f ? +16 ff ? +17 ff ? +18 f7 ? +19 80 ? +1a d0 set frequency: 125 kHz scale, n*125 kHz +1b 00 ? +1c 89 ? +1d 00 ? +1e 00 ? looks like operation register; write cmd here, read result from 1f-26 +1f 20 ? chip initialization +20 e0 ? chip initialization +21 20 ? +22 d0 ? +23 d0 ? +24 d0 ? +25 40 ? chip initialization +26 08 ? +27 29 ? +28 55 ? +29 39 ? +2a 13 ? +2b 01 ? +2c ea ? +2d 00 ? +2e 00 ? not used? +2f 00 ? not used? +*/ + +#define QT1010_STEP 125000 /* 125 kHz used by Windows drivers, + hw could be more precise but we don't + know how to use */ +#define QT1010_MIN_FREQ 48000000 /* 48 MHz */ +#define QT1010_MAX_FREQ 860000000 /* 860 MHz */ +#define QT1010_OFFSET 1246000000 /* 1246 MHz */ + +#define QT1010_WR 0 +#define QT1010_RD 1 +#define QT1010_M1 3 + +typedef struct { + u8 oper, reg, val; +} qt1010_i2c_oper_t; + +struct qt1010_priv { + struct qt1010_config *cfg; + struct i2c_adapter *i2c; + + u8 reg1f_init_val; + u8 reg20_init_val; + u8 reg25_init_val; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c index 1ca6424..9a34397 100644 --- a/drivers/media/dvb/frontends/stv0297.c +++ b/drivers/media/dvb/frontends/stv0297.c @@ -35,6 +35,7 @@ struct stv0297_state { const struct stv0297_config *config; struct dvb_frontend frontend; + unsigned long last_ber; unsigned long base_freq; }; @@ -310,6 +311,8 @@ static int stv0297_init(struct dvb_frontend *fe) stv0297_writereg(state, state->config->inittab[i], state->config->inittab[i+1]); msleep(200); + state->last_ber = 0; + return 0; } @@ -340,11 +343,13 @@ static int stv0297_read_ber(struct dvb_frontend *fe, u32 * ber) struct stv0297_state *state = fe->demodulator_priv; u8 BER[3]; - stv0297_writereg(state, 0xA0, 0x80); // Start Counting bit errors for 4096 Bytes - mdelay(25); // Hopefully got 4096 Bytes stv0297_readregs(state, 0xA0, BER, 3); - mdelay(25); - *ber = (BER[2] << 8 | BER[1]) / (8 * 4096); + if (!(BER[0] & 0x80)) { + state->last_ber = BER[2] << 8 | BER[1]; + stv0297_writereg_mask(state, 0xA0, 0x80, 0x80); + } + + *ber = state->last_ber; return 0; } @@ -376,9 +381,14 @@ static int stv0297_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) { struct stv0297_state *state = fe->demodulator_priv; + stv0297_writereg_mask(state, 0xDF, 0x03, 0x03); /* freeze the counters */ + *ucblocks = (stv0297_readreg(state, 0xD5) << 8) | stv0297_readreg(state, 0xD4); + stv0297_writereg_mask(state, 0xDF, 0x03, 0x02); /* clear the counters */ + stv0297_writereg_mask(state, 0xDF, 0x03, 0x01); /* re-enable the counters */ + return 0; } @@ -648,6 +658,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, /* setup the state */ state->config = config; state->i2c = i2c; + state->last_ber = 0; state->base_freq = 0; /* check if the demod is there */ diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 9348376..18768d2 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -92,7 +92,7 @@ static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data) return (ret != 1) ? -EREMOTEIO : 0; } -int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len) +static int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len) { struct stv0299_state* state = fe->demodulator_priv; diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index dca8917..1105368 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -30,13 +30,13 @@ #include <linux/slab.h> #include "dvb_frontend.h" -#include "tda10021.h" +#include "tda1002x.h" struct tda10021_state { struct i2c_adapter* i2c; /* configuration settings */ - const struct tda10021_config* config; + const struct tda1002x_config* config; struct dvb_frontend frontend; u8 pwm; @@ -53,9 +53,6 @@ struct tda10021_state { static int verbose; #define XIN 57840000UL -#define DISABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0) -#define ENABLE_INVERSION(reg0) do { reg0 &= ~0x20; } while (0) -#define HAS_INVERSION(reg0) (!(reg0 & 0x20)) #define FIN (XIN >> 4) @@ -64,7 +61,7 @@ static u8 tda10021_inittab[0x40]= { 0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a, 0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40, - 0xb8, 0x3f, 0xa0, 0x00, 0xcd, 0x01, 0x00, 0xff, + 0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff, 0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00, 0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58, @@ -97,7 +94,8 @@ static u8 tda10021_readreg (struct tda10021_state* state, u8 reg) int ret; ret = i2c_transfer (state->i2c, msg, 2); - if (ret != 2) + // Don't print an error message if the id is read. + if (ret != 2 && reg != 0x1a) printk("DVB: TDA10021: %s: readreg error (ret == %i)\n", __FUNCTION__, ret); return b1[0]; @@ -136,10 +134,10 @@ static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0, { reg0 |= state->reg0 & 0x63; - if (INVERSION_ON == inversion) - ENABLE_INVERSION(reg0); - else if (INVERSION_OFF == inversion) - DISABLE_INVERSION(reg0); + if ((INVERSION_ON == inversion) ^ (state->config->invert == 0)) + reg0 &= ~0x20; + else + reg0 |= 0x20; _tda10021_writereg (state, 0x00, reg0 & 0xfe); _tda10021_writereg (state, 0x00, reg0 | 0x01); @@ -201,16 +199,6 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate return 0; } -int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len) -{ - struct tda10021_state* state = fe->demodulator_priv; - - if (len != 2) - return -EINVAL; - - return _tda10021_writereg(state, buf[0], buf[1]); -} - static int tda10021_init (struct dvb_frontend *fe) { struct tda10021_state* state = fe->demodulator_priv; @@ -258,6 +246,9 @@ static int tda10021_set_parameters (struct dvb_frontend *fe, if (qam < 0 || qam > 5) return -EINVAL; + if (p->inversion != INVERSION_ON && p->inversion != INVERSION_OFF) + return -EINVAL; + //printk("tda10021: set frequency to %d qam=%d symrate=%d\n", p->frequency,qam,p->u.qam.symbol_rate); if (fe->ops.tuner_ops.set_params) { @@ -366,7 +357,7 @@ static int tda10021_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_pa -((s32)p->u.qam.symbol_rate * afc) >> 10); } - p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF; + p->inversion = ((state->reg0 & 0x20) == 0x20) ^ (state->config->invert != 0) ? INVERSION_ON : INVERSION_OFF; p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; p->u.qam.fec_inner = FEC_NONE; @@ -408,11 +399,12 @@ static void tda10021_release(struct dvb_frontend* fe) static struct dvb_frontend_ops tda10021_ops; -struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, +struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm) { struct tda10021_state* state = NULL; + u8 id; /* allocate memory for the internal state */ state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); @@ -425,7 +417,11 @@ struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, state->reg0 = tda10021_inittab[0]; /* check if the demod is there */ - if ((tda10021_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + id = tda10021_readreg(state, 0x1a); + if ((id & 0xf0) != 0x70) goto error; + + printk("TDA10021: i2c-addr = 0x%02x, id = 0x%02x\n", + state->config->demod_address, id); /* create dvb_frontend */ memcpy(&state->frontend.ops, &tda10021_ops, sizeof(struct dvb_frontend_ops)); @@ -447,7 +443,7 @@ static struct dvb_frontend_ops tda10021_ops = { .frequency_max = 858000000, .symbol_rate_min = (XIN/2)/64, /* SACLK/64 == (XIN/2)/64 */ .symbol_rate_max = (XIN/2)/4, /* SACLK/4 */ -#if 0 + #if 0 .frequency_tolerance = ???, .symbol_rate_tolerance = ???, /* ppm */ /* == 8% (spec p. 5) */ #endif @@ -461,7 +457,6 @@ static struct dvb_frontend_ops tda10021_ops = { .init = tda10021_init, .sleep = tda10021_sleep, - .write = tda10021_write, .i2c_gate_ctrl = tda10021_i2c_gate_ctrl, .set_frontend = tda10021_set_parameters, diff --git a/drivers/media/dvb/frontends/tda10023.c b/drivers/media/dvb/frontends/tda10023.c new file mode 100644 index 0000000..da796e7 --- /dev/null +++ b/drivers/media/dvb/frontends/tda10023.c @@ -0,0 +1,540 @@ +/* + TDA10023 - DVB-C decoder + (as used in Philips CU1216-3 NIM and the Reelbox DVB-C tuner card) + + Copyright (C) 2005 Georg Acher, BayCom GmbH (acher at baycom dot de) + Copyright (c) 2006 Hartmut Birr (e9hack at gmail dot com) + + Remotely based on tda10021.c + Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> + Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> + Support for TDA10021 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/slab.h> + +#include <asm/div64.h> + +#include "dvb_frontend.h" +#include "tda1002x.h" + + +struct tda10023_state { + struct i2c_adapter* i2c; + /* configuration settings */ + const struct tda1002x_config* config; + struct dvb_frontend frontend; + + u8 pwm; + u8 reg0; +}; + + +#define dprintk(x...) + +static int verbose; + +#define XTAL 28920000UL +#define PLL_M 8UL +#define PLL_P 4UL +#define PLL_N 1UL +#define SYSCLK (XTAL*PLL_M/(PLL_N*PLL_P)) // -> 57840000 + +static u8 tda10023_inittab[]={ + // reg mask val + 0x2a,0xff,0x02, // PLL3, Bypass, Power Down + 0xff,0x64,0x00, // Sleep 100ms + 0x2a,0xff,0x03, // PLL3, Bypass, Power Down + 0xff,0x64,0x00, // Sleep 100ms + 0x28,0xff,PLL_M-1, // PLL1 M=8 + 0x29,0xff,((PLL_P-1)<<6)|(PLL_N-1), // PLL2 + 0x00,0xff,0x23, // GPR FSAMPLING=1 + 0x2a,0xff,0x08, // PLL3 PSACLK=1 + 0xff,0x64,0x00, // Sleep 100ms + 0x1f,0xff,0x00, // RESET + 0xff,0x64,0x00, // Sleep 100ms + 0xe6,0x0c,0x04, // RSCFG_IND + 0x10,0xc0,0x80, // DECDVBCFG1 PBER=1 + + 0x0e,0xff,0x82, // GAIN1 + 0x03,0x08,0x08, // CLKCONF DYN=1 + 0x2e,0xbf,0x30, // AGCCONF2 TRIAGC=0,POSAGC=ENAGCIF=1 PPWMTUN=0 PPWMIF=0 + 0x01,0xff,0x30, // AGCREF + 0x1e,0x84,0x84, // CONTROL SACLK_ON=1 + 0x1b,0xff,0xc8, // ADC TWOS=1 + 0x3b,0xff,0xff, // IFMAX + 0x3c,0xff,0x00, // IFMIN + 0x34,0xff,0x00, // PWMREF + 0x35,0xff,0xff, // TUNMAX + 0x36,0xff,0x00, // TUNMIN + 0x06,0xff,0x7f, // EQCONF1 POSI=7 ENADAPT=ENEQUAL=DFE=1 // 0x77 + 0x1c,0x30,0x30, // EQCONF2 STEPALGO=SGNALGO=1 + 0x37,0xff,0xf6, // DELTAF_LSB + 0x38,0xff,0xff, // DELTAF_MSB + 0x02,0xff,0x93, // AGCCONF1 IFS=1 KAGCIF=2 KAGCTUN=3 + 0x2d,0xff,0xf6, // SWEEP SWPOS=1 SWDYN=7 SWSTEP=1 SWLEN=2 + 0x04,0x10,0x00, // SWRAMP=1 + 0x12,0xff,0xa1, // INTP1 POCLKP=1 FEL=1 MFS=0 + 0x2b,0x01,0xa1, // INTS1 + 0x20,0xff,0x04, // INTP2 SWAPP=? MSBFIRSTP=? INTPSEL=? + 0x2c,0xff,0x0d, // INTP/S TRIP=0 TRIS=0 + 0xc4,0xff,0x00, + 0xc3,0x30,0x00, + 0xb5,0xff,0x19, // ERAGC_THD + 0x00,0x03,0x01, // GPR, CLBS soft reset + 0x00,0x03,0x03, // GPR, CLBS soft reset + 0xff,0x64,0x00, // Sleep 100ms + 0xff,0xff,0xff +}; + +static u8 tda10023_readreg (struct tda10023_state* state, u8 reg) +{ + u8 b0 [] = { reg }; + u8 b1 [] = { 0 }; + struct i2c_msg msg [] = { { .addr = state->config->demod_address, .flags = 0, .buf = b0, .len = 1 }, + { .addr = state->config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; + int ret; + + ret = i2c_transfer (state->i2c, msg, 2); + if (ret != 2) + printk("DVB: TDA10023: %s: readreg error (ret == %i)\n", + __FUNCTION__, ret); + return b1[0]; +} + +static int tda10023_writereg (struct tda10023_state* state, u8 reg, u8 data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 }; + int ret; + + ret = i2c_transfer (state->i2c, &msg, 1); + if (ret != 1) + printk("DVB: TDA10023(%d): %s, writereg error " + "(reg == 0x%02x, val == 0x%02x, ret == %i)\n", + state->frontend.dvb->num, __FUNCTION__, reg, data, ret); + + return (ret != 1) ? -EREMOTEIO : 0; +} + + +static int tda10023_writebit (struct tda10023_state* state, u8 reg, u8 mask,u8 data) +{ + if (mask==0xff) + return tda10023_writereg(state, reg, data); + else { + u8 val; + val=tda10023_readreg(state,reg); + val&=~mask; + val|=(data&mask); + return tda10023_writereg(state, reg, val); + } +} + +static void tda10023_writetab(struct tda10023_state* state, u8* tab) +{ + u8 r,m,v; + while (1) { + r=*tab++; + m=*tab++; + v=*tab++; + if (r==0xff) { + if (m==0xff) + break; + else + msleep(m); + } + else + tda10023_writebit(state,r,m,v); + } +} + +//get access to tuner +static int lock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0xc0 }; + struct i2c_msg msg = {.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg, 1) != 1) + { + printk("tda10023: lock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +//release access from tuner +static int unlock_tuner(struct tda10023_state* state) +{ + u8 buf[2] = { 0x0f, 0x40 }; + struct i2c_msg msg_post={.addr=state->config->demod_address, .flags=0, .buf=buf, .len=2}; + + if(i2c_transfer(state->i2c, &msg_post, 1) != 1) + { + printk("tda10023: unlock tuner fails\n"); + return -EREMOTEIO; + } + return 0; +} + +static int tda10023_setup_reg0 (struct tda10023_state* state, u8 reg0) +{ + reg0 |= state->reg0 & 0x63; + + tda10023_writereg (state, 0x00, reg0 & 0xfe); + tda10023_writereg (state, 0x00, reg0 | 0x01); + + state->reg0 = reg0; + return 0; +} + +static int tda10023_set_symbolrate (struct tda10023_state* state, u32 sr) +{ + s32 BDR; + s32 BDRI; + s16 SFIL=0; + u16 NDEC = 0; + + if (sr > (SYSCLK/(2*4))) + sr=SYSCLK/(2*4); + + if (sr<870000) + sr=870000; + + if (sr < (u32)(SYSCLK/98.40)) { + NDEC=3; + SFIL=1; + } else if (sr<(u32)(SYSCLK/64.0)) { + NDEC=3; + SFIL=0; + } else if (sr<(u32)(SYSCLK/49.2)) { + NDEC=2; + SFIL=1; + } else if (sr<(u32)(SYSCLK/32.0)) { + NDEC=2; + SFIL=0; + } else if (sr<(u32)(SYSCLK/24.6)) { + NDEC=1; + SFIL=1; + } else if (sr<(u32)(SYSCLK/16.0)) { + NDEC=1; + SFIL=0; + } else if (sr<(u32)(SYSCLK/12.3)) { + NDEC=0; + SFIL=1; + } + + BDRI=SYSCLK*16; + BDRI>>=NDEC; + BDRI +=sr/2; + BDRI /=sr; + + if (BDRI>255) + BDRI=255; + + { + u64 BDRX; + + BDRX=1<<(24+NDEC); + BDRX*=sr; + do_div(BDRX,SYSCLK); // BDRX/=SYSCLK; + + BDR=(s32)BDRX; + } +// printk("Symbolrate %i, BDR %i BDRI %i, NDEC %i\n",sr,BDR,BDRI,NDEC); + tda10023_writebit (state, 0x03, 0xc0, NDEC<<6); + tda10023_writereg (state, 0x0a, BDR&255); + tda10023_writereg (state, 0x0b, (BDR>>8)&255); + tda10023_writereg (state, 0x0c, (BDR>>16)&31); + tda10023_writereg (state, 0x0d, BDRI); + tda10023_writereg (state, 0x3d, (SFIL<<7)); + return 0; +} + +static int tda10023_init (struct dvb_frontend *fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + + dprintk("DVB: TDA10023(%d): init chip\n", fe->adapter->num); + + tda10023_writetab(state, tda10023_inittab); + + return 0; +} + +static int tda10023_set_parameters (struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tda10023_state* state = fe->demodulator_priv; + + static int qamvals[6][6] = { + // QAM LOCKTHR MSETH AREF AGCREFNYQ ERAGCNYQ_THD + { (5<<2), 0x78, 0x8c, 0x96, 0x78, 0x4c }, // 4 QAM + { (0<<2), 0x87, 0xa2, 0x91, 0x8c, 0x57 }, // 16 QAM + { (1<<2), 0x64, 0x74, 0x96, 0x8c, 0x57 }, // 32 QAM + { (2<<2), 0x46, 0x43, 0x6a, 0x6a, 0x44 }, // 64 QAM + { (3<<2), 0x36, 0x34, 0x7e, 0x78, 0x4c }, // 128 QAM + { (4<<2), 0x26, 0x23, 0x6c, 0x5c, 0x3c }, // 256 QAM + }; + + int qam = p->u.qam.modulation; + + if (qam < 0 || qam > 5) + return -EINVAL; + + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, p); + if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + } + + tda10023_set_symbolrate (state, p->u.qam.symbol_rate); + tda10023_writereg (state, 0x05, qamvals[qam][1]); + tda10023_writereg (state, 0x08, qamvals[qam][2]); + tda10023_writereg (state, 0x09, qamvals[qam][3]); + tda10023_writereg (state, 0xb4, qamvals[qam][4]); + tda10023_writereg (state, 0xb6, qamvals[qam][5]); + +// tda10023_writereg (state, 0x04, (p->inversion?0x12:0x32)); +// tda10023_writebit (state, 0x04, 0x60, (p->inversion?0:0x20)); + tda10023_writebit (state, 0x04, 0x40, 0x40); + tda10023_setup_reg0 (state, qamvals[qam][0]); + + return 0; +} + +static int tda10023_read_status(struct dvb_frontend* fe, fe_status_t* status) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync; + + *status = 0; + + //0x11[1] == CARLOCK -> Carrier locked + //0x11[2] == FSYNC -> Frame synchronisation + //0x11[3] == FEL -> Front End locked + //0x11[6] == NODVB -> DVB Mode Information + sync = tda10023_readreg (state, 0x11); + + if (sync & 2) + *status |= FE_HAS_SIGNAL|FE_HAS_CARRIER; + + if (sync & 4) + *status |= FE_HAS_SYNC|FE_HAS_VITERBI; + + if (sync & 8) + *status |= FE_HAS_LOCK; + + return 0; +} + +static int tda10023_read_ber(struct dvb_frontend* fe, u32* ber) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c; + a=tda10023_readreg(state, 0x14); + b=tda10023_readreg(state, 0x15); + c=tda10023_readreg(state, 0x16)&0xf; + tda10023_writebit (state, 0x10, 0xc0, 0x00); + + *ber = a | (b<<8)| (c<<16); + return 0; +} + +static int tda10023_read_signal_strength(struct dvb_frontend* fe, u16* strength) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 ifgain=tda10023_readreg(state, 0x2f); + + u16 gain = ((255-tda10023_readreg(state, 0x17))) + (255-ifgain)/16; + // Max raw value is about 0xb0 -> Normalize to >0xf0 after 0x90 + if (gain>0x90) + gain=gain+2*(gain-0x90); + if (gain>255) + gain=255; + + *strength = (gain<<8)|gain; + return 0; +} + +static int tda10023_read_snr(struct dvb_frontend* fe, u16* snr) +{ + struct tda10023_state* state = fe->demodulator_priv; + + u8 quality = ~tda10023_readreg(state, 0x18); + *snr = (quality << 8) | quality; + return 0; +} + +static int tda10023_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) +{ + struct tda10023_state* state = fe->demodulator_priv; + u8 a,b,c,d; + a= tda10023_readreg (state, 0x74); + b= tda10023_readreg (state, 0x75); + c= tda10023_readreg (state, 0x76); + d= tda10023_readreg (state, 0x77); + *ucblocks = a | (b<<8)|(c<<16)|(d<<24); + + tda10023_writebit (state, 0x10, 0x20,0x00); + tda10023_writebit (state, 0x10, 0x20,0x20); + tda10023_writebit (state, 0x13, 0x01, 0x00); + + return 0; +} + +static int tda10023_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *p) +{ + struct tda10023_state* state = fe->demodulator_priv; + int sync,inv; + s8 afc = 0; + + sync = tda10023_readreg(state, 0x11); + afc = tda10023_readreg(state, 0x19); + inv = tda10023_readreg(state, 0x04); + + if (verbose) { + /* AFC only valid when carrier has been recovered */ + printk(sync & 2 ? "DVB: TDA10023(%d): AFC (%d) %dHz\n" : + "DVB: TDA10023(%d): [AFC (%d) %dHz]\n", + state->frontend.dvb->num, afc, + -((s32)p->u.qam.symbol_rate * afc) >> 10); + } + + p->inversion = (inv&0x20?0:1); + p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16; + + p->u.qam.fec_inner = FEC_NONE; + p->frequency = ((p->frequency + 31250) / 62500) * 62500; + + if (sync & 2) + p->frequency -= ((s32)p->u.qam.symbol_rate * afc) >> 10; + + return 0; +} + +static int tda10023_sleep(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + + tda10023_writereg (state, 0x1b, 0x02); /* pdown ADC */ + tda10023_writereg (state, 0x00, 0x80); /* standby */ + + return 0; +} + +static int tda10023_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct tda10023_state* state = fe->demodulator_priv; + + if (enable) { + lock_tuner(state); + } else { + unlock_tuner(state); + } + return 0; +} + +static void tda10023_release(struct dvb_frontend* fe) +{ + struct tda10023_state* state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops tda10023_ops; + +struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, + u8 pwm) +{ + struct tda10023_state* state = NULL; + int i; + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct tda10023_state), GFP_KERNEL); + if (state == NULL) goto error; + + /* setup the state */ + state->config = config; + state->i2c = i2c; + memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->pwm = pwm; + for (i=0; i < sizeof(tda10023_inittab)/sizeof(*tda10023_inittab);i+=3) { + if (tda10023_inittab[i] == 0x00) { + state->reg0 = tda10023_inittab[i+2]; + break; + } + } + + // Wakeup if in standby + tda10023_writereg (state, 0x00, 0x33); + /* check if the demod is there */ + if ((tda10023_readreg(state, 0x1a) & 0xf0) != 0x70) goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &tda10023_ops, sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error: + kfree(state); + return NULL; +} + +static struct dvb_frontend_ops tda10023_ops = { + + .info = { + .name = "Philips TDA10023 DVB-C", + .type = FE_QAM, + .frequency_stepsize = 62500, + .frequency_min = 51000000, + .frequency_max = 858000000, + .symbol_rate_min = (SYSCLK/2)/64, /* SACLK/64 == (SYSCLK/2)/64 */ + .symbol_rate_max = (SYSCLK/2)/4, /* SACLK/4 */ + .caps = 0x400 | //FE_CAN_QAM_4 + FE_CAN_QAM_16 | FE_CAN_QAM_32 | FE_CAN_QAM_64 | + FE_CAN_QAM_128 | FE_CAN_QAM_256 | + FE_CAN_FEC_AUTO + }, + + .release = tda10023_release, + + .init = tda10023_init, + .sleep = tda10023_sleep, + .i2c_gate_ctrl = tda10023_i2c_gate_ctrl, + + .set_frontend = tda10023_set_parameters, + .get_frontend = tda10023_get_frontend, + + .read_status = tda10023_read_status, + .read_ber = tda10023_read_ber, + .read_signal_strength = tda10023_read_signal_strength, + .read_snr = tda10023_read_snr, + .read_ucblocks = tda10023_read_ucblocks, +}; + + +MODULE_DESCRIPTION("Philips TDA10023 DVB-C demodulator driver"); +MODULE_AUTHOR("Georg Acher, Hartmut Birr"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(tda10023_attach); diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda1002x.h index e3da780..e9094d8 100644 --- a/drivers/media/dvb/frontends/tda10021.h +++ b/drivers/media/dvb/frontends/tda1002x.h @@ -1,6 +1,6 @@ /* - TDA10021 - Single Chip Cable Channel Receiver driver module - used on the the Siemens DVB-C cards + TDA10021/TDA10023 - Single Chip Cable Channel Receiver driver module + used on the the Siemens DVB-C cards Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de> Copyright (C) 2004 Markus Schulz <msc@antzsystem.de> @@ -21,22 +21,23 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#ifndef TDA10021_H -#define TDA10021_H +#ifndef TDA1002x_H +#define TDA1002x_H #include <linux/dvb/frontend.h> -struct tda10021_config +struct tda1002x_config { /* the demodulator's i2c address */ u8 demod_address; + u8 invert; }; #if defined(CONFIG_DVB_TDA10021) || (defined(CONFIG_DVB_TDA10021_MODULE) && defined(MODULE)) -extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, +extern struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm); #else -static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config, +static inline struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, struct i2c_adapter* i2c, u8 pwm) { printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); @@ -44,12 +45,16 @@ static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* } #endif // CONFIG_DVB_TDA10021 -static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) { - int r = 0; - u8 buf[] = {reg, val}; - if (fe->ops.write) - r = fe->ops.write(fe, buf, 2); - return r; +#if defined(CONFIG_DVB_TDA10023) || (defined(CONFIG_DVB_TDA10023_MODULE) && defined(MODULE)) +extern struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm); +#else +static inline struct dvb_frontend* tda10023_attach(const struct tda1002x_config* config, + struct i2c_adapter* i2c, u8 pwm) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; } +#endif // CONFIG_DVB_TDA10023 -#endif // TDA10021_H +#endif // TDA1002x_H diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 00e4bcd..33a8437 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -40,20 +40,6 @@ #include "dvb_frontend.h" #include "tda1004x.h" -enum tda1004x_demod { - TDA1004X_DEMOD_TDA10045, - TDA1004X_DEMOD_TDA10046, -}; - -struct tda1004x_state { - struct i2c_adapter* i2c; - const struct tda1004x_config* config; - struct dvb_frontend frontend; - - /* private demod data */ - enum tda1004x_demod demod_type; -}; - static int debug; #define dprintk(args...) \ do { \ @@ -507,35 +493,51 @@ static int tda10046_fwupload(struct dvb_frontend* fe) tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x80); } tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0); + /* set GPIO 1 and 3 */ + if (state->config->gpio_config != TDA10046_GPTRI) { + tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE2, 0x33); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, state->config->gpio_config &0x0f); + } /* let the clocks recover from sleep */ - msleep(5); + msleep(10); /* The PLLs need to be reprogrammed after sleep */ tda10046_init_plls(fe); + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0); /* don't re-upload unless necessary */ if (tda1004x_check_upload_ok(state) == 0) return 0; + printk(KERN_INFO "tda1004x: trying to boot from eeprom\n"); + tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4); + msleep(300); + /* don't re-upload unless necessary */ + if (tda1004x_check_upload_ok(state) == 0) + return 0; + if (state->config->request_firmware != NULL) { /* request the firmware, this will block until someone uploads it */ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n"); ret = state->config->request_firmware(fe, &fw, TDA10046_DEFAULT_FIRMWARE); if (ret) { - printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); - return ret; + /* remain compatible to old bug: try to load with tda10045 image name */ + ret = state->config->request_firmware(fe, &fw, TDA10045_DEFAULT_FIRMWARE); + if (ret) { + printk(KERN_ERR "tda1004x: no firmware upload (timeout or file not found?)\n"); + return ret; + } else { + printk(KERN_INFO "tda1004x: please rename the firmware file to %s\n", + TDA10046_DEFAULT_FIRMWARE); + } } - tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST - ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); - release_firmware(fw); - if (ret) - return ret; } else { - /* boot from firmware eeprom */ - printk(KERN_INFO "tda1004x: booting from eeprom\n"); - tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4); - msleep(300); + printk(KERN_ERR "tda1004x: no request function defined, can't upload from file\n"); + return -EIO; } + tda1004x_write_mask(state, TDA1004X_CONFC4, 8, 8); // going to boot from HOST + ret = tda1004x_do_upload(state, fw->data, fw->size, TDA10046H_CODE_CPT, TDA10046H_CODE_IN); + release_firmware(fw); return tda1004x_check_upload_ok(state); } @@ -579,7 +581,7 @@ static int tda1004x_decode_fec(int tdafec) return -1; } -int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len) +static int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len) { struct tda1004x_state* state = fe->demodulator_priv; @@ -638,37 +640,33 @@ static int tda10046_init(struct dvb_frontend* fe) switch (state->config->agc_config) { case TDA10046_AGC_DEFAULT: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x00); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; case TDA10046_AGC_IFO_AUTO_NEG: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; case TDA10046_AGC_IFO_AUTO_POS: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x0a); // AGC setup - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x00); // set AGC polarities - break; - case TDA10046_AGC_TDA827X_GP11: - tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup - tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold - tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x6a); // set AGC polarities - break; - case TDA10046_AGC_TDA827X_GP00: - tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup - tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold - tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x60); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x00); // set AGC polarities break; - case TDA10046_AGC_TDA827X_GP01: + case TDA10046_AGC_TDA827X: tda1004x_write_byteI(state, TDA10046H_AGC_CONF, 0x02); // AGC setup tda1004x_write_byteI(state, TDA10046H_AGC_THR, 0x70); // AGC Threshold tda1004x_write_byteI(state, TDA10046H_AGC_RENORM, 0x08); // Gain Renormalize - tda1004x_write_byteI(state, TDA10046H_CONF_POLARITY, 0x62); // set AGC polarities + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0xf0, 0x60); // set AGC polarities break; } + if (state->config->ts_mode == 0) { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x40); + tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); + } else { + tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 0xc0, 0x80); + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x10, + state->config->invert_oclk << 4); + } tda1004x_write_byteI(state, TDA1004X_CONFADC2, 0x38); - tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0x61); // Turn both AGC outputs on + tda1004x_write_mask (state, TDA10046H_CONF_TRISTATE1, 0x3e, 0x38); // Turn IF AGC output on tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MIN, 0); // } tda1004x_write_byteI(state, TDA10046H_AGC_TUN_MAX, 0xff); // } AGC min/max values tda1004x_write_byteI(state, TDA10046H_AGC_IF_MIN, 0); // } @@ -678,7 +676,6 @@ static int tda10046_init(struct dvb_frontend* fe) tda1004x_write_byteI(state, TDA1004X_CONF_TS1, 7); // MPEG2 interface config tda1004x_write_byteI(state, TDA1004X_CONF_TS2, 0xc0); // MPEG2 interface config // tda1004x_write_mask(state, 0x50, 0x80, 0x80); // handle out of guard echoes - tda1004x_write_mask(state, 0x3a, 0x80, state->config->invert_oclk << 7); return 0; } @@ -705,7 +702,8 @@ static int tda1004x_set_fe(struct dvb_frontend* fe, // set frequency if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe, fe_params); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } // Hardcoded to use auto as much as possible on the TDA10045 as it @@ -1165,6 +1163,7 @@ static int tda1004x_read_ber(struct dvb_frontend* fe, u32* ber) static int tda1004x_sleep(struct dvb_frontend* fe) { struct tda1004x_state* state = fe->demodulator_priv; + int gpio_conf; switch (state->demod_type) { case TDA1004X_DEMOD_TDA10045: @@ -1174,6 +1173,13 @@ static int tda1004x_sleep(struct dvb_frontend* fe) case TDA1004X_DEMOD_TDA10046: /* set outputs to tristate */ tda1004x_write_byteI(state, TDA10046H_CONF_TRISTATE1, 0xff); + /* invert GPIO 1 and 3 if desired*/ + gpio_conf = state->config->gpio_config; + if (gpio_conf >= TDA10046_GP00_I) + tda1004x_write_mask(state, TDA10046H_CONF_POLARITY, 0x0f, + (gpio_conf & 0x0f) ^ 0x0a); + + tda1004x_write_mask(state, TDA1004X_CONFADC2, 0xc0, 0xc0); tda1004x_write_mask(state, TDA1004X_CONFC4, 1, 1); break; } diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h index ec502d7..abae843 100644 --- a/drivers/media/dvb/frontends/tda1004x.h +++ b/drivers/media/dvb/frontends/tda1004x.h @@ -35,9 +35,23 @@ enum tda10046_agc { TDA10046_AGC_DEFAULT, /* original configuration */ TDA10046_AGC_IFO_AUTO_NEG, /* IF AGC only, automatic, negtive */ TDA10046_AGC_IFO_AUTO_POS, /* IF AGC only, automatic, positive */ - TDA10046_AGC_TDA827X_GP11, /* IF AGC only, special setup for tda827x */ - TDA10046_AGC_TDA827X_GP00, /* same as above, but GPIOs 0 */ - TDA10046_AGC_TDA827X_GP01, /* same as above, but GPIO3=0 GPIO1=1*/ + TDA10046_AGC_TDA827X, /* IF AGC only, special setup for tda827x */ +}; + +/* Many (hybrid) boards use GPIO 1 and 3 + GPIO1 analog - dvb switch + GPIO3 firmware eeprom address switch +*/ +enum tda10046_gpio { + TDA10046_GPTRI = 0x00, /* All GPIOs tristate */ + TDA10046_GP00 = 0x40, /* GPIO3=0, GPIO1=0 */ + TDA10046_GP01 = 0x42, /* GPIO3=0, GPIO1=1 */ + TDA10046_GP10 = 0x48, /* GPIO3=1, GPIO1=0 */ + TDA10046_GP11 = 0x4a, /* GPIO3=1, GPIO1=1 */ + TDA10046_GP00_I = 0x80, /* GPIO3=0, GPIO1=0, invert in sleep mode*/ + TDA10046_GP01_I = 0x82, /* GPIO3=0, GPIO1=1, invert in sleep mode */ + TDA10046_GP10_I = 0x88, /* GPIO3=1, GPIO1=0, invert in sleep mode */ + TDA10046_GP11_I = 0x8a, /* GPIO3=1, GPIO1=1, invert in sleep mode */ }; enum tda10046_if { @@ -47,6 +61,11 @@ enum tda10046_if { TDA10046_FREQ_052, /* low IF, 5.1667 MHZ for tda9889 */ }; +enum tda10046_tsout { + TDA10046_TS_PARALLEL = 0x00, /* parallel transport stream, default */ + TDA10046_TS_SERIAL = 0x01, /* serial transport stream */ +}; + struct tda1004x_config { /* the demodulator's i2c address */ @@ -58,6 +77,9 @@ struct tda1004x_config /* Does the OCLK signal need inverted? */ u8 invert_oclk; + /* parallel or serial transport stream */ + enum tda10046_tsout ts_mode; + /* Xtal frequency, 4 or 16MHz*/ enum tda10046_xtal xtal_freq; @@ -67,11 +89,35 @@ struct tda1004x_config /* AGC configuration */ enum tda10046_agc agc_config; + /* setting of GPIO1 and 3 */ + enum tda10046_gpio gpio_config; + + /* slave address and configuration of the tuner */ + u8 tuner_address; + u8 tuner_config; + u8 antenna_switch; + + /* if the board uses another I2c Bridge (tda8290), its address */ + u8 i2c_gate; + /* request firmware for device */ - /* set this to NULL if the card has a firmware EEPROM */ int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name); }; +enum tda1004x_demod { + TDA1004X_DEMOD_TDA10045, + TDA1004X_DEMOD_TDA10046, +}; + +struct tda1004x_state { + struct i2c_adapter* i2c; + const struct tda1004x_config* config; + struct dvb_frontend frontend; + + /* private demod data */ + enum tda1004x_demod demod_type; +}; + #if defined(CONFIG_DVB_TDA1004X) || (defined(CONFIG_DVB_TDA1004X_MODULE) && defined(MODULE)) extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, struct i2c_adapter* i2c); diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c index 4c27a2d9..ccc429c 100644 --- a/drivers/media/dvb/frontends/tda10086.c +++ b/drivers/media/dvb/frontends/tda10086.c @@ -212,7 +212,7 @@ static int tda10086_send_master_cmd (struct dvb_frontend* fe, for(i=0; i< cmd->msg_len; i++) { tda10086_write_byte(state, 0x48+i, cmd->msg[i]); } - tda10086_write_byte(state, 0x36, 0x08 | ((cmd->msg_len + 1) << 4)); + tda10086_write_byte(state, 0x36, 0x08 | ((cmd->msg_len - 1) << 4)); tda10086_diseqc_wait(state); diff --git a/drivers/media/dvb/frontends/tda827x.c b/drivers/media/dvb/frontends/tda827x.c new file mode 100644 index 0000000..256fc4b --- /dev/null +++ b/drivers/media/dvb/frontends/tda827x.c @@ -0,0 +1,512 @@ +/* + * + * (c) 2005 Hartmut Hackmann + * (c) 2007 Michael Krufky + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/dvb/frontend.h> +#include <asm/types.h> + +#include "tda827x.h" + +static int debug = 0; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "tda827x: " args); \ + } while (0) + +struct tda827x_priv { + int i2c_addr; + struct i2c_adapter *i2c_adap; + struct tda827x_config *cfg; + u32 frequency; + u32 bandwidth; +}; + +struct tda827x_data { + u32 lomax; + u8 spd; + u8 bs; + u8 bp; + u8 cp; + u8 gc3; + u8 div1p5; +}; + +static const struct tda827x_data tda827x_dvbt[] = { + { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, + { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, + { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, + { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, + { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, + { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, + { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} +}; + +static int tda827xo_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + u8 buf[14]; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + int i, tuner_freq, if_freq; + u32 N; + + dprintk("%s:\n", __FUNCTION__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + if_freq = 4000000; + break; + case BANDWIDTH_7_MHZ: + if_freq = 4500000; + break; + default: /* 8 MHz or Auto */ + if_freq = 5000000; + break; + } + tuner_freq = params->frequency + if_freq; + + i = 0; + while (tda827x_dvbt[i].lomax < tuner_freq) { + if(tda827x_dvbt[i + 1].lomax == 0) + break; + i++; + } + + N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2); + buf[0] = 0; + buf[1] = (N>>8) | 0x40; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x52; + buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) + + (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp; + buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f; + buf[7] = 0xbf; + buf[8] = 0x2a; + buf[9] = 0x05; + buf[10] = 0xff; + buf[11] = 0x00; + buf[12] = 0x00; + buf[13] = 0x40; + + msg.len = 14; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not write to tuner at addr: 0x%02x\n", + __FUNCTION__, priv->i2c_addr << 1); + return -EIO; + } + msleep(500); + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x50 + tda827x_dvbt[i].cp; + msg.len = 2; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + priv->frequency = tuner_freq - if_freq; // FIXME + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + + return 0; +} + +static int tda827xo_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0xd0 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __FUNCTION__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +struct tda827xa_data { + u32 lomax; + u8 svco; + u8 spd; + u8 scr; + u8 sbs; + u8 gc3; +}; + +static const struct tda827xa_data tda827xa_dvbt[] = { + { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, + { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, + { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, + { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, + { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, + { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, + { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, + { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, + { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} +}; + +static int tda827xa_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct tda827x_priv *priv = fe->tuner_priv; + u8 buf[11]; + + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + int i, tuner_freq, if_freq; + u32 N; + + dprintk("%s:\n", __FUNCTION__); + if (priv->cfg && priv->cfg->lna_gain) + priv->cfg->lna_gain(fe, 1); + msleep(20); + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + if_freq = 4000000; + break; + case BANDWIDTH_7_MHZ: + if_freq = 4500000; + break; + default: /* 8 MHz or Auto */ + if_freq = 5000000; + break; + } + tuner_freq = params->frequency + if_freq; + + i = 0; + while (tda827xa_dvbt[i].lomax < tuner_freq) { + if(tda827xa_dvbt[i + 1].lomax == 0) + break; + i++; + } + + N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd; + buf[0] = 0; // subaddress + buf[1] = N >> 8; + buf[2] = N & 0xff; + buf[3] = 0; + buf[4] = 0x16; + buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) + + tda827xa_dvbt[i].sbs; + buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4); + buf[7] = 0x1c; + buf[8] = 0x06; + buf[9] = 0x24; + buf[10] = 0x00; + msg.len = 11; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not write to tuner at addr: 0x%02x\n", + __FUNCTION__, priv->i2c_addr << 1); + return -EIO; + } + buf[0] = 0x90; + buf[1] = 0xff; + buf[2] = 0x60; + buf[3] = 0x00; + buf[4] = 0x59; // lpsel, for 6MHz + 2 + msg.len = 5; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + buf[0] = 0xa0; + buf[1] = 0x40; + msg.len = 2; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(11); + msg.flags = I2C_M_RD; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + msg.flags = 0; + + buf[1] >>= 4; + dprintk("tda8275a AGC2 gain is: %d\n", buf[1]); + if ((buf[1]) < 2) { + if (priv->cfg && priv->cfg->lna_gain) + priv->cfg->lna_gain(fe, 0); + buf[0] = 0x60; + buf[1] = 0x0c; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + } + + buf[0] = 0xc0; + buf[1] = 0x99; // lpsel, for 6MHz + 2 + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + buf[0] = 0x60; + buf[1] = 0x3c; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + /* correct CP value */ + buf[0] = 0x30; + buf[1] = 0x10 + tda827xa_dvbt[i].scr; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(163); + buf[0] = 0xc0; + buf[1] = 0x39; // lpsel, for 6MHz + 2 + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + msleep(3); + /* freeze AGC1 */ + buf[0] = 0x50; + buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + i2c_transfer(priv->i2c_adap, &msg, 1); + + priv->frequency = tuner_freq - if_freq; // FIXME + priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0; + + return 0; +} + +static int tda827xa_sleep(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + static u8 buf[] = { 0x30, 0x90 }; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0, + .buf = buf, .len = sizeof(buf) }; + + dprintk("%s:\n", __FUNCTION__); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + i2c_transfer(priv->i2c_adap, &msg, 1); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (priv->cfg && priv->cfg->sleep) + priv->cfg->sleep(fe); + + return 0; +} + +static int tda827x_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int tda827x_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int tda827x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct tda827x_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int tda827x_init(struct dvb_frontend *fe) +{ + struct tda827x_priv *priv = fe->tuner_priv; + dprintk("%s:\n", __FUNCTION__); + if (priv->cfg && priv->cfg->init) + priv->cfg->init(fe); + + return 0; +} + +static int tda827x_probe_version(struct dvb_frontend *fe); + +static int tda827x_initial_init(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.init(fe); +} + +static int tda827x_initial_sleep(struct dvb_frontend *fe) +{ + int ret; + ret = tda827x_probe_version(fe); + if (ret) + return ret; + return fe->ops.tuner_ops.sleep(fe); +} + +static struct dvb_tuner_ops tda827xo_tuner_ops = { + .info = { + .name = "Philips TDA827X", + .frequency_min = 55000000, + .frequency_max = 860000000, + .frequency_step = 250000 + }, + .release = tda827x_release, + .init = tda827x_initial_init, + .sleep = tda827x_initial_sleep, + .set_params = tda827xo_set_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static struct dvb_tuner_ops tda827xa_tuner_ops = { + .info = { + .name = "Philips TDA827XA", + .frequency_min = 44000000, + .frequency_max = 906000000, + .frequency_step = 62500 + }, + .release = tda827x_release, + .init = tda827x_init, + .sleep = tda827xa_sleep, + .set_params = tda827xa_set_params, + .get_frequency = tda827x_get_frequency, + .get_bandwidth = tda827x_get_bandwidth, +}; + +static int tda827x_probe_version(struct dvb_frontend *fe) +{ u8 data; + struct tda827x_priv *priv = fe->tuner_priv; + struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD, + .buf = &data, .len = 1 }; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) { + printk("%s: could not read from tuner at addr: 0x%02x\n", + __FUNCTION__, msg.addr << 1); + return -EIO; + } + if ((data & 0x3c) == 0) { + dprintk("tda827x tuner found\n"); + fe->ops.tuner_ops.init = tda827x_init; + fe->ops.tuner_ops.sleep = tda827xo_sleep; + } else { + dprintk("tda827xa tuner found\n"); + memcpy(&fe->ops.tuner_ops, &tda827xa_tuner_ops, sizeof(struct dvb_tuner_ops)); + } + return 0; +} + +struct dvb_frontend *tda827x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg) +{ + struct tda827x_priv *priv = NULL; + + dprintk("%s:\n", __FUNCTION__); + priv = kzalloc(sizeof(struct tda827x_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c_addr = addr; + priv->i2c_adap = i2c; + priv->cfg = cfg; + memcpy(&fe->ops.tuner_ops, &tda827xo_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + return fe; +} + +EXPORT_SYMBOL(tda827x_attach); + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +MODULE_DESCRIPTION("DVB TDA827x driver"); +MODULE_AUTHOR("Hartmut Hackmann <hartmut.hackmann@t-online.de>"); +MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>"); +MODULE_LICENSE("GPL"); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/frontends/tda827x.h b/drivers/media/dvb/frontends/tda827x.h new file mode 100644 index 0000000..69e8263 --- /dev/null +++ b/drivers/media/dvb/frontends/tda827x.h @@ -0,0 +1,62 @@ + /* + DVB Driver for Philips tda827x / tda827xa Silicon tuners + + (c) 2005 Hartmut Hackmann + (c) 2007 Michael Krufky + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + */ + +#ifndef __DVB_TDA827X_H__ +#define __DVB_TDA827X_H__ + +#include <linux/i2c.h> +#include "dvb_frontend.h" + +struct tda827x_config +{ + void (*lna_gain) (struct dvb_frontend *fe, int high); + int (*init) (struct dvb_frontend *fe); + int (*sleep) (struct dvb_frontend *fe); +}; + + +/** + * Attach a tda827x tuner to the supplied frontend structure. + * + * @param fe Frontend to attach to. + * @param addr i2c address of the tuner. + * @param i2c i2c adapter to use. + * @param cfg optional callback function pointers. + * @return FE pointer on success, NULL on failure. + */ +#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE)) +extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg); +#else +static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, + int addr, + struct i2c_adapter *i2c, + struct tda827x_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__); + return NULL; +} +#endif // CONFIG_DVB_TDA827X + +#endif // __DVB_TDA827X_H__ diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 0e9b59a..245f9b7 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -38,6 +38,12 @@ struct zl10353_state { struct zl10353_config config; }; +static int debug; +#define dprintk(args...) \ + do { \ + if (debug) printk(KERN_DEBUG "zl10353: " args); \ + } while (0) + static int debug_regs = 0; static int zl10353_single_write(struct dvb_frontend *fe, u8 reg, u8 val) @@ -54,7 +60,7 @@ static int zl10353_single_write(struct dvb_frontend *fe, u8 reg, u8 val) return 0; } -int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen) +static int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen) { int err, i; for (i = 0; i < ilen - 1; i++) @@ -113,6 +119,36 @@ static void zl10353_dump_regs(struct dvb_frontend *fe) printk(KERN_DEBUG "%s\n", buf); } +static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, + enum fe_bandwidth bandwidth, + u16 *nominal_rate) +{ + u32 adc_clock = 22528; /* 20.480 MHz on the board(!?) */ + u8 bw; + struct zl10353_state *state = fe->demodulator_priv; + + if (state->config.adc_clock) + adc_clock = state->config.adc_clock; + + switch (bandwidth) { + case BANDWIDTH_6_MHZ: + bw = 6; + break; + case BANDWIDTH_7_MHZ: + bw = 7; + break; + case BANDWIDTH_8_MHZ: + default: + bw = 8; + break; + } + + *nominal_rate = (64 * bw * (1<<16) / (7 * 8) * 4000 / adc_clock + 2) / 4; + + dprintk("%s: bw %d, adc_clock %d => 0x%x\n", + __FUNCTION__, bw, adc_clock, *nominal_rate); +} + static int zl10353_sleep(struct dvb_frontend *fe) { static u8 zl10353_softdown[] = { 0x50, 0x0C, 0x44 }; @@ -125,7 +161,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, struct dvb_frontend_parameters *param) { struct zl10353_state *state = fe->demodulator_priv; - + u16 nominal_rate; u8 pllbuf[6] = { 0x67 }; /* These settings set "auto-everything" and start the FSM. */ @@ -138,18 +174,23 @@ static int zl10353_set_parameters(struct dvb_frontend *fe, zl10353_single_write(fe, 0x56, 0x28); zl10353_single_write(fe, 0x89, 0x20); zl10353_single_write(fe, 0x5E, 0x00); - zl10353_single_write(fe, 0x65, 0x5A); - zl10353_single_write(fe, 0x66, 0xE9); + + zl10353_calc_nominal_rate(fe, param->u.ofdm.bandwidth, &nominal_rate); + zl10353_single_write(fe, TRL_NOMINAL_RATE_1, msb(nominal_rate)); + zl10353_single_write(fe, TRL_NOMINAL_RATE_0, lsb(nominal_rate)); + zl10353_single_write(fe, 0x6C, 0xCD); zl10353_single_write(fe, 0x6D, 0x7E); - zl10353_single_write(fe, 0x62, 0x0A); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); // if there is no attached secondary tuner, we call set_params to program // a potential tuner attached somewhere else if (state->config.no_tuner) { if (fe->ops.tuner_ops.set_params) { fe->ops.tuner_ops.set_params(fe, param); - if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); } } @@ -213,6 +254,29 @@ static int zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status) return 0; } +static int zl10353_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct zl10353_state *state = fe->demodulator_priv; + + *ber = zl10353_read_register(state, RS_ERR_CNT_2) << 16 | + zl10353_read_register(state, RS_ERR_CNT_1) << 8 | + zl10353_read_register(state, RS_ERR_CNT_0); + + return 0; +} + +static int zl10353_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct zl10353_state *state = fe->demodulator_priv; + + u16 signal = zl10353_read_register(state, AGC_GAIN_1) << 10 | + zl10353_read_register(state, AGC_GAIN_0) << 2 | 3; + + *strength = ~signal; + + return 0; +} + static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) { struct zl10353_state *state = fe->demodulator_priv; @@ -227,6 +291,16 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) return 0; } +static int zl10353_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct zl10353_state *state = fe->demodulator_priv; + + *ucblocks = zl10353_read_register(state, RS_UBC_1) << 8 | + zl10353_read_register(state, RS_UBC_0); + + return 0; +} + static int zl10353_get_tune_settings(struct dvb_frontend *fe, struct dvb_frontend_tune_settings *fe_tune_settings) @@ -261,6 +335,16 @@ static int zl10353_init(struct dvb_frontend *fe) return 0; } +static int zl10353_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + u8 val = 0x0a; + + if (enable) + val |= 0x10; + + return zl10353_single_write(fe, 0x62, val); +} + static void zl10353_release(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; @@ -319,15 +403,22 @@ static struct dvb_frontend_ops zl10353_ops = { .init = zl10353_init, .sleep = zl10353_sleep, + .i2c_gate_ctrl = zl10353_i2c_gate_ctrl, .write = zl10353_write, .set_frontend = zl10353_set_parameters, .get_tune_settings = zl10353_get_tune_settings, .read_status = zl10353_read_status, + .read_ber = zl10353_read_ber, + .read_signal_strength = zl10353_read_signal_strength, .read_snr = zl10353_read_snr, + .read_ucblocks = zl10353_read_ucblocks, }; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + module_param(debug_regs, int, 0644); MODULE_PARM_DESC(debug_regs, "Turn on/off frontend register dumps (default:off)."); diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h index 0bc0109..cb274dc 100644 --- a/drivers/media/dvb/frontends/zl10353.h +++ b/drivers/media/dvb/frontends/zl10353.h @@ -29,6 +29,9 @@ struct zl10353_config /* demodulator's I2C address */ u8 demod_address; + /* frequencies in kHz */ + int adc_clock; // default: 22528 + /* set if no pll is connected to the secondary i2c bus */ int no_tuner; diff --git a/drivers/media/dvb/frontends/zl10353_priv.h b/drivers/media/dvb/frontends/zl10353_priv.h index b72224b..4962434 100644 --- a/drivers/media/dvb/frontends/zl10353_priv.h +++ b/drivers/media/dvb/frontends/zl10353_priv.h @@ -24,19 +24,31 @@ #define ID_ZL10353 0x14 +#define msb(x) (((x) >> 8) & 0xff) +#define lsb(x) ((x) & 0xff) + enum zl10353_reg_addr { - INTERRUPT_0 = 0x00, - INTERRUPT_1 = 0x01, - INTERRUPT_2 = 0x02, - INTERRUPT_3 = 0x03, - INTERRUPT_4 = 0x04, - INTERRUPT_5 = 0x05, - STATUS_6 = 0x06, - STATUS_7 = 0x07, - STATUS_8 = 0x08, - STATUS_9 = 0x09, - SNR = 0x10, - CHIP_ID = 0x7F, + INTERRUPT_0 = 0x00, + INTERRUPT_1 = 0x01, + INTERRUPT_2 = 0x02, + INTERRUPT_3 = 0x03, + INTERRUPT_4 = 0x04, + INTERRUPT_5 = 0x05, + STATUS_6 = 0x06, + STATUS_7 = 0x07, + STATUS_8 = 0x08, + STATUS_9 = 0x09, + AGC_GAIN_1 = 0x0A, + AGC_GAIN_0 = 0x0B, + SNR = 0x10, + RS_ERR_CNT_2 = 0x11, + RS_ERR_CNT_1 = 0x12, + RS_ERR_CNT_0 = 0x13, + RS_UBC_1 = 0x14, + RS_UBC_0 = 0x15, + TRL_NOMINAL_RATE_1 = 0x65, + TRL_NOMINAL_RATE_0 = 0x66, + CHIP_ID = 0x7F, }; #endif /* _ZL10353_PRIV_ */ diff --git a/drivers/media/dvb/pluto2/Kconfig b/drivers/media/dvb/pluto2/Kconfig index 9b84b1b..7d8e6e8 100644 --- a/drivers/media/dvb/pluto2/Kconfig +++ b/drivers/media/dvb/pluto2/Kconfig @@ -2,7 +2,6 @@ config DVB_PLUTO2 tristate "Pluto2 cards" depends on DVB_CORE && PCI && I2C select I2C_ALGOBIT - select DVB_PLL select DVB_TDA1004X help Support for PCI cards based on the Pluto2 FPGA like the Satelco diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index ffda71d..058df5c 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -149,6 +149,15 @@ static inline void pluto_rw(struct pluto *pluto, u32 reg, u32 mask, u32 bits) writel(val, &pluto->io_mem[reg]); } +static void pluto_write_tscr(struct pluto *pluto, u32 val) +{ + /* set the number of packets */ + val &= ~TSCR_ADEF; + val |= TS_DMA_PACKETS / 2; + + pluto_writereg(pluto, REG_TSCR, val); +} + static void pluto_setsda(void *data, int state) { struct pluto *pluto = data; @@ -213,11 +222,11 @@ static void pluto_reset_ts(struct pluto *pluto, int reenable) if (val & TSCR_RSTN) { val &= ~TSCR_RSTN; - pluto_writereg(pluto, REG_TSCR, val); + pluto_write_tscr(pluto, val); } if (reenable) { val |= TSCR_RSTN; - pluto_writereg(pluto, REG_TSCR, val); + pluto_write_tscr(pluto, val); } } @@ -339,7 +348,7 @@ static irqreturn_t pluto_irq(int irq, void *dev_id) } /* ACK the interrupt */ - pluto_writereg(pluto, REG_TSCR, tscr | TSCR_IACK); + pluto_write_tscr(pluto, tscr | TSCR_IACK); return IRQ_HANDLED; } @@ -348,9 +357,6 @@ static void __devinit pluto_enable_irqs(struct pluto *pluto) { u32 val = pluto_readreg(pluto, REG_TSCR); - /* set the number of packets */ - val &= ~TSCR_ADEF; - val |= TS_DMA_PACKETS / 2; /* disable AFUL and LOCK interrupts */ val |= (TSCR_MSKA | TSCR_MSKL); /* enable DMA and OVERFLOW interrupts */ @@ -358,7 +364,7 @@ static void __devinit pluto_enable_irqs(struct pluto *pluto) /* clear pending interrupts */ val |= TSCR_IACK; - pluto_writereg(pluto, REG_TSCR, val); + pluto_write_tscr(pluto, val); } static void pluto_disable_irqs(struct pluto *pluto) @@ -370,7 +376,7 @@ static void pluto_disable_irqs(struct pluto *pluto) /* clear pending interrupts */ val |= TSCR_IACK; - pluto_writereg(pluto, REG_TSCR, val); + pluto_write_tscr(pluto, val); } static int __devinit pluto_hw_init(struct pluto *pluto) diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index eec7ccf..7751628 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -3,7 +3,6 @@ config DVB_AV7110 depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select FW_LOADER if !DVB_AV7110_FIRMWARE select VIDEO_SAA7146_VV - select DVB_PLL select DVB_VES1820 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE @@ -62,13 +61,13 @@ config DVB_BUDGET tristate "Budget cards" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 - select DVB_PLL select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_VES1820 if !DVB_FE_CUSTOMISE select DVB_L64781 if !DVB_FE_CUSTOMISE select DVB_TDA8083 if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_S5H1420 if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE @@ -87,7 +86,6 @@ config DVB_BUDGET_CI tristate "Budget cards with onboard CI connector" depends on DVB_CORE && PCI && I2C && VIDEO_V4L1 select VIDEO_SAA7146 - select DVB_PLL select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE @@ -114,6 +112,7 @@ config DVB_BUDGET_AV select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_TDA10021 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select DVB_TUA6100 if !DVB_FE_CUSTOMISE select FW_LOADER help @@ -130,7 +129,6 @@ config DVB_BUDGET_PATCH tristate "AV7110 cards with Budget Patch" depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1 select DVB_AV7110 - select DVB_PLL select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_VES1X93 if !DVB_FE_CUSTOMISE select DVB_TDA8083 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c index 366c137..67becdd 100644 --- a/drivers/media/dvb/ttpci/av7110.c +++ b/drivers/media/dvb/ttpci/av7110.c @@ -51,6 +51,7 @@ #include <linux/firmware.h> #include <linux/crc32.h> #include <linux/i2c.h> +#include <linux/kthread.h> #include <asm/system.h> @@ -218,16 +219,18 @@ static void recover_arm(struct av7110 *av7110) av7110->recover(av7110); restart_feeds(av7110); - av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, av7110->ir_config); + +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, true); +#endif } static void av7110_arm_sync(struct av7110 *av7110) { - av7110->arm_rmmod = 1; - wake_up_interruptible(&av7110->arm_wait); + if (av7110->arm_thread) + kthread_stop(av7110->arm_thread); - while (av7110->arm_thread) - msleep(1); + av7110->arm_thread = NULL; } static int arm_thread(void *data) @@ -238,17 +241,11 @@ static int arm_thread(void *data) dprintk(4, "%p\n",av7110); - lock_kernel(); - daemonize("arm_mon"); - sigfillset(¤t->blocked); - unlock_kernel(); - - av7110->arm_thread = current; - for (;;) { timeout = wait_event_interruptible_timeout(av7110->arm_wait, - av7110->arm_rmmod, 5 * HZ); - if (-ERESTARTSYS == timeout || av7110->arm_rmmod) { + kthread_should_stop(), 5 * HZ); + + if (-ERESTARTSYS == timeout || kthread_should_stop()) { /* got signal or told to quit*/ break; } @@ -256,6 +253,10 @@ static int arm_thread(void *data) if (!av7110->arm_ready) continue; +#if defined(CONFIG_INPUT_EVDEV) || defined(CONFIG_INPUT_EVDEV_MODULE) + av7110_check_ir_config(av7110, false); +#endif + if (mutex_lock_interruptible(&av7110->dcomlock)) break; newloops = rdebi(av7110, DEBINOSWAP, STATUS_LOOPS, 0, 2); @@ -276,7 +277,6 @@ static int arm_thread(void *data) av7110->arm_errors = 0; } - av7110->arm_thread = NULL; return 0; } @@ -674,8 +674,8 @@ static void gpioirq(unsigned long data) return; case DATA_IRCOMMAND: - if (av7110->ir_handler) - av7110->ir_handler(av7110, + if (av7110->ir.ir_handler) + av7110->ir.ir_handler(av7110, swahw32(irdebi(av7110, DEBINOSWAP, Reserved, 0, 4))); iwdebi(av7110, DEBINOSWAP, RX_BUFF, 0, 2); break; @@ -695,8 +695,8 @@ static void gpioirq(unsigned long data) static int dvb_osd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(4, "%p\n", av7110); @@ -786,7 +786,7 @@ int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) { struct dvb_demux_feed *dvbdmxfeed = dvbdmxfilter->feed; - struct av7110 *av7110 = (struct av7110 *) dvbdmxfeed->demux->priv; + struct av7110 *av7110 = dvbdmxfeed->demux->priv; u16 buf[20]; int ret, i; u16 handle; @@ -835,7 +835,7 @@ static int StartHWFilter(struct dvb_demux_filter *dvbdmxfilter) static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) { - struct av7110 *av7110 = (struct av7110 *) dvbdmxfilter->feed->demux->priv; + struct av7110 *av7110 = dvbdmxfilter->feed->demux->priv; u16 buf[3]; u16 answ[2]; int ret; @@ -871,7 +871,7 @@ static int StopHWFilter(struct dvb_demux_filter *dvbdmxfilter) static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = (struct av7110 *) dvbdmx->priv; + struct av7110 *av7110 = dvbdmx->priv; u16 *pid = dvbdmx->pids, npids[5]; int i; int ret = 0; @@ -914,7 +914,7 @@ static int dvb_feed_start_pid(struct dvb_demux_feed *dvbdmxfeed) static int dvb_feed_stop_pid(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; - struct av7110 *av7110 = (struct av7110 *) dvbdmx->priv; + struct av7110 *av7110 = dvbdmx->priv; u16 *pid = dvbdmx->pids, npids[5]; int i; @@ -1103,9 +1103,9 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, /* pointer casting paranoia... */ BUG_ON(!demux); - dvbdemux = (struct dvb_demux *) demux->priv; + dvbdemux = demux->priv; BUG_ON(!dvbdemux); - av7110 = (struct av7110 *) dvbdemux->priv; + av7110 = dvbdemux->priv; dprintk(4, "%p\n", av7110); @@ -1137,7 +1137,7 @@ static int dvb_get_stc(struct dmx_demux *demux, unsigned int num, static int av7110_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; switch (tone) { case SEC_TONE_ON: @@ -1197,7 +1197,7 @@ static int start_ts_capture(struct av7110 *budget) static int budget_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *budget = (struct av7110 *) demux->priv; + struct av7110 *budget = demux->priv; int status; dprintk(2, "av7110: %p\n", budget); @@ -1212,7 +1212,7 @@ static int budget_start_feed(struct dvb_demux_feed *feed) static int budget_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; - struct av7110 *budget = (struct av7110 *) demux->priv; + struct av7110 *budget = demux->priv; int status; dprintk(2, "budget: %p\n", budget); @@ -1551,7 +1551,7 @@ static int get_firmware(struct av7110* av7110) static int alps_bsrv2_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u8 pwr = 0; u8 buf[4]; struct i2c_msg msg = { .addr = 0x61, .flags = 0, .buf = buf, .len = sizeof(buf) }; @@ -1702,7 +1702,7 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_front static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name) { #if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE) - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; return request_firmware(fw, name, &av7110->dev->pci->dev); #else @@ -1867,7 +1867,7 @@ static struct stv0297_config nexusca_stv0297_config = { static int grundig_29504_401_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend_parameters *params) { - struct av7110* av7110 = (struct av7110*) fe->dvb->priv; + struct av7110* av7110 = fe->dvb->priv; u32 div; u8 cfg, cpump, band_select; u8 data[4]; @@ -1914,8 +1914,10 @@ static int av7110_fe_lock_fix(struct av7110* av7110, fe_status_t status) if (av7110->fe_synced == synced) return 0; - if (av7110->playing) + if (av7110->playing) { + av7110->fe_synced = synced; return 0; + } if (mutex_lock_interruptible(&av7110->pid_mutex)) return -ERESTARTSYS; @@ -2338,6 +2340,7 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, const int length = TS_WIDTH * TS_HEIGHT; struct pci_dev *pdev = dev->pci; struct av7110 *av7110; + struct task_struct *thread; int ret, count = 0; dprintk(4, "dev: %p\n", dev); @@ -2622,9 +2625,12 @@ static int __devinit av7110_attach(struct saa7146_dev* dev, printk ("dvb-ttpci: Warning, firmware version 0x%04x is too old. " "System might be unstable!\n", FW_VERSION(av7110->arm_app)); - ret = kernel_thread(arm_thread, (void *) av7110, 0); - if (ret < 0) + thread = kthread_run(arm_thread, (void *) av7110, "arm_mon"); + if (IS_ERR(thread)) { + ret = PTR_ERR(thread); goto err_stop_arm_9; + } + av7110->arm_thread = thread; /* set initial volume in mixer struct */ av7110->mixer.volume_left = volume; diff --git a/drivers/media/dvb/ttpci/av7110.h b/drivers/media/dvb/ttpci/av7110.h index 9c79696..115002b 100644 --- a/drivers/media/dvb/ttpci/av7110.h +++ b/drivers/media/dvb/ttpci/av7110.h @@ -5,6 +5,7 @@ #include <linux/socket.h> #include <linux/netdevice.h> #include <linux/i2c.h> +#include <linux/input.h> #include <linux/dvb/video.h> #include <linux/dvb/audio.h> @@ -35,7 +36,6 @@ #define ANALOG_TUNER_VES1820 1 #define ANALOG_TUNER_STV0297 2 -#define ANALOG_TUNER_VBI 0x100 extern int av7110_debug; @@ -67,6 +67,27 @@ struct dvb_video_events { }; +struct av7110; + +/* infrared remote control */ +struct infrared { + u16 key_map[256]; + struct input_dev *input_dev; + char input_phys[32]; + struct timer_list keyup_timer; + struct tasklet_struct ir_tasklet; + void (*ir_handler)(struct av7110 *av7110, u32 ircom); + u32 ir_command; + u32 ir_config; + u32 device_mask; + u8 protocol; + u8 inversion; + u16 last_key; + u16 last_toggle; + u8 delay_timer_finished; +}; + + /* place to store all the necessary device information */ struct av7110 { @@ -205,7 +226,6 @@ struct av7110 { struct task_struct *arm_thread; wait_queue_head_t arm_wait; u16 arm_loops; - int arm_rmmod; void *debi_virt; dma_addr_t debi_bus; @@ -229,10 +249,7 @@ struct av7110 { u16 wssMode; u16 wssData; - u32 ir_config; - u32 ir_command; - void (*ir_handler)(struct av7110 *av7110, u32 ircom); - struct tasklet_struct ir_tasklet; + struct infrared ir; /* firmware stuff */ unsigned char *bin_fw; @@ -270,6 +287,7 @@ struct av7110 { extern int ChangePIDs(struct av7110 *av7110, u16 vpid, u16 apid, u16 ttpid, u16 subpid, u16 pcrpid); +extern int av7110_check_ir_config(struct av7110 *av7110, int force); extern int av7110_ir_init(struct av7110 *av7110); extern void av7110_ir_exit(struct av7110 *av7110); diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c index 795e6e9..654c9e9 100644 --- a/drivers/media/dvb/ttpci/av7110_av.c +++ b/drivers/media/dvb/ttpci/av7110_av.c @@ -880,8 +880,8 @@ static int dvb_video_get_event (struct av7110 *av7110, struct video_event *event static unsigned int dvb_video_poll(struct file *file, poll_table *wait) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; unsigned int mask = 0; dprintk(2, "av7110:%p, \n", av7110); @@ -908,8 +908,8 @@ static unsigned int dvb_video_poll(struct file *file, poll_table *wait) static ssize_t dvb_video_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(2, "av7110:%p, \n", av7110); @@ -924,8 +924,8 @@ static ssize_t dvb_video_write(struct file *file, const char __user *buf, static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; unsigned int mask = 0; dprintk(2, "av7110:%p, \n", av7110); @@ -944,8 +944,8 @@ static unsigned int dvb_audio_poll(struct file *file, poll_table *wait) static ssize_t dvb_audio_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(2, "av7110:%p, \n", av7110); @@ -989,8 +989,8 @@ static int play_iframe(struct av7110 *av7110, u8 __user *buf, unsigned int len, static int dvb_video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; int ret = 0; @@ -1009,7 +1009,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, if (av7110->videostate.stream_source == VIDEO_SOURCE_MEMORY) ret = av7110_av_stop(av7110, RP_VIDEO); else - ret = vidcom(av7110, VIDEO_CMD_STOP, + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, av7110->videostate.video_blank ? 0 : 1); if (!ret) av7110->trickmode = TRICK_NONE; @@ -1019,7 +1019,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, av7110->trickmode = TRICK_NONE; if (av7110->videostate.play_state == VIDEO_FREEZED) { av7110->videostate.play_state = VIDEO_PLAYING; - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (ret) break; } @@ -1034,7 +1034,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, ret = av7110_av_start_play(av7110, RP_VIDEO); } if (!ret) - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) av7110->videostate.play_state = VIDEO_PLAYING; break; @@ -1044,7 +1044,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Pause, 0); else - ret = vidcom(av7110, VIDEO_CMD_FREEZE, 1); + ret = vidcom(av7110, AV_VIDEO_CMD_FREEZE, 1); if (!ret) av7110->trickmode = TRICK_FREEZE; break; @@ -1053,7 +1053,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, if (av7110->playing & RP_VIDEO) ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Continue, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) { av7110->videostate.play_state = VIDEO_PLAYING; av7110->trickmode = TRICK_NONE; @@ -1136,7 +1136,7 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Scan_I, 2, AV_PES, 0); else - ret = vidcom(av7110, VIDEO_CMD_FFWD, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_FFWD, arg); if (!ret) { av7110->trickmode = TRICK_FAST; av7110->videostate.play_state = VIDEO_PLAYING; @@ -1147,13 +1147,13 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, if (av7110->playing&RP_VIDEO) { ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } else { - ret = vidcom(av7110, VIDEO_CMD_PLAY, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_PLAY, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_STOP, 0); + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } if (!ret) { av7110->trickmode = TRICK_SLOW; @@ -1182,10 +1182,10 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, ret = av7110_fw_cmd(av7110, COMTYPE_REC_PLAY, __Slow, 2, 0, 0); if (!ret) - ret = vidcom(av7110, VIDEO_CMD_SLOW, arg); + ret = vidcom(av7110, AV_VIDEO_CMD_SLOW, arg); } if (av7110->trickmode == TRICK_FREEZE) - ret = vidcom(av7110, VIDEO_CMD_STOP, 1); + ret = vidcom(av7110, AV_VIDEO_CMD_STOP, 1); } break; @@ -1203,8 +1203,8 @@ static int dvb_video_ioctl(struct inode *inode, struct file *file, static int dvb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; int ret = 0; @@ -1349,8 +1349,8 @@ static int dvb_audio_ioctl(struct inode *inode, struct file *file, static int dvb_video_open(struct inode *inode, struct file *file) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; int err; dprintk(2, "av7110:%p, \n", av7110); @@ -1374,8 +1374,8 @@ static int dvb_video_open(struct inode *inode, struct file *file) static int dvb_video_release(struct inode *inode, struct file *file) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(2, "av7110:%p, \n", av7110); @@ -1388,9 +1388,9 @@ static int dvb_video_release(struct inode *inode, struct file *file) static int dvb_audio_open(struct inode *inode, struct file *file) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; - int err=dvb_generic_open(inode, file); + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; + int err = dvb_generic_open(inode, file); dprintk(2, "av7110:%p, \n", av7110); @@ -1403,8 +1403,8 @@ static int dvb_audio_open(struct inode *inode, struct file *file) static int dvb_audio_release(struct inode *inode, struct file *file) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(2, "av7110:%p, \n", av7110); diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c index 4251a97..e9b4e88 100644 --- a/drivers/media/dvb/ttpci/av7110_ca.c +++ b/drivers/media/dvb/ttpci/av7110_ca.c @@ -214,8 +214,8 @@ static ssize_t ci_ll_read(struct dvb_ringbuffer *cibuf, struct file *file, static int dvb_ca_open(struct inode *inode, struct file *file) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; int err = dvb_generic_open(inode, file); dprintk(8, "av7110:%p\n",av7110); @@ -228,8 +228,8 @@ static int dvb_ca_open(struct inode *inode, struct file *file) static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; struct dvb_ringbuffer *rbuf = &av7110->ci_rbuffer; struct dvb_ringbuffer *wbuf = &av7110->ci_wbuffer; unsigned int mask = 0; @@ -251,8 +251,8 @@ static unsigned int dvb_ca_poll (struct file *file, poll_table *wait) static int dvb_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *parg) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; unsigned long arg = (unsigned long) parg; dprintk(8, "av7110:%p\n",av7110); @@ -329,8 +329,8 @@ static int dvb_ca_ioctl(struct inode *inode, struct file *file, static ssize_t dvb_ca_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(8, "av7110:%p\n",av7110); return ci_ll_write(&av7110->ci_wbuffer, file, buf, count, ppos); @@ -339,15 +339,13 @@ static ssize_t dvb_ca_write(struct file *file, const char __user *buf, static ssize_t dvb_ca_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct dvb_device *dvbdev = (struct dvb_device *) file->private_data; - struct av7110 *av7110 = (struct av7110 *) dvbdev->priv; + struct dvb_device *dvbdev = file->private_data; + struct av7110 *av7110 = dvbdev->priv; dprintk(8, "av7110:%p\n",av7110); return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos); } - - static struct file_operations dvb_ca_fops = { .owner = THIS_MODULE, .read = dvb_ca_read, diff --git a/drivers/media/dvb/ttpci/av7110_hw.h b/drivers/media/dvb/ttpci/av7110_hw.h index 4e173c6..673d9b3 100644 --- a/drivers/media/dvb/ttpci/av7110_hw.h +++ b/drivers/media/dvb/ttpci/av7110_hw.h @@ -216,11 +216,11 @@ enum av7110_command_type { #define VID_CENTRE_CUT_PREF 0x05 /* PanScan with zero vector */ /* MPEG video decoder commands */ -#define VIDEO_CMD_STOP 0x000e -#define VIDEO_CMD_PLAY 0x000d -#define VIDEO_CMD_FREEZE 0x0102 -#define VIDEO_CMD_FFWD 0x0016 -#define VIDEO_CMD_SLOW 0x0022 +#define AV_VIDEO_CMD_STOP 0x000e +#define AV_VIDEO_CMD_PLAY 0x000d +#define AV_VIDEO_CMD_FREEZE 0x0102 +#define AV_VIDEO_CMD_FFWD 0x0016 +#define AV_VIDEO_CMD_SLOW 0x0022 /* MPEG audio decoder commands */ #define AUDIO_CMD_MUTE 0x0001 diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index e4544ea..a97f166 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/drivers/media/dvb/ttpci/av7110_ir.c @@ -1,25 +1,69 @@ +/* + * Driver for the remote control of SAA7146 based AV7110 cards + * + * Copyright (C) 1999-2003 Holger Waechtler <holger@convergence.de> + * Copyright (C) 2003-2007 Oliver Endriss <o.endriss@gmx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + * + */ + + #include <linux/types.h> #include <linux/init.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <linux/input.h> #include <linux/proc_fs.h> +#include <linux/kernel.h> #include <asm/bitops.h> #include "av7110.h" #include "av7110_hw.h" -#define UP_TIMEOUT (HZ*7/25) -/* enable ir debugging by or'ing debug with 16 */ +#define AV_CNT 4 + +#define IR_RC5 0 +#define IR_RCMM 1 +#define IR_RC5_EXT 2 /* internal only */ + +#define IR_ALL 0xffffffff + +#define UP_TIMEOUT (HZ*7/25) + + +/* Note: enable ir debugging by or'ing debug with 16 */ + +static int ir_protocol[AV_CNT] = { IR_RCMM, IR_RCMM, IR_RCMM, IR_RCMM}; +module_param_array(ir_protocol, int, NULL, 0644); +MODULE_PARM_DESC(ir_protocol, "Infrared protocol: 0 RC5, 1 RCMM (default)"); + +static int ir_inversion[AV_CNT]; +module_param_array(ir_inversion, int, NULL, 0644); +MODULE_PARM_DESC(ir_inversion, "Inversion of infrared signal: 0 not inverted (default), 1 inverted"); + +static uint ir_device_mask[AV_CNT] = { IR_ALL, IR_ALL, IR_ALL, IR_ALL }; +module_param_array(ir_device_mask, uint, NULL, 0644); +MODULE_PARM_DESC(ir_device_mask, "Bitmask of infrared devices: bit 0..31 = device 0..31 (default: all)"); -static int av_cnt; -static struct av7110 *av_list[4]; -static struct input_dev *input_dev; -static u8 delay_timer_finished; +static int av_cnt; +static struct av7110 *av_list[AV_CNT]; -static u16 key_map [256] = { +static u16 default_key_map [256] = { KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_BACK, 0, KEY_POWER, KEY_MUTE, 0, KEY_INFO, KEY_VOLUMEUP, KEY_VOLUMEDOWN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -43,141 +87,194 @@ static u16 key_map [256] = { }; -static void av7110_emit_keyup(unsigned long data) +/* key-up timer */ +static void av7110_emit_keyup(unsigned long parm) { - if (!data || !test_bit(data, input_dev->key)) + struct infrared *ir = (struct infrared *) parm; + + if (!ir || !test_bit(ir->last_key, ir->input_dev->key)) return; - input_report_key(input_dev, data, 0); - input_sync(input_dev); + input_report_key(ir->input_dev, ir->last_key, 0); + input_sync(ir->input_dev); } -static struct timer_list keyup_timer = { .function = av7110_emit_keyup }; - - +/* tasklet */ static void av7110_emit_key(unsigned long parm) { - struct av7110 *av7110 = (struct av7110 *) parm; - u32 ir_config = av7110->ir_config; - u32 ircom = av7110->ir_command; + struct infrared *ir = (struct infrared *) parm; + u32 ircom = ir->ir_command; u8 data; u8 addr; - static u16 old_toggle = 0; - u16 new_toggle; + u16 toggle; u16 keycode; /* extract device address and data */ - switch (ir_config & 0x0003) { - case 0: /* RC5: 5 bits device address, 6 bits data */ + switch (ir->protocol) { + case IR_RC5: /* RC5: 5 bits device address, 6 bits data */ data = ircom & 0x3f; addr = (ircom >> 6) & 0x1f; + toggle = ircom & 0x0800; break; - case 1: /* RCMM: 8(?) bits device address, 8(?) bits data */ + case IR_RCMM: /* RCMM: ? bits device address, ? bits data */ data = ircom & 0xff; - addr = (ircom >> 8) & 0xff; + addr = (ircom >> 8) & 0x1f; + toggle = ircom & 0x8000; break; - case 2: /* extended RC5: 5 bits device address, 7 bits data */ + case IR_RC5_EXT: /* extended RC5: 5 bits device address, 7 bits data */ data = ircom & 0x3f; addr = (ircom >> 6) & 0x1f; /* invert 7th data bit for backward compatibility with RC5 keymaps */ if (!(ircom & 0x1000)) data |= 0x40; + toggle = ircom & 0x0800; break; default: - printk("invalid ir_config %x\n", ir_config); + printk("%s invalid protocol %x\n", __FUNCTION__, ir->protocol); return; } - keycode = key_map[data]; + input_event(ir->input_dev, EV_MSC, MSC_RAW, (addr << 16) | data); + input_event(ir->input_dev, EV_MSC, MSC_SCAN, data); + + keycode = ir->key_map[data]; - dprintk(16, "code %08x -> addr %i data 0x%02x -> keycode %i\n", - ircom, addr, data, keycode); + dprintk(16, "%s: code %08x -> addr %i data 0x%02x -> keycode %i\n", + __FUNCTION__, ircom, addr, data, keycode); - /* check device address (if selected) */ - if (ir_config & 0x4000) - if (addr != ((ir_config >> 16) & 0xff)) - return; + /* check device address */ + if (!(ir->device_mask & (1 << addr))) + return; if (!keycode) { - printk ("%s: unknown key 0x%02x!!\n", __FUNCTION__, data); + printk ("%s: code %08x -> addr %i data 0x%02x -> unknown key!\n", + __FUNCTION__, ircom, addr, data); return; } - if ((ir_config & 0x0003) == 1) - new_toggle = 0; /* RCMM */ - else - new_toggle = (ircom & 0x800); /* RC5, extended RC5 */ - - if (timer_pending(&keyup_timer)) { - del_timer(&keyup_timer); - if (keyup_timer.data != keycode || new_toggle != old_toggle) { - delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keyup_timer.data, 0); - input_event(input_dev, EV_KEY, keycode, 1); - input_sync(input_dev); - } else if (delay_timer_finished) { - input_event(input_dev, EV_KEY, keycode, 2); - input_sync(input_dev); + if (timer_pending(&ir->keyup_timer)) { + del_timer(&ir->keyup_timer); + if (ir->last_key != keycode || toggle != ir->last_toggle) { + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, ir->last_key, 0); + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); + } else if (ir->delay_timer_finished) { + input_event(ir->input_dev, EV_KEY, keycode, 2); + input_sync(ir->input_dev); } } else { - delay_timer_finished = 0; - input_event(input_dev, EV_KEY, keycode, 1); - input_sync(input_dev); + ir->delay_timer_finished = 0; + input_event(ir->input_dev, EV_KEY, keycode, 1); + input_sync(ir->input_dev); } - keyup_timer.expires = jiffies + UP_TIMEOUT; - keyup_timer.data = keycode; + ir->last_key = keycode; + ir->last_toggle = toggle; - add_timer(&keyup_timer); + ir->keyup_timer.expires = jiffies + UP_TIMEOUT; + add_timer(&ir->keyup_timer); - old_toggle = new_toggle; } -static void input_register_keys(void) + +/* register with input layer */ +static void input_register_keys(struct infrared *ir) { int i; - memset(input_dev->keybit, 0, sizeof(input_dev->keybit)); + set_bit(EV_KEY, ir->input_dev->evbit); + set_bit(EV_REP, ir->input_dev->evbit); + set_bit(EV_MSC, ir->input_dev->evbit); + + set_bit(MSC_RAW, ir->input_dev->mscbit); + set_bit(MSC_SCAN, ir->input_dev->mscbit); + + memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit)); - for (i = 0; i < ARRAY_SIZE(key_map); i++) { - if (key_map[i] > KEY_MAX) - key_map[i] = 0; - else if (key_map[i] > KEY_RESERVED) - set_bit(key_map[i], input_dev->keybit); + for (i = 0; i < ARRAY_SIZE(ir->key_map); i++) { + if (ir->key_map[i] > KEY_MAX) + ir->key_map[i] = 0; + else if (ir->key_map[i] > KEY_RESERVED) + set_bit(ir->key_map[i], ir->input_dev->keybit); } + + ir->input_dev->keycode = ir->key_map; + ir->input_dev->keycodesize = sizeof(ir->key_map[0]); + ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map); } -static void input_repeat_key(unsigned long data) +/* called by the input driver after rep[REP_DELAY] ms */ +static void input_repeat_key(unsigned long parm) { - /* called by the input driver after rep[REP_DELAY] ms */ - delay_timer_finished = 1; + struct infrared *ir = (struct infrared *) parm; + + ir->delay_timer_finished = 1; } -static int av7110_setup_irc_config(struct av7110 *av7110, u32 ir_config) +/* check for configuration changes */ +int av7110_check_ir_config(struct av7110 *av7110, int force) { - int ret = 0; + int i; + int modified = force; + int ret = -ENODEV; + + for (i = 0; i < av_cnt; i++) + if (av7110 == av_list[i]) + break; - dprintk(4, "%p\n", av7110); - if (av7110) { - ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, ir_config); - av7110->ir_config = ir_config; + if (i < av_cnt && av7110) { + if ((av7110->ir.protocol & 1) != ir_protocol[i] || + av7110->ir.inversion != ir_inversion[i]) + modified = true; + + if (modified) { + /* protocol */ + if (ir_protocol[i]) { + ir_protocol[i] = 1; + av7110->ir.protocol = IR_RCMM; + av7110->ir.ir_config = 0x0001; + } else if (FW_VERSION(av7110->arm_app) >= 0x2620) { + av7110->ir.protocol = IR_RC5_EXT; + av7110->ir.ir_config = 0x0002; + } else { + av7110->ir.protocol = IR_RC5; + av7110->ir.ir_config = 0x0000; + } + /* inversion */ + if (ir_inversion[i]) { + ir_inversion[i] = 1; + av7110->ir.ir_config |= 0x8000; + } + av7110->ir.inversion = ir_inversion[i]; + /* update ARM */ + ret = av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, SetIR, 1, + av7110->ir.ir_config); + } else + ret = 0; + + /* address */ + if (av7110->ir.device_mask != ir_device_mask[i]) + av7110->ir.device_mask = ir_device_mask[i]; } + return ret; } +/* /proc/av7110_ir interface */ static int av7110_ir_write_proc(struct file *file, const char __user *buffer, unsigned long count, void *data) { char *page; - int size = 4 + 256 * sizeof(u16); u32 ir_config; + int size = sizeof ir_config + sizeof av_list[0]->ir.key_map; int i; if (count < size) @@ -192,57 +289,86 @@ static int av7110_ir_write_proc(struct file *file, const char __user *buffer, return -EFAULT; } - memcpy(&ir_config, page, 4); - memcpy(&key_map, page + 4, 256 * sizeof(u16)); + memcpy(&ir_config, page, sizeof ir_config); + + for (i = 0; i < av_cnt; i++) { + /* keymap */ + memcpy(av_list[i]->ir.key_map, page + sizeof ir_config, + sizeof(av_list[i]->ir.key_map)); + /* protocol, inversion, address */ + ir_protocol[i] = ir_config & 0x0001; + ir_inversion[i] = ir_config & 0x8000 ? 1 : 0; + if (ir_config & 0x4000) + ir_device_mask[i] = 1 << ((ir_config >> 16) & 0x1f); + else + ir_device_mask[i] = IR_ALL; + /* update configuration */ + av7110_check_ir_config(av_list[i], false); + input_register_keys(&av_list[i]->ir); + } vfree(page); - if (FW_VERSION(av_list[0]->arm_app) >= 0x2620 && !(ir_config & 0x0001)) - ir_config |= 0x0002; /* enable extended RC5 */ - for (i = 0; i < av_cnt; i++) - av7110_setup_irc_config(av_list[i], ir_config); - input_register_keys(); return count; } +/* interrupt handler */ static void ir_handler(struct av7110 *av7110, u32 ircom) { - dprintk(4, "ircommand = %08x\n", ircom); - av7110->ir_command = ircom; - tasklet_schedule(&av7110->ir_tasklet); + dprintk(4, "ir command = %08x\n", ircom); + av7110->ir.ir_command = ircom; + tasklet_schedule(&av7110->ir.ir_tasklet); } int __devinit av7110_ir_init(struct av7110 *av7110) { + struct input_dev *input_dev; static struct proc_dir_entry *e; int err; - if (av_cnt >= sizeof av_list/sizeof av_list[0]) + if (av_cnt >= ARRAY_SIZE(av_list)) return -ENOSPC; - av7110_setup_irc_config(av7110, 0x0001); av_list[av_cnt++] = av7110; + av7110_check_ir_config(av7110, true); - if (av_cnt == 1) { - init_timer(&keyup_timer); - keyup_timer.data = 0; - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - input_dev->name = "DVB on-card IR receiver"; - - set_bit(EV_KEY, input_dev->evbit); - set_bit(EV_REP, input_dev->evbit); - input_register_keys(); - err = input_register_device(input_dev); - if (err) { - input_free_device(input_dev); - return err; - } - input_dev->timer.function = input_repeat_key; + init_timer(&av7110->ir.keyup_timer); + av7110->ir.keyup_timer.function = av7110_emit_keyup; + av7110->ir.keyup_timer.data = (unsigned long) &av7110->ir; + + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + av7110->ir.input_dev = input_dev; + snprintf(av7110->ir.input_phys, sizeof(av7110->ir.input_phys), + "pci-%s/ir0", pci_name(av7110->dev->pci)); + + input_dev->name = "DVB on-card IR receiver"; + + input_dev->phys = av7110->ir.input_phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 2; + if (av7110->dev->pci->subsystem_vendor) { + input_dev->id.vendor = av7110->dev->pci->subsystem_vendor; + input_dev->id.product = av7110->dev->pci->subsystem_device; + } else { + input_dev->id.vendor = av7110->dev->pci->vendor; + input_dev->id.product = av7110->dev->pci->device; + } + input_dev->cdev.dev = &av7110->dev->pci->dev; + /* initial keymap */ + memcpy(av7110->ir.key_map, default_key_map, sizeof av7110->ir.key_map); + input_register_keys(&av7110->ir); + err = input_register_device(input_dev); + if (err) { + input_free_device(input_dev); + return err; + } + input_dev->timer.function = input_repeat_key; + input_dev->timer.data = (unsigned long) &av7110->ir; + + if (av_cnt == 1) { e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL); if (e) { e->write_proc = av7110_ir_write_proc; @@ -250,8 +376,8 @@ int __devinit av7110_ir_init(struct av7110 *av7110) } } - tasklet_init(&av7110->ir_tasklet, av7110_emit_key, (unsigned long) av7110); - av7110->ir_handler = ir_handler; + tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); + av7110->ir.ir_handler = ir_handler; return 0; } @@ -264,8 +390,10 @@ void __devexit av7110_ir_exit(struct av7110 *av7110) if (av_cnt == 0) return; - av7110->ir_handler = NULL; - tasklet_kill(&av7110->ir_tasklet); + del_timer_sync(&av7110->ir.keyup_timer); + av7110->ir.ir_handler = NULL; + tasklet_kill(&av7110->ir.ir_tasklet); + for (i = 0; i < av_cnt; i++) if (av_list[i] == av7110) { av_list[i] = av_list[av_cnt-1]; @@ -273,14 +401,13 @@ void __devexit av7110_ir_exit(struct av7110 *av7110) break; } - if (av_cnt == 1) { - del_timer_sync(&keyup_timer); + if (av_cnt == 1) remove_proc_entry("av7110_ir", NULL); - input_unregister_device(input_dev); - } + + input_unregister_device(av7110->ir.input_dev); av_cnt--; } -//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>"); +//MODULE_AUTHOR("Holger Waechtler <holger@convergence.de>, Oliver Endriss <o.endriss@gmx.de>"); //MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index dbfd5e7..cde5d3a 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -140,17 +140,6 @@ static int ves1820_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) return 0; } -static int stv0297_writereg(struct saa7146_dev *dev, u8 addr, u8 reg, u8 data) -{ - u8 buf [] = { reg, data }; - struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = buf, .len = 2 }; - - if (1 != saa7146_i2c_transfer(dev, &msg, 1, 1)) - return -1; - return 0; -} - - static int tuner_write(struct saa7146_dev *dev, u8 addr, u8 data [4]) { struct i2c_msg msg = { .addr = addr, .flags = 0, .buf = data, .len = 4 }; @@ -193,6 +182,7 @@ static int ves1820_set_tv_freq(struct saa7146_dev *dev, u32 freq) static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) { + struct av7110 *av7110 = (struct av7110*)dev->ext_priv; u32 div; u8 data[4]; @@ -213,8 +203,8 @@ static int stv0297_set_tv_freq(struct saa7146_dev *dev, u32 freq) else return -EINVAL; - stv0297_writereg(dev, 0x1C, 0x87, 0x78); - stv0297_writereg(dev, 0x1C, 0x86, 0xc8); + if (av7110->fe->ops.i2c_gate_ctrl) + av7110->fe->ops.i2c_gate_ctrl(av7110->fe, 1); return tuner_write(dev, 0x63, data); } @@ -817,20 +807,20 @@ int av7110_init_v4l(struct av7110 *av7110) saa7146_vv_release(dev); return -ENODEV; } - if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) { + if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) ERR(("cannot register vbi v4l2 device. skipping.\n")); - } else { - if (av7110->analog_tuner_flags) - av7110->analog_tuner_flags |= ANALOG_TUNER_VBI; - } return 0; } int av7110_exit_v4l(struct av7110 *av7110) { + struct saa7146_dev* dev = av7110->dev; + saa7146_unregister_device(&av7110->v4l_dev, av7110->dev); - if (av7110->analog_tuner_flags & ANALOG_TUNER_VBI) - saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); + saa7146_unregister_device(&av7110->vbi_dev, av7110->dev); + + saa7146_vv_release(dev); + return 0; } diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 89ab4b5..0e817d6 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -35,7 +35,7 @@ #include "budget.h" #include "stv0299.h" -#include "tda10021.h" +#include "tda1002x.h" #include "tda1004x.h" #include "tua6100.h" #include "dvb-pll.h" @@ -66,9 +66,6 @@ struct budget_av { int slot_status; struct dvb_ca_en50221 ca; u8 reinitialise_demod:1; - u8 tda10021_poclkp:1; - u8 tda10021_ts_enabled; - int (*tda10021_set_frontend)(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); }; static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot); @@ -234,12 +231,6 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) if (budget_av->reinitialise_demod) dvb_frontend_reinitialise(budget_av->budget.dvb_frontend); - /* set tda10021 back to original clock configuration on reset */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - budget_av->tda10021_ts_enabled = 0; - } - return 0; } @@ -256,11 +247,6 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB); budget_av->slot_status = SLOTSTATUS_NONE; - /* set tda10021 back to original clock configuration when cam removed */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - budget_av->tda10021_ts_enabled = 0; - } return 0; } @@ -276,12 +262,6 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA); - /* tda10021 seems to need a different TS clock config when data is routed to the CAM */ - if (budget_av->tda10021_poclkp) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); - budget_av->tda10021_ts_enabled = 1; - } - return 0; } @@ -631,37 +611,62 @@ static struct stv0299_config cinergy_1200s_1894_0010_config = { static int philips_cu1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget *budget = (struct budget *) fe->dvb->priv; - u8 buf[4]; + u8 buf[6]; struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) }; + int i; +#define CU1216_IF 36125000 #define TUNER_MUL 62500 - u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL; + u32 div = (params->frequency + CU1216_IF + TUNER_MUL / 2) / TUNER_MUL; buf[0] = (div >> 8) & 0x7f; buf[1] = div & 0xff; - buf[2] = 0x86; + buf[2] = 0xce; buf[3] = (params->frequency < 150000000 ? 0x01 : params->frequency < 445000000 ? 0x02 : 0x04); + buf[4] = 0xde; + buf[5] = 0x20; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; + + /* wait for the pll lock */ + msg.flags = I2C_M_RD; + msg.len = 1; + for (i = 0; i < 20; i++) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) == 1 && (buf[0] & 0x40)) + break; + msleep(10); + } + + /* switch the charge pump to the lower current */ + msg.flags = 0; + msg.len = 2; + msg.buf = &buf[2]; + buf[2] &= ~0x40; + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) + return -EIO; + return 0; } -static struct tda10021_config philips_cu1216_config = { +static struct tda1002x_config philips_cu1216_config = { .demod_address = 0x0c, + .invert = 1, }; -static struct tda10021_config philips_cu1216_config_altaddress = { +static struct tda1002x_config philips_cu1216_config_altaddress = { .demod_address = 0x0d, + .invert = 0, }; - - - static int philips_tu1216_tuner_init(struct dvb_frontend *fe) { struct budget *budget = (struct budget *) fe->dvb->priv; @@ -908,41 +913,28 @@ static u8 read_pwm(struct budget_av *budget_av) return pwm; } -#define SUBID_DVBS_KNC1 0x0010 -#define SUBID_DVBS_KNC1_PLUS 0x0011 -#define SUBID_DVBS_TYPHOON 0x4f56 -#define SUBID_DVBS_CINERGY1200 0x1154 -#define SUBID_DVBS_CYNERGY1200N 0x1155 - -#define SUBID_DVBS_TV_STAR 0x0014 -#define SUBID_DVBS_TV_STAR_CI 0x0016 -#define SUBID_DVBS_EASYWATCH_1 0x001a -#define SUBID_DVBS_EASYWATCH 0x001e -#define SUBID_DVBC_EASYWATCH 0x002a -#define SUBID_DVBC_KNC1 0x0020 -#define SUBID_DVBC_KNC1_PLUS 0x0021 -#define SUBID_DVBC_CINERGY1200 0x1156 - -#define SUBID_DVBT_KNC1_PLUS 0x0031 -#define SUBID_DVBT_KNC1 0x0030 -#define SUBID_DVBT_CINERGY1200 0x1157 - - -static int tda10021_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *p) -{ - struct budget_av* budget_av = fe->dvb->priv; - int result; - - result = budget_av->tda10021_set_frontend(fe, p); - if (budget_av->tda10021_ts_enabled) { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1); - } else { - tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0); - } - - return result; -} +#define SUBID_DVBS_KNC1 0x0010 +#define SUBID_DVBS_KNC1_PLUS 0x0011 +#define SUBID_DVBS_TYPHOON 0x4f56 +#define SUBID_DVBS_CINERGY1200 0x1154 +#define SUBID_DVBS_CYNERGY1200N 0x1155 +#define SUBID_DVBS_TV_STAR 0x0014 +#define SUBID_DVBS_TV_STAR_CI 0x0016 +#define SUBID_DVBS_EASYWATCH_1 0x001a +#define SUBID_DVBS_EASYWATCH 0x001e + +#define SUBID_DVBC_EASYWATCH 0x002a +#define SUBID_DVBC_EASYWATCH_MK3 0x002c +#define SUBID_DVBC_KNC1 0x0020 +#define SUBID_DVBC_KNC1_PLUS 0x0021 +#define SUBID_DVBC_KNC1_MK3 0x0022 +#define SUBID_DVBC_KNC1_PLUS_MK3 0x0023 +#define SUBID_DVBC_CINERGY1200 0x1156 +#define SUBID_DVBC_CINERGY1200_MK3 0x1176 + +#define SUBID_DVBT_KNC1_PLUS 0x0031 +#define SUBID_DVBT_KNC1 0x0030 +#define SUBID_DVBT_CINERGY1200 0x1157 static void frontend_init(struct budget_av *budget_av) { @@ -961,6 +953,7 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: case SUBID_DVBC_EASYWATCH: + case SUBID_DVBC_KNC1_PLUS_MK3: saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } @@ -1017,6 +1010,7 @@ static void frontend_init(struct budget_av *budget_av) case SUBID_DVBC_CINERGY1200: case SUBID_DVBC_EASYWATCH: budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; fe = dvb_attach(tda10021_attach, &philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); @@ -1025,9 +1019,20 @@ static void frontend_init(struct budget_av *budget_av) &budget_av->budget.i2c_adap, read_pwm(budget_av)); if (fe) { - budget_av->tda10021_poclkp = 1; - budget_av->tda10021_set_frontend = fe->ops.set_frontend; - fe->ops.set_frontend = tda10021_set_frontend; + fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; + } + break; + + case SUBID_DVBC_EASYWATCH_MK3: + case SUBID_DVBC_CINERGY1200_MK3: + case SUBID_DVBC_KNC1_MK3: + case SUBID_DVBC_KNC1_PLUS_MK3: + budget_av->reinitialise_demod = 1; + budget_av->budget.dev->i2c_bitrate = SAA7146_I2C_BUS_BIT_RATE_240; + fe = dvb_attach(tda10023_attach, &philips_cu1216_config, + &budget_av->budget.i2c_adap, + read_pwm(budget_av)); + if (fe) { fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params; } break; @@ -1089,6 +1094,8 @@ static int budget_av_detach(struct saa7146_dev *dev) msleep(200); saa7146_unregister_device(&budget_av->vd, dev); + + saa7146_vv_release(dev); } if (budget_av->budget.ci_present) @@ -1145,6 +1152,7 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { /* fixme: proper cleanup here */ ERR(("cannot register capture v4l2 device.\n")); + saa7146_vv_release(dev); return err; } @@ -1257,12 +1265,16 @@ MAKE_BUDGET_INFO(kncxs, "KNC TV STAR DVB-S", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls, "Satelco EasyWatch DVB-S light", BUDGET_TVSTAR); MAKE_BUDGET_INFO(satewpls1, "Satelco EasyWatch DVB-S light", BUDGET_KNC1S); MAKE_BUDGET_INFO(satewplc, "Satelco EasyWatch DVB-C", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(satewcmk3, "Satelco EasyWatch DVB-C MK3", BUDGET_KNC1C_MK3); MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP); MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP); +MAKE_BUDGET_INFO(knc1cmk3, "KNC1 DVB-C MK3", BUDGET_KNC1C_MK3); +MAKE_BUDGET_INFO(knc1cpmk3, "KNC1 DVB-C Plus MK3", BUDGET_KNC1CP_MK3); MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP); MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); MAKE_BUDGET_INFO(cin1200sn, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S); MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C); +MAKE_BUDGET_INFO(cin1200cmk3, "Terratec Cinergy 1200 DVB-C MK3", BUDGET_CIN1200C_MK3); MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T); static struct pci_device_id pci_tbl[] = { @@ -1276,13 +1288,17 @@ static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e), MAKE_EXTENSION_PCI(satewpls1, 0x1894, 0x001a), MAKE_EXTENSION_PCI(satewplc, 0x1894, 0x002a), + MAKE_EXTENSION_PCI(satewcmk3, 0x1894, 0x002c), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), + MAKE_EXTENSION_PCI(knc1cmk3, 0x1894, 0x0022), + MAKE_EXTENSION_PCI(knc1cpmk3, 0x1894, 0x0023), MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), MAKE_EXTENSION_PCI(cin1200sn, 0x153b, 0x1155), MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), + MAKE_EXTENSION_PCI(cin1200cmk3, 0x153b, 0x1176), MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), { .vendor = 0, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index f2066b4..4ed4599 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -29,8 +29,6 @@ * the project's page is at http://www.linuxtv.org/dvb/ */ -#include "budget.h" - #include <linux/module.h> #include <linux/errno.h> #include <linux/slab.h> @@ -39,6 +37,8 @@ #include <linux/spinlock.h> #include <media/ir-common.h> +#include "budget.h" + #include "dvb_ca_en50221.h" #include "stv0299.h" #include "stv0297.h" @@ -73,21 +73,15 @@ #define SLOTSTATUS_READY 8 #define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY) -/* Milliseconds during which key presses are regarded as key repeat and during - * which the debounce logic is active +/* + * Milliseconds during which a key is regarded as pressed. + * If an identical command arrives within this time, the timer will start over. */ -#define IR_REPEAT_TIMEOUT 350 +#define IR_KEYPRESS_TIMEOUT 250 /* RC5 device wildcard */ #define IR_DEVICE_ANY 255 -/* Some remotes sends multiple sequences per keypress (e.g. Zenith sends two), - * this setting allows the superflous sequences to be ignored - */ -static int debounce = 0; -module_param(debounce, int, 0644); -MODULE_PARM_DESC(debounce, "ignore repeated IR sequences (default: 0 = ignore no sequences)"); - static int rc5_device = -1; module_param(rc5_device, int, 0644); MODULE_PARM_DESC(rc5_device, "only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)"); @@ -99,10 +93,14 @@ MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding"); struct budget_ci_ir { struct input_dev *dev; struct tasklet_struct msp430_irq_tasklet; + struct timer_list timer_keyup; char name[72]; /* 40 + 32 for (struct saa7146_dev).name */ char phys[32]; struct ir_input_state state; int rc5_device; + u32 last_raw; + u32 ir_key; + bool have_command; }; struct budget_ci { @@ -125,12 +123,8 @@ static void msp430_ir_interrupt(unsigned long data) { struct budget_ci *budget_ci = (struct budget_ci *) data; struct input_dev *dev = budget_ci->ir.dev; - static int bounces = 0; - int device; - int toggle; - static int prev_toggle = -1; - static u32 ir_key; u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8; + u32 raw; /* * The msp430 chip can generate two different bytes, command and device @@ -138,53 +132,48 @@ static void msp430_ir_interrupt(unsigned long data) * type1: X1CCCCCC, C = command bits (0 - 63) * type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit * - * More than one command byte may be generated before the device byte - * Only when we have both, a correct keypress is generated + * Each signal from the remote control can generate one or more command + * bytes and one or more device bytes. For the repeated bytes, the + * highest bit (X) is set. The first command byte is always generated + * before the first device byte. Other than that, no specific order + * seems to apply. To make life interesting, bytes can also be lost. + * + * Only when we have a command and device byte, a keypress is + * generated. */ + if (ir_debug) + printk("budget_ci: received byte 0x%02x\n", command); + + /* Remove repeat bit, we use every command */ + command = command & 0x7f; + /* Is this a RC5 command byte? */ if (command & 0x40) { - if (ir_debug) - printk("budget_ci: received command byte 0x%02x\n", command); - ir_key = command & 0x3f; + budget_ci->ir.have_command = true; + budget_ci->ir.ir_key = command & 0x3f; return; } /* It's a RC5 device byte */ - if (ir_debug) - printk("budget_ci: received device byte 0x%02x\n", command); - device = command & 0x1f; - toggle = command & 0x20; - - if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && budget_ci->ir.rc5_device != device) + if (!budget_ci->ir.have_command) return; + budget_ci->ir.have_command = false; - /* Ignore repeated key sequences if requested */ - if (toggle == prev_toggle && ir_key == dev->repeat_key && - bounces > 0 && timer_pending(&dev->timer)) { - if (ir_debug) - printk("budget_ci: debounce logic ignored IR command\n"); - bounces--; + if (budget_ci->ir.rc5_device != IR_DEVICE_ANY && + budget_ci->ir.rc5_device != (command & 0x1f)) return; - } - prev_toggle = toggle; - - /* Are we still waiting for a keyup event? */ - if (del_timer(&dev->timer)) - ir_input_nokey(dev, &budget_ci->ir.state); - /* Generate keypress */ - if (ir_debug) - printk("budget_ci: generating keypress 0x%02x\n", ir_key); - ir_input_keydown(dev, &budget_ci->ir.state, ir_key, (ir_key & (command << 8))); - - /* Do we want to delay the keyup event? */ - if (debounce) { - bounces = debounce; - mod_timer(&dev->timer, jiffies + msecs_to_jiffies(IR_REPEAT_TIMEOUT)); - } else { + /* Is this a repeated key sequence? (same device, command, toggle) */ + raw = budget_ci->ir.ir_key | (command << 8); + if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) { ir_input_nokey(dev, &budget_ci->ir.state); + ir_input_keydown(dev, &budget_ci->ir.state, + budget_ci->ir.ir_key, raw); + budget_ci->ir.last_raw = raw; } + + mod_timer(&budget_ci->ir.timer_keyup, jiffies + msecs_to_jiffies(IR_KEYPRESS_TIMEOUT)); } static int msp430_ir_init(struct budget_ci *budget_ci) @@ -223,7 +212,6 @@ static int msp430_ir_init(struct budget_ci *budget_ci) switch (budget_ci->budget.dev->pci->subsystem_device) { case 0x100c: case 0x100f: - case 0x1010: case 0x1011: case 0x1012: case 0x1017: @@ -236,6 +224,16 @@ static int msp430_ir_init(struct budget_ci *budget_ci) else budget_ci->ir.rc5_device = rc5_device; break; + case 0x1010: + /* for the Technotrend 1500 bundled remote */ + ir_input_init(input_dev, &budget_ci->ir.state, + IR_TYPE_RC5, ir_codes_tt_1500); + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + break; default: /* unknown remote */ ir_input_init(input_dev, &budget_ci->ir.state, @@ -248,16 +246,21 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; } - /* initialise the key-up debounce timeout handler */ - input_dev->timer.function = msp430_ir_keyup; - input_dev->timer.data = (unsigned long) &budget_ci->ir; - + /* initialise the key-up timeout handler */ + init_timer(&budget_ci->ir.timer_keyup); + budget_ci->ir.timer_keyup.function = msp430_ir_keyup; + budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir; + budget_ci->ir.last_raw = 0xffff; /* An impossible value */ error = input_register_device(input_dev); if (error) { printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); goto out2; } + /* note: these must be after input_register_device */ + input_dev->rep[REP_DELAY] = 400; + input_dev->rep[REP_PERIOD] = 250; + tasklet_init(&budget_ci->ir.msp430_irq_tasklet, msp430_ir_interrupt, (unsigned long) budget_ci); @@ -281,10 +284,8 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT); tasklet_kill(&budget_ci->ir.msp430_irq_tasklet); - if (del_timer(&dev->timer)) { - ir_input_nokey(dev, &budget_ci->ir.state); - input_sync(dev); - } + del_timer_sync(&dev->timer); + ir_input_nokey(dev, &budget_ci->ir.state); input_unregister_device(dev); } @@ -869,6 +870,17 @@ static struct tda1004x_config philips_tdm1316l_config = { .request_firmware = philips_tdm1316l_request_firmware, }; +static struct tda1004x_config philips_tdm1316l_config_invert = { + + .demod_address = 0x8, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_4M, + .agc_config = TDA10046_AGC_DEFAULT, + .if_freq = TDA10046_FREQ_3617, + .request_firmware = philips_tdm1316l_request_firmware, +}; + static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv; @@ -1092,9 +1104,8 @@ static void frontend_init(struct budget_ci *budget_ci) case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt)) budget_ci->tuner_pll_address = 0x60; - philips_tdm1316l_config.invert = 1; budget_ci->budget.dvb_frontend = - dvb_attach(tda10046_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap); + dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &budget_ci->budget.i2c_adap); if (budget_ci->budget.dvb_frontend) { budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init; budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params; diff --git a/drivers/media/dvb/ttpci/budget-core.c b/drivers/media/dvb/ttpci/budget-core.c index e15562f..6b97dc1 100644 --- a/drivers/media/dvb/ttpci/budget-core.c +++ b/drivers/media/dvb/ttpci/budget-core.c @@ -41,11 +41,14 @@ #define TS_WIDTH (2 * TS_SIZE) #define TS_WIDTH_ACTIVY TS_SIZE +#define TS_WIDTH_DVBC TS_SIZE #define TS_HEIGHT_MASK 0xf00 #define TS_HEIGHT_MASK_ACTIVY 0xc00 +#define TS_HEIGHT_MASK_DVBC 0xe00 #define TS_MIN_BUFSIZE_K 188 #define TS_MAX_BUFSIZE_K 1410 #define TS_MAX_BUFSIZE_K_ACTIVY 564 +#define TS_MAX_BUFSIZE_K_DVBC 1316 #define BUFFER_WARNING_WAIT (30*HZ) int budget_debug; @@ -106,6 +109,19 @@ static int start_ts_capture(struct budget *budget) saa7146_write(dev, MC2, (MASK_10 | MASK_26)); saa7146_write(dev, BRS_CTRL, 0x60000000); break; + case BUDGET_CIN1200C_MK3: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1CP_MK3: + if (budget->video_port == BUDGET_VIDEO_PORTA) { + saa7146_write(dev, DD1_INIT, 0x06000200); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x00000000); + } else { + saa7146_write(dev, DD1_INIT, 0x00000600); + saa7146_write(dev, MC2, (MASK_09 | MASK_25 | MASK_10 | MASK_26)); + saa7146_write(dev, BRS_CTRL, 0x60000000); + } + break; default: if (budget->video_port == BUDGET_VIDEO_PORTA) { saa7146_write(dev, DD1_INIT, 0x06000200); @@ -122,7 +138,13 @@ static int start_ts_capture(struct budget *budget) mdelay(10); saa7146_write(dev, BASE_ODD3, 0); - saa7146_write(dev, BASE_EVEN3, 0); + if (budget->buffer_size > budget->buffer_height * budget->buffer_width) { + // using odd/even buffers + saa7146_write(dev, BASE_EVEN3, budget->buffer_height * budget->buffer_width); + } else { + // using a single buffer + saa7146_write(dev, BASE_EVEN3, 0); + } saa7146_write(dev, PROT_ADDR3, budget->buffer_size); saa7146_write(dev, BASE_PAGE3, budget->pt.dma | ME1 | 0x90); @@ -399,11 +421,25 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, budget->card = bi; budget->dev = (struct saa7146_dev *) dev; - if (budget->card->type == BUDGET_FS_ACTIVY) { + switch(budget->card->type) { + case BUDGET_FS_ACTIVY: budget->buffer_width = TS_WIDTH_ACTIVY; max_bufsize = TS_MAX_BUFSIZE_K_ACTIVY; height_mask = TS_HEIGHT_MASK_ACTIVY; - } else { + break; + + case BUDGET_KNC1C: + case BUDGET_KNC1CP: + case BUDGET_CIN1200C: + case BUDGET_KNC1C_MK3: + case BUDGET_KNC1CP_MK3: + case BUDGET_CIN1200C_MK3: + budget->buffer_width = TS_WIDTH_DVBC; + max_bufsize = TS_MAX_BUFSIZE_K_DVBC; + height_mask = TS_HEIGHT_MASK_DVBC; + break; + + default: budget->buffer_width = TS_WIDTH; max_bufsize = TS_MAX_BUFSIZE_K; height_mask = TS_HEIGHT_MASK; @@ -415,14 +451,22 @@ int ttpci_budget_init(struct budget *budget, struct saa7146_dev *dev, dma_buffer_size = max_bufsize; budget->buffer_height = dma_buffer_size * 1024 / budget->buffer_width; - budget->buffer_height &= height_mask; - budget->buffer_size = budget->buffer_height * budget->buffer_width; + if (budget->buffer_height > 0xfff) { + budget->buffer_height /= 2; + budget->buffer_height &= height_mask; + budget->buffer_size = 2 * budget->buffer_height * budget->buffer_width; + } else { + budget->buffer_height &= height_mask; + budget->buffer_size = budget->buffer_height * budget->buffer_width; + } budget->buffer_warning_threshold = budget->buffer_size * 80/100; budget->buffer_warnings = 0; budget->buffer_warning_time = jiffies; - dprintk(2, "%s: width = %d, height = %d\n", - budget->dev->name, budget->buffer_width, budget->buffer_height); + dprintk(2, "%s: buffer type = %s, width = %d, height = %d\n", + budget->dev->name, + budget->buffer_size > budget->buffer_width * budget->buffer_height ? "odd/even" : "single", + budget->buffer_width, budget->buffer_height); printk("%s: dma buffer size %u\n", budget->dev->name, budget->buffer_size); if ((ret = dvb_register_adapter(&budget->dvb_adapter, budget->card->name, owner, &budget->dev->pci->dev)) < 0) { diff --git a/drivers/media/dvb/ttpci/budget.h b/drivers/media/dvb/ttpci/budget.h index e8a5c79..d764ffa 100644 --- a/drivers/media/dvb/ttpci/budget.h +++ b/drivers/media/dvb/ttpci/budget.h @@ -99,6 +99,9 @@ static struct saa7146_pci_extension_data x_var = { \ #define BUDGET_KNC1CP 12 #define BUDGET_KNC1TP 13 #define BUDGET_TVSTAR 14 +#define BUDGET_CIN1200C_MK3 15 +#define BUDGET_KNC1C_MK3 16 +#define BUDGET_KNC1CP_MK3 17 #define BUDGET_VIDEO_PORTA 0 #define BUDGET_VIDEO_PORTB 1 diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig index e78ea92..f546bcc 100644 --- a/drivers/media/dvb/ttusb-budget/Kconfig +++ b/drivers/media/dvb/ttusb-budget/Kconfig @@ -1,7 +1,6 @@ config DVB_TTUSB_BUDGET tristate "Technotrend/Hauppauge Nova-USB devices" depends on DVB_CORE && USB && I2C - select DVB_PLL select DVB_CX22700 if !DVB_FE_CUSTOMISE select DVB_TDA1004X if !DVB_FE_CUSTOMISE select DVB_VES1820 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index bd6e7ba..78c98b0 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -20,8 +20,6 @@ * */ -#include <linux/mutex.h> - #include <linux/list.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -35,6 +33,8 @@ #include <linux/init.h> #include <linux/input.h> +#include <linux/mutex.h> + #include "dmxdev.h" #include "dvb_demux.h" #include "dvb_filter.h" diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 920b63f..af66a5d 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -3,7 +3,7 @@ # menu "Radio Adapters" - depends on VIDEO_DEV!=n + depends on VIDEO_DEV config RADIO_CADET tristate "ADS Cadet AM/FM Tuner" diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c index b2e88ad..5adc27c 100644 --- a/drivers/media/radio/radio-aimslab.c +++ b/drivers/media/radio/radio-aimslab.c @@ -231,129 +231,149 @@ static struct v4l2_queryctrl radio_qctrl[] = { } }; -static int rt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-aimslab", sizeof(v->driver)); + strlcpy(v->card, "RadioTrack", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct rt_device *rt=dev->priv; + struct rt_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-aimslab", sizeof (v->driver)); - strlcpy(v->card, "RadioTrack", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff*rt_getsigstr(rt); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*rt_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - rt->curfreq = f->frequency; - rt_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = rt->curvol * 6554; + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=rt->curvol * 6554; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - rt_mute(rt); - } else { - rt_setvol(rt,rt->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - rt_setvol(rt,ctrl->value); - return (0); - } - return -EINVAL; - } +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - rt_do_ioctl); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + rt_mute(rt); + else + rt_setvol(rt,rt->curvol); + return 0; + case V4L2_CID_AUDIO_VOLUME: + rt_setvol(rt,ctrl->value); + return 0; } + return -EINVAL; +} + +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; } -static int rt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); + if (a->index != 0) + return -EINVAL; + return 0; } static struct rt_device rtrack_unit; @@ -362,7 +382,7 @@ static const struct file_operations rtrack_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = rt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -374,6 +394,18 @@ static struct video_device rtrack_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &rtrack_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init rtrack_init(void) diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c index 19d45cc..9f1adda 100644 --- a/drivers/media/radio/radio-aztech.c +++ b/drivers/media/radio/radio-aztech.c @@ -180,136 +180,163 @@ static int az_setfreq(struct az_device *dev, unsigned long frequency) return 0; } -static int az_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-aztech", sizeof (v->driver)); + strlcpy(v->card, "Aztech Radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct az_device *az = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-aztech", sizeof (v->driver)); - strlcpy(v->card, "Aztech Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(az_getstereo(az)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*az_getsigstr(az); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + v->rangelow=(87*16000); + v->rangehigh=(108*16000); + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(az_getstereo(az)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xFFFF*az_getsigstr(az); - if (v->index > 0) - return -EINVAL; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - az->curfreq = f->frequency; - az_setfreq(az, az->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - f->type = V4L2_TUNER_RADIO; - f->frequency = az->curfreq; + return 0; +} - return 0; - } +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (az->curvol==0) - ctrl->value=1; - else - ctrl->value=0; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=az->curvol * 6554; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - az_setvol(az,0); - } else { - az_setvol(az,az->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - az_setvol(az,ctrl->value); - return (0); - } - return -EINVAL; + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + + +static int vidioc_s_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + + return 0; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct az_device *az = dev->priv; + + az->curfreq = f->frequency; + az_setfreq(az, az->curfreq); + return 0; +} + +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct az_device *az = dev->priv; + + f->type = V4L2_TUNER_RADIO; + f->frequency = az->curfreq; + + return 0; +} + +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); + return (0); } + } + return -EINVAL; +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct az_device *az = dev->priv; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - az_do_ioctl); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (az->curvol==0) + ctrl->value=1; + else + ctrl->value=0; + return (0); + case V4L2_CID_AUDIO_VOLUME: + ctrl->value=az->curvol * 6554; + return (0); } + return -EINVAL; } -static int az_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) { - return video_usercopy(inode, file, cmd, arg, az_do_ioctl); + struct video_device *dev = video_devdata(file); + struct az_device *az = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) { + az_setvol(az,0); + } else { + az_setvol(az,az->curvol); + } + return (0); + case V4L2_CID_AUDIO_VOLUME: + az_setvol(az,ctrl->value); + return (0); + } + return -EINVAL; } static struct az_device aztech_unit; @@ -318,20 +345,35 @@ static const struct file_operations aztech_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = az_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; static struct video_device aztech_radio= { - .owner = THIS_MODULE, - .name = "Aztech radio", - .type = VID_TYPE_TUNER, - .hardware = 0, - .fops = &aztech_fops, + .owner = THIS_MODULE, + .name = "Aztech radio", + .type = VID_TYPE_TUNER, + .hardware = 0, + .fops = &aztech_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; +module_param_named(debug,aztech_radio.debug, int, 0644); +MODULE_PARM_DESC(debug,"activates debug info"); + static int __init aztech_init(void) { if(io==-1) diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c index 05e5aa7..fdf5d6e 100644 --- a/drivers/media/radio/radio-gemtek-pci.c +++ b/drivers/media/radio/radio-gemtek-pci.c @@ -89,14 +89,6 @@ static struct v4l2_queryctrl radio_qctrl[] = { #define GEMTEK_PCI_RANGE_HIGH (108*16000) #endif -#ifndef TRUE -#define TRUE (1) -#endif - -#ifndef FALSE -#define FALSE (0) -#endif - struct gemtek_pci_card { struct video_device *videodev; @@ -146,12 +138,12 @@ static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep ) static inline void gemtek_pci_nil( u32 port, u8 *last_byte ) { - __gemtek_pci_cmd( 0x00, port, last_byte, FALSE ); + __gemtek_pci_cmd( 0x00, port, last_byte, false ); } static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte ) { - __gemtek_pci_cmd( cmd, port, last_byte, TRUE ); + __gemtek_pci_cmd( cmd, port, last_byte, true ); } static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency ) @@ -184,14 +176,14 @@ static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long static inline void gemtek_pci_mute( struct gemtek_pci_card *card ) { outb( 0x1f, card->iobase ); - card->mute = TRUE; + card->mute = true; } static inline void gemtek_pci_unmute( struct gemtek_pci_card *card ) { if ( card->mute ) { gemtek_pci_setfrequency( card, card->current_frequency ); - card->mute = FALSE; + card->mute = false; } } @@ -200,131 +192,158 @@ static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card ) return ( inb( card->iobase ) & 0x08 ) ? 0 : 1; } -static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver)); + strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct gemtek_pci_card *card = dev->priv; - switch ( cmd ) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-gemtek-pci", sizeof (v->driver)); - strlcpy(v->card, "GemTek PCI Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; - - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = GEMTEK_PCI_RANGE_LOW; + v->rangehigh = GEMTEK_PCI_RANGE_HIGH; + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff * gemtek_pci_getsignal(card); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; - v->rangelow = GEMTEK_PCI_RANGE_LOW; - v->rangehigh = GEMTEK_PCI_RANGE_HIGH; - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*gemtek_pci_getsignal( card ); + if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || + (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) + return -EINVAL; + gemtek_pci_setfrequency(card, f->frequency); + card->current_frequency = f->frequency; + card->mute = false; + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = card->current_frequency; + return 0; +} +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) || - (f->frequency > GEMTEK_PCI_RANGE_HIGH) ) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = card->mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (card->mute) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} - gemtek_pci_setfrequency( card, f->frequency ); - card->current_frequency = f->frequency; - card->mute = FALSE; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=card->mute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (card->mute) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - gemtek_pci_mute(card); - } else { - gemtek_pci_unmute(card); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - gemtek_pci_unmute(card); - } else { - gemtek_pci_mute(card); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - gemtek_pci_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_pci_card *card = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + gemtek_pci_mute(card); + else + gemtek_pci_unmute(card); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + gemtek_pci_unmute(card); + else + gemtek_pci_mute(card); + return 0; } + return -EINVAL; } -static int gemtek_pci_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, gemtek_pci_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } enum { @@ -350,7 +369,7 @@ static const struct file_operations gemtek_pci_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = gemtek_pci_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -361,6 +380,18 @@ static struct video_device vdev_template = { .type = VID_TYPE_TUNER, .hardware = 0, .fops = &gemtek_pci_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id ) diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 36c4be6..b04b6a7 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -161,137 +161,157 @@ static int gemtek_getsigstr(struct gemtek_device *dev) return 1; /* signal present */ } -static int gemtek_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-gemtek", sizeof(v->driver)); + strlcpy(v->card, "GemTek", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct gemtek_device *rt=dev->priv; + struct gemtek_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-gemtek", sizeof (v->driver)); - strlcpy(v->card, "GemTek", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xffff*gemtek_getsigstr(rt); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + /* needs to be called twice in order for getsigstr to work */ + gemtek_setfreq(rt, rt->curfreq); + gemtek_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*gemtek_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - rt->curfreq = f->frequency; - /* needs to be called twice in order for getsigstr to work */ - gemtek_setfreq(rt, rt->curfreq); - gemtek_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (rt->muted) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - gemtek_mute(rt); - } else { - gemtek_unmute(rt); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - gemtek_unmute(rt); - } else { - gemtek_mute(rt); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - gemtek_do_ioctl); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct gemtek_device *rt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + gemtek_mute(rt); + else + gemtek_unmute(rt); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + gemtek_unmute(rt); + else + gemtek_mute(rt); + return 0; } + return -EINVAL; } -static int gemtek_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, gemtek_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct gemtek_device gemtek_unit; @@ -300,7 +320,7 @@ static const struct file_operations gemtek_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = gemtek_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -312,6 +332,18 @@ static struct video_device gemtek_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &gemtek_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init gemtek_init(void) diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c index e67b7f2..11f80ca 100644 --- a/drivers/media/radio/radio-maestro.c +++ b/drivers/media/radio/radio-maestro.c @@ -75,8 +75,6 @@ static struct v4l2_queryctrl radio_qctrl[] = { static int radio_nr = -1; module_param(radio_nr, int, 0); -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent); static void maestro_remove(struct pci_dev *pdev); @@ -102,18 +100,11 @@ static const struct file_operations maestro_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; -static struct video_device maestro_radio = { - .name = "Maestro radio", - .type = VID_TYPE_TUNER, - .hardware = 0, - .fops = &maestro_fops, -}; - struct radio_device { u16 io, /* base of Maestro card radio io (GPIO_DATA)*/ muted, /* VIDEO_AUDIO_MUTE */ @@ -190,142 +181,153 @@ static void radio_bits_set(struct radio_device *dev, u32 data) msleep(125); } -static inline int radio_function(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-maestro", sizeof(v->driver)); + strlcpy(v->card, "Maestro Radio", sizeof(v->card)); + sprintf(v->bus_info, "PCI"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct radio_device *card = video_get_drvdata(dev); - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-maestro", sizeof (v->driver)); - strlcpy(v->card, "Maestro Radio", sizeof (v->card)); - sprintf(v->bus_info,"PCI"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; - - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - (void)radio_bits_get(card); + if (v->index > 0) + return -EINVAL; + + (void)radio_bits_get(card); + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = FREQ_LO; + v->rangehigh = FREQ_HI; + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if(card->stereo) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = card->tuned; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - v->rangelow = FREQ_LO; - v->rangehigh = FREQ_HI; - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(card->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=card->tuned; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) + return -EINVAL; + radio_bits_set(card, FREQ2BITS(f->frequency)); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = BITS2FREQ(radio_bits_get(card)); + return 0; +} - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; - radio_bits_set(card, FREQ2BITS(f->frequency)); +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = BITS2FREQ(radio_bits_get(card)); +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card = video_get_drvdata(dev); - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=card->muted; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - { - register u16 io = card->io; - register u16 omask = inw(io + IO_MASK); - outw(~STR_WREN, io + IO_MASK); - outw((card->muted = ctrl->value ) ? - STR_WREN : 0, io); - udelay(4); - outw(omask, io + IO_MASK); - msleep(125); - - return (0); - } - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_function); + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = card->muted; + return 0; } + return -EINVAL; } -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) { struct video_device *dev = video_devdata(file); struct radio_device *card = video_get_drvdata(dev); - int ret; + register u16 io = card->io; + register u16 omask = inw(io + IO_MASK); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + outw(~STR_WREN, io + IO_MASK); + outw((card->muted = ctrl->value ) ? + STR_WREN : 0, io); + udelay(4); + outw(omask, io + IO_MASK); + msleep(125); + return 0; + } + return -EINVAL; +} - mutex_lock(&card->lock); - ret = video_usercopy(inode, file, cmd, arg, radio_function); - mutex_unlock(&card->lock); +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - return ret; + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static u16 __devinit radio_power_on(struct radio_device *dev) @@ -352,6 +354,24 @@ static u16 __devinit radio_power_on(struct radio_device *dev) return (ofreq == radio_bits_get(dev)); } +static struct video_device maestro_radio = { + .name = "Maestro radio", + .type = VID_TYPE_TUNER, + .fops = &maestro_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + static int __devinit maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c index 6beeb74..8e184cf 100644 --- a/drivers/media/radio/radio-maxiradio.c +++ b/drivers/media/radio/radio-maxiradio.c @@ -27,7 +27,9 @@ * BUGS: * - card unmutes if you change frequency * - * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org> + * (c) 2006, 2007 by Mauro Carvalho Chehab <mchehab@infradead.org>: + * - Conversion to V4L2 API + * - Uses video_ioctl2 for parsing and to add debug support */ @@ -43,10 +45,18 @@ #include <linux/videodev2.h> #include <media/v4l2-common.h> -#define DRIVER_VERSION "0.76" +#define DRIVER_VERSION "0.77" #include <linux/version.h> /* for KERNEL_VERSION MACRO */ -#define RADIO_VERSION KERNEL_VERSION(0,7,6) +#define RADIO_VERSION KERNEL_VERSION(0,7,7) + +static struct video_device maxiradio_radio; + +#define dprintk(num, fmt, arg...) \ + do { \ + if (maxiradio_radio.debug >= num) \ + printk(KERN_DEBUG "%s: " fmt, \ + maxiradio_radio.name, ## arg); } while (0) static struct v4l2_queryctrl radio_qctrl[] = { { @@ -81,30 +91,21 @@ module_param(radio_nr, int, 0); #define FREQ_IF 171200 /* 10.7*16000 */ #define FREQ_STEP 200 /* 12.5*16 */ -#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1))\ - /(FREQ_STEP<<2))<<2) /* (x==fmhz*16*1000) -> bits */ +/* (x==fmhz*16*1000) -> bits */ +#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1)) \ + /(FREQ_STEP<<2))<<2) #define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF) -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); - static const struct file_operations maxiradio_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; -static struct video_device maxiradio_radio = -{ - .owner = THIS_MODULE, - .name = "Maxi Radio FM2000 radio", - .type = VID_TYPE_TUNER, - .fops = &maxiradio_fops, -}; static struct radio_device { @@ -116,12 +117,14 @@ static struct radio_device unsigned long freq; struct mutex lock; -} radio_unit = {0, 0, 0, 0, }; - +} radio_unit = { + .muted =1, + .freq = FREQ_LO, +}; static void outbit(unsigned long bit, __u16 io) { - if(bit != 0) + if (bit != 0) { outb( power|wren|data ,io); udelay(4); outb( power|wren|data|clk ,io); udelay(4); @@ -137,14 +140,20 @@ static void outbit(unsigned long bit, __u16 io) static void turn_power(__u16 io, int p) { - if(p != 0) outb(power, io); else outb(0,io); + if (p != 0) { + dprintk(1, "Radio powered on\n"); + outb(power, io); + } else { + dprintk(1, "Radio powered off\n"); + outb(0,io); + } } - -static void set_freq(__u16 io, __u32 data) +static void set_freq(__u16 io, __u32 freq) { unsigned long int si; int bl; + int data = FREQ2BITS(freq); /* TEA5757 shift register bits (see pdf) */ @@ -163,161 +172,225 @@ static void set_freq(__u16 io, __u32 data) outbit(0,io); // 16 search level si = 0x8000; - for(bl = 1; bl <= 16 ; bl++) { outbit(data & si,io); si >>=1; } + for (bl = 1; bl <= 16 ; bl++) { + outbit(data & si,io); + si >>=1; + } - outb(power,io); + dprintk(1, "Radio freq set to %d.%02d MHz\n", + freq / 16000, + freq % 16000 * 100 / 16000); + + turn_power(io, 1); } static int get_stereo(__u16 io) { - outb(power,io); udelay(4); + outb(power,io); + udelay(4); + return !(inb(io) & mo_st); } static int get_tune(__u16 io) { - outb(power+clk,io); udelay(4); + outb(power+clk,io); + udelay(4); + return !(inb(io) & mo_st); } -static inline int radio_function(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver)); + strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card)); + sprintf(v->bus_info,"ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct radio_device *card=dev->priv; - switch(cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver)); - strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + memset(v,0,sizeof(*v)); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; - if (v->index > 0) - return -EINVAL; + v->rangelow=FREQ_LO; + v->rangehigh=FREQ_HI; + v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability=V4L2_TUNER_CAP_LOW; + if(get_stereo(card->io)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal=0xffff*get_tune(card->io); - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + return 0; +} - v->rangelow=FREQ_LO; - v->rangehigh=FREQ_HI; - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(get_stereo(card->io)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xffff*get_tune(card->io); +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_g_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + strcpy(a->name, "FM"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} - if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) - return -EINVAL; +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; - card->freq = f->frequency; - set_freq(card->io, FREQ2BITS(card->freq)); - msleep(125); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + return 0; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = card->freq; +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=card->muted; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - card->muted = ctrl->value; - if(card->muted) - turn_power(card->io, 0); - else - set_freq(card->io, FREQ2BITS(card->freq)); - return 0; - } - return -EINVAL; - } + return 0; +} + + +static int vidioc_s_audio (struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + + return 0; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card=dev->priv; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_function); + if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) { + dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n", + f->frequency / 16000, + f->frequency % 16000 * 100 / 16000, + FREQ_LO / 16000, FREQ_HI / 16000); + return -EINVAL; } + + card->freq = f->frequency; + set_freq(card->io, card->freq); + msleep(125); + + return 0; } -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) { struct video_device *dev = video_devdata(file); struct radio_device *card=dev->priv; - int ret; - mutex_lock(&card->lock); - ret = video_usercopy(inode, file, cmd, arg, radio_function); - mutex_unlock(&card->lock); - return ret; + f->type = V4L2_TUNER_RADIO; + f->frequency = card->freq; + + dprintk(4, "radio freq is %d.%02d MHz", + f->frequency / 16000, + f->frequency % 16000 * 100 / 16000); + + return 0; } -MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); -MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); -MODULE_LICENSE("GPL"); +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), sizeof(*qc)); + return (0); + } + } + + return -EINVAL; +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card=dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value=card->muted; + return (0); + } + + return -EINVAL; +} + +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct radio_device *card=dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + card->muted = ctrl->value; + if(card->muted) + turn_power(card->io, 0); + else + set_freq(card->io, card->freq); + return 0; + } + + return -EINVAL; +} + +static struct video_device maxiradio_radio = +{ + .owner = THIS_MODULE, + .name = "Maxi Radio FM2000 radio", + .type = VID_TYPE_TUNER, + .fops = &maxiradio_fops, + + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) { @@ -334,7 +407,7 @@ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_d mutex_init(&radio_unit.lock); maxiradio_radio.priv = &radio_unit; - if(video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) { + if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr)==-1) { printk("radio-maxiradio: can't register device!"); goto err_out_free_region; } @@ -389,3 +462,10 @@ static void __exit maxiradio_radio_exit(void) module_init(maxiradio_radio_init); module_exit(maxiradio_radio_exit); + +MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net"); +MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio."); +MODULE_LICENSE("GPL"); + +module_param_named(debug,maxiradio_radio.debug, int, 0644); +MODULE_PARM_DESC(debug,"activates debug info"); diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index f668387..9b493b3 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -122,6 +122,26 @@ static int rt_setfreq(struct rt_device *dev, unsigned long freq) return 0; } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver)); + strlcpy(v->card, "RadioTrack II", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + return 0; +} + static int rt_getsigstr(struct rt_device *dev) { if (inb(io) & 2) /* bit set = no signal present */ @@ -129,135 +149,136 @@ static int rt_getsigstr(struct rt_device *dev) return 1; /* signal present */ } -static int rt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct rt_device *rt=dev->priv; + struct rt_device *rt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-rtrack2", sizeof (v->driver)); - strlcpy(v->card, "RadioTrack II", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (88*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*rt_getsigstr(rt); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + rt->curfreq = f->frequency; + rt_setfreq(rt, rt->curfreq); + return 0; +} - v->rangelow=(88*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*rt_getsigstr(rt); +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + f->type = V4L2_TUNER_RADIO; + f->frequency = rt->curfreq; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - rt->curfreq = f->frequency; - rt_setfreq(rt, rt->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = rt->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = rt->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (rt->muted) + ctrl->value = 0; + else + ctrl->value = 65535; + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=rt->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (rt->muted) - ctrl->value=0; - else - ctrl->value=65535; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - rt_mute(rt); - } else { - rt_unmute(rt); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - if (ctrl->value) { - rt_unmute(rt); - } else { - rt_mute(rt); - } - return (0); - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - rt_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct rt_device *rt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + rt_mute(rt); + else + rt_unmute(rt); + return 0; + case V4L2_CID_AUDIO_VOLUME: + if (ctrl->value) + rt_unmute(rt); + else + rt_mute(rt); + return 0; } + return -EINVAL; } -static int rt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, rt_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct rt_device rtrack2_unit; @@ -266,7 +287,7 @@ static const struct file_operations rtrack2_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = rt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -278,6 +299,18 @@ static struct video_device rtrack2_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &rtrack2_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init rtrack2_init(void) diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index f4619e4..dc33f19 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -130,137 +130,155 @@ static inline int fmi_getsigstr(struct fmi_device *dev) return (res & 2) ? 0 : 0xFFFF; } -static int fmi_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { + strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); + strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + int mult; struct video_device *dev = video_devdata(file); - struct fmi_device *fmi=dev->priv; + struct fmi_device *fmi = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-sf16fmi", sizeof (v->driver)); - strlcpy(v->card, "SF16-FMx radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - int mult; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; - v->rangelow = RSF16_MINFREQ/mult; - v->rangehigh = RSF16_MAXFREQ/mult; - v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; - v->capability=fmi->flags&V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_STEREO; - v->signal = fmi_getsigstr(fmi); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability = fmi->flags&V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = fmi_getsigstr(fmi); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) - f->frequency *= 1000; - if (f->frequency < RSF16_MINFREQ || - f->frequency > RSF16_MAXFREQ ) - return -EINVAL; - /*rounding in steps of 800 to match th freq - that will be used */ - fmi->curfreq = (f->frequency/800)*800; - fmi_setfreq(fmi); + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) + return -EINVAL; + /*rounding in steps of 800 to match th freq + that will be used */ + fmi->curfreq = (f->frequency/800)*800; + fmi_setfreq(fmi); + return 0; +} - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = fmi->curfreq; - if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) - f->frequency /= 1000; + f->type = V4L2_TUNER_RADIO; + f->frequency = fmi->curfreq; + if (!(fmi->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=fmi->curvol; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - { - if (ctrl->value) - fmi_mute(fmi->port); - else - fmi_unmute(fmi->port); - - fmi->curvol=ctrl->value; - return (0); - } - } - return -EINVAL; - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - fmi_do_ioctl); } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = fmi->curvol; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmi_device *fmi = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + fmi_mute(fmi->port); + else + fmi_unmute(fmi->port); + fmi->curvol = ctrl->value; + return 0; + } + return -EINVAL; } -static int fmi_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, fmi_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct fmi_device fmi_unit; @@ -269,7 +287,7 @@ static const struct file_operations fmi_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = fmi_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -281,6 +299,18 @@ static struct video_device fmi_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &fmi_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; /* ladis: this is my card. does any other types exist? */ diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index b96fafe..e6c125d 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -226,186 +226,204 @@ static int fmr2_setvolume(struct fmr2_device *dev) return 0; } -static int fmr2_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { + strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver)); + strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + int mult; struct video_device *dev = video_devdata(file); struct fmr2_device *fmr2 = dev->priv; - debug_print((KERN_DEBUG "freq %ld flags %d vol %d mute %d " - "stereo %d type %d\n", - fmr2->curfreq, fmr2->flags, fmr2->curvol, fmr2->mute, - fmr2->stereo, fmr2->card_type)); - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-sf16fmr2", sizeof (v->driver)); - strlcpy(v->card, "SF16-FMR2 radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - int mult; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; - v->rangelow = RSF16_MINFREQ/mult; - v->rangehigh = RSF16_MAXFREQ/mult; - v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; - v->capability=fmr2->flags&V4L2_TUNER_CAP_LOW; - - v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO: - V4L2_TUNER_MODE_MONO; - mutex_lock(&lock); - v->signal = fmr2_getsigstr(fmr2); - mutex_unlock(&lock); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000; + v->rangelow = RSF16_MINFREQ/mult; + v->rangehigh = RSF16_MAXFREQ/mult; + v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO; + v->capability = fmr2->flags&V4L2_TUNER_CAP_LOW; + v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO: + V4L2_TUNER_MODE_MONO; + mutex_lock(&lock); + v->signal = fmr2_getsigstr(fmr2); + mutex_unlock(&lock); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) - f->frequency *= 1000; - if (f->frequency < RSF16_MINFREQ || - f->frequency > RSF16_MAXFREQ ) - return -EINVAL; - /*rounding in steps of 200 to match th freq - that will be used */ - fmr2->curfreq = (f->frequency/200)*200; - - /* set card freq (if not muted) */ - if (fmr2->curvol && !fmr2->mute) - { - mutex_lock(&lock); - fmr2_setfreq(fmr2); - mutex_unlock(&lock); - } +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency *= 1000; + if (f->frequency < RSF16_MINFREQ || + f->frequency > RSF16_MAXFREQ ) + return -EINVAL; + /*rounding in steps of 200 to match th freq + that will be used */ + fmr2->curfreq = (f->frequency/200)*200; + + /* set card freq (if not muted) */ + if (fmr2->curvol && !fmr2->mute) { + mutex_lock(&lock); + fmr2_setfreq(fmr2); + mutex_unlock(&lock); + } + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = fmr2->curfreq; - if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) - f->frequency /= 1000; + f->type = V4L2_TUNER_RADIO; + f->frequency = fmr2->curfreq; + if (!(fmr2->flags & V4L2_TUNER_CAP_LOW)) + f->frequency /= 1000; + return 0; +} +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if ((fmr2->card_type != 11) + && V4L2_CID_AUDIO_VOLUME) + radio_qctrl[i].step = 65535; + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if ((fmr2->card_type != 11) - && V4L2_CID_AUDIO_VOLUME) - radio_qctrl[i].step=65535; - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = fmr2->mute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = fmr2->curvol; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct fmr2_device *fmr2 = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + fmr2->mute = ctrl->value; + if (fmr2->card_type != 11) { + if (!fmr2->mute) + fmr2->curvol = 65535; + else + fmr2->curvol = 0; } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=fmr2->mute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=fmr2->curvol; - return (0); + break; + case V4L2_CID_AUDIO_VOLUME: + fmr2->curvol = ctrl->value; + if (fmr2->card_type != 11) { + if (fmr2->curvol) { + fmr2->curvol = 65535; + fmr2->mute = 0; + } else { + fmr2->curvol = 0; + fmr2->mute = 1; } - return -EINVAL; } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - fmr2->mute=ctrl->value; - if (fmr2->card_type != 11) { - if (!fmr2->mute) { - fmr2->curvol = 65535; - } else { - fmr2->curvol = 0; - } - } - break; - case V4L2_CID_AUDIO_VOLUME: - fmr2->curvol = ctrl->value; - if (fmr2->card_type != 11) { - if (fmr2->curvol) { - fmr2->curvol = 65535; - fmr2->mute = 0; - } else { - fmr2->curvol = 0; - fmr2->mute = 1; - } - } - break; - default: - return -EINVAL; - } + break; + default: + return -EINVAL; + } + #ifdef DEBUG - if (fmr2->curvol && !fmr2->mute) - printk(KERN_DEBUG "unmute\n"); - else - printk(KERN_DEBUG "mute\n"); + if (fmr2->curvol && !fmr2->mute) + printk(KERN_DEBUG "unmute\n"); + else + printk(KERN_DEBUG "mute\n"); #endif - mutex_lock(&lock); - if (fmr2->curvol && !fmr2->mute) { - fmr2_setvolume(fmr2); - fmr2_setfreq(fmr2); - } else - fmr2_mute(fmr2->port); - mutex_unlock(&lock); - return (0); - } - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - fmr2_do_ioctl); - } + mutex_lock(&lock); + if (fmr2->curvol && !fmr2->mute) { + fmr2_setvolume(fmr2); + fmr2_setfreq(fmr2); + } else + fmr2_mute(fmr2->port); + mutex_unlock(&lock); + return 0; } -static int fmr2_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) - { - return video_usercopy(inode, file, cmd, arg, fmr2_do_ioctl); +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct fmr2_device fmr2_unit; @@ -414,7 +432,7 @@ static const struct file_operations fmr2_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = fmr2_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -426,6 +444,18 @@ static struct video_device fmr2_radio= . type = VID_TYPE_TUNER, .hardware = 0, .fops = &fmr2_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init fmr2_init(void) diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c index d59a27a..e43acfd 100644 --- a/drivers/media/radio/radio-terratec.c +++ b/drivers/media/radio/radio-terratec.c @@ -205,135 +205,152 @@ static int tt_getsigstr(struct tt_device *dev) /* TODO */ return 1; /* signal present */ } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-terratec", sizeof(v->driver)); + strlcpy(v->card, "ActiveRadio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} -/* implement the video4linux api */ - -static int tt_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); - struct tt_device *tt=dev->priv; + struct tt_device *tt = dev->priv; - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-terratec", sizeof (v->driver)); - strlcpy(v->card, "ActiveRadio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*tt_getsigstr(tt); + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - v->rangelow=(87*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*tt_getsigstr(tt); + tt->curfreq = f->frequency; + tt_setfreq(tt, tt->curfreq); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = tt->curfreq; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; - tt->curfreq = f->frequency; - tt_setfreq(tt, tt->curfreq); + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + } + return -EINVAL; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = tt->curfreq; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (tt->muted) - ctrl->value=1; - else - ctrl->value=0; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=tt->curvol * 6554; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - tt_mute(tt); - } else { - tt_setvol(tt,tt->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - tt_setvol(tt,ctrl->value); - return (0); - } - return -EINVAL; - } + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (tt->muted) + ctrl->value = 1; + else + ctrl->value = 0; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = tt->curvol * 6554; + return 0; + } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - tt_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct tt_device *tt = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + tt_mute(tt); + else + tt_setvol(tt,tt->curvol); + return 0; + case V4L2_CID_AUDIO_VOLUME: + tt_setvol(tt,ctrl->value); + return 0; } + return -EINVAL; +} + +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static int tt_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - return video_usercopy(inode, file, cmd, arg, tt_do_ioctl); + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct tt_device terratec_unit; @@ -342,7 +359,7 @@ static const struct file_operations terratec_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = tt_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -354,6 +371,18 @@ static struct video_device terratec_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &terratec_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init terratec_init(void) diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c index 6d7f1e7..c27c629 100644 --- a/drivers/media/radio/radio-trust.c +++ b/drivers/media/radio/radio-trust.c @@ -192,144 +192,154 @@ static void tr_setfreq(unsigned long f) write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0); } -static int tr_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) { - switch(cmd) - { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-trust", sizeof (v->driver)); - strlcpy(v->card, "Trust FM Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + strlcpy(v->driver, "radio-trust", sizeof(v->driver)); + strlcpy(v->card, "Trust FM Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - v->rangelow=(87.5*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(tr_getstereo()) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=tr_getsigstr(); +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87.5*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if (tr_getstereo()) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = tr_getsigstr(); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; - if (v->index > 0) - return -EINVAL; + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + curfreq = f->frequency; + tr_setfreq(curfreq); + return 0; +} - curfreq = f->frequency; - tr_setfreq(curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + f->type = V4L2_TUNER_RADIO; + f->frequency = curfreq; + return 0; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = curfreq; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=curmute; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value= curvol * 2048; - return (0); - case V4L2_CID_AUDIO_BASS: - ctrl->value= curbass * 4370; - return (0); - case V4L2_CID_AUDIO_TREBLE: - ctrl->value= curtreble * 4370; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - tr_setmute(ctrl->value); - return 0; - case V4L2_CID_AUDIO_VOLUME: - tr_setvol(ctrl->value); - return 0; - case V4L2_CID_AUDIO_BASS: - tr_setbass(ctrl->value); - return 0; - case V4L2_CID_AUDIO_TREBLE: - tr_settreble(ctrl->value); - return (0); - } - return -EINVAL; - } + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = curmute; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = curvol * 2048; + return 0; + case V4L2_CID_AUDIO_BASS: + ctrl->value = curbass * 4370; + return 0; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = curtreble * 4370; + return 0; + } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - tr_do_ioctl); +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + tr_setmute(ctrl->value); + return 0; + case V4L2_CID_AUDIO_VOLUME: + tr_setvol(ctrl->value); + return 0; + case V4L2_CID_AUDIO_BASS: + tr_setbass(ctrl->value); + return 0; + case V4L2_CID_AUDIO_TREBLE: + tr_settreble(ctrl->value); + return 0; } + return -EINVAL; } -static int tr_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, tr_do_ioctl); + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static const struct file_operations trust_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = tr_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -341,6 +351,18 @@ static struct video_device trust_radio= .type = VID_TYPE_TUNER, .hardware = 0, .fops = &trust_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, }; static int __init trust_init(void) diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c index 3031fef..8ff5a23 100644 --- a/drivers/media/radio/radio-typhoon.c +++ b/drivers/media/radio/radio-typhoon.c @@ -93,8 +93,6 @@ static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency); static void typhoon_mute(struct typhoon_device *dev); static void typhoon_unmute(struct typhoon_device *dev); static int typhoon_setvol(struct typhoon_device *dev, int vol); -static int typhoon_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg); #ifdef CONFIG_RADIO_TYPHOON_PROC_FS static int typhoon_get_info(char *buf, char **start, off_t offset, int len); #endif @@ -186,129 +184,148 @@ static int typhoon_setvol(struct typhoon_device *dev, int vol) return 0; } +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-typhoon", sizeof(v->driver)); + strlcpy(v->card, "Typhoon Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (87.5*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF; /* We can't get the signal strength */ + return 0; +} -static int typhoon_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) { struct video_device *dev = video_devdata(file); struct typhoon_device *typhoon = dev->priv; - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-typhoon", sizeof (v->driver)); - strlcpy(v->card, "Typhoon Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + typhoon->curfreq = f->frequency; + typhoon_setfreq(typhoon, typhoon->curfreq); + return 0; +} - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - if (v->index > 0) - return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = typhoon->curfreq; - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; + return 0; +} - v->rangelow=(87.5*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO; - v->capability=V4L2_TUNER_CAP_LOW; - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xFFFF; /* We can't get the signal strength */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; + } + return -EINVAL; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = typhoon->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = typhoon->curvol; + return 0; + } + return -EINVAL; +} - typhoon->curfreq = f->frequency; - typhoon_setfreq(typhoon, typhoon->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct typhoon_device *typhoon = dev->priv; - f->type = V4L2_TUNER_RADIO; - f->frequency = typhoon->curfreq; + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + typhoon_mute(typhoon); + else + typhoon_unmute(typhoon); + return 0; + case V4L2_CID_AUDIO_VOLUME: + typhoon_setvol(typhoon, ctrl->value); + return 0; + } + return -EINVAL; +} - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=typhoon->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=typhoon->curvol; - return (0); - } - return -EINVAL; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - typhoon_mute(typhoon); - } else { - typhoon_unmute(typhoon); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - typhoon_setvol(typhoon, ctrl->value); - return (0); - } - return -EINVAL; - } +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - typhoon_do_ioctl); - } + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; } -static int typhoon_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) { - return video_usercopy(inode, file, cmd, arg, typhoon_do_ioctl); + if (a->index != 0) + return -EINVAL; + return 0; } static struct typhoon_device typhoon_unit = @@ -322,7 +339,7 @@ static const struct file_operations typhoon_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = typhoon_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -334,6 +351,18 @@ static struct video_device typhoon_radio = .type = VID_TYPE_TUNER, .hardware = 0, .fops = &typhoon_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; #ifdef CONFIG_RADIO_TYPHOON_PROC_FS diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c index ec08491..a471590 100644 --- a/drivers/media/radio/radio-zoltrix.c +++ b/drivers/media/radio/radio-zoltrix.c @@ -230,121 +230,123 @@ static int zol_is_stereo (struct zol_device *dev) return 0; } -static int zol_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver)); + strlcpy(v->card, "Zoltrix Radio", sizeof(v->card)); + sprintf(v->bus_info, "ISA"); + v->version = RADIO_VERSION; + v->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) { struct video_device *dev = video_devdata(file); struct zol_device *zol = dev->priv; - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *v = arg; - memset(v,0,sizeof(*v)); - strlcpy(v->driver, "radio-zoltrix", sizeof (v->driver)); - strlcpy(v->card, "Zoltrix Radio", sizeof (v->card)); - sprintf(v->bus_info,"ISA"); - v->version = RADIO_VERSION; - v->capabilities = V4L2_CAP_TUNER; + if (v->index > 0) + return -EINVAL; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *v = arg; - - if (v->index > 0) - return -EINVAL; - - memset(v,0,sizeof(*v)); - strcpy(v->name, "FM"); - v->type = V4L2_TUNER_RADIO; - - v->rangelow=(88*16000); - v->rangehigh=(108*16000); - v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; - v->capability=V4L2_TUNER_CAP_LOW; - if(zol_is_stereo(zol)) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal=0xFFFF*zol_getsigstr(zol); + strcpy(v->name, "FM"); + v->type = V4L2_TUNER_RADIO; + v->rangelow = (88*16000); + v->rangehigh = (108*16000); + v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO; + v->capability = V4L2_TUNER_CAP_LOW; + if (zol_is_stereo(zol)) + v->audmode = V4L2_TUNER_MODE_STEREO; + else + v->audmode = V4L2_TUNER_MODE_MONO; + v->signal = 0xFFFF*zol_getsigstr(zol); + return 0; +} - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *v = arg; +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + return 0; +} - if (v->index > 0) - return -EINVAL; +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; + zol->curfreq = f->frequency; + zol_setfreq(zol, zol->curfreq); + return 0; +} - zol->curfreq = f->frequency; - zol_setfreq(zol, zol->curfreq); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + f->type = V4L2_TUNER_RADIO; + f->frequency = zol->curfreq; + return 0; +} - f->type = V4L2_TUNER_RADIO; - f->frequency = zol->curfreq; +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { + if (qc->id && qc->id == radio_qctrl[i].id) { + memcpy(qc, &(radio_qctrl[i]), + sizeof(*qc)); return 0; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - int i; - - for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) { - if (qc->id && qc->id == radio_qctrl[i].id) { - memcpy(qc, &(radio_qctrl[i]), - sizeof(*qc)); - return (0); - } - } - return -EINVAL; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value=zol->muted; - return (0); - case V4L2_CID_AUDIO_VOLUME: - ctrl->value=zol->curvol * 4096; - return (0); - } - return -EINVAL; + } + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = zol->muted; + return 0; + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = zol->curvol * 4096; + return 0; + } + return -EINVAL; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = video_devdata(file); + struct zol_device *zol = dev->priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + zol_mute(zol); + else { + zol_unmute(zol); + zol_setvol(zol,zol->curvol); } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl= arg; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - zol_mute(zol); - } else { - zol_unmute(zol); - zol_setvol(zol,zol->curvol); - } - return (0); - case V4L2_CID_AUDIO_VOLUME: - zol_setvol(zol,ctrl->value/4096); - return (0); - } - zol->stereo = 1; - zol_setfreq(zol, zol->curfreq); + return 0; + case V4L2_CID_AUDIO_VOLUME: + zol_setvol(zol,ctrl->value/4096); + return 0; + } + zol->stereo = 1; + zol_setfreq(zol, zol->curfreq); #if 0 /* FIXME: Implement stereo/mono switch on V4L2 */ if (v->mode & VIDEO_SOUND_STEREO) { @@ -356,19 +358,39 @@ static int zol_do_ioctl(struct inode *inode, struct file *file, zol_setfreq(zol, zol->curfreq); } #endif - return -EINVAL; - } + return -EINVAL; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - zol_do_ioctl); - } +static int vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index > 1) + return -EINVAL; + + strcpy(a->name, "Radio"); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; } -static int zol_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) { - return video_usercopy(inode, file, cmd, arg, zol_do_ioctl); + if (i != 0) + return -EINVAL; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + if (a->index != 0) + return -EINVAL; + return 0; } static struct zol_device zoltrix_unit; @@ -378,7 +400,7 @@ static const struct file_operations zoltrix_fops = .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, - .ioctl = zol_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; @@ -390,6 +412,18 @@ static struct video_device zoltrix_radio = .type = VID_TYPE_TUNER, .hardware = 0, .fops = &zoltrix_fops, + .vidioc_querycap = vidioc_querycap, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, }; static int __init zoltrix_init(void) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 57357db..bc77378 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -342,7 +342,7 @@ endmenu # encoder / decoder chips config VIDEO_VIVI tristate "Virtual Video Driver" - depends on VIDEO_V4L2 && !SPARC32 && !SPARC64 + depends on VIDEO_V4L2 && !SPARC32 && !SPARC64 && PCI select VIDEO_BUF default n ---help--- @@ -577,14 +577,14 @@ config VIDEO_ZORAN_AVS6EYES config VIDEO_MEYE tristate "Sony Vaio Picturebook Motion Eye Video For Linux" - depends on PCI && SONYPI && VIDEO_V4L1 + depends on PCI && SONY_LAPTOP && VIDEO_V4L1 ---help--- This is the video4linux driver for the Motion Eye camera found in the Vaio Picturebook laptops. Please read the material in <file:Documentation/video4linux/meye.txt> for more information. - If you say Y or M here, you need to say Y or M to "Sony Programmable - I/O Control Device" in the character device section. + If you say Y or M here, you need to say Y or M to "Sony Laptop + Extras" in the misc device section. To compile this driver as a module, choose M here: the module will be called meye. @@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI source "drivers/media/video/cx88/Kconfig" +source "drivers/media/video/ivtv/Kconfig" + config VIDEO_M32R_AR tristate "AR devices" depends on M32R && VIDEO_V4L1 @@ -761,6 +763,18 @@ source "drivers/media/video/zc0301/Kconfig" source "drivers/media/video/pwc/Kconfig" +config USB_ZR364XX + tristate "USB ZR364XX Camera support" + depends on USB && VIDEO_V4L2 + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. + See <file:Documentation/video4linux/zr364xx.txt> for more info + and list of supported cameras. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + endmenu # V4L USB devices endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 9b1f3f0..9c2de50 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ +obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o @@ -99,6 +100,7 @@ obj-$(CONFIG_USB_OV511) += ov511.o obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_STV680) += stv680.o obj-$(CONFIG_USB_W9968CF) += w9968cf.o +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ obj-$(CONFIG_USB_ET61X251) += et61x251/ @@ -113,4 +115,3 @@ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core -extra-cflags-$(CONFIG_VIDEO_V4L1_COMPAT) += -DCONFIG_VIDEO_V4L1_COMPAT diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c index 21ebe8f..6b31e50 100644 --- a/drivers/media/video/bt8xx/bttv-cards.c +++ b/drivers/media/video/bt8xx/bttv-cards.c @@ -291,6 +291,9 @@ static struct CARD { { 0x15409511, BTTV_BOARD_ACORP_Y878F, "Acorp Y878F" }, + { 0x53534149, BTTV_BOARD_SSAI_SECURITY, "SSAI Security Video Interface" }, + { 0x5353414a, BTTV_BOARD_SSAI_ULTRASOUND, "SSAI Ultrasound Video Interface" }, + /* likely broken, vendor id doesn't match the other magic views ... * { 0xa0fca04f, BTTV_BOARD_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, */ @@ -307,6 +310,7 @@ static struct CARD { { 0x07711461, BTTV_BOARD_AVDVBT_771, "AVermedia AverTV DVB-T 771" }, { 0x07611461, BTTV_BOARD_AVDVBT_761, "AverMedia AverTV DVB-T 761" }, { 0xdb1018ac, BTTV_BOARD_DVICO_DVBT_LITE, "DViCO FusionHDTV DVB-T Lite" }, + { 0xdb1118ac, BTTV_BOARD_DVICO_DVBT_LITE, "Ultraview DVB-T Lite" }, { 0xd50018ac, BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE, "DViCO FusionHDTV 5 Lite" }, { 0x00261822, BTTV_BOARD_TWINHAN_DST, "DNTV Live! Mini "}, @@ -578,14 +582,9 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x01fe00, .muxsel = { 2, 3, 1, 1 }, - #if 0 - /* old */ - .gpiomux = { 0x01c000, 0, 0x018000, 0x014000, 0x002000 }, - #else /* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 }, .gpiomute = 0x002000, - #endif .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -894,15 +893,10 @@ struct tvcard bttv_tvcards[] = { .tuner = 0, .svhs = 2, .muxsel = { 2, 3, 1, 1, 0 }, /* TV, CVid, SVid, CVid over SVid connector */ - #if 0 - .gpiomask = 0xc33000, - .gpiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000 }, - #else /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */ .gpiomask = 0xb33000, .gpiomux = { 0x122000,0x1000,0x0000,0x620000 }, .gpiomute = 0x800000, - #endif /* Audio Routing for "WinFast 2000 XP" (no tv stereo !) gpio23 -- hef4052:nEnable (0x800000) gpio12 -- hef4052:A1 @@ -1937,11 +1931,6 @@ struct tvcard bttv_tvcards[] = { .video_inputs = 4, .audio_inputs = 1, .tuner = -1, - #if 0 /* TODO ... */ - .svhs = OSPREY540_SVID_ANALOG, - .muxsel = { [OSPREY540_COMP_ANALOG] = 2, - [OSPREY540_SVID_ANALOG] = 3, }, - #endif .pll = PLL_28, .tuner_type = -1, .tuner_addr = ADDR_UNSET, @@ -1949,10 +1938,6 @@ struct tvcard bttv_tvcards[] = { .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, - #if 0 /* TODO ... */ - .muxsel_hook = osprey_540_muxsel, - .picture_hook = osprey_540_set_picture, - #endif }, /* ---- card 0x5C ---------------------------------- */ @@ -2627,9 +2612,6 @@ struct tvcard bttv_tvcards[] = { .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .has_radio = 0, - #if 0 - .has_remote = 1, - #endif }, [BTTV_BOARD_SUPER_TV] = { /* Rick C <cryptdragoon@gmail.com> */ @@ -2928,6 +2910,28 @@ struct tvcard bttv_tvcards[] = { .has_radio = 1, .has_remote = 1, }, + [BTTV_BOARD_SSAI_SECURITY] = { + .name = "SSAI Security Video Interface", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .muxsel = { 0, 1, 2, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [BTTV_BOARD_SSAI_ULTRASOUND] = { + .name = "SSAI Ultrasound Video Interface", + .video_inputs = 2, + .audio_inputs = 0, + .tuner = -1, + .svhs = 1, + .muxsel = { 2, 0, 1, 3 }, + .tuner_type = -1, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2991,20 +2995,20 @@ void __devinit bttv_idcard(struct bttv *btv) if (UNSET != audiomux[0]) { gpiobits = 0; - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audiomux[i]; gpiobits |= audiomux[i]; } } else { gpiobits = audioall; - for (i = 0; i < 4; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { bttv_tvcards[btv->c.type].gpiomux[i] = audioall; } } bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=", btv->c.nr,bttv_tvcards[btv->c.type].gpiomask); - for (i = 0; i < 5; i++) { + for (i = 0; i < ARRAY_SIZE(bttv_tvcards->gpiomux); i++) { printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].gpiomux[i]); } printk("\n"); @@ -3659,7 +3663,7 @@ static int __devinit pvr_altera_load(struct bttv *btv, u8 *micro, u32 microlen) for (n = 0; n < microlen; n++) { bits = micro[n]; - for ( i = 0 ; i < 8 ; i++ ) { + for (i = 0 ; i < 8 ; i++) { gpio_bits(BTTV_ALT_DCLK,0); if (bits & 0x01) gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA); @@ -3712,7 +3716,7 @@ static void __devinit osprey_eeprom(struct bttv *btv) /* this might be an antique... check for MMAC label in eeprom */ if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) { unsigned char checksum = 0; - for (i =0; i<21; i++) + for (i = 0; i < 21; i++) checksum += ee[i]; if (checksum != ee[21]) return; @@ -3724,12 +3728,13 @@ static void __devinit osprey_eeprom(struct bttv *btv) unsigned short type; int offset = 4*16; - for(; offset < 8*16; offset += 16) { + for (; offset < 8*16; offset += 16) { unsigned short checksum = 0; /* verify the checksum */ - for(i = 0; i<14; i++) checksum += ee[i+offset]; - checksum = ~checksum; /* no idea why */ - if ((((checksum>>8)&0x0FF) == ee[offset+14]) && + for (i = 0; i < 14; i++) + checksum += ee[i+offset]; + checksum = ~checksum; /* no idea why */ + if ((((checksum>>8)&0x0FF) == ee[offset+14]) && ((checksum & 0x0FF) == ee[offset+15])) { break; } @@ -3742,7 +3747,6 @@ static void __devinit osprey_eeprom(struct bttv *btv) type = (ee[offset+4]<<8) | (ee[offset+5]); switch(type) { - /* 848 based */ case 0x0004: btv->c.type = BTTV_BOARD_OSPREY1x0_848; @@ -4170,8 +4174,7 @@ static int tea5757_read(struct bttv *btv) } dprintk("bttv%d: tea5757:",btv->c.nr); - for(i = 0; i < 24; i++) - { + for (i = 0; i < 24; i++) { udelay(5); bus_high(btv,btv->mbox_clk); udelay(5); @@ -4203,8 +4206,7 @@ static int tea5757_write(struct bttv *btv, int value) dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value); bus_low(btv,btv->mbox_clk); bus_high(btv,btv->mbox_we); - for(i = 0; i < 25; i++) - { + for (i = 0; i < 25; i++) { if (reg & 0x1000000) bus_high(btv,btv->mbox_data); else @@ -4776,7 +4778,7 @@ static void kodicom4400r_init(struct bttv *btv) gpio_write(1 << 9); /* reset MUX */ gpio_write(0); /* Preset camera 0 to the 4 controllers */ - for (ix=0; ix<4; ix++) { + for (ix = 0; ix < 4; ix++) { sw_status[ix] = ix; kodicom4400r_write(btv, ix, ix, 1); } diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 41fd09d..1c38723 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -9,6 +9,10 @@ some v4l2 code lines are taken from Justin's bttv2 driver which is (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za> + Cropping and overscan support + Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at> + Sponsored by OPQ Systems AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -64,6 +68,7 @@ static unsigned int radio[BTTV_MAX]; static unsigned int irq_debug; static unsigned int gbuffers = 8; static unsigned int gbufsize = 0x208000; +static unsigned int reset_crop = 1; static int video_nr = -1; static int radio_nr = -1; @@ -103,6 +108,7 @@ module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); module_param(gbuffers, int, 0444); module_param(gbufsize, int, 0444); +module_param(reset_crop, int, 0444); module_param(v4l2, int, 0644); module_param(bigendian, int, 0644); @@ -129,6 +135,8 @@ MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); +MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " + "is 1 (yes) for compatibility with older applications"); MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); @@ -156,6 +164,24 @@ static ssize_t show_card(struct class_device *cd, char *buf) static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL); /* ----------------------------------------------------------------------- */ +/* dvb auto-load setup */ +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + request_module("dvb-bt8xx"); +} + +static void request_modules(struct bttv *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + + +/* ----------------------------------------------------------------------- */ /* static data */ /* special timing tables from conexant... */ @@ -192,6 +218,33 @@ static u8 SRAM_Table[][60] = } }; +/* minhdelayx1 first video pixel we can capture on a line and + hdelayx1 start of active video, both relative to rising edge of + /HRESET pulse (0H) in 1 / fCLKx1. + swidth width of active video and + totalwidth total line width, both in 1 / fCLKx1. + sqwidth total line width in square pixels. + vdelay start of active video in 2 * field lines relative to + trailing edge of /VRESET pulse (VDELAY register). + sheight height of active video in 2 * field lines. + videostart0 ITU-R frame line number of the line corresponding + to vdelay in the first field. */ +#define CROPCAP(minhdelayx1, hdelayx1, swidth, totalwidth, sqwidth, \ + vdelay, sheight, videostart0) \ + .cropcap.bounds.left = minhdelayx1, \ + /* * 2 because vertically we count field lines times two, */ \ + /* e.g. 23 * 2 to 23 * 2 + 576 in PAL-BGHI defrect. */ \ + .cropcap.bounds.top = (videostart0) * 2 - (vdelay) + MIN_VDELAY, \ + /* 4 is a safety margin at the end of the line. */ \ + .cropcap.bounds.width = (totalwidth) - (minhdelayx1) - 4, \ + .cropcap.bounds.height = (sheight) + (vdelay) - MIN_VDELAY, \ + .cropcap.defrect.left = hdelayx1, \ + .cropcap.defrect.top = (videostart0) * 2, \ + .cropcap.defrect.width = swidth, \ + .cropcap.defrect.height = sheight, \ + .cropcap.pixelaspect.numerator = totalwidth, \ + .cropcap.pixelaspect.denominator = sqwidth, + const struct bttv_tvnorm bttv_tvnorms[] = { /* PAL-BDGHI */ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ @@ -210,11 +263,26 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .hdelayx1 = 186, .hactivex1 = 924, .vdelay = 0x20, - .vbipack = 255, + .vbipack = 255, /* min (2048 / 4, 0x1ff) & 0xff */ .sram = 0, /* ITU-R frame line number of the first VBI line - we can capture, of the first and second field. */ - .vbistart = { 7,320 }, + we can capture, of the first and second field. + The last line is determined by cropcap.bounds. */ + .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* Should be (768 * 1135 + 944 / 2) / 944. + cropcap.defrect is used for image width + checks, so we keep the old value 924. */ + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) + /* bt878 (and bt848?) can capture another + line below active video. */ + .cropcap.bounds.height = (576 + 2) + 0x20 - 2, },{ .v4l2_id = V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_KR, .name = "NTSC", @@ -229,9 +297,18 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .hdelayx1 = 128, .hactivex1 = 910, .vdelay = 0x1a, - .vbipack = 144, + .vbipack = 144, /* min (1600 / 4, 0x1ff) & 0xff */ .sram = 1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 128, + /* Should be (640 * 910 + 780 / 2) / 780? */ + /* swidth */ 768, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_SECAM, .name = "SECAM", @@ -249,6 +326,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 255, .sram = 0, /* like PAL, correct? */ .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_Nc, .name = "PAL-Nc", @@ -266,6 +351,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 144, .sram = -1, .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 130, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_M, .name = "PAL-M", @@ -283,6 +376,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vbipack = 144, .sram = -1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 135, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_PAL_N, .name = "PAL-N", @@ -299,7 +400,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x20, .vbipack = 144, .sram = -1, - .vbistart = { 7, 320}, + .vbistart = { 7, 320 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ (768 * 1135 + 944 / 2) / 944, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x20, + /* sheight */ 576, + /* videostart0 */ 23) },{ .v4l2_id = V4L2_STD_NTSC_M_JP, .name = "NTSC-JP", @@ -316,7 +425,15 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vdelay = 0x16, .vbipack = 144, .sram = -1, - .vbistart = {10, 273}, + .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 135, + /* swidth */ (640 * 910 + 780 / 2) / 780, + /* totalwidth */ 910, + /* sqwidth */ 780, + /* vdelay */ 0x16, + /* sheight */ 480, + /* videostart0 */ 23) },{ /* that one hopefully works with the strange timing * which video recorders produce when playing a NTSC @@ -338,6 +455,14 @@ const struct bttv_tvnorm bttv_tvnorms[] = { .vtotal = 524, .sram = -1, .vbistart = { 10, 273 }, + CROPCAP(/* minhdelayx1 */ 68, + /* hdelayx1 */ 186, + /* swidth */ 924, + /* totalwidth */ 1135, + /* sqwidth */ 944, + /* vdelay */ 0x1a, + /* sheight */ 480, + /* videostart0 */ 23) } }; static const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); @@ -678,25 +803,89 @@ static const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls); /* ----------------------------------------------------------------------- */ /* resource management */ +/* + RESOURCE_ allocated by freed by + + VIDEO_READ bttv_read 1) bttv_read 2) + + VIDEO_STREAM VIDIOC_STREAMON VIDIOC_STREAMOFF + VIDIOC_QBUF 1) bttv_release + VIDIOCMCAPTURE 1) + + OVERLAY VIDIOCCAPTURE on VIDIOCCAPTURE off + VIDIOC_OVERLAY on VIDIOC_OVERLAY off + 3) bttv_release + + VBI VIDIOC_STREAMON VIDIOC_STREAMOFF + VIDIOC_QBUF 1) bttv_release + bttv_read, bttv_poll 1) 4) + + 1) The resource must be allocated when we enter buffer prepare functions + and remain allocated while buffers are in the DMA queue. + 2) This is a single frame read. + 3) VIDIOC_S_FBUF and VIDIOC_S_FMT (OVERLAY) still work when + RESOURCE_OVERLAY is allocated. + 4) This is a continuous read, implies VIDIOC_STREAMON. + + Note this driver permits video input and standard changes regardless if + resources are allocated. +*/ + +#define VBI_RESOURCES (RESOURCE_VBI) +#define VIDEO_RESOURCES (RESOURCE_VIDEO_READ | \ + RESOURCE_VIDEO_STREAM | \ + RESOURCE_OVERLAY) + static int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit) { + int xbits; /* mutual exclusive resources */ + if (fh->resources & bit) /* have it already allocated */ return 1; + xbits = bit; + if (bit & (RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM)) + xbits |= RESOURCE_VIDEO_READ | RESOURCE_VIDEO_STREAM; + /* is it free? */ - mutex_lock(&btv->reslock); - if (btv->resources & bit) { + mutex_lock(&btv->lock); + if (btv->resources & xbits) { /* no, someone else uses it */ - mutex_unlock(&btv->reslock); - return 0; + goto fail; } + + if ((bit & VIDEO_RESOURCES) + && 0 == (btv->resources & VIDEO_RESOURCES)) { + /* Do crop - use current, don't - use default parameters. */ + __s32 top = btv->crop[!!fh->do_crop].rect.top; + + if (btv->vbi_end > top) + goto fail; + + /* We cannot capture the same line as video and VBI data. + Claim scan lines crop[].rect.top to bottom. */ + btv->crop_start = top; + } else if (bit & VBI_RESOURCES) { + __s32 end = fh->vbi_fmt.end; + + if (end > btv->crop_start) + goto fail; + + /* Claim scan lines above fh->vbi_fmt.end. */ + btv->vbi_end = end; + } + /* it's free, grab it */ fh->resources |= bit; btv->resources |= bit; - mutex_unlock(&btv->reslock); + mutex_unlock(&btv->lock); return 1; + + fail: + mutex_unlock(&btv->lock); + return 0; } static @@ -711,6 +900,35 @@ int locked_btres(struct bttv *btv, int bit) return (btv->resources & bit); } +/* Call with btv->lock down. */ +static void +disclaim_vbi_lines(struct bttv *btv) +{ + btv->vbi_end = 0; +} + +/* Call with btv->lock down. */ +static void +disclaim_video_lines(struct bttv *btv) +{ + const struct bttv_tvnorm *tvnorm; + u8 crop; + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + btv->crop_start = tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height; + + /* VBI capturing ends at VDELAY, start of video capturing, no + matter how many lines the VBI RISC program expects. When video + capturing is off, it shall no longer "preempt" VBI capturing, + so we set VDELAY to maximum. */ + crop = btread(BT848_E_CROP) | 0xc0; + btwrite(crop, BT848_E_CROP); + btwrite(0xfe, BT848_E_VDELAY_LO); + btwrite(crop, BT848_O_CROP); + btwrite(0xfe, BT848_O_VDELAY_LO); +} + static void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) { @@ -718,10 +936,19 @@ void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) /* trying to free ressources not allocated by us ... */ printk("bttv: BUG! (btres)\n"); } - mutex_lock(&btv->reslock); + mutex_lock(&btv->lock); fh->resources &= ~bits; btv->resources &= ~bits; - mutex_unlock(&btv->reslock); + + bits = btv->resources; + + if (0 == (bits & VIDEO_RESOURCES)) + disclaim_video_lines(btv); + + if (0 == (bits & VBI_RESOURCES)) + disclaim_vbi_lines(btv); + + mutex_unlock(&btv->lock); } /* ----------------------------------------------------------------------- */ @@ -1030,6 +1257,36 @@ i2c_vidiocschan(struct bttv *btv) bttv_tda9880_setnorm(btv,btv->tvnorm); } +static void +bttv_crop_calc_limits(struct bttv_crop *c) +{ + /* Scale factor min. 1:1, max. 16:1. Min. image size + 48 x 32. Scaled width must be a multiple of 4. */ + + if (1) { + /* For bug compatibility with VIDIOCGCAP and image + size checks in earlier driver versions. */ + c->min_scaled_width = 48; + c->min_scaled_height = 32; + } else { + c->min_scaled_width = + (max(48, c->rect.width >> 4) + 3) & ~3; + c->min_scaled_height = + max(32, c->rect.height >> 4); + } + + c->max_scaled_width = c->rect.width & ~3; + c->max_scaled_height = c->rect.height; +} + +static void +bttv_crop_reset(struct bttv_crop *c, int norm) +{ + c->rect = bttv_tvnorms[norm].cropcap.defrect; + bttv_crop_calc_limits(c); +} + +/* Call with btv->lock down. */ static int set_tvnorm(struct bttv *btv, unsigned int norm) { @@ -1038,9 +1295,24 @@ set_tvnorm(struct bttv *btv, unsigned int norm) if (norm < 0 || norm >= BTTV_TVNORMS) return -EINVAL; - btv->tvnorm = norm; tvnorm = &bttv_tvnorms[norm]; + if (btv->tvnorm < 0 || + btv->tvnorm >= BTTV_TVNORMS || + 0 != memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, + &tvnorm->cropcap, + sizeof (tvnorm->cropcap))) { + bttv_crop_reset(&btv->crop[0], norm); + btv->crop[1] = btv->crop[0]; /* current = default */ + + if (0 == (btv->resources & VIDEO_RESOURCES)) { + btv->crop_start = tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height; + } + } + + btv->tvnorm = norm; + btwrite(tvnorm->adelay, BT848_ADELAY); btwrite(tvnorm->bdelay, BT848_BDELAY); btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), @@ -1057,6 +1329,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) return 0; } +/* Call with btv->lock down. */ static void set_input(struct bttv *btv, unsigned int input) { @@ -1459,13 +1732,13 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, btv->loop_irq |= 1; bttv_set_dma(btv, 0x03); spin_unlock_irqrestore(&btv->s_lock,flags); - if (NULL == new) - free_btres(btv,fh,RESOURCE_OVERLAY); if (NULL != old) { dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); bttv_dma_free(&fh->cap,btv, old); kfree(old); } + if (NULL == new) + free_btres(btv,fh,RESOURCE_OVERLAY); dprintk("switch_overlay: done\n"); return retval; } @@ -1479,7 +1752,10 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, unsigned int width, unsigned int height, enum v4l2_field field) { + struct bttv_fh *fh = q->priv_data; int redo_dma_risc = 0; + struct bttv_crop c; + int norm; int rc; /* check settings */ @@ -1491,12 +1767,52 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, if (width*height > buf->vb.bsize) return -EINVAL; buf->vb.size = buf->vb.bsize; + + /* Make sure tvnorm and vbi_end remain consistent + until we're done. */ + mutex_lock(&btv->lock); + + norm = btv->tvnorm; + + /* In this mode capturing always starts at defrect.top + (default VDELAY), ignoring cropping parameters. */ + if (btv->vbi_end > bttv_tvnorms[norm].cropcap.defrect.top) { + mutex_unlock(&btv->lock); + return -EINVAL; + } + + mutex_unlock(&btv->lock); + + c.rect = bttv_tvnorms[norm].cropcap.defrect; } else { - if (width < 48 || - height < 32 || - width > bttv_tvnorms[btv->tvnorm].swidth || - height > bttv_tvnorms[btv->tvnorm].sheight) + mutex_lock(&btv->lock); + + norm = btv->tvnorm; + c = btv->crop[!!fh->do_crop]; + + mutex_unlock(&btv->lock); + + if (width < c.min_scaled_width || + width > c.max_scaled_width || + height < c.min_scaled_height) return -EINVAL; + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + /* btv->crop counts frame lines. Max. scale + factor is 16:1 for frames, 8:1 for fields. */ + if (height * 2 > c.max_scaled_height) + return -EINVAL; + break; + + default: + if (height > c.max_scaled_height) + return -EINVAL; + break; + } + buf->vb.size = (width * height * fmt->depth) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -1505,12 +1821,17 @@ static int bttv_prepare_buffer(struct videobuf_queue *q,struct bttv *btv, /* alloc + fill struct bttv_buffer (if changed) */ if (buf->vb.width != width || buf->vb.height != height || buf->vb.field != field || - buf->tvnorm != btv->tvnorm || buf->fmt != fmt) { + buf->tvnorm != norm || buf->fmt != fmt || + buf->crop.top != c.rect.top || + buf->crop.left != c.rect.left || + buf->crop.width != c.rect.width || + buf->crop.height != c.rect.height) { buf->vb.width = width; buf->vb.height = height; buf->vb.field = field; - buf->tvnorm = btv->tvnorm; + buf->tvnorm = norm; buf->fmt = fmt; + buf->crop = c.rect; redo_dma_risc = 1; } @@ -1577,7 +1898,7 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); struct bttv_fh *fh = q->priv_data; - bttv_dma_free(&fh->cap,fh->btv,buf); + bttv_dma_free(q,fh->btv,buf); } static struct videobuf_queue_ops bttv_video_qops = { @@ -1939,11 +2260,179 @@ static int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) return 0; } -static int verify_window(const struct bttv_tvnorm *tvn, - struct v4l2_window *win, int fixup) +/* Given cropping boundaries b and the scaled width and height of a + single field or frame, which must not exceed hardware limits, this + function adjusts the cropping parameters c. */ +static void +bttv_crop_adjust (struct bttv_crop * c, + const struct v4l2_rect * b, + __s32 width, + __s32 height, + enum v4l2_field field) +{ + __s32 frame_height = height << !V4L2_FIELD_HAS_BOTH(field); + __s32 max_left; + __s32 max_top; + + if (width < c->min_scaled_width) { + /* Max. hor. scale factor 16:1. */ + c->rect.width = width * 16; + } else if (width > c->max_scaled_width) { + /* Min. hor. scale factor 1:1. */ + c->rect.width = width; + + max_left = b->left + b->width - width; + max_left = min(max_left, (__s32) MAX_HDELAY); + if (c->rect.left > max_left) + c->rect.left = max_left; + } + + if (height < c->min_scaled_height) { + /* Max. vert. scale factor 16:1, single fields 8:1. */ + c->rect.height = height * 16; + } else if (frame_height > c->max_scaled_height) { + /* Min. vert. scale factor 1:1. + Top and height count field lines times two. */ + c->rect.height = (frame_height + 1) & ~1; + + max_top = b->top + b->height - c->rect.height; + if (c->rect.top > max_top) + c->rect.top = max_top; + } + + bttv_crop_calc_limits(c); +} + +/* Returns an error if scaling to a frame or single field with the given + width and height is not possible with the current cropping parameters + and width aligned according to width_mask. If adjust_size is TRUE the + function may adjust the width and/or height instead, rounding width + to (width + width_bias) & width_mask. If adjust_crop is TRUE it may + also adjust the current cropping parameters to get closer to the + desired image size. */ +static int +limit_scaled_size (struct bttv_fh * fh, + __s32 * width, + __s32 * height, + enum v4l2_field field, + unsigned int width_mask, + unsigned int width_bias, + int adjust_size, + int adjust_crop) +{ + struct bttv *btv = fh->btv; + const struct v4l2_rect *b; + struct bttv_crop *c; + __s32 min_width; + __s32 min_height; + __s32 max_width; + __s32 max_height; + int rc; + + BUG_ON((int) width_mask >= 0 || + width_bias >= (unsigned int) -width_mask); + + /* Make sure tvnorm, vbi_end and the current cropping parameters + remain consistent until we're done. */ + mutex_lock(&btv->lock); + + b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; + + /* Do crop - use current, don't - use default parameters. */ + c = &btv->crop[!!fh->do_crop]; + + if (fh->do_crop + && adjust_size + && adjust_crop + && !locked_btres(btv, VIDEO_RESOURCES)) { + min_width = 48; + min_height = 32; + + /* We cannot scale up. When the scaled image is larger + than crop.rect we adjust the crop.rect as required + by the V4L2 spec, hence cropcap.bounds are our limit. */ + max_width = min(b->width, (__s32) MAX_HACTIVE); + max_height = b->height; + + /* We cannot capture the same line as video and VBI data. + Note btv->vbi_end is really a minimum, see + bttv_vbi_try_fmt(). */ + if (btv->vbi_end > b->top) { + max_height -= btv->vbi_end - b->top; + rc = -EBUSY; + if (min_height > max_height) + goto fail; + } + } else { + rc = -EBUSY; + if (btv->vbi_end > c->rect.top) + goto fail; + + min_width = c->min_scaled_width; + min_height = c->min_scaled_height; + max_width = c->max_scaled_width; + max_height = c->max_scaled_height; + + adjust_crop = 0; + } + + min_width = (min_width - width_mask - 1) & width_mask; + max_width = max_width & width_mask; + + /* Max. scale factor is 16:1 for frames, 8:1 for fields. */ + min_height = min_height; + /* Min. scale factor is 1:1. */ + max_height >>= !V4L2_FIELD_HAS_BOTH(field); + + if (adjust_size) { + *width = clamp(*width, min_width, max_width); + *height = clamp(*height, min_height, max_height); + + /* Round after clamping to avoid overflow. */ + *width = (*width + width_bias) & width_mask; + + if (adjust_crop) { + bttv_crop_adjust(c, b, *width, *height, field); + + if (btv->vbi_end > c->rect.top) { + /* Move the crop window out of the way. */ + c->rect.top = btv->vbi_end; + } + } + } else { + rc = -EINVAL; + if (*width < min_width || + *height < min_height || + *width > max_width || + *height > max_height || + 0 != (*width & ~width_mask)) + goto fail; + } + + rc = 0; /* success */ + + fail: + mutex_unlock(&btv->lock); + + return rc; +} + +/* Returns an error if the given overlay window dimensions are not + possible with the current cropping parameters. If adjust_size is + TRUE the function may adjust the window width and/or height + instead, however it always rounds the horizontal position and + width as btcx_align() does. If adjust_crop is TRUE the function + may also adjust the current cropping parameters to get closer + to the desired window size. */ +static int +verify_window (struct bttv_fh * fh, + struct v4l2_window * win, + int adjust_size, + int adjust_crop) { enum v4l2_field field; - int maxw, maxh; + unsigned int width_mask; + int rc; if (win->w.width < 48 || win->w.height < 32) return -EINVAL; @@ -1951,32 +2440,52 @@ static int verify_window(const struct bttv_tvnorm *tvn, return -EINVAL; field = win->field; - maxw = tvn->swidth; - maxh = tvn->sheight; if (V4L2_FIELD_ANY == field) { - field = (win->w.height > maxh/2) + __s32 height2; + + height2 = fh->btv->crop[!!fh->do_crop].rect.height >> 1; + field = (win->w.height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; } switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; case V4L2_FIELD_INTERLACED: break; default: return -EINVAL; } - if (!fixup && (win->w.width > maxw || win->w.height > maxh)) + /* 4-byte alignment. */ + if (NULL == fh->ovfmt) return -EINVAL; + width_mask = ~0; + switch (fh->ovfmt->depth) { + case 8: + case 24: + width_mask = ~3; + break; + case 16: + width_mask = ~1; + break; + case 32: + break; + default: + BUG(); + } + + win->w.width -= win->w.left & ~width_mask; + win->w.left = (win->w.left - width_mask - 1) & width_mask; + + rc = limit_scaled_size(fh, &win->w.width, &win->w.height, + field, width_mask, + /* width_bias: round down */ 0, + adjust_size, adjust_crop); + if (0 != rc) + return rc; - if (win->w.width > maxw) - win->w.width = maxw; - if (win->w.height > maxh) - win->w.height = maxh; win->field = field; return 0; } @@ -1991,7 +2500,9 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, return -EINVAL; if (!(fh->ovfmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; - retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); + retval = verify_window(fh, win, + /* adjust_size */ fixup, + /* adjust_crop */ fixup); if (0 != retval) return retval; @@ -2048,6 +2559,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); retval = bttv_switch_overlay(btv,fh,new); } @@ -2080,7 +2592,7 @@ static int bttv_resource(struct bttv_fh *fh) switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - res = RESOURCE_VIDEO; + res = RESOURCE_VIDEO_STREAM; break; case V4L2_BUF_TYPE_VBI_CAPTURE: res = RESOURCE_VBI; @@ -2138,7 +2650,7 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) f->fmt.win.field = fh->ov.field; return 0; case V4L2_BUF_TYPE_VBI_CAPTURE: - bttv_vbi_get_fmt(fh,f); + bttv_vbi_get_fmt(fh, &f->fmt.vbi); return 0; default: return -EINVAL; @@ -2146,35 +2658,35 @@ static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) } static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, - struct v4l2_format *f) + struct v4l2_format *f, int adjust_crop) { switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { const struct bttv_format *fmt; enum v4l2_field field; - unsigned int maxw,maxh; + __s32 width, height; + int rc; fmt = format_by_fourcc(f->fmt.pix.pixelformat); if (NULL == fmt) return -EINVAL; - /* fixup format */ - maxw = bttv_tvnorms[btv->tvnorm].swidth; - maxh = bttv_tvnorms[btv->tvnorm].sheight; field = f->fmt.pix.field; - if (V4L2_FIELD_ANY == field) - field = (f->fmt.pix.height > maxh/2) + if (V4L2_FIELD_ANY == field) { + __s32 height2; + + height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + field = (f->fmt.pix.height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; + } if (V4L2_FIELD_SEQ_BT == field) field = V4L2_FIELD_SEQ_TB; switch (field) { case V4L2_FIELD_TOP: case V4L2_FIELD_BOTTOM: case V4L2_FIELD_ALTERNATE: - maxh = maxh/2; - break; case V4L2_FIELD_INTERLACED: break; case V4L2_FIELD_SEQ_TB: @@ -2185,28 +2697,29 @@ static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, return -EINVAL; } + width = f->fmt.pix.width; + height = f->fmt.pix.height; + + rc = limit_scaled_size(fh, &width, &height, field, + /* width_mask: 4 pixels */ ~3, + /* width_bias: nearest */ 2, + /* adjust_size */ 1, + adjust_crop); + if (0 != rc) + return rc; + /* update data for the application */ f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - pix_format_set_size (&f->fmt.pix, fmt, - f->fmt.pix.width & ~3, - f->fmt.pix.height); + pix_format_set_size(&f->fmt.pix, fmt, width, height); return 0; } case V4L2_BUF_TYPE_VIDEO_OVERLAY: - return verify_window(&bttv_tvnorms[btv->tvnorm], - &f->fmt.win, 1); + return verify_window(fh, &f->fmt.win, + /* adjust_size */ 1, + /* adjust_crop */ 0); case V4L2_BUF_TYPE_VBI_CAPTURE: - bttv_vbi_try_fmt(fh,f); - return 0; + return bttv_vbi_try_fmt(fh, &f->fmt.vbi); default: return -EINVAL; } @@ -2225,7 +2738,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, retval = bttv_switch_type(fh,f->type); if (0 != retval) return retval; - retval = bttv_try_fmt(fh,btv,f); + retval = bttv_try_fmt(fh,btv,f, /* adjust_crop */ 1); if (0 != retval) return retval; fmt = format_by_fourcc(f->fmt.pix.pixelformat); @@ -2254,12 +2767,7 @@ static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, retval = bttv_switch_type(fh,f->type); if (0 != retval) return retval; - if (locked_btres(fh->btv, RESOURCE_VBI)) - return -EBUSY; - bttv_vbi_try_fmt(fh,f); - bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); - bttv_vbi_get_fmt(fh,f); - return 0; + return bttv_vbi_set_fmt(fh, &f->fmt.vbi); default: return -EINVAL; } @@ -2517,6 +3025,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, if (*on) { fh->ov.tvnorm = btv->tvnorm; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); } else { new = NULL; @@ -2551,10 +3060,16 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, struct video_mmap *vm = arg; struct bttv_buffer *buf; enum v4l2_field field; + __s32 height2; + int res; if (vm->frame >= VIDEO_MAX_FRAME) return -EINVAL; + res = bttv_resource(fh); + if (!check_alloc_btres(btv, fh, res)) + return -EBUSY; + mutex_lock(&fh->cap.lock); retval = -EINVAL; buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame]; @@ -2566,7 +3081,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, buf->vb.state == STATE_ACTIVE) goto fh_unlock_and_return; - field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) + height2 = btv->crop[!!fh->do_crop].rect.height >> 1; + field = (vm->height > height2) ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; retval = bttv_prepare_buffer(&fh->cap,btv,buf, @@ -2613,54 +3129,17 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, } case VIDIOCGVBIFMT: - { - struct vbi_format *fmt = (void *) arg; - struct v4l2_format fmt2; - if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) { retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); if (0 != retval) return retval; } - bttv_vbi_get_fmt(fh, &fmt2); - - memset(fmt,0,sizeof(*fmt)); - fmt->sampling_rate = fmt2.fmt.vbi.sampling_rate; - fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line; - fmt->sample_format = VIDEO_PALETTE_RAW; - fmt->start[0] = fmt2.fmt.vbi.start[0]; - fmt->count[0] = fmt2.fmt.vbi.count[0]; - fmt->start[1] = fmt2.fmt.vbi.start[1]; - fmt->count[1] = fmt2.fmt.vbi.count[1]; - if (fmt2.fmt.vbi.flags & V4L2_VBI_UNSYNC) - fmt->flags |= VBI_UNSYNC; - if (fmt2.fmt.vbi.flags & V4L2_VBI_INTERLACED) - fmt->flags |= VBI_INTERLACED; - return 0; - } - case VIDIOCSVBIFMT: - { - struct vbi_format *fmt = (void *) arg; - struct v4l2_format fmt2; - retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); - if (0 != retval) - return retval; - bttv_vbi_get_fmt(fh, &fmt2); - - if (fmt->sampling_rate != fmt2.fmt.vbi.sampling_rate || - fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line || - fmt->sample_format != VIDEO_PALETTE_RAW || - fmt->start[0] != fmt2.fmt.vbi.start[0] || - fmt->start[1] != fmt2.fmt.vbi.start[1] || - fmt->count[0] != fmt->count[1] || - fmt->count[0] < 1 || - fmt->count[0] > 32 /* VBI_MAXLINES */) - return -EINVAL; + /* fall through */ - bttv_vbi_setlines(fh,btv,fmt->count[0]); - return 0; - } + case VIDIOCSVBIFMT: + return v4l_compat_translate_ioctl(inode, file, cmd, + arg, bttv_do_ioctl); case BTTV_VERSION: case VIDIOCGFREQ: @@ -2753,7 +3232,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_TRY_FMT: { struct v4l2_format *f = arg; - return bttv_try_fmt(fh,btv,f); + return bttv_try_fmt(fh,btv,f, /* adjust_crop */ 0); } case VIDIOC_G_FMT: { @@ -2792,16 +3271,23 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) return -EINVAL; - mutex_lock(&fh->cap.lock); retval = -EINVAL; if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { - if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) - goto fh_unlock_and_return; - if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight) - goto fh_unlock_and_return; + __s32 width = fb->fmt.width; + __s32 height = fb->fmt.height; + + retval = limit_scaled_size(fh, &width, &height, + V4L2_FIELD_INTERLACED, + /* width_mask */ ~3, + /* width_bias */ 2, + /* adjust_size */ 0, + /* adjust_crop */ 0); + if (0 != retval) + return retval; } /* ok, accept it */ + mutex_lock(&fh->cap.lock); btv->fbuf.base = fb->base; btv->fbuf.fmt.width = fb->fmt.width; btv->fbuf.fmt.height = fb->fmt.height; @@ -2828,6 +3314,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, struct bttv_buffer *new; new = videobuf_alloc(sizeof(*new)); + new->crop = btv->crop[!!fh->do_crop].rect; bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); retval = bttv_switch_overlay(btv,fh,new); } @@ -2843,7 +3330,13 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return videobuf_querybuf(bttv_queue(fh),arg); case VIDIOC_QBUF: + { + int res = bttv_resource(fh); + + if (!check_alloc_btres(btv, fh, res)) + return -EBUSY; return videobuf_qbuf(bttv_queue(fh),arg); + } case VIDIOC_DQBUF: return videobuf_dqbuf(bttv_queue(fh),arg, @@ -2942,6 +3435,122 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, return v4l2_prio_change(&btv->prio, &fh->prio, *prio); } + case VIDIOC_CROPCAP: + { + struct v4l2_cropcap *cap = arg; + enum v4l2_buf_type type; + + type = cap->type; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + *cap = bttv_tvnorms[btv->tvnorm].cropcap; + cap->type = type; + + return 0; + } + case VIDIOC_G_CROP: + { + struct v4l2_crop * crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + /* No fh->do_crop = 1; because btv->crop[1] may be + inconsistent with fh->width or fh->height and apps + do not expect a change here. */ + + crop->c = btv->crop[!!fh->do_crop].rect; + + return 0; + } + case VIDIOC_S_CROP: + { + struct v4l2_crop *crop = arg; + const struct v4l2_rect *b; + struct bttv_crop c; + __s32 b_left; + __s32 b_top; + __s32 b_right; + __s32 b_bottom; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + crop->type != V4L2_BUF_TYPE_VIDEO_OVERLAY) + return -EINVAL; + + retval = v4l2_prio_check(&btv->prio,&fh->prio); + if (0 != retval) + return retval; + + /* Make sure tvnorm, vbi_end and the current cropping + parameters remain consistent until we're done. Note + read() may change vbi_end in check_alloc_btres(). */ + mutex_lock(&btv->lock); + + retval = -EBUSY; + + if (locked_btres(fh->btv, VIDEO_RESOURCES)) + goto btv_unlock_and_return; + + b = &bttv_tvnorms[btv->tvnorm].cropcap.bounds; + + b_left = b->left; + b_right = b_left + b->width; + b_bottom = b->top + b->height; + + b_top = max(b->top, btv->vbi_end); + if (b_top + 32 >= b_bottom) + goto btv_unlock_and_return; + + /* Min. scaled size 48 x 32. */ + c.rect.left = clamp(crop->c.left, b_left, b_right - 48); + c.rect.left = min(c.rect.left, (__s32) MAX_HDELAY); + + c.rect.width = clamp(crop->c.width, + 48, b_right - c.rect.left); + + c.rect.top = clamp(crop->c.top, b_top, b_bottom - 32); + /* Top and height must be a multiple of two. */ + c.rect.top = (c.rect.top + 1) & ~1; + + c.rect.height = clamp(crop->c.height, + 32, b_bottom - c.rect.top); + c.rect.height = (c.rect.height + 1) & ~1; + + bttv_crop_calc_limits(&c); + + btv->crop[1] = c; + + mutex_unlock(&btv->lock); + + fh->do_crop = 1; + + mutex_lock(&fh->cap.lock); + + if (fh->width < c.min_scaled_width) { + fh->width = c.min_scaled_width; + btv->init.width = c.min_scaled_width; + } else if (fh->width > c.max_scaled_width) { + fh->width = c.max_scaled_width; + btv->init.width = c.max_scaled_width; + } + + if (fh->height < c.min_scaled_height) { + fh->height = c.min_scaled_height; + btv->init.height = c.min_scaled_height; + } else if (fh->height > c.max_scaled_height) { + fh->height = c.max_scaled_height; + btv->init.height = c.max_scaled_height; + } + + mutex_unlock(&fh->cap.lock); + + return 0; + } + case VIDIOC_ENUMSTD: case VIDIOC_G_STD: case VIDIOC_S_STD: @@ -2963,6 +3572,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, fh_unlock_and_return: mutex_unlock(&fh->cap.lock); return retval; + + btv_unlock_and_return: + mutex_unlock(&btv->lock); + return retval; } static int bttv_ioctl(struct inode *inode, struct file *file, @@ -2972,8 +3585,26 @@ static int bttv_ioctl(struct inode *inode, struct file *file, switch (cmd) { case BTTV_VBISIZE: + { + const struct bttv_tvnorm *tvnorm; + + tvnorm = fh->vbi_fmt.tvnorm; + + if (fh->vbi_fmt.fmt.start[0] != tvnorm->vbistart[0] || + fh->vbi_fmt.fmt.start[1] != tvnorm->vbistart[1] || + fh->vbi_fmt.fmt.count[0] != fh->vbi_fmt.fmt.count[1]) { + /* BTTV_VBISIZE cannot express these parameters, + however open() resets the paramters to defaults + and apps shouldn't call BTTV_VBISIZE after + VIDIOC_S_FMT. */ + return -EINVAL; + } + bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); - return fh->lines * 2 * 2048; + return (fh->vbi_fmt.fmt.count[0] * 2 + * fh->vbi_fmt.fmt.samples_per_line); + } + default: return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); } @@ -2992,10 +3623,14 @@ static ssize_t bttv_read(struct file *file, char __user *data, switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (locked_btres(fh->btv,RESOURCE_VIDEO)) + if (!check_alloc_btres(fh->btv, fh, RESOURCE_VIDEO_READ)) { + /* VIDEO_READ in use by another fh, + or VIDEO_STREAM by any fh. */ return -EBUSY; + } retval = videobuf_read_one(&fh->cap, data, count, ppos, file->f_flags & O_NONBLOCK); + free_btres(fh->btv, fh, RESOURCE_VIDEO_READ); break; case V4L2_BUF_TYPE_VBI_CAPTURE: if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) @@ -3021,7 +3656,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) return videobuf_poll_stream(file, &fh->vbi, wait); } - if (check_btres(fh,RESOURCE_VIDEO)) { + if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { /* streaming capture */ if (list_empty(&fh->cap.stream)) return POLLERR; @@ -3031,7 +3666,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) mutex_lock(&fh->cap.lock); if (NULL == fh->cap.read_buf) { /* need to capture a new frame */ - if (locked_btres(fh->btv,RESOURCE_VIDEO)) { + if (locked_btres(fh->btv,RESOURCE_VIDEO_STREAM)) { mutex_unlock(&fh->cap.lock); return POLLERR; } @@ -3117,8 +3752,23 @@ static int bttv_open(struct inode *inode, struct file *file) i2c_vidiocschan(btv); btv->users++; - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) - bttv_vbi_setlines(fh,btv,16); + + /* The V4L2 spec requires one global set of cropping parameters + which only change on request. These are stored in btv->crop[1]. + However for compatibility with V4L apps and cropping unaware + V4L2 apps we now reset the cropping parameters as seen through + this fh, which is to say VIDIOC_G_CROP and scaling limit checks + will use btv->crop[0], the default cropping parameters for the + current video standard, and VIDIOC_S_FMT will not implicitely + change the cropping parameters until VIDIOC_S_CROP has been + called. */ + fh->do_crop = !reset_crop; /* module parameter */ + + /* Likewise there should be one global set of VBI capture + parameters, but for compatibility with V4L apps and earlier + driver versions each fh has its own parameters. */ + bttv_vbi_fmt_reset(&fh->vbi_fmt, btv->tvnorm); + bttv_field_count(btv); return 0; } @@ -3133,14 +3783,17 @@ static int bttv_release(struct inode *inode, struct file *file) bttv_switch_overlay(btv,fh,NULL); /* stop video capture */ - if (check_btres(fh, RESOURCE_VIDEO)) { + if (check_btres(fh, RESOURCE_VIDEO_STREAM)) { videobuf_streamoff(&fh->cap); - free_btres(btv,fh,RESOURCE_VIDEO); + free_btres(btv,fh,RESOURCE_VIDEO_STREAM); } if (fh->cap.read_buf) { buffer_release(&fh->cap,fh->cap.read_buf); kfree(fh->cap.read_buf); } + if (check_btres(fh, RESOURCE_VIDEO_READ)) { + free_btres(btv, fh, RESOURCE_VIDEO_READ); + } /* stop vbi capture */ if (check_btres(fh, RESOURCE_VBI)) { @@ -3997,7 +4650,6 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* initialize structs / fill in defaults */ mutex_init(&btv->lock); - mutex_init(&btv->reslock); spin_lock_init(&btv->s_lock); spin_lock_init(&btv->gpio_lock); init_waitqueue_head(&btv->gpioq); @@ -4095,7 +4747,6 @@ static int __devinit bttv_probe(struct pci_dev *dev, btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24); btv->init.width = 320; btv->init.height = 240; - btv->init.lines = 16; btv->input = 0; /* initialize hardware */ @@ -4130,11 +4781,17 @@ static int __devinit bttv_probe(struct pci_dev *dev, bt848_sat(btv,32768); audio_mute(btv, 1); set_input(btv,0); + bttv_crop_reset(&btv->crop[0], btv->tvnorm); + btv->crop[1] = btv->crop[0]; /* current = default */ + disclaim_vbi_lines(btv); + disclaim_video_lines(btv); } - /* add subdevices */ - if (bttv_tvcards[btv->c.type].has_dvb) + /* add subdevices and autoload dvb-bt8xx if needed */ + if (bttv_tvcards[btv->c.type].has_dvb) { bttv_sub_add_device(&btv->c, "dvb"); + request_modules(btv); + } bttv_input_init(btv); diff --git a/drivers/media/video/bt8xx/bttv-gpio.c b/drivers/media/video/bt8xx/bttv-gpio.c index ba081f6..84154c2 100644 --- a/drivers/media/video/bt8xx/bttv-gpio.c +++ b/drivers/media/video/bt8xx/bttv-gpio.c @@ -71,7 +71,6 @@ struct bus_type bttv_sub_bus_type = { .probe = bttv_sub_probe, .remove = bttv_sub_remove, }; -EXPORT_SYMBOL(bttv_sub_bus_type); static void release_sub_device(struct device *dev) { @@ -152,7 +151,6 @@ void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits) btwrite(data,BT848_GPIO_OUT_EN); spin_unlock_irqrestore(&btv->gpio_lock,flags); } -EXPORT_SYMBOL(bttv_gpio_inout); u32 bttv_gpio_read(struct bttv_core *core) { @@ -162,7 +160,6 @@ u32 bttv_gpio_read(struct bttv_core *core) value = btread(BT848_GPIO_DATA); return value; } -EXPORT_SYMBOL(bttv_gpio_read); void bttv_gpio_write(struct bttv_core *core, u32 value) { @@ -170,7 +167,6 @@ void bttv_gpio_write(struct bttv_core *core, u32 value) btwrite(value,BT848_GPIO_DATA); } -EXPORT_SYMBOL(bttv_gpio_write); void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) { @@ -185,7 +181,6 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) btwrite(data,BT848_GPIO_DATA); spin_unlock_irqrestore(&btv->gpio_lock,flags); } -EXPORT_SYMBOL(bttv_gpio_bits); /* * Local variables: diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 62b8730..0dfa49b 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -412,7 +412,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c) unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c index 19b564a..ecf0798 100644 --- a/drivers/media/video/bt8xx/bttv-if.c +++ b/drivers/media/video/bt8xx/bttv-if.c @@ -33,32 +33,16 @@ #include "bttvp.h" -EXPORT_SYMBOL(bttv_get_cardinfo); EXPORT_SYMBOL(bttv_get_pcidev); -EXPORT_SYMBOL(bttv_get_id); EXPORT_SYMBOL(bttv_gpio_enable); EXPORT_SYMBOL(bttv_read_gpio); EXPORT_SYMBOL(bttv_write_gpio); -EXPORT_SYMBOL(bttv_get_gpio_queue); -EXPORT_SYMBOL(bttv_i2c_call); /* ----------------------------------------------------------------------- */ /* Exported functions - for other modules which want to access the */ /* gpio ports (IR for example) */ /* see bttv.h for comments */ -int bttv_get_cardinfo(unsigned int card, int *type, unsigned *cardid) -{ - printk("The bttv_* interface is obsolete and will go away,\n" - "please use the new, sysfs based interface instead.\n"); - if (card >= bttv_num) { - return -1; - } - *type = bttvs[card].c.type; - *cardid = bttvs[card].cardid; - return 0; -} - struct pci_dev* bttv_get_pcidev(unsigned int card) { if (card >= bttv_num) @@ -66,16 +50,6 @@ struct pci_dev* bttv_get_pcidev(unsigned int card) return bttvs[card].c.pci; } -int bttv_get_id(unsigned int card) -{ - printk("The bttv_* interface is obsolete and will go away,\n" - "please use the new, sysfs based interface instead.\n"); - if (card >= bttv_num) { - return -1; - } - return bttvs[card].c.type; -} - int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) { @@ -130,28 +104,6 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) return 0; } -wait_queue_head_t* bttv_get_gpio_queue(unsigned int card) -{ - struct bttv *btv; - - if (card >= bttv_num) { - return NULL; - } - - btv = &bttvs[card]; - if (bttvs[card].shutdown) { - return NULL; - } - return &btv->gpioq; -} - -void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) -{ - if (card >= bttv_num) - return; - bttv_call_i2c_clients(&bttvs[card], cmd, arg); -} - /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index cbc012f..6f74c80 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -36,13 +36,18 @@ module_param(repeat_delay, int, 0644); static int repeat_period = 33; module_param(repeat_period, int, 0644); +static int ir_rc5_remote_gap = 885; +module_param(ir_rc5_remote_gap, int, 0644); +static int ir_rc5_key_timeout = 200; +module_param(ir_rc5_key_timeout, int, 0644); + #define DEVNAME "bttv-input" /* ---------------------------------------------------------------------- */ static void ir_handle_key(struct bttv *btv) { - struct bttv_ir *ir = btv->remote; + struct card_ir *ir = btv->remote; u32 gpio,data; /* read gpio value */ @@ -72,7 +77,7 @@ static void ir_handle_key(struct bttv *btv) void bttv_input_irq(struct bttv *btv) { - struct bttv_ir *ir = btv->remote; + struct card_ir *ir = btv->remote; if (!ir->polling) ir_handle_key(btv); @@ -81,65 +86,21 @@ void bttv_input_irq(struct bttv *btv) static void bttv_input_timer(unsigned long data) { struct bttv *btv = (struct bttv*)data; - struct bttv_ir *ir = btv->remote; - unsigned long timeout; + struct card_ir *ir = btv->remote; ir_handle_key(btv); - timeout = jiffies + (ir->polling * HZ / 1000); - mod_timer(&ir->timer, timeout); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); } /* ---------------------------------------------------------------*/ -static int rc5_remote_gap = 885; -module_param(rc5_remote_gap, int, 0644); -static int rc5_key_timeout = 200; -module_param(rc5_key_timeout, int, 0644); - -#define RC5_START(x) (((x)>>12)&3) -#define RC5_TOGGLE(x) (((x)>>11)&1) -#define RC5_ADDR(x) (((x)>>6)&31) -#define RC5_INSTR(x) ((x)&63) - -/* decode raw bit pattern to RC5 code */ -static u32 rc5_decode(unsigned int code) -{ - unsigned int org_code = code; - unsigned int pair; - unsigned int rc5 = 0; - int i; - - code = (code << 1) | 1; - for (i = 0; i < 14; ++i) { - pair = code & 0x3; - code >>= 2; - - rc5 <<= 1; - switch (pair) { - case 0: - case 2: - break; - case 1: - rc5 |= 1; - break; - case 3: - dprintk(KERN_WARNING "bad code: %x\n", org_code); - return 0; - } - } - dprintk(KERN_WARNING "code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " - "instr=%x\n", rc5, org_code, RC5_START(rc5), - RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); - return rc5; -} - static int bttv_rc5_irq(struct bttv *btv) { - struct bttv_ir *ir = btv->remote; + struct card_ir *ir = btv->remote; struct timeval tv; u32 gpio; u32 gap; - unsigned long current_jiffies, timeout; + unsigned long current_jiffies; /* read gpio port */ gpio = bttv_gpio_read(&btv->c); @@ -165,8 +126,8 @@ static int bttv_rc5_irq(struct bttv *btv) /* only if in the code (otherwise spurious IRQ or timer late) */ if (ir->last_bit < 28) { - ir->last_bit = (gap - rc5_remote_gap / 2) / - rc5_remote_gap; + ir->last_bit = (gap - ir_rc5_remote_gap / 2) / + ir_rc5_remote_gap; ir->code |= 1 << ir->last_bit; } /* starting new code */ @@ -176,8 +137,8 @@ static int bttv_rc5_irq(struct bttv *btv) ir->base_time = tv; ir->last_bit = 0; - timeout = current_jiffies + (500 + 30 * HZ) / 1000; - mod_timer(&ir->timer_end, timeout); + mod_timer(&ir->timer_end, + current_jiffies + msecs_to_jiffies(30)); } /* toggle GPIO pin 4 to reset the irq */ @@ -186,96 +147,28 @@ static int bttv_rc5_irq(struct bttv *btv) return 1; } - -static void bttv_rc5_timer_end(unsigned long data) -{ - struct bttv_ir *ir = (struct bttv_ir *)data; - struct timeval tv; - unsigned long current_jiffies, timeout; - u32 gap; - - /* get time */ - current_jiffies = jiffies; - do_gettimeofday(&tv); - - /* avoid overflow with gap >1s */ - if (tv.tv_sec - ir->base_time.tv_sec > 1) { - gap = 200000; - } else { - gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + - tv.tv_usec - ir->base_time.tv_usec; - } - - /* Allow some timmer jitter (RC5 is ~24ms anyway so this is ok) */ - if (gap < 28000) { - dprintk(KERN_WARNING "spurious timer_end\n"); - return; - } - - ir->active = 0; - if (ir->last_bit < 20) { - /* ignore spurious codes (caused by light/other remotes) */ - dprintk(KERN_WARNING "short code: %x\n", ir->code); - } else { - u32 rc5 = rc5_decode(ir->code); - - /* two start bits? */ - if (RC5_START(rc5) != 3) { - dprintk(KERN_WARNING "rc5 start bits invalid: %u\n", RC5_START(rc5)); - - /* right address? */ - } else if (RC5_ADDR(rc5) == 0x0) { - u32 toggle = RC5_TOGGLE(rc5); - u32 instr = RC5_INSTR(rc5); - - /* Good code, decide if repeat/repress */ - if (toggle != RC5_TOGGLE(ir->last_rc5) || - instr != RC5_INSTR(ir->last_rc5)) { - dprintk(KERN_WARNING "instruction %x, toggle %x\n", instr, - toggle); - ir_input_nokey(ir->dev, &ir->ir); - ir_input_keydown(ir->dev, &ir->ir, instr, - instr); - } - - /* Set/reset key-up timer */ - timeout = current_jiffies + (500 + rc5_key_timeout - * HZ) / 1000; - mod_timer(&ir->timer_keyup, timeout); - - /* Save code for repeat test */ - ir->last_rc5 = rc5; - } - } -} - -static void bttv_rc5_timer_keyup(unsigned long data) -{ - struct bttv_ir *ir = (struct bttv_ir *)data; - - dprintk(KERN_DEBUG "key released\n"); - ir_input_nokey(ir->dev, &ir->ir); -} - /* ---------------------------------------------------------------------- */ -static void bttv_ir_start(struct bttv *btv, struct bttv_ir *ir) +static void bttv_ir_start(struct bttv *btv, struct card_ir *ir) { if (ir->polling) { - init_timer(&ir->timer); - ir->timer.function = bttv_input_timer; - ir->timer.data = (unsigned long)btv; + setup_timer(&ir->timer, bttv_input_timer, (unsigned long)btv); ir->timer.expires = jiffies + HZ; add_timer(&ir->timer); } else if (ir->rc5_gpio) { /* set timer_end for code completion */ init_timer(&ir->timer_end); - ir->timer_end.function = bttv_rc5_timer_end; + ir->timer_end.function = ir_rc5_timer_end; ir->timer_end.data = (unsigned long)ir; init_timer(&ir->timer_keyup); - ir->timer_keyup.function = bttv_rc5_timer_keyup; + ir->timer_keyup.function = ir_rc5_timer_keyup; ir->timer_keyup.data = (unsigned long)ir; + ir->shift_by = 1; + ir->start = 3; + ir->addr = 0x0; + ir->rc5_key_timeout = ir_rc5_key_timeout; + ir->rc5_remote_gap = ir_rc5_remote_gap; } } @@ -299,7 +192,7 @@ static void bttv_ir_stop(struct bttv *btv) int bttv_input_init(struct bttv *btv) { - struct bttv_ir *ir; + struct card_ir *ir; IR_KEYTAB_TYPE *ir_codes = NULL; struct input_dev *input_dev; int ir_type = IR_TYPE_OTHER; diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c index afcfe71..e7104d9 100644 --- a/drivers/media/video/bt8xx/bttv-risc.c +++ b/drivers/media/video/bt8xx/bttv-risc.c @@ -43,7 +43,8 @@ int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int offset, unsigned int bpl, - unsigned int padding, unsigned int lines) + unsigned int padding, unsigned int skip_lines, + unsigned int store_lines) { u32 instructions,line,todo; struct scatterlist *sg; @@ -54,9 +55,11 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, one write per scan line + sync + jump (all 2 dwords). padding can cause next bpl to start close to a page border. First DMA region may be smaller than PAGE_SIZE */ - instructions = 1 + ((bpl + padding) * lines) / PAGE_SIZE + lines; - instructions += 2; - if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) + instructions = skip_lines * 4; + instructions += (1 + ((bpl + padding) * store_lines) + / PAGE_SIZE + store_lines) * 8; + instructions += 2 * 8; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions)) < 0) return rc; /* sync instruction */ @@ -64,11 +67,16 @@ bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(rp++) = cpu_to_le32(0); + while (skip_lines-- > 0) { + *(rp++) = cpu_to_le32(BT848_RISC_SKIP | BT848_RISC_SOL | + BT848_RISC_EOL | bpl); + } + /* scan lines */ sg = sglist; - for (line = 0; line < lines; line++) { + for (line = 0; line < store_lines; line++) { if ((btv->opt_vcr_hack) && - (line >= (lines - VCR_HACK_LINES))) + (line >= (store_lines - VCR_HACK_LINES))) continue; while (offset && offset >= sg_dma_len(sg)) { offset -= sg_dma_len(sg); @@ -130,7 +138,8 @@ bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, /* estimate risc mem: worst case is one write per page border + one write per scan line (5 dwords) plus sync + jump (2 dwords) */ - instructions = (ybpl * ylines * 2) / PAGE_SIZE + ylines; + instructions = ((3 + (ybpl + ypadding) * ylines * 2) + / PAGE_SIZE) + ylines; instructions += 2; if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0) return rc; @@ -317,10 +326,10 @@ bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, /* ---------------------------------------------------------- */ static void -bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, - int width, int height, int interleaved, int norm) +bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo, + int width, int height, int interleaved, + const struct bttv_tvnorm *tvnorm) { - const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm]; u32 xsf, sr; int vdelay; @@ -361,6 +370,62 @@ bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, } static void +bttv_calc_geo (struct bttv * btv, + struct bttv_geometry * geo, + unsigned int width, + unsigned int height, + int both_fields, + const struct bttv_tvnorm * tvnorm, + const struct v4l2_rect * crop) +{ + unsigned int c_width; + unsigned int c_height; + u32 sr; + + if ((crop->left == tvnorm->cropcap.defrect.left + && crop->top == tvnorm->cropcap.defrect.top + && crop->width == tvnorm->cropcap.defrect.width + && crop->height == tvnorm->cropcap.defrect.height + && width <= tvnorm->swidth /* see PAL-Nc et al */) + || bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) { + bttv_calc_geo_old(btv, geo, width, height, + both_fields, tvnorm); + return; + } + + /* For bug compatibility the image size checks permit scale + factors > 16. See bttv_crop_calc_limits(). */ + c_width = min((unsigned int) crop->width, width * 16); + c_height = min((unsigned int) crop->height, height * 16); + + geo->width = width; + geo->hscale = (c_width * 4096U + (width >> 1)) / width - 4096; + /* Even to store Cb first, odd for Cr. */ + geo->hdelay = ((crop->left * width + c_width) / c_width) & ~1; + + geo->sheight = c_height; + geo->vdelay = crop->top - tvnorm->cropcap.bounds.top + MIN_VDELAY; + sr = c_height >> !both_fields; + sr = (sr * 512U + (height >> 1)) / height - 512; + geo->vscale = (0x10000UL - sr) & 0x1fff; + geo->vscale |= both_fields ? (BT848_VSCALE_INT << 8) : 0; + geo->vtotal = tvnorm->vtotal; + + geo->crop = (((geo->width >> 8) & 0x03) | + ((geo->hdelay >> 6) & 0x0c) | + ((geo->sheight >> 4) & 0x30) | + ((geo->vdelay >> 2) & 0xc0)); + + if (btv->opt_combfilter) { + geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); + geo->comb = (width < 769) ? 1 : 0; + } else { + geo->vtc = 0; + geo->comb = 0; + } +} + +static void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd) { int off = odd ? 0x80 : 0x00; @@ -522,16 +587,51 @@ int bttv_buffer_activate_vbi(struct bttv *btv, struct bttv_buffer *vbi) { - /* vbi capture */ + struct btcx_riscmem *top; + struct btcx_riscmem *bottom; + int top_irq_flags; + int bottom_irq_flags; + + top = NULL; + bottom = NULL; + top_irq_flags = 0; + bottom_irq_flags = 0; + if (vbi) { + unsigned int crop, vdelay; + vbi->vb.state = STATE_ACTIVE; list_del(&vbi->vb.queue); - bttv_risc_hook(btv, RISC_SLOT_O_VBI, &vbi->top, 0); - bttv_risc_hook(btv, RISC_SLOT_E_VBI, &vbi->bottom, 4); - } else { - bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0); - bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0); + + /* VDELAY is start of video, end of VBI capturing. */ + crop = btread(BT848_E_CROP); + vdelay = btread(BT848_E_VDELAY_LO) + ((crop & 0xc0) << 2); + + if (vbi->geo.vdelay > vdelay) { + vdelay = vbi->geo.vdelay & 0xfe; + crop = (crop & 0x3f) | ((vbi->geo.vdelay >> 2) & 0xc0); + + btwrite(vdelay, BT848_E_VDELAY_LO); + btwrite(crop, BT848_E_CROP); + btwrite(vdelay, BT848_O_VDELAY_LO); + btwrite(crop, BT848_O_CROP); + } + + if (vbi->vbi_count[0] > 0) { + top = &vbi->top; + top_irq_flags = 4; + } + + if (vbi->vbi_count[1] > 0) { + top_irq_flags = 0; + bottom = &vbi->bottom; + bottom_irq_flags = 4; + } } + + bttv_risc_hook(btv, RISC_SLOT_O_VBI, top, top_irq_flags); + bttv_risc_hook(btv, RISC_SLOT_E_VBI, bottom, bottom_irq_flags); + return 0; } @@ -611,28 +711,31 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) int bpf = bpl * (buf->vb.height >> 1); bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, - V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm); + V4L2_FIELD_HAS_BOTH(buf->vb.field), + tvnorm,&buf->crop); switch (buf->vb.field) { case V4L2_FIELD_TOP: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height); + /* offset */ 0,bpl, + /* padding */ 0,/* skip_lines */ 0, + buf->vb.height); break; case V4L2_FIELD_BOTTOM: bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height); + 0,bpl,0,0,buf->vb.height); break; case V4L2_FIELD_INTERLACED: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,bpl,buf->vb.height >> 1); + 0,bpl,bpl,0,buf->vb.height >> 1); bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - bpl,bpl,bpl,buf->vb.height >> 1); + bpl,bpl,bpl,0,buf->vb.height >> 1); break; case V4L2_FIELD_SEQ_TB: bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, - 0,bpl,0,buf->vb.height >> 1); + 0,bpl,0,0,buf->vb.height >> 1); bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, - bpf,bpl,0,buf->vb.height >> 1); + bpf,bpl,0,0,buf->vb.height >> 1); break; default: BUG(); @@ -662,7 +765,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) switch (buf->vb.field) { case V4L2_FIELD_TOP: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); + buf->vb.height,/* both_fields */ 0, + tvnorm,&buf->crop); bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist, 0,buf->vb.width,0,buf->vb.height, uoffset,voffset,buf->fmt->hshift, @@ -670,7 +774,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_BOTTOM: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,0,buf->tvnorm); + buf->vb.height,0, + tvnorm,&buf->crop); bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist, 0,buf->vb.width,0,buf->vb.height, uoffset,voffset,buf->fmt->hshift, @@ -678,7 +783,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_INTERLACED: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1,buf->tvnorm); + buf->vb.height,1, + tvnorm,&buf->crop); lines = buf->vb.height >> 1; ypadding = buf->vb.width; cpadding = buf->vb.width >> buf->fmt->hshift; @@ -700,7 +806,8 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) break; case V4L2_FIELD_SEQ_TB: bttv_calc_geo(btv,&buf->geo,buf->vb.width, - buf->vb.height,1,buf->tvnorm); + buf->vb.height,1, + tvnorm,&buf->crop); lines = buf->vb.height >> 1; ypadding = buf->vb.width; cpadding = buf->vb.width >> buf->fmt->hshift; @@ -731,11 +838,12 @@ bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) /* build risc code */ buf->vb.field = V4L2_FIELD_SEQ_TB; bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, - 1,buf->tvnorm); + 1,tvnorm,&buf->crop); bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, - 0, RAW_BPL, 0, RAW_LINES); + /* offset */ 0, RAW_BPL, /* padding */ 0, + /* skip_lines */ 0, RAW_LINES); bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, - buf->vb.size/2 , RAW_BPL, 0, RAW_LINES); + buf->vb.size/2 , RAW_BPL, 0, 0, RAW_LINES); } /* copy format info */ @@ -761,7 +869,8 @@ bttv_overlay_risc(struct bttv *btv, /* calculate geometry */ bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, - V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm); + V4L2_FIELD_HAS_BOTH(ov->field), + &bttv_tvnorms[ov->tvnorm],&buf->crop); /* build risc code */ switch (ov->field) { diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c index 6fc6b02..93e35de 100644 --- a/drivers/media/video/bt8xx/bttv-vbi.c +++ b/drivers/media/video/bt8xx/bttv-vbi.c @@ -5,6 +5,9 @@ (c) 2002 Gerd Knorr <kraxel@bytesex.org> + Copyright (C) 2005, 2006 Michael H. Schimek <mschimek@gmx.at> + Sponsored by OPQ Systems AB + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -41,8 +44,15 @@ to be about 244. */ #define VBI_OFFSET 244 +/* 2048 for compatibility with earlier driver versions. The driver + really stores 1024 + tvnorm->vbipack * 4 samples per line in the + buffer. Note tvnorm->vbipack is <= 0xFF (limit of VBIPACK_LO + HI + is 0x1FF DWORDs) and VBI read()s store a frame counter in the last + four bytes of the VBI image. */ +#define VBI_BPL 2048 + +/* Compatibility. */ #define VBI_DEFLINES 16 -#define VBI_MAXLINES 32 static unsigned int vbibufs = 4; static unsigned int vbi_debug = 0; @@ -58,21 +68,12 @@ MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)"); #define dprintk(fmt, arg...) if (vbi_debug) \ printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg) +#define IMAGE_SIZE(fmt) \ + (((fmt)->count[0] + (fmt)->count[1]) * (fmt)->samples_per_line) + /* ----------------------------------------------------------------------- */ /* vbi risc code + mm */ -static int -vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines) -{ - int bpl = 2048; - - bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, - 0, bpl-4, 4, lines); - bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, - lines * bpl, bpl-4, 4, lines); - return 0; -} - static int vbi_buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { @@ -81,8 +82,16 @@ static int vbi_buffer_setup(struct videobuf_queue *q, if (0 == *count) *count = vbibufs; - *size = fh->lines * 2 * 2048; - dprintk("setup: lines=%d\n",fh->lines); + + *size = IMAGE_SIZE(&fh->vbi_fmt.fmt); + + dprintk("setup: samples=%u start=%d,%d count=%u,%u\n", + fh->vbi_fmt.fmt.samples_per_line, + fh->vbi_fmt.fmt.start[0], + fh->vbi_fmt.fmt.start[1], + fh->vbi_fmt.fmt.count[0], + fh->vbi_fmt.fmt.count[1]); + return 0; } @@ -93,18 +102,93 @@ static int vbi_buffer_prepare(struct videobuf_queue *q, struct bttv_fh *fh = q->priv_data; struct bttv *btv = fh->btv; struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); + const struct bttv_tvnorm *tvnorm; + unsigned int skip_lines0, skip_lines1, min_vdelay; + int redo_dma_risc; int rc; - buf->vb.size = fh->lines * 2 * 2048; + buf->vb.size = IMAGE_SIZE(&fh->vbi_fmt.fmt); if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; + tvnorm = fh->vbi_fmt.tvnorm; + + /* There's no VBI_VDELAY register, RISC must skip the lines + we don't want. With default parameters we skip zero lines + as earlier driver versions did. The driver permits video + standard changes while capturing, so we use vbi_fmt.tvnorm + instead of btv->tvnorm to skip zero lines after video + standard changes as well. */ + + skip_lines0 = 0; + skip_lines1 = 0; + + if (fh->vbi_fmt.fmt.count[0] > 0) + skip_lines0 = max(0, (fh->vbi_fmt.fmt.start[0] + - tvnorm->vbistart[0])); + if (fh->vbi_fmt.fmt.count[1] > 0) + skip_lines1 = max(0, (fh->vbi_fmt.fmt.start[1] + - tvnorm->vbistart[1])); + + redo_dma_risc = 0; + + if (buf->vbi_skip[0] != skip_lines0 || + buf->vbi_skip[1] != skip_lines1 || + buf->vbi_count[0] != fh->vbi_fmt.fmt.count[0] || + buf->vbi_count[1] != fh->vbi_fmt.fmt.count[1]) { + buf->vbi_skip[0] = skip_lines0; + buf->vbi_skip[1] = skip_lines1; + buf->vbi_count[0] = fh->vbi_fmt.fmt.count[0]; + buf->vbi_count[1] = fh->vbi_fmt.fmt.count[1]; + redo_dma_risc = 1; + } + if (STATE_NEEDS_INIT == buf->vb.state) { + redo_dma_risc = 1; if (0 != (rc = videobuf_iolock(q, &buf->vb, NULL))) goto fail; - if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines))) - goto fail; } + + if (redo_dma_risc) { + unsigned int bpl, padding, offset; + + bpl = 2044; /* max. vbipack */ + padding = VBI_BPL - bpl; + + if (fh->vbi_fmt.fmt.count[0] > 0) { + rc = bttv_risc_packed(btv, &buf->top, + buf->vb.dma.sglist, + /* offset */ 0, bpl, + padding, skip_lines0, + fh->vbi_fmt.fmt.count[0]); + if (0 != rc) + goto fail; + } + + if (fh->vbi_fmt.fmt.count[1] > 0) { + offset = fh->vbi_fmt.fmt.count[0] * VBI_BPL; + + rc = bttv_risc_packed(btv, &buf->bottom, + buf->vb.dma.sglist, + offset, bpl, + padding, skip_lines1, + fh->vbi_fmt.fmt.count[1]); + if (0 != rc) + goto fail; + } + } + + /* VBI capturing ends at VDELAY, start of video capturing, + no matter where the RISC program ends. VDELAY minimum is 2, + bounds.top is the corresponding first field line number + times two. VDELAY counts half field lines. */ + min_vdelay = MIN_VDELAY; + if (fh->vbi_fmt.end >= tvnorm->cropcap.bounds.top) + min_vdelay += fh->vbi_fmt.end - tvnorm->cropcap.bounds.top; + + /* For bttv_buffer_activate_vbi(). */ + buf->geo.vdelay = min_vdelay; + buf->vb.state = STATE_PREPARED; buf->vb.field = field; dprintk("buf prepare %p: top=%p bottom=%p field=%s\n", @@ -140,7 +224,7 @@ static void vbi_buffer_release(struct videobuf_queue *q, struct videobuf_buffer struct bttv_buffer *buf = container_of(vb,struct bttv_buffer,vb); dprintk("free %p\n",vb); - bttv_dma_free(&fh->cap,fh->btv,buf); + bttv_dma_free(q,fh->btv,buf); } struct videobuf_queue_ops bttv_vbi_qops = { @@ -152,69 +236,215 @@ struct videobuf_queue_ops bttv_vbi_qops = { /* ----------------------------------------------------------------------- */ -void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines) +static int +try_fmt (struct v4l2_vbi_format * f, + const struct bttv_tvnorm * tvnorm, + __s32 crop_start) { - int vdelay; - - if (lines < 1) - lines = 1; - if (lines > VBI_MAXLINES) - lines = VBI_MAXLINES; - fh->lines = lines; - - vdelay = btread(BT848_E_VDELAY_LO); - if (vdelay < lines*2) { - vdelay = lines*2; - btwrite(vdelay,BT848_E_VDELAY_LO); - btwrite(vdelay,BT848_O_VDELAY_LO); + __s32 min_start, max_start, max_end, f2_offset; + unsigned int i; + + /* For compatibility with earlier driver versions we must pretend + the VBI and video capture window may overlap. In reality RISC + magic aborts VBI capturing at the first line of video capturing, + leaving the rest of the buffer unchanged, usually all zero. + VBI capturing must always start before video capturing. >> 1 + because cropping counts field lines times two. */ + min_start = tvnorm->vbistart[0]; + max_start = (crop_start >> 1) - 1; + max_end = (tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height) >> 1; + + if (min_start > max_start) + return -EBUSY; + + BUG_ON(max_start >= max_end); + + f->sampling_rate = tvnorm->Fsc; + f->samples_per_line = VBI_BPL; + f->sample_format = V4L2_PIX_FMT_GREY; + f->offset = VBI_OFFSET; + + f2_offset = tvnorm->vbistart[1] - tvnorm->vbistart[0]; + + for (i = 0; i < 2; ++i) { + if (0 == f->count[i]) { + /* No data from this field. We leave f->start[i] + alone because VIDIOCSVBIFMT is w/o and EINVALs + when a driver does not support exactly the + requested parameters. */ + } else { + s64 start, count; + + start = clamp(f->start[i], min_start, max_start); + /* s64 to prevent overflow. */ + count = (s64) f->start[i] + f->count[i] - start; + f->start[i] = start; + f->count[i] = clamp(count, (s64) 1, + max_end - start); + } + + min_start += f2_offset; + max_start += f2_offset; + max_end += f2_offset; } + + if (0 == (f->count[0] | f->count[1])) { + /* As in earlier driver versions. */ + f->start[0] = tvnorm->vbistart[0]; + f->start[1] = tvnorm->vbistart[1]; + f->count[0] = 1; + f->count[1] = 1; + } + + f->flags = 0; + + f->reserved[0] = 0; + f->reserved[1] = 0; + + return 0; } -void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f) +int +bttv_vbi_try_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) { + struct bttv *btv = fh->btv; const struct bttv_tvnorm *tvnorm; - s64 count0,count1,count; + __s32 crop_start; - tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; - f->fmt.vbi.sampling_rate = tvnorm->Fsc; - f->fmt.vbi.samples_per_line = 2048; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = VBI_OFFSET; - f->fmt.vbi.flags = 0; - - /* s64 to prevent overflow. */ - count0 = (s64) f->fmt.vbi.start[0] + f->fmt.vbi.count[0] - - tvnorm->vbistart[0]; - count1 = (s64) f->fmt.vbi.start[1] + f->fmt.vbi.count[1] - - tvnorm->vbistart[1]; - count = clamp (max (count0, count1), (s64) 1, (s64) VBI_MAXLINES); - - f->fmt.vbi.start[0] = tvnorm->vbistart[0]; - f->fmt.vbi.start[1] = tvnorm->vbistart[1]; - f->fmt.vbi.count[0] = count; - f->fmt.vbi.count[1] = count; - - f->fmt.vbi.reserved[0] = 0; - f->fmt.vbi.reserved[1] = 0; + mutex_lock(&btv->lock); + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + crop_start = btv->crop_start; + + mutex_unlock(&btv->lock); + + return try_fmt(f, tvnorm, crop_start); } -void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f) +int +bttv_vbi_set_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) { + struct bttv *btv = fh->btv; const struct bttv_tvnorm *tvnorm; + __s32 start1, end; + int rc; + + mutex_lock(&btv->lock); + + rc = -EBUSY; + if (fh->resources & RESOURCE_VBI) + goto fail; + + tvnorm = &bttv_tvnorms[btv->tvnorm]; + + rc = try_fmt(f, tvnorm, btv->crop_start); + if (0 != rc) + goto fail; + + start1 = f->start[1] - tvnorm->vbistart[1] + tvnorm->vbistart[0]; + + /* First possible line of video capturing. Should be + max(f->start[0] + f->count[0], start1 + f->count[1]) * 2 + when capturing both fields. But for compatibility we must + pretend the VBI and video capture window may overlap, + so end = start + 1, the lowest possible value, times two + because vbi_fmt.end counts field lines times two. */ + end = max(f->start[0], start1) * 2 + 2; + + mutex_lock(&fh->vbi.lock); + + fh->vbi_fmt.fmt = *f; + fh->vbi_fmt.tvnorm = tvnorm; + fh->vbi_fmt.end = end; + + mutex_unlock(&fh->vbi.lock); + + rc = 0; + + fail: + mutex_unlock(&btv->lock); + + return rc; +} + +void +bttv_vbi_get_fmt (struct bttv_fh * fh, + struct v4l2_vbi_format * f) +{ + const struct bttv_tvnorm *tvnorm; + + *f = fh->vbi_fmt.fmt; tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; - memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_VBI_CAPTURE; - f->fmt.vbi.sampling_rate = tvnorm->Fsc; - f->fmt.vbi.samples_per_line = 2048; - f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - f->fmt.vbi.offset = VBI_OFFSET; - f->fmt.vbi.start[0] = tvnorm->vbistart[0]; - f->fmt.vbi.start[1] = tvnorm->vbistart[1]; - f->fmt.vbi.count[0] = fh->lines; - f->fmt.vbi.count[1] = fh->lines; - f->fmt.vbi.flags = 0; + + if (tvnorm != fh->vbi_fmt.tvnorm) { + __s32 max_end; + unsigned int i; + + /* As in vbi_buffer_prepare() this imitates the + behaviour of earlier driver versions after video + standard changes, with default parameters anyway. */ + + max_end = (tvnorm->cropcap.bounds.top + + tvnorm->cropcap.bounds.height) >> 1; + + f->sampling_rate = tvnorm->Fsc; + + for (i = 0; i < 2; ++i) { + __s32 new_start; + + new_start = f->start[i] + + tvnorm->vbistart[i] + - fh->vbi_fmt.tvnorm->vbistart[i]; + + f->start[i] = min(new_start, max_end - 1); + f->count[i] = min((__s32) f->count[i], + max_end - f->start[i]); + + max_end += tvnorm->vbistart[1] + - tvnorm->vbistart[0]; + } + } +} + +void +bttv_vbi_fmt_reset (struct bttv_vbi_fmt * f, + int norm) +{ + const struct bttv_tvnorm *tvnorm; + unsigned int real_samples_per_line; + unsigned int real_count; + + tvnorm = &bttv_tvnorms[norm]; + + f->fmt.sampling_rate = tvnorm->Fsc; + f->fmt.samples_per_line = VBI_BPL; + f->fmt.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.offset = VBI_OFFSET; + f->fmt.start[0] = tvnorm->vbistart[0]; + f->fmt.start[1] = tvnorm->vbistart[1]; + f->fmt.count[0] = VBI_DEFLINES; + f->fmt.count[1] = VBI_DEFLINES; + f->fmt.flags = 0; + f->fmt.reserved[0] = 0; + f->fmt.reserved[1] = 0; + + /* For compatibility the buffer size must be 2 * VBI_DEFLINES * + VBI_BPL regardless of the current video standard. */ + real_samples_per_line = 1024 + tvnorm->vbipack * 4; + real_count = ((tvnorm->cropcap.defrect.top >> 1) + - tvnorm->vbistart[0]); + + BUG_ON(real_samples_per_line > VBI_BPL); + BUG_ON(real_count > VBI_DEFLINES); + + f->tvnorm = tvnorm; + + /* See bttv_vbi_fmt_set(). */ + f->end = tvnorm->vbistart[0] * 2 + 2; } /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h index f9c9e3c..f821ba6 100644 --- a/drivers/media/video/bt8xx/bttv.h +++ b/drivers/media/video/bt8xx/bttv.h @@ -168,6 +168,8 @@ #define BTTV_BOARD_SABRENT_TVFM 0x8e #define BTTV_BOARD_HAUPPAUGE_IMPACTVCB 0x8f #define BTTV_BOARD_MACHTV_MAGICTV 0x90 +#define BTTV_BOARD_SSAI_SECURITY 0x91 +#define BTTV_BOARD_SSAI_ULTRASOUND 0x92 /* more card-specific defines */ #define PT2254_L_CHANNEL 0x10 @@ -197,33 +199,6 @@ struct bttv_core { struct bttv; -struct bttv_ir { - struct input_dev *dev; - struct ir_input_state ir; - char name[32]; - char phys[32]; - - /* Usual gpio signalling */ - - u32 mask_keycode; - u32 mask_keydown; - u32 mask_keyup; - u32 polling; - u32 last_gpio; - struct work_struct work; - struct timer_list timer; - - /* RC5 gpio */ - u32 rc5_gpio; - struct timer_list timer_end; /* timer_end for code completion */ - struct timer_list timer_keyup; /* timer_end for key release */ - u32 last_rc5; /* last good rc5 code */ - u32 last_bit; /* last raw bit seen */ - u32 code; /* raw code under construction */ - struct timeval base_time; /* time of last seen code */ - int active; /* building raw code */ -}; - struct tvcard { char *name; @@ -287,17 +262,8 @@ extern int bttv_handle_chipset(struct bttv *btv); /* this obsolete -- please use the sysfs-based interface below for new code */ -/* returns card type + card ID (for bt878-based ones) - for possible values see lines below beginning with #define BTTV_BOARD_UNKNOWN - returns negative value if error occurred -*/ -extern int bttv_get_cardinfo(unsigned int card, int *type, - unsigned int *cardid); extern struct pci_dev* bttv_get_pcidev(unsigned int card); -/* obsolete, use bttv_get_cardinfo instead */ -extern int bttv_get_id(unsigned int card); - /* sets GPOE register (BT848_GPIO_OUT_EN) to new value: data | (current_GPOE_value & ~mask) returns negative value if error occurred @@ -317,20 +283,6 @@ extern int bttv_read_gpio(unsigned int card, unsigned long *data); extern int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data); -/* returns pointer to task queue which can be used as parameter to - interruptible_sleep_on - in interrupt handler if BT848_INT_GPINT bit is set - this queue is activated - (wake_up_interruptible) and following call to the function bttv_read_gpio - should return new value of GPDATA, - returns NULL value if error occurred or queue is not available - WARNING: because there is no buffer for GPIO data, one MUST - process data ASAP -*/ -extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card); - -/* call i2c clients -*/ -extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg); diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 311c4c5..8f44f02 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -26,7 +26,7 @@ #define _BTTVP_H_ #include <linux/version.h> -#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,16) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,17) #include <linux/types.h> #include <linux/wait.h> @@ -66,14 +66,22 @@ #define RISC_SLOT_LOOP 14 #define RESOURCE_OVERLAY 1 -#define RESOURCE_VIDEO 2 +#define RESOURCE_VIDEO_STREAM 2 #define RESOURCE_VBI 4 +#define RESOURCE_VIDEO_READ 8 #define RAW_LINES 640 #define RAW_BPL 1024 #define UNSET (-1U) +/* Min. value in VDELAY register. */ +#define MIN_VDELAY 2 +/* Even to get Cb first, odd for Cr. */ +#define MAX_HDELAY (0x3FF & -2) +/* Limits scaled width, which must be a multiple of 4. */ +#define MAX_HACTIVE (0x3FF & -4) + #define clamp(x, low, high) min (max (low, x), high) /* ---------------------------------------------------------- */ @@ -92,8 +100,13 @@ struct bttv_tvnorm { u16 vtotal; int sram; /* ITU-R frame line number of the first VBI line we can - capture, of the first and second field. */ + capture, of the first and second field. The last possible line + is determined by cropcap.bounds. */ u16 vbistart[2]; + /* Horizontally this counts fCLKx1 samples following the leading + edge of the horizontal sync pulse, vertically ITU-R frame line + numbers of the first field times two (2, 4, 6, ... 524 or 624). */ + struct v4l2_cropcap cropcap; }; extern const struct bttv_tvnorm bttv_tvnorms[]; @@ -128,6 +141,9 @@ struct bttv_buffer { struct bttv_geometry geo; struct btcx_riscmem top; struct btcx_riscmem bottom; + struct v4l2_rect crop; + unsigned int vbi_skip[2]; + unsigned int vbi_count[2]; }; struct bttv_buffer_set { @@ -146,6 +162,34 @@ struct bttv_overlay { int setup_ok; }; +struct bttv_vbi_fmt { + struct v4l2_vbi_format fmt; + + /* fmt.start[] and count[] refer to this video standard. */ + const struct bttv_tvnorm *tvnorm; + + /* Earliest possible start of video capturing with this + v4l2_vbi_format, in struct bttv_crop.rect units. */ + __s32 end; +}; + +/* bttv-vbi.c */ +void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm); + +struct bttv_crop { + /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */ + struct v4l2_rect rect; + + /* Scaled image size limits with this crop rect. Divide + max_height, but not min_height, by two when capturing + single fields. See also bttv_crop_reset() and + bttv_crop_adjust() in bttv-driver.c. */ + __s32 min_scaled_width; + __s32 min_scaled_height; + __s32 max_scaled_width; + __s32 max_scaled_height; +}; + struct bttv_fh { struct bttv *btv; int resources; @@ -160,13 +204,19 @@ struct bttv_fh { int width; int height; - /* current settings */ + /* video overlay */ const struct bttv_format *ovfmt; struct bttv_overlay ov; - /* video overlay */ + /* Application called VIDIOC_S_CROP. */ + int do_crop; + + /* vbi capture */ struct videobuf_queue vbi; - int lines; + /* Current VBI capture window as seen through this fh (cannot + be global for compatibility with earlier drivers). Protected + by struct bttv.lock and struct bttv_fh.vbi.lock. */ + struct bttv_vbi_fmt vbi_fmt; }; /* ---------------------------------------------------------- */ @@ -176,7 +226,8 @@ struct bttv_fh { int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, struct scatterlist *sglist, unsigned int offset, unsigned int bpl, - unsigned int pitch, unsigned int lines); + unsigned int pitch, unsigned int skip_lines, + unsigned int store_lines); /* control dma register + risc main loop */ void bttv_set_dma(struct bttv *btv, int override); @@ -202,9 +253,9 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, /* ---------------------------------------------------------- */ /* bttv-vbi.c */ -void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f); -void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f); -void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines); +int bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); +void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); +int bttv_vbi_set_fmt(struct bttv_fh *fh, struct v4l2_vbi_format *f); extern struct videobuf_queue_ops bttv_vbi_qops; @@ -233,7 +284,6 @@ extern int fini_bttv_i2c(struct bttv *btv); #define d2printk if (bttv_debug >= 2) printk #define BTTV_MAX_FBUF 0x208000 -#define VBIBUF_SIZE (2048*VBI_MAXLINES*2) #define BTTV_TIMEOUT (HZ/2) /* 0.5 seconds */ #define BTTV_FREE_IDLE (HZ) /* one second */ @@ -308,13 +358,12 @@ struct bttv { /* infrared remote */ int has_remote; - struct bttv_ir *remote; + struct card_ir *remote; /* locking */ spinlock_t s_lock; struct mutex lock; int resources; - struct mutex reslock; #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif @@ -384,6 +433,24 @@ struct bttv { unsigned int users; struct bttv_fh init; + + /* used to make dvb-bt8xx autoloadable */ + struct work_struct request_module_wk; + + /* Default (0) and current (1) video capturing and overlay + cropping parameters in bttv_tvnorm.cropcap units. Protected + by bttv.lock. */ + struct bttv_crop crop[2]; + + /* Earliest possible start of video capturing in + bttv_tvnorm.cropcap line units. Set by check_alloc_btres() + and free_btres(). Protected by bttv.lock. */ + __s32 vbi_end; + + /* Latest possible end of VBI capturing (= crop[x].rect.top when + VIDEO_RESOURCES are locked). Set by check_alloc_btres() + and free_btres(). Protected by bttv.lock. */ + __s32 crop_start; }; /* our devices */ diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 4dae892..96254db 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -4,6 +4,7 @@ * sensor. * * Copyright 2006 One Laptop Per Child Association, Inc. + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> * * Written by Jonathan Corbet, corbet@lwn.net. * @@ -22,6 +23,7 @@ #include <linux/spinlock.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <linux/device.h> #include <linux/wait.h> #include <linux/list.h> @@ -36,7 +38,7 @@ #include "cafe_ccic-regs.h" -#define CAFE_VERSION 0x000001 +#define CAFE_VERSION 0x000002 /* @@ -164,7 +166,7 @@ struct cafe_camera struct tasklet_struct s_tasklet; /* Current operating parameters */ - enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */ + u32 sensor_type; /* Currently ov7670 only */ struct v4l2_pix_format pix_format; /* Locks */ @@ -704,7 +706,13 @@ static void cafe_ctlr_init(struct cafe_camera *cam) cafe_reg_write(cam, REG_GL_CSR, GCSR_SRS|GCSR_MRS); /* Needed? */ cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRC); cafe_reg_write(cam, REG_GL_CSR, GCSR_SRC|GCSR_MRS); + /* + * Here we must wait a bit for the controller to come around. + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); mdelay(5); /* FIXME revisit this */ + spin_lock_irqsave(&cam->dev_lock, flags); + cafe_reg_write(cam, REG_GL_CSR, GCSR_CCIC_EN|GCSR_SRC|GCSR_MRC); cafe_reg_set_bit(cam, REG_GL_IMASK, GIMSK_CCIC_EN); /* @@ -772,9 +780,9 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam) * Control 1 is power down, set to 0 to operate. */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ - mdelay(1); /* Marvell says 1ms will do it */ +// mdelay(1); /* Marvell says 1ms will do it */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); - mdelay(1); /* Enough? */ +// mdelay(1); /* Enough? */ spin_unlock_irqrestore(&cam->dev_lock, flags); } @@ -818,6 +826,7 @@ static int __cafe_cam_reset(struct cafe_camera *cam) */ static int cafe_cam_init(struct cafe_camera *cam) { + struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 }; int ret; mutex_lock(&cam->s_mutex); @@ -827,9 +836,11 @@ static int cafe_cam_init(struct cafe_camera *cam) ret = __cafe_cam_reset(cam); if (ret) goto out; - ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type); + chip.match_chip = cam->sensor->addr; + ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip); if (ret) goto out; + cam->sensor_type = chip.ident; // if (cam->sensor->addr != OV7xx0_SID) { if (cam->sensor_type != V4L2_IDENT_OV7670) { cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); @@ -1022,7 +1033,7 @@ static ssize_t cafe_v4l_read(struct file *filp, char __user *buffer, size_t len, loff_t *pos) { struct cafe_camera *cam = filp->private_data; - int ret; + int ret = 0; /* * Perhaps we're in speculative read mode and already @@ -1195,7 +1206,7 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv, struct v4l2_requestbuffers *req) { struct cafe_camera *cam = filp->private_data; - int ret; + int ret = 0; /* Silence warning */ /* * Make sure it's something we can do. User pointers could be @@ -1251,8 +1262,6 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv, if (cam->n_sbufs == 0) /* no luck at all - ret already set */ kfree(cam->sb_bufs); - else - ret = 0; req->count = cam->n_sbufs; /* In case of partial success */ out: @@ -1794,18 +1803,19 @@ static void cafe_frame_tasklet(unsigned long data) if (list_empty(&cam->sb_avail)) break; /* Leave it valid, hope for better later */ clear_bit(bufno, &cam->flags); - /* - * We could perhaps drop the spinlock during this - * big copy. Something to consider. - */ sbuf = list_entry(cam->sb_avail.next, struct cafe_sio_buffer, list); + /* + * Drop the lock during the big copy. This *should* be safe... + */ + spin_unlock_irqrestore(&cam->dev_lock, flags); memcpy(sbuf->buffer, cam->dma_bufs[bufno], cam->pix_format.sizeimage); sbuf->v4lbuf.bytesused = cam->pix_format.sizeimage; sbuf->v4lbuf.sequence = cam->buf_seq[bufno]; sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&cam->dev_lock, flags); list_move_tail(&sbuf->list, &cam->sb_full); } if (! list_empty(&cam->sb_full)) @@ -2109,6 +2119,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam->v4ldev = cafe_v4l_template; cam->v4ldev.debug = 0; // cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; + cam->v4ldev.dev = &pdev->dev; ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; @@ -2178,10 +2189,52 @@ static void cafe_pci_remove(struct pci_dev *pdev) } +#ifdef CONFIG_PM +/* + * Basic power management. + */ +static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + int ret; + + ret = pci_save_state(pdev); + if (ret) + return ret; + cafe_ctlr_stop_dma(cam); + cafe_ctlr_power_down(cam); + pci_disable_device(pdev); + return 0; +} + + +static int cafe_pci_resume(struct pci_dev *pdev) +{ + struct cafe_camera *cam = cafe_find_by_pdev(pdev); + int ret = 0; + + ret = pci_restore_state(pdev); + if (ret) + return ret; + ret = pci_enable_device(pdev); + if (ret) { + cam_warn(cam, "Unable to re-enable device on resume!\n"); + return ret; + } + cafe_ctlr_init(cam); + cafe_ctlr_power_up(cam); + set_bit(CF_CONFIG_NEEDED, &cam->flags); + if (cam->state == S_SPECREAD) + cam->state = S_IDLE; /* Don't bother restarting */ + else if (cam->state == S_SINGLEREAD || cam->state == S_STREAMING) + ret = cafe_read_setup(cam, cam->state); + return ret; +} + +#endif /* CONFIG_PM */ static struct pci_device_id cafe_ids[] = { - { PCI_DEVICE(0x1148, 0x4340) }, /* Temporary ID on devel board */ { PCI_DEVICE(0x11ab, 0x4100) }, /* Eventual real ID */ { PCI_DEVICE(0x11ab, 0x4102) }, /* Really eventual real ID */ { 0, } @@ -2194,6 +2247,10 @@ static struct pci_driver cafe_pci_driver = { .id_table = cafe_ids, .probe = cafe_pci_probe, .remove = cafe_pci_remove, +#ifdef CONFIG_PM + .suspend = cafe_pci_suspend, + .resume = cafe_pci_resume, +#endif }; diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 7e8d5ef..78c9699 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -1350,13 +1350,13 @@ out: static void create_proc_cpia_cam(struct cam_data *cam) { - char name[7]; + char name[5 + 1 + 10 + 1]; struct proc_dir_entry *ent; if (!cpia_proc_root || !cam) return; - sprintf(name, "video%d", cam->vdev.minor); + snprintf(name, sizeof(name), "video%d", cam->vdev.minor); ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); if (!ent) @@ -1376,12 +1376,12 @@ static void create_proc_cpia_cam(struct cam_data *cam) static void destroy_proc_cpia_cam(struct cam_data *cam) { - char name[7]; + char name[5 + 1 + 10 + 1]; if (!cam || !cam->proc_entry) return; - sprintf(name, "video%d", cam->vdev.minor); + snprintf(name, sizeof(name), "video%d", cam->vdev.minor); remove_proc_entry(name, cpia_proc_root); cam->proc_entry = NULL; } @@ -3153,8 +3153,7 @@ static int reset_camera(struct cam_data *cam) static void put_cam(struct cpia_camera_ops* ops) { - if (ops->owner) - module_put(ops->owner); + module_put(ops->owner); } /* ------------------------- V4L interface --------------------- */ diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c index b12cec9..19711aa 100644 --- a/drivers/media/video/cpia_pp.c +++ b/drivers/media/video/cpia_pp.c @@ -62,7 +62,6 @@ static int cpia_pp_close(void *privdata); #define PPCPIA_PARPORT_OFF -2 #define PPCPIA_PARPORT_NONE -1 -#ifdef MODULE static int parport_nr[PARPORT_MAX] = {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; static char *parport[PARPORT_MAX] = {NULL,}; @@ -72,11 +71,6 @@ MODULE_LICENSE("GPL"); module_param_array(parport, charp, NULL, 0); MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); -#else -static int parport_nr[PARPORT_MAX] __initdata = - {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; -static int parport_ptr = 0; -#endif struct pp_cam_entry { struct pardevice *pdev; @@ -141,7 +135,6 @@ static void cpia_pp_run_callback(struct work_struct *work) cam = container_of(work, struct pp_cam_entry, cb_task); cb_func = cam->cb_func; cb_data = cam->cb_data; - work_release(work); cb_func(cb_data); } @@ -682,7 +675,7 @@ static int cpia_pp_registerCallback(void *privdata, void (*cb)(void *cbdata), vo if(cam->port->irq != PARPORT_IRQ_NONE) { cam->cb_func = cb; cam->cb_data = cbdata; - INIT_WORK_NAR(&cam->cb_task, cpia_pp_run_callback); + INIT_WORK(&cam->cb_task, cpia_pp_run_callback); } else { retval = -1; } @@ -820,7 +813,7 @@ static struct parport_driver cpia_pp_driver = { .detach = cpia_pp_detach, }; -static int cpia_pp_init(void) +static int __init cpia_pp_init(void) { printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); @@ -839,8 +832,7 @@ static int cpia_pp_init(void) return 0; } -#ifdef MODULE -int init_module(void) +static int __init cpia_init(void) { if (parport[0]) { /* The user gave some parameters. Let's see what they were. */ @@ -867,38 +859,11 @@ int init_module(void) return cpia_pp_init(); } -void cleanup_module(void) +static void __exit cpia_cleanup(void) { - parport_unregister_driver (&cpia_pp_driver); + parport_unregister_driver(&cpia_pp_driver); return; } -#else /* !MODULE */ - -static int __init cpia_pp_setup(char *str) -{ - int err; - - if (!strncmp(str, "parport", 7)) { - int n = simple_strtoul(str + 7, NULL, 10); - if (parport_ptr < PARPORT_MAX) { - parport_nr[parport_ptr++] = n; - } else { - LOG("too many ports, %s ignored.\n", str); - } - } else if (!strcmp(str, "auto")) { - parport_nr[0] = PPCPIA_PARPORT_AUTO; - } else if (!strcmp(str, "none")) { - parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; - } - - err=cpia_pp_init(); - if (err) - return err; - - return 1; -} - -__setup("cpia_pp=", cpia_pp_setup); - -#endif /* !MODULE */ +module_init(cpia_init); +module_exit(cpia_cleanup); diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index de87247..a73e285 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -28,6 +28,7 @@ #include <linux/i2c-id.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); MODULE_AUTHOR("Martin Vaughan"); @@ -103,6 +104,9 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd, cs53l32a_write(client, 0x05, (u8) ctrl->value); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0); + case VIDIOC_LOG_STATUS: { u8 v = cs53l32a_read(client, 0x01); diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 2f5ca71..88dbddd 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -51,16 +51,18 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, V4L2_CID_MPEG_AUDIO_EMPHASIS, V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_CID_MPEG_VIDEO_ASPECT, V4L2_CID_MPEG_VIDEO_B_FRAMES, V4L2_CID_MPEG_VIDEO_GOP_SIZE, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_PULLDOWN, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_CID_MPEG_VIDEO_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, @@ -72,6 +74,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, 0 }; @@ -103,6 +106,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_AUDIO_CRC: ctrl->value = params->audio_crc; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; case V4L2_CID_MPEG_VIDEO_ENCODING: ctrl->value = params->video_encoding; break; @@ -118,9 +124,6 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: ctrl->value = params->video_gop_closure; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - ctrl->value = params->video_pulldown; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ctrl->value = params->video_bitrate_mode; break; @@ -133,6 +136,12 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: ctrl->value = params->video_temporal_decimation; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; case V4L2_CID_MPEG_STREAM_TYPE: ctrl->value = params->stream_type; break; @@ -172,6 +181,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: ctrl->value = params->video_chroma_median_filter_bottom; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; default: return -EINVAL; } @@ -205,6 +217,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_AUDIO_CRC: params->audio_crc = ctrl->value; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; case V4L2_CID_MPEG_VIDEO_ASPECT: params->video_aspect = ctrl->value; break; @@ -231,9 +246,6 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: params->video_gop_closure = ctrl->value; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - params->video_pulldown = ctrl->value; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: /* MPEG-1 only allows CBR */ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && @@ -250,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: params->video_temporal_decimation = ctrl->value; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; case V4L2_CID_MPEG_STREAM_TYPE: params->stream_type = ctrl->value; params->video_encoding = @@ -297,6 +315,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: params->video_chroma_median_filter_bottom = ctrl->value; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; default: return -EINVAL; } @@ -343,6 +364,9 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: name = "Median Chroma Filter Minimum"; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + name = "Insert Navigation Packets"; + break; default: return v4l2_ctrl_query_fill(qctrl, min, max, step, def); @@ -357,6 +381,12 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma min = 0; step = 1; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; + min = 0; + max = 1; + step = 1; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; @@ -512,6 +542,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0); + default: return v4l2_ctrl_query_fill_std(qctrl); @@ -663,6 +696,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) /* stream */ .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, /* audio */ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, @@ -672,6 +706,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, /* video */ .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, @@ -679,11 +714,12 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .video_b_frames = 2, .video_gop_size = 12, .video_gop_closure = 1, - .video_pulldown = 0, .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, .video_bitrate = 6000000, .video_bitrate_peak = 8000000, .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ /* encoding filters */ .video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, @@ -783,14 +819,14 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, new->video_gop_closure); if (err) return err; } - if (old == NULL || old->video_pulldown != new->video_pulldown) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_3_2_PULLDOWN, 1, new->video_pulldown); - if (err) return err; - } if (old == NULL || old->audio_properties != new->audio_properties) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties); if (err) return err; } + if (old == NULL || old->audio_mute != new->audio_mute) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute); + if (err) return err; + } if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode || old->video_bitrate != new->video_bitrate || old->video_bitrate_peak != new->video_bitrate_peak) { @@ -838,6 +874,15 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, new->video_temporal_decimation); if (err) return err; } + if (old == NULL || old->video_mute != new->video_mute || + (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets); + if (err) return err; + } return 0; } @@ -866,18 +911,22 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) int temporal = p->video_temporal_filter; /* Stream */ - printk(KERN_INFO "%s: Stream: %s\n", + printk(KERN_INFO "%s: Stream: %s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); printk(KERN_INFO "%s: VBI Format: %s\n", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps\n", + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", prefix, p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30); + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); printk(KERN_INFO "%s: Video: %s, %s, %s, %d", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), @@ -888,23 +937,23 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) printk(", Peak %d", p->video_bitrate_peak); } printk("\n"); - printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n", + printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", prefix, p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No ", - p->video_pulldown ? "" : "No "); + p->video_gop_closure ? "" : "No "); if (p->video_temporal_decimation) { printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", prefix, p->video_temporal_decimation); } /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s", + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE)); + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) { printk(", %s", cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 7bb7589..1757a58 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -35,6 +35,7 @@ #include <linux/videodev2.h> #include <linux/i2c.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/cx25840.h> #include "cx25840-core.h" @@ -628,25 +629,19 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, #ifdef CONFIG_VIDEO_ADV_DEBUG /* ioctls to allow direct access to the * cx25840 registers for testing */ - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_CX25840) - return -EINVAL; - reg->val = cx25840_read(client, reg->reg & 0x0fff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (reg->i2c_id != I2C_DRIVERID_CX25840) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = cx25840_read(client, reg->reg & 0x0fff); + else + cx25840_write(client, reg->reg & 0x0fff, reg->val & 0xff); break; } #endif @@ -833,9 +828,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, cx25840_initialize(client, 0); break; - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->id; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); default: return -EINVAL; @@ -853,7 +847,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, { struct i2c_client *client; struct cx25840_state *state; - enum v4l2_chip_ident id; + u32 id; u16 device_id; /* Check if the adapter supports the needed features @@ -893,9 +887,11 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, return 0; } + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : 3, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), address << 1, adapter->name); i2c_set_clientdata(client, state); @@ -906,14 +902,15 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, state->audmode = V4L2_TUNER_MODE_LANG1; state->vbi_line_offset = 8; state->id = id; + state->rev = device_id; + + i2c_attach_client(client); if (state->is_cx25836) cx25836_initialize(client); else cx25840_initialize(client, 1); - i2c_attach_client(client); - return 0; } diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 2804906..f4b56d2 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -43,7 +43,8 @@ struct cx25840_state { u32 audclk_freq; int audmode; int vbi_line_offset; - enum v4l2_chip_ident id; + u32 id; + u32 rev; int is_cx25836; }; diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 1958d40..e852024 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -17,7 +17,6 @@ #include <linux/module.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include <linux/firmware.h> #include <media/v4l2-common.h> #include <media/cx25840.h> @@ -37,7 +36,7 @@ */ #define FWSEND 48 -#define FWDEV(x) &((x)->adapter->dev) +#define FWDEV(x) &((x)->dev) static char *firmware = FWFILE; diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index b2a66ba..0f9d969 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -53,7 +53,6 @@ config VIDEO_CX88_DVB select DVB_OR51132 if !DVB_FE_CUSTOMISE select DVB_CX22702 if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE - select DVB_TUNER_LGH06XF if !DVB_FE_CUSTOMISE select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_CX24123 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index 639c3b6..532cee3 100644 --- a/drivers/media/video/cx88/Makefile +++ b/drivers/media/video/cx88/Makefile @@ -12,8 +12,3 @@ obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends - -extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 -extra-cflags-$(CONFIG_VIDEO_CX88_VP3054)+= -DHAVE_VP3054_I2C=1 - -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index e4355fd..3956c25 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -232,7 +232,8 @@ static void cx8801_aud_irq(snd_cx88_card_t *chip) cx_write(MO_AUD_INTSTAT, status); if (debug > 1 || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq aud", - cx88_aud_irqs, status, mask); + cx88_aud_irqs, ARRAY_SIZE(cx88_aud_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { printk(KERN_WARNING "%s/0: audio risc op code error\n",core->name); @@ -413,11 +414,9 @@ static int snd_cx88_hw_params(struct snd_pcm_substream * substream, dprintk(1,"Setting buffer\n"); - buf = kmalloc(sizeof(*buf),GFP_KERNEL); + buf = kzalloc(sizeof(*buf),GFP_KERNEL); if (NULL == buf) return -ENOMEM; - memset(buf,0,sizeof(*buf)); - buf->vb.memory = V4L2_MEMORY_MMAP; buf->vb.width = chip->period_size; @@ -682,7 +681,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, return err; } - if (!pci_dma_supported(pci,0xffffffff)) { + if (!pci_dma_supported(pci,DMA_32BIT_MASK)) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; cx88_core_put(core,pci); diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c index 9a7a299..b0466b8 100644 --- a/drivers/media/video/cx88/cx88-blackbird.c +++ b/drivers/media/video/cx88/cx88-blackbird.c @@ -6,6 +6,9 @@ * (c) 2004 Jelle Foks <jelle@foks.8m.com> * (c) 2004 Gerd Knorr <kraxel@bytesex.org> * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - video_ioctl2 conversion + * * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), * * This program is free software; you can redistribute it and/or modify @@ -53,7 +56,8 @@ MODULE_PARM_DESC(debug,"enable debug messages [blackbird]"); /* ------------------------------------------------------------------ */ -#define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024 +#define OLD_BLACKBIRD_FIRM_IMAGE_SIZE 262144 +#define BLACKBIRD_FIRM_IMAGE_SIZE 376836 /* defines below are from ivtv-driver.h */ @@ -401,7 +405,7 @@ static int blackbird_find_mailbox(struct cx8802_dev *dev) u32 value; int i; - for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) { + for (i = 0; i < dev->fw_size; i++) { memory_read(dev->core, i, &value); if (value == signature[signaturecnt]) signaturecnt++; @@ -449,12 +453,15 @@ static int blackbird_load_firmware(struct cx8802_dev *dev) return -1; } - if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { - dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d)\n", - firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); + if ((firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) && + (firmware->size != OLD_BLACKBIRD_FIRM_IMAGE_SIZE)) { + dprintk(0, "ERROR: Firmware size mismatch (have %zd, expected %d or %d)\n", + firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE, + OLD_BLACKBIRD_FIRM_IMAGE_SIZE); release_firmware(firmware); return -1; } + dev->fw_size = firmware->size; if (0 != memcmp(firmware->data, magic, 8)) { dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n"); @@ -520,7 +527,7 @@ static void blackbird_codec_settings(struct cx8802_dev *dev) dev->params.width = dev->width; dev->params.height = dev->height; - dev->params.is_50hz = (dev->core->tvnorm->id & V4L2_STD_625_50) != 0; + dev->params.is_50hz = (dev->core->tvnorm & V4L2_STD_625_50) != 0; cx2341x_update(dev, blackbird_mbox_func, NULL, &dev->params); } @@ -710,8 +717,13 @@ static int blackbird_queryctrl(struct cx8802_dev *dev, struct v4l2_queryctrl *qc return 0; } -static int blackbird_querymenu(struct cx8802_dev *dev, struct v4l2_querymenu *qmenu) +/* ------------------------------------------------------------------ */ +/* IOCTL Handlers */ + +static int vidioc_querymenu (struct file *file, void *priv, + struct v4l2_querymenu *qmenu) { + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; struct v4l2_queryctrl qctrl; qctrl.id = qmenu->id; @@ -719,221 +731,347 @@ static int blackbird_querymenu(struct cx8802_dev *dev, struct v4l2_querymenu *qm return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); } -/* ------------------------------------------------------------------ */ +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx88_core *core = dev->core; -static int mpeg_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) + strcpy(cap->driver, "cx88_blackbird"); + strlcpy(cap->card, cx88_boards[core->board].name,sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + if (UNSET != core->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_fmt_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) { - struct cx8802_fh *fh = file->private_data; - struct cx8802_dev *dev = fh->dev; + if (f->index != 0) + return -EINVAL; + + strlcpy(f->description, "MPEG", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; +} + +static int vidioc_g_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ + f->fmt.pix.colorspace = 0; + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.field = fh->mpegq.field; + dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", + dev->width, dev->height, fh->mpegq.field ); + return 0; +} + +static int vidioc_try_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = 0; + dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", + dev->width, dev->height, fh->mpegq.field ); + return 0; +} + +static int vidioc_s_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; struct cx88_core *core = dev->core; - if (debug > 1) - v4l_print_ioctl(core->name,cmd); - - switch (cmd) { - - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx88_blackbird"); - strlcpy(cap->card, cx88_boards[core->board].name,sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - 0; - if (UNSET != core->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; + f->fmt.pix.colorspace = 0; + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + fh->mpegq.field = f->fmt.pix.field; + cx88_set_scale(core, f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + blackbird_api_cmd(dev, CX2341X_ENC_SET_FRAME_SIZE, 2, 0, + f->fmt.pix.height, f->fmt.pix.width); + dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); + return 0; +} - return 0; - } +static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_reqbufs(&fh->mpegq, p)); +} - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - int index; - - index = f->index; - if (index != 0) - return -EINVAL; - - memset(f,0,sizeof(*f)); - f->index = index; - strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->pixelformat = V4L2_PIX_FMT_MPEG; - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - - memset(f,0,sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ - f->fmt.pix.colorspace = 0; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.field = fh->mpegq.field; - dprintk(0,"VIDIOC_G_FMT: w: %d, h: %d, f: %d\n", - dev->width, dev->height, fh->mpegq.field ); - return 0; - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; - dprintk(0,"VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", - dev->width, dev->height, fh->mpegq.field ); - return 0; - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; - f->fmt.pix.bytesperline = 0; - f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; - f->fmt.pix.colorspace = 0; - dprintk(0,"VIDIOC_S_FMT: w: %d, h: %d, f: %d\n", - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field ); - return 0; - } +static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_querybuf(&fh->mpegq, p)); +} - /* --- streaming capture ------------------------------------- */ - case VIDIOC_REQBUFS: - return videobuf_reqbufs(&fh->mpegq, arg); +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_qbuf(&fh->mpegq, p)); +} - case VIDIOC_QUERYBUF: - return videobuf_querybuf(&fh->mpegq, arg); +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8802_fh *fh = priv; + return (videobuf_dqbuf(&fh->mpegq, p, + file->f_flags & O_NONBLOCK)); +} - case VIDIOC_QBUF: - return videobuf_qbuf(&fh->mpegq, arg); +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8802_fh *fh = priv; + return videobuf_streamon(&fh->mpegq); +} - case VIDIOC_DQBUF: - return videobuf_dqbuf(&fh->mpegq, arg, - file->f_flags & O_NONBLOCK); +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8802_fh *fh = priv; + return videobuf_streamoff(&fh->mpegq); +} - case VIDIOC_STREAMON: - return videobuf_streamon(&fh->mpegq); +static int vidioc_g_mpegcomp (struct file *file, void *fh, + struct v4l2_mpeg_compression *f) +{ + printk(KERN_WARNING "VIDIOC_G_MPEGCOMP is obsolete. " + "Replace with VIDIOC_G_EXT_CTRLS!"); + memcpy(f,&default_mpeg_params,sizeof(*f)); + return 0; +} - case VIDIOC_STREAMOFF: - return videobuf_streamoff(&fh->mpegq); +static int vidioc_s_mpegcomp (struct file *file, void *fh, + struct v4l2_mpeg_compression *f) +{ + printk(KERN_WARNING "VIDIOC_S_MPEGCOMP is obsolete. " + "Replace with VIDIOC_S_EXT_CTRLS!"); + return 0; +} - /* --- mpeg compression -------------------------------------- */ - case VIDIOC_G_MPEGCOMP: - { - struct v4l2_mpeg_compression *f = arg; +static int vidioc_g_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; - printk(KERN_WARNING "VIDIOC_G_MPEGCOMP is obsolete. " - "Replace with VIDIOC_G_EXT_CTRLS!"); - memcpy(f,&default_mpeg_params,sizeof(*f)); - return 0; - } - case VIDIOC_S_MPEGCOMP: - printk(KERN_WARNING "VIDIOC_S_MPEGCOMP is obsolete. " - "Replace with VIDIOC_S_EXT_CTRLS!"); - return 0; - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + return cx2341x_ext_ctrls(&dev->params, f, VIDIOC_G_EXT_CTRLS); +} - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - return cx2341x_ext_ctrls(&dev->params, f, cmd); - } - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *f = arg; - struct cx2341x_mpeg_params p; - int err; - - if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) - return -EINVAL; - p = dev->params; - err = cx2341x_ext_ctrls(&p, f, cmd); - if (err == 0 && cmd == VIDIOC_S_EXT_CTRLS) { - err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); - dev->params = p; - } - return err; - } - case VIDIOC_S_FREQUENCY: - { - blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, - BLACKBIRD_END_NOW, - BLACKBIRD_MPEG_CAPTURE, - BLACKBIRD_RAW_BITS_NONE); - - cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); - - blackbird_initialize_codec(dev); - cx88_set_scale(dev->core, dev->width, dev->height, - fh->mpegq.field); - return 0; +static int vidioc_s_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->params; + err = cx2341x_ext_ctrls(&p, f, VIDIOC_S_EXT_CTRLS); + if (!err) { + err = cx2341x_update(dev, blackbird_mbox_func, &dev->params, &p); + dev->params = p; } - case VIDIOC_LOG_STATUS: - { - char name[32 + 2]; - - snprintf(name, sizeof(name), "%s/2", core->name); - printk("%s/2: ============ START LOG STATUS ============\n", - core->name); - cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); - cx2341x_log_status(&dev->params, name); - printk("%s/2: ============= END LOG STATUS =============\n", - core->name); + return err; +} + +static int vidioc_try_ext_ctrls (struct file *file, void *priv, + struct v4l2_ext_controls *f) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx2341x_mpeg_params p; + int err; + + if (f->ctrl_class != V4L2_CTRL_CLASS_MPEG) + return -EINVAL; + p = dev->params; + err = cx2341x_ext_ctrls(&p, f, VIDIOC_TRY_EXT_CTRLS); + + return err; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8802_fh *fh = priv; + struct cx8802_dev *dev = fh->dev; + struct cx88_core *core = dev->core; + + blackbird_api_cmd(fh->dev, CX2341X_ENC_STOP_CAPTURE, 3, 0, + BLACKBIRD_END_NOW, + BLACKBIRD_MPEG_CAPTURE, + BLACKBIRD_RAW_BITS_NONE); + cx88_set_freq (core,f); + blackbird_initialize_codec(dev); + cx88_set_scale(dev->core, dev->width, dev->height, + fh->mpegq.field); + return 0; +} + +static int vidioc_log_status (struct file *file, void *priv) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + struct cx88_core *core = dev->core; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", core->name); + printk("%s/2: ============ START LOG STATUS ============\n", + core->name); + cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); + cx2341x_log_status(&dev->params, name); + printk("%s/2: ============= END LOG STATUS =============\n", + core->name); + return 0; +} + +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct cx8802_dev *dev = ((struct cx8802_fh *)priv)->dev; + + if (blackbird_queryctrl(dev, qctrl) == 0) return 0; - } - case VIDIOC_QUERYMENU: - return blackbird_querymenu(dev, arg); - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; - if (blackbird_queryctrl(dev, c) == 0) - return 0; - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl); - } + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (unlikely(qctrl->id == 0)) + return -EINVAL; + return cx8800_ctrl_query(qctrl); +} - default: - return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook); - } +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return cx88_enum_input (core,i); +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return + cx88_get_control(core,ctl); +} + +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + return + cx88_set_control(core,ctl); +} + +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8802_fh *fh = priv; + struct cx88_core *core = fh->dev->core; + + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = core->freq; + cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + + return 0; +} + +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + *i = core->input; + return 0; +} + +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + if (i >= 4) + return -EINVAL; + + mutex_lock(&core->lock); + cx88_newstation(core); + cx88_video_mux(core,i); + mutex_unlock(&core->lock); return 0; } -int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); -unsigned int (*cx88_ioctl_translator)(unsigned int cmd); +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + u32 reg; + + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + + cx88_get_stereo(core ,t); + reg = cx_read(MO_DEVICE_STATUS); + t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; + return 0; +} -static unsigned int mpeg_translate_ioctl(unsigned int cmd) +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) { - return cmd; + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + if (UNSET == core->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + cx88_set_stereo(core, t->audmode, 1); + return 0; } -static int mpeg_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) { - cmd = cx88_ioctl_translator( cmd ); - return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook); + struct cx88_core *core = ((struct cx8802_fh *)priv)->dev->core; + + mutex_lock(&core->lock); + cx88_set_tvnorm(core,*id); + mutex_unlock(&core->lock); + return 0; } +/* FIXME: cx88_ioctl_hook not implemented */ + static int mpeg_open(struct inode *inode, struct file *file) { int minor = iminor(inode); @@ -1059,17 +1197,47 @@ static const struct file_operations mpeg_fops = .read = mpeg_read, .poll = mpeg_poll, .mmap = mpeg_mmap, - .ioctl = mpeg_ioctl, + .ioctl = video_ioctl2, .llseek = no_llseek, }; static struct video_device cx8802_mpeg_template = { - .name = "cx8802", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, - .hardware = 0, - .fops = &mpeg_fops, - .minor = -1, + .name = "cx8802", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, + .fops = &mpeg_fops, + .minor = -1, + .vidioc_querymenu = vidioc_querymenu, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_mpegcomp = vidioc_g_mpegcomp, + .vidioc_s_mpegcomp = vidioc_s_mpegcomp, + .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls, + .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_log_status = vidioc_log_status, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_s_std = vidioc_s_std, + .tvnorms = CX88_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; /* ------------------------------------------------------------------ */ @@ -1164,7 +1332,9 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) cx2341x_fill_defaults(&dev->params); dev->params.port = CX2341X_PORT_STREAMING; - if (core->tvnorm->id & V4L2_STD_525_60) { + cx8802_mpeg_template.current_norm = core->tvnorm; + + if (core->tvnorm & V4L2_STD_525_60) { dev->height = 480; } else { dev->height = 576; @@ -1178,6 +1348,11 @@ static int cx8802_blackbird_probe(struct cx8802_driver *drv) blackbird_register_video(dev); /* initial device configuration: needed ? */ + mutex_lock(&dev->core->lock); +// init_controls(core); + cx88_set_tvnorm(core,core->tvnorm); + cx88_video_mux(core,0); + mutex_unlock(&dev->core->lock); return 0; @@ -1212,8 +1387,6 @@ static int blackbird_init(void) printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n", SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); #endif - cx88_ioctl_hook = mpeg_do_ioctl; - cx88_ioctl_translator = mpeg_translate_ioctl; return cx8802_register_driver(&cx8802_blackbird_driver); } @@ -1225,8 +1398,8 @@ static void blackbird_fini(void) module_init(blackbird_init); module_exit(blackbird_fini); -EXPORT_SYMBOL(cx88_ioctl_hook); -EXPORT_SYMBOL(cx88_ioctl_translator); +module_param_named(video_debug,cx8802_mpeg_template.debug, int, 0644); +MODULE_PARM_DESC(debug,"enable debug messages [video]"); /* ----------------------------------------------------------- */ /* diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 434b78a..e61102d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -764,6 +764,12 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 2, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, }}, .mpeg = CX88_MPEG_DVB, }, @@ -879,6 +885,12 @@ struct cx88_board cx88_boards[] = { .input = {{ .type = CX88_VMUX_DVB, .vmux = 0, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, }}, .mpeg = CX88_MPEG_DVB, }, @@ -1531,10 +1543,10 @@ struct cx88_subid cx88_subids[] = { },{ .subvendor = 0x17de, .subdevice = 0x0840, - .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, - },{ - .subvendor = 0x1421, - .subdevice = 0x0305, + .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, + },{ + .subvendor = 0x1421, + .subdevice = 0x0305, .card = CX88_BOARD_KWORLD_HARDWARE_MPEG_TV_XPERT, },{ .subvendor = 0x18ac, @@ -1625,6 +1637,10 @@ struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x1402, .card = CX88_BOARD_HAUPPAUGE_HVR3000, + },{ + .subvendor = 0x1421, + .subdevice = 0x0341, /* ADS Tech InstantTV DVB-S */ + .card = CX88_BOARD_KWORLD_DVBS_100, }, }; const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); @@ -1780,7 +1796,7 @@ static void dvico_fusionhdtv_hybrid_init(struct cx88_core *core) { 0x03, 0x0C }, }; - for (i = 0; i < 13; i++) { + for (i = 0; i < ARRAY_SIZE(init_bufs); i++) { msg.buf = init_bufs[i]; msg.len = (i != 12 ? 5 : 2); err = i2c_transfer(&core->i2c_adap, &msg, 1); @@ -1907,12 +1923,21 @@ void cx88_card_setup(struct cx88_core *core) if (0 == core->i2c_rc) { /* enable tuner */ int i; - static const u8 buffer [] = { 0x10,0x12,0x13,0x04,0x16,0x00,0x14,0x04,0x017,0x00 }; + static const u8 buffer [][2] = { + {0x10,0x12}, + {0x13,0x04}, + {0x16,0x00}, + {0x14,0x04}, + {0x17,0x00} + }; core->i2c_client.addr = 0x0a; - for (i = 0; i < 5; i++) - if (2 != i2c_master_send(&core->i2c_client,&buffer[i*2],2)) - printk(KERN_WARNING "%s: Unable to enable tuner(%i).\n", + for (i = 0; i < ARRAY_SIZE(buffer); i++) + if (2 != i2c_master_send(&core->i2c_client, + buffer[i],2)) + printk(KERN_WARNING + "%s: Unable to enable " + "tuner(%i).\n", core->name, i); } break; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 1899736..f31ec96 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -5,6 +5,11 @@ * * (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Multituner support + * - video_ioctl2 conversion + * - PAL/M fixes + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -484,12 +489,12 @@ static char *cx88_pci_irqs[32] = { }; void cx88_print_irqbits(char *name, char *tag, char **strings, - u32 bits, u32 mask) + int len, u32 bits, u32 mask) { unsigned int i; printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); - for (i = 0; i < 32; i++) { + for (i = 0; i < len; i++) { if (!(bits & (1 << i))) continue; if (strings[i]) @@ -515,8 +520,8 @@ int cx88_core_irq(struct cx88_core *core, u32 status) } if (!handled) cx88_print_irqbits(core->name, "irq pci", - cx88_pci_irqs, status, - core->pci_irqmask); + cx88_pci_irqs, ARRAY_SIZE(cx88_pci_irqs), + status, core->pci_irqmask); return handled; } @@ -631,30 +636,30 @@ int cx88_reset(struct cx88_core *core) /* ------------------------------------------------------------------ */ -static unsigned int inline norm_swidth(struct cx88_tvnorm *norm) +static unsigned int inline norm_swidth(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922; } -static unsigned int inline norm_hdelay(struct cx88_tvnorm *norm) +static unsigned int inline norm_hdelay(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 135 : 186; } -static unsigned int inline norm_vdelay(struct cx88_tvnorm *norm) +static unsigned int inline norm_vdelay(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18; + return (norm & V4L2_STD_625_50) ? 0x24 : 0x18; } -static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) +static unsigned int inline norm_fsc8(v4l2_std_id norm) { - if (norm->id & V4L2_STD_PAL_M) + if (norm & V4L2_STD_PAL_M) return 28604892; // 3.575611 MHz - if (norm->id & (V4L2_STD_PAL_Nc)) + if (norm & (V4L2_STD_PAL_Nc)) return 28656448; // 3.582056 MHz - if (norm->id & V4L2_STD_NTSC) // All NTSC/M and variants + if (norm & V4L2_STD_NTSC) // All NTSC/M and variants return 28636360; // 3.57954545 MHz +/- 10 Hz /* SECAM have also different sub carrier for chroma, @@ -666,20 +671,20 @@ static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm) return 35468950; // 4.43361875 MHz +/- 5 Hz } -static unsigned int inline norm_htotal(struct cx88_tvnorm *norm) +static unsigned int inline norm_htotal(v4l2_std_id norm) { unsigned int fsc4=norm_fsc8(norm)/2; /* returns 4*FSC / vtotal / frames per seconds */ - return (norm->id & V4L2_STD_625_50) ? + return (norm & V4L2_STD_625_50) ? ((fsc4+312)/625+12)/25 : ((fsc4+262)/525*1001+15000)/30000; } -static unsigned int inline norm_vbipack(struct cx88_tvnorm *norm) +static unsigned int inline norm_vbipack(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 511 : 400; + return (norm & V4L2_STD_625_50) ? 511 : 400; } int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, @@ -692,7 +697,7 @@ int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int heig dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height, V4L2_FIELD_HAS_TOP(field) ? "T" : "", V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "", - core->tvnorm->name); + v4l2_norm_to_name(core->tvnorm)); if (!V4L2_FIELD_HAS_BOTH(field)) height *= 2; @@ -729,7 +734,7 @@ int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int heig // setup filters value = 0; value |= (1 << 19); // CFILT (default) - if (core->tvnorm->id & V4L2_STD_SECAM) { + if (core->tvnorm & V4L2_STD_SECAM) { value |= (1 << 15); value |= (1 << 16); } @@ -826,36 +831,36 @@ int cx88_stop_audio_dma(struct cx88_core *core) static int set_tvaudio(struct cx88_core *core) { - struct cx88_tvnorm *norm = core->tvnorm; + v4l2_std_id norm = core->tvnorm; if (CX88_VMUX_TELEVISION != INPUT(core->input)->type) return 0; - if (V4L2_STD_PAL_BG & norm->id) { + if (V4L2_STD_PAL_BG & norm) { core->tvaudio = WW_BG; - } else if (V4L2_STD_PAL_DK & norm->id) { + } else if (V4L2_STD_PAL_DK & norm) { core->tvaudio = WW_DK; - } else if (V4L2_STD_PAL_I & norm->id) { + } else if (V4L2_STD_PAL_I & norm) { core->tvaudio = WW_I; - } else if (V4L2_STD_SECAM_L & norm->id) { + } else if (V4L2_STD_SECAM_L & norm) { core->tvaudio = WW_L; - } else if (V4L2_STD_SECAM_DK & norm->id) { + } else if (V4L2_STD_SECAM_DK & norm) { core->tvaudio = WW_DK; - } else if ((V4L2_STD_NTSC_M & norm->id) || - (V4L2_STD_PAL_M & norm->id)) { + } else if ((V4L2_STD_NTSC_M & norm) || + (V4L2_STD_PAL_M & norm)) { core->tvaudio = WW_BTSC; - } else if (V4L2_STD_NTSC_M_JP & norm->id) { + } else if (V4L2_STD_NTSC_M_JP & norm) { core->tvaudio = WW_EIAJ; } else { printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", - core->name, norm->name); + core->name, v4l2_norm_to_name(core->tvnorm)); core->tvaudio = 0; return 0; } @@ -874,7 +879,7 @@ static int set_tvaudio(struct cx88_core *core) -int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) +int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) { u32 fsc8; u32 adc_clock; @@ -882,6 +887,7 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) u32 step_db,step_dr; u64 tmp64; u32 bdelay,agcdelay,htotal; + u32 cxiformat, cxoformat; core->tvnorm = norm; fsc8 = norm_fsc8(norm); @@ -890,23 +896,51 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) step_db = fsc8; step_dr = fsc8; - if (norm->id & V4L2_STD_SECAM) { + if (norm & V4L2_STD_NTSC_M_JP) { + cxiformat = VideoFormatNTSCJapan; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_NTSC_443) { + cxiformat = VideoFormatNTSC443; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_PAL_M) { + cxiformat = VideoFormatPALM; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_N) { + cxiformat = VideoFormatPALN; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_Nc) { + cxiformat = VideoFormatPALNC; + cxoformat = 0x1c1f0008; + } else if (norm & V4L2_STD_PAL_60) { + cxiformat = VideoFormatPAL60; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_NTSC) { + cxiformat = VideoFormatNTSC; + cxoformat = 0x181f0008; + } else if (norm & V4L2_STD_SECAM) { step_db = 4250000 * 8; step_dr = 4406250 * 8; + + cxiformat = VideoFormatSECAM; + cxoformat = 0x181f0008; + } else { /* PAL */ + cxiformat = VideoFormatPAL; + cxoformat = 0x181f0008; } dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n", - norm->name, fsc8, adc_clock, vdec_clock, step_db, step_dr); + v4l2_norm_to_name(core->tvnorm), fsc8, adc_clock, vdec_clock, + step_db, step_dr); set_pll(core,2,vdec_clock); dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); - cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat); + cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f); + cx_andor(MO_INPUT_FORMAT, 0xf, cxiformat); // FIXME: as-is from DScaler dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n", - norm->cxoformat, cx_read(MO_OUTPUT_FORMAT)); - cx_write(MO_OUTPUT_FORMAT, norm->cxoformat); + cxoformat, cx_read(MO_OUTPUT_FORMAT)); + cx_write(MO_OUTPUT_FORMAT, cxoformat); // MO_SCONV_REG = adc clock / video dec clock * 2^17 tmp64 = adc_clock * (u64)(1 << 17); @@ -955,7 +989,7 @@ int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm) set_tvaudio(core); // tell i2c chips - cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm->id); + cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm); // done return 0; diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 8b20335..dbfe4dc 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -35,14 +35,13 @@ #include "mt352.h" #include "mt352_priv.h" -#ifdef HAVE_VP3054_I2C +#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) # include "cx88-vp3054-i2c.h" #endif #include "zl10353.h" #include "cx22702.h" #include "or51132.h" #include "lgdt330x.h" -#include "lgh06xf.h" #include "nxt200x.h" #include "cx24123.h" #include "isl6421.h" @@ -200,7 +199,7 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; -#ifdef HAVE_VP3054_I2C +#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { static u8 clock_config [] = { 0x89, 0x38, 0x38 }; @@ -476,6 +475,8 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_WINFAST_DTV2000H: case CX88_BOARD_HAUPPAUGE_HVR1100: case CX88_BOARD_HAUPPAUGE_HVR1100LP: + case CX88_BOARD_HAUPPAUGE_HVR1300: + case CX88_BOARD_HAUPPAUGE_HVR3000: dev->dvb.frontend = dvb_attach(cx22702_attach, &hauppauge_hvr_config, &dev->core->i2c_adap); @@ -543,7 +544,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: -#ifdef HAVE_VP3054_I2C +#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) dev->core->pll_addr = 0x61; dev->core->pll_desc = &dvb_pll_fmd1216me; dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config, @@ -631,8 +632,9 @@ static int dvb_register(struct cx8802_dev *dev) &fusionhdtv_5_gold, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(lgh06xf_attach, dev->dvb.frontend, - &dev->core->i2c_adap); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); } } break; @@ -650,8 +652,9 @@ static int dvb_register(struct cx8802_dev *dev) &pchdtv_hd5500, &dev->core->i2c_adap); if (dev->dvb.frontend != NULL) { - dvb_attach(lgh06xf_attach, dev->dvb.frontend, - &dev->core->i2c_adap); + dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, + &dev->core->i2c_adap, + &dvb_pll_lg_tdvs_h06xf); } } break; @@ -692,24 +695,6 @@ static int dvb_register(struct cx8802_dev *dev) dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage; } break; - case CX88_BOARD_HAUPPAUGE_HVR1300: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, &dvb_pll_fmd1216me); - } - break; - case CX88_BOARD_HAUPPAUGE_HVR3000: - dev->dvb.frontend = dvb_attach(cx22702_attach, - &hauppauge_hvr_config, - &dev->core->i2c_adap); - if (dev->dvb.frontend != NULL) { - dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61, - &dev->core->i2c_adap, &dvb_pll_fmd1216me); - } - break; default: printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n", dev->core->name); @@ -793,7 +778,7 @@ static int cx8802_dvb_probe(struct cx8802_driver *drv) if (!(cx88_boards[core->board].mpeg & CX88_MPEG_DVB)) goto fail_core; -#ifdef HAVE_VP3054_I2C +#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) err = vp3054_i2c_probe(dev); if (0 != err) goto fail_core; @@ -822,7 +807,7 @@ static int cx8802_dvb_remove(struct cx8802_driver *drv) /* dvb */ videobuf_dvb_unregister(&dev->dvb); -#ifdef HAVE_VP3054_I2C +#if defined(CONFIG_VIDEO_CX88_VP3054) || defined(CONFIG_VIDEO_CX88_VP3054_MODULE) vp3054_i2c_remove(dev); #endif diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 88af23a..7919a1f 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -1,3 +1,4 @@ + /* cx88-i2c.c -- all the i2c code is here @@ -145,6 +146,7 @@ void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) if (0 != core->i2c_rc) return; +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) if ( (core->dvbdev) && (core->dvbdev->dvb.frontend) ) { if (core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl) core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl(core->dvbdev->dvb.frontend, 1); @@ -154,6 +156,7 @@ void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) if (core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl) core->dvbdev->dvb.frontend->ops.i2c_gate_ctrl(core->dvbdev->dvb.frontend, 0); } else +#endif i2c_clients_command(&core->i2c_adap, cmd, arg); } @@ -193,7 +196,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c) unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index 1fe1a83..b2eb32e 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -49,6 +49,27 @@ MODULE_PARM_DESC(debug,"enable debug messages [mpeg]"); #define mpeg_dbg(level,fmt, arg...) if (debug >= level) \ printk(KERN_DEBUG "%s/2-mpeg: " fmt, core->name, ## arg) +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct cx8802_dev *dev=container_of(work, struct cx8802_dev, request_module_wk); + + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_DVB) + request_module("cx88-dvb"); + if (cx88_boards[dev->core->board].mpeg & CX88_MPEG_BLACKBIRD) + request_module("cx88-blackbird"); +} + +static void request_modules(struct cx8802_dev *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + + static LIST_HEAD(cx8802_devlist); /* ------------------------------------------------------------------ */ @@ -345,7 +366,8 @@ static void cx8802_mpeg_irq(struct cx8802_dev *dev) if (debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq mpeg ", - cx88_mpeg_irqs, status, mask); + cx88_mpeg_irqs, ARRAY_SIZE(cx88_mpeg_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { @@ -427,7 +449,7 @@ int cx8802_init_common(struct cx8802_dev *dev) if (pci_enable_device(dev->pci)) return -EIO; pci_set_master(dev->pci); - if (!pci_dma_supported(dev->pci,0xffffffff)) { + if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) { printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); return -EIO; } @@ -778,6 +800,9 @@ static int __devinit cx8802_probe(struct pci_dev *pci_dev, /* Maintain a reference so cx88-video can query the 8802 device. */ core->dvbdev = dev; + + /* now autoload cx88-dvb or cx88-blackbird */ + request_modules(dev); return 0; fail_free: diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 063df03..97ef421 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -797,55 +797,6 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) Add some code here later. */ -# if 0 - t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | - V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - t->rxsubchans = V4L2_TUNER_SUB_MONO; - t->audmode = V4L2_TUNER_MODE_MONO; - - switch (core->tvaudio) { - case WW_BTSC: - t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - if (1 == pilot) { - /* SAP */ - t->rxsubchans |= V4L2_TUNER_SUB_SAP; - } - break; - case WW_A2_BG: - case WW_A2_DK: - case WW_A2_M: - if (1 == pilot) { - /* stereo */ - t->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (0 == mode) - t->audmode = V4L2_TUNER_MODE_STEREO; - } - if (2 == pilot) { - /* dual language -- FIXME */ - t->rxsubchans = - V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - t->audmode = V4L2_TUNER_MODE_LANG1; - } - break; - case WW_NICAM_BGDKL: - if (0 == mode) { - t->audmode = V4L2_TUNER_MODE_STEREO; - t->rxsubchans |= V4L2_TUNER_SUB_STEREO; - } - break; - case WW_SYSTEM_L_AM: - if (0x0 == mode && !(cx_read(AUD_INIT) & 0x04)) { - t->audmode = V4L2_TUNER_MODE_STEREO; - t->rxsubchans |= V4L2_TUNER_SUB_STEREO; - } - break; - default: - /* nothing */ - break; - } -# endif return; } diff --git a/drivers/media/video/cx88/cx88-vbi.c b/drivers/media/video/cx88/cx88-vbi.c index aa2a697..86c1cf8 100644 --- a/drivers/media/video/cx88/cx88-vbi.c +++ b/drivers/media/video/cx88/cx88-vbi.c @@ -21,9 +21,11 @@ MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]"); /* ------------------------------------------------------------------ */ -void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) +int cx8800_vbi_fmt (struct file *file, void *priv, + struct v4l2_format *f) { - memset(&f->fmt.vbi,0,sizeof(f->fmt.vbi)); + struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; @@ -31,18 +33,19 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f) f->fmt.vbi.count[0] = VBI_LINE_COUNT; f->fmt.vbi.count[1] = VBI_LINE_COUNT; - if (dev->core->tvnorm->id & V4L2_STD_525_60) { + if (dev->core->tvnorm & V4L2_STD_525_60) { /* ntsc */ f->fmt.vbi.sampling_rate = 28636363; f->fmt.vbi.start[0] = 10; f->fmt.vbi.start[1] = 273; - } else if (dev->core->tvnorm->id & V4L2_STD_625_50) { + } else if (dev->core->tvnorm & V4L2_STD_625_50) { /* pal */ f->fmt.vbi.sampling_rate = 35468950; f->fmt.vbi.start[0] = 7 -1; f->fmt.vbi.start[1] = 319 -1; } + return 0; } static int cx8800_start_vbi_dma(struct cx8800_dev *dev, diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index c86a7e0..fbce1d5 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1,3 +1,4 @@ + /* * * device driver for Conexant 2388x based TV cards @@ -5,6 +6,11 @@ * * (c) 2003-04 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] * + * (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org> + * - Multituner support + * - video_ioctl2 conversion + * - PAL/M fixes + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -80,65 +86,6 @@ static LIST_HEAD(cx8800_devlist); /* ------------------------------------------------------------------- */ /* static data */ -static struct cx88_tvnorm tvnorms[] = { - { - .name = "NTSC-M", - .id = V4L2_STD_NTSC_M, - .cxiformat = VideoFormatNTSC, - .cxoformat = 0x181f0008, - },{ - .name = "NTSC-JP", - .id = V4L2_STD_NTSC_M_JP, - .cxiformat = VideoFormatNTSCJapan, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-BG", - .id = V4L2_STD_PAL_BG, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-DK", - .id = V4L2_STD_PAL_DK, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-I", - .id = V4L2_STD_PAL_I, - .cxiformat = VideoFormatPAL, - .cxoformat = 0x181f0008, - },{ - .name = "PAL-M", - .id = V4L2_STD_PAL_M, - .cxiformat = VideoFormatPALM, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-N", - .id = V4L2_STD_PAL_N, - .cxiformat = VideoFormatPALN, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-Nc", - .id = V4L2_STD_PAL_Nc, - .cxiformat = VideoFormatPALNC, - .cxoformat = 0x1c1f0008, - },{ - .name = "PAL-60", - .id = V4L2_STD_PAL_60, - .cxiformat = VideoFormatPAL60, - .cxoformat = 0x181f0008, - },{ - .name = "SECAM-L", - .id = V4L2_STD_SECAM_L, - .cxiformat = VideoFormatSECAM, - .cxoformat = 0x181f0008, - },{ - .name = "SECAM-DK", - .id = V4L2_STD_SECAM_DK, - .cxiformat = VideoFormatSECAM, - .cxoformat = 0x181f0008, - } -}; - static struct cx8800_fmt formats[] = { { .name = "8 bpp, gray", @@ -364,14 +311,6 @@ int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl) } EXPORT_SYMBOL(cx8800_ctrl_query); -static int cx88_queryctrl(struct v4l2_queryctrl *qctrl) -{ - qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); - if (qctrl->id == 0) - return -EINVAL; - return cx8800_ctrl_query(qctrl); -} - /* ------------------------------------------------------------------- */ /* resource management */ @@ -424,8 +363,7 @@ void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits) /* ------------------------------------------------------------------ */ -/* static int video_mux(struct cx8800_dev *dev, unsigned int input) */ -static int video_mux(struct cx88_core *core, unsigned int input) +int cx88_video_mux(struct cx88_core *core, unsigned int input) { /* struct cx88_core *core = dev->core; */ @@ -464,6 +402,7 @@ static int video_mux(struct cx88_core *core, unsigned int input) } return 0; } +EXPORT_SYMBOL(cx88_video_mux); /* ------------------------------------------------------------------ */ @@ -944,19 +883,18 @@ video_mmap(struct file *file, struct vm_area_struct * vma) } /* ------------------------------------------------------------------ */ +/* VIDEO CTRL IOCTLS */ -/* static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */ -static int get_control(struct cx88_core *core, struct v4l2_control *ctl) +int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl) { - /* struct cx88_core *core = dev->core; */ - struct cx88_ctrl *c = NULL; + struct cx88_ctrl *c = NULL; u32 value; int i; for (i = 0; i < CX8800_CTLS; i++) if (cx8800_ctls[i].v.id == ctl->id) c = &cx8800_ctls[i]; - if (NULL == c) + if (unlikely(NULL == c)) return -EINVAL; value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg); @@ -977,20 +915,20 @@ static int get_control(struct cx88_core *core, struct v4l2_control *ctl) value,c->mask, c->sreg ? " [shadowed]" : ""); return 0; } +EXPORT_SYMBOL(cx88_get_control); -/* static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) */ -static int set_control(struct cx88_core *core, struct v4l2_control *ctl) +int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl) { - /* struct cx88_core *core = dev->core; */ struct cx88_ctrl *c = NULL; u32 value,mask; int i; + for (i = 0; i < CX8800_CTLS; i++) { if (cx8800_ctls[i].v.id == ctl->id) { c = &cx8800_ctls[i]; } } - if (NULL == c) + if (unlikely(NULL == c)) return -EINVAL; if (ctl->value < c->v.minimum) @@ -1010,7 +948,7 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) value = ((ctl->value - c->off) << c->shift) & c->mask; - if (core->tvnorm->id & V4L2_STD_SECAM) { + if (core->tvnorm & V4L2_STD_SECAM) { /* For SECAM, both U and V sat should be equal */ value=value<<8|value; } else { @@ -1033,6 +971,7 @@ static int set_control(struct cx88_core *core, struct v4l2_control *ctl) } return 0; } +EXPORT_SYMBOL(cx88_set_control); static void init_controls(struct cx88_core *core) { @@ -1042,648 +981,531 @@ static void init_controls(struct cx88_core *core) for (i = 0; i < CX8800_CTLS; i++) { ctrl.id=cx8800_ctls[i].v.id; ctrl.value=cx8800_ctls[i].v.default_value; - set_control(core, &ctrl); + + cx88_set_control(core, &ctrl); } } /* ------------------------------------------------------------------ */ +/* VIDEO IOCTLS */ -static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, +static int vidioc_g_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx8800_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_try_fmt_cap (struct file *file, void *priv, struct v4l2_format *f) { - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memset(&f->fmt.pix,0,sizeof(f->fmt.pix)); - f->fmt.pix.width = fh->width; - f->fmt.pix.height = fh->height; - f->fmt.pix.field = fh->vidq.field; - f->fmt.pix.pixelformat = fh->fmt->fourcc; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fh->fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + struct cx8800_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = norm_maxw(core->tvnorm); + maxh = norm_maxh(core->tvnorm); + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + } + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; default: return -EINVAL; } + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; } -static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, - struct v4l2_format *f) +static int vidioc_s_fmt_cap (struct file *file, void *priv, + struct v4l2_format *f) { - struct cx88_core *core = dev->core; + struct cx8800_fh *fh = priv; + int err = vidioc_try_fmt_cap (file,priv,f); - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - struct cx8800_fmt *fmt; - enum v4l2_field field; - unsigned int maxw, maxh; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - if (NULL == fmt) - return -EINVAL; - - field = f->fmt.pix.field; - maxw = norm_maxw(core->tvnorm); - maxh = norm_maxh(core->tvnorm); - - if (V4L2_FIELD_ANY == field) { - field = (f->fmt.pix.height > maxh/2) - ? V4L2_FIELD_INTERLACED - : V4L2_FIELD_BOTTOM; - } + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + return 0; +} - switch (field) { - case V4L2_FIELD_TOP: - case V4L2_FIELD_BOTTOM: - maxh = maxh / 2; - break; - case V4L2_FIELD_INTERLACED: - break; - default: - return -EINVAL; - } +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; + struct cx88_core *core = dev->core; - f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; + strcpy(cap->driver, "cx8800"); + strlcpy(cap->card, cx88_boards[core->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_VBI_CAPTURE; + if (UNSET != core->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} - return 0; - } - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; - default: +static int vidioc_enum_fmt_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(formats))) return -EINVAL; - } + + strlcpy(f->description,formats[f->index].name,sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; + + return 0; } -static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, - struct v4l2_format *f) +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf) { + struct cx8800_fh *fh = priv; + struct videobuf_queue *q; + struct v4l2_requestbuffers req; + unsigned int i; int err; - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - err = cx8800_try_fmt(dev,fh,f); - if (0 != err) - return err; + q = get_queue(fh); + memset(&req,0,sizeof(req)); + req.type = q->type; + req.count = 8; + req.memory = V4L2_MEMORY_MMAP; + err = videobuf_reqbufs(q,&req); + if (err < 0) + return err; - fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); - fh->width = f->fmt.pix.width; - fh->height = f->fmt.pix.height; - fh->vidq.field = f->fmt.pix.field; - return 0; - case V4L2_BUF_TYPE_VBI_CAPTURE: - cx8800_vbi_fmt(dev, f); - return 0; - default: - return -EINVAL; + mbuf->frames = req.count; + mbuf->size = 0; + for (i = 0; i < mbuf->frames; i++) { + mbuf->offsets[i] = q->bufs[i]->boff; + mbuf->size += q->bufs[i]->bsize; } + return 0; } +#endif -/* - * This function is _not_ called directly, but from - * video_generic_ioctl (and maybe others). userspace - * copying is done already, arg is a kernel pointer. - */ -static int video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_reqbufs(get_queue(fh), p)); +} + +static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_querybuf(get_queue(fh), p)); +} + +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_qbuf(get_queue(fh), p)); +} + +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx8800_fh *fh = priv; + return (videobuf_dqbuf(get_queue(fh), p, + file->f_flags & O_NONBLOCK)); +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { - struct cx8800_fh *fh = file->private_data; + struct cx8800_fh *fh = priv; struct cx8800_dev *dev = fh->dev; - struct cx88_core *core = dev->core; - int err; - if (video_debug > 1) - v4l_print_ioctl(core->name,cmd); - switch (cmd) { + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) + return -EINVAL; + if (unlikely(i != fh->type)) + return -EINVAL; - /* --- capabilities ------------------------------------------ */ - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[core->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING | - V4L2_CAP_VBI_CAPTURE | - 0; - if (UNSET != core->tuner_type) - cap->capabilities |= V4L2_CAP_TUNER; - return 0; - } + if (unlikely(!res_get(dev,fh,get_ressource(fh)))) + return -EBUSY; + return videobuf_streamon(get_queue(fh)); +} - /* --- capture ioctls ---------------------------------------- */ - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = arg; - enum v4l2_buf_type type; - unsigned int index; - - index = f->index; - type = f->type; - switch (type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (index >= ARRAY_SIZE(formats)) - return -EINVAL; - memset(f,0,sizeof(*f)); - f->index = index; - f->type = type; - strlcpy(f->description,formats[index].name,sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - break; - default: - return -EINVAL; - } - return 0; - } - case VIDIOC_G_FMT: - { - struct v4l2_format *f = arg; - return cx8800_g_fmt(dev,fh,f); - } - case VIDIOC_S_FMT: - { - struct v4l2_format *f = arg; - return cx8800_s_fmt(dev,fh,f); - } - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = arg; - return cx8800_try_fmt(dev,fh,f); - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - /* --- streaming capture ------------------------------------- */ - case VIDIOCGMBUF: - { - struct video_mbuf *mbuf = arg; - struct videobuf_queue *q; - struct v4l2_requestbuffers req; - unsigned int i; - - q = get_queue(fh); - memset(&req,0,sizeof(req)); - req.type = q->type; - req.count = 8; - req.memory = V4L2_MEMORY_MMAP; - err = videobuf_reqbufs(q,&req); - if (err < 0) - return err; - memset(mbuf,0,sizeof(*mbuf)); - mbuf->frames = req.count; - mbuf->size = 0; - for (i = 0; i < mbuf->frames; i++) { - mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; - } - return 0; - } -#endif - case VIDIOC_REQBUFS: - return videobuf_reqbufs(get_queue(fh), arg); +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx8800_fh *fh = priv; + struct cx8800_dev *dev = fh->dev; + int err, res; - case VIDIOC_QUERYBUF: - return videobuf_querybuf(get_queue(fh), arg); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; - case VIDIOC_QBUF: - return videobuf_qbuf(get_queue(fh), arg); + res = get_ressource(fh); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev,fh,res); + return 0; +} - case VIDIOC_DQBUF: - return videobuf_dqbuf(get_queue(fh), arg, - file->f_flags & O_NONBLOCK); +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - case VIDIOC_STREAMON: - { - int res = get_ressource(fh); + mutex_lock(&core->lock); + cx88_set_tvnorm(core,*tvnorms); + mutex_unlock(&core->lock); - if (!res_get(dev,fh,res)) - return -EBUSY; - return videobuf_streamon(get_queue(fh)); - } - case VIDIOC_STREAMOFF: - { - int res = get_ressource(fh); + return 0; +} - err = videobuf_streamoff(get_queue(fh)); - if (err < 0) - return err; - res_free(dev,fh,res); - return 0; - } - default: - return cx88_do_ioctl( inode, file, fh->radio, core, cmd, arg, video_do_ioctl ); - } +/* only one input in this sample driver */ +int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i) +{ + static const char *iname[] = { + [ CX88_VMUX_COMPOSITE1 ] = "Composite1", + [ CX88_VMUX_COMPOSITE2 ] = "Composite2", + [ CX88_VMUX_COMPOSITE3 ] = "Composite3", + [ CX88_VMUX_COMPOSITE4 ] = "Composite4", + [ CX88_VMUX_SVIDEO ] = "S-Video", + [ CX88_VMUX_TELEVISION ] = "Television", + [ CX88_VMUX_CABLE ] = "Cable TV", + [ CX88_VMUX_DVB ] = "DVB", + [ CX88_VMUX_DEBUG ] = "for debug only", + }; + unsigned int n; + + n = i->index; + if (n >= 4) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + memset(i,0,sizeof(*i)); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name,iname[INPUT(n)->type]); + if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || + (CX88_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + i->std = CX88_NORMS; return 0; } +EXPORT_SYMBOL(cx88_enum_input); -int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, - struct cx88_core *core, unsigned int cmd, void *arg, v4l2_kioctl driver_ioctl) +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *i) { - int err; + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return cx88_enum_input (core,i); +} - if (video_debug) { - if (video_debug > 1) { - if (_IOC_DIR(cmd) & _IOC_WRITE) - v4l_printk_ioctl_arg("cx88(w)",cmd, arg); - else if (!_IOC_DIR(cmd) & _IOC_READ) { - v4l_print_ioctl("cx88", cmd); - } - } else - v4l_print_ioctl(core->name,cmd); - - } - - switch (cmd) { - /* ---------- tv norms ---------- */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = arg; - unsigned int i; - - i = e->index; - if (i >= ARRAY_SIZE(tvnorms)) - return -EINVAL; - err = v4l2_video_std_construct(e, tvnorms[e->index].id, - tvnorms[e->index].name); - e->index = i; - if (err < 0) - return err; - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - *id = core->tvnorm->id; - return 0; - } - case VIDIOC_S_STD: - { - v4l2_std_id *id = arg; - unsigned int i; + *i = core->input; + return 0; +} - for(i = 0; i < ARRAY_SIZE(tvnorms); i++) - if (*id & tvnorms[i].id) - break; - if (i == ARRAY_SIZE(tvnorms)) - return -EINVAL; +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - mutex_lock(&core->lock); - cx88_set_tvnorm(core,&tvnorms[i]); - mutex_unlock(&core->lock); - return 0; - } + if (i >= 4) + return -EINVAL; - /* ------ input switching ---------- */ - case VIDIOC_ENUMINPUT: - { - static const char *iname[] = { - [ CX88_VMUX_COMPOSITE1 ] = "Composite1", - [ CX88_VMUX_COMPOSITE2 ] = "Composite2", - [ CX88_VMUX_COMPOSITE3 ] = "Composite3", - [ CX88_VMUX_COMPOSITE4 ] = "Composite4", - [ CX88_VMUX_SVIDEO ] = "S-Video", - [ CX88_VMUX_TELEVISION ] = "Television", - [ CX88_VMUX_CABLE ] = "Cable TV", - [ CX88_VMUX_DVB ] = "DVB", - [ CX88_VMUX_DEBUG ] = "for debug only", - }; - struct v4l2_input *i = arg; - unsigned int n; - - n = i->index; - if (n >= 4) - return -EINVAL; - if (0 == INPUT(n)->type) - return -EINVAL; - memset(i,0,sizeof(*i)); - i->index = n; - i->type = V4L2_INPUT_TYPE_CAMERA; - strcpy(i->name,iname[INPUT(n)->type]); - if ((CX88_VMUX_TELEVISION == INPUT(n)->type) || - (CX88_VMUX_CABLE == INPUT(n)->type)) - i->type = V4L2_INPUT_TYPE_TUNER; - for (n = 0; n < ARRAY_SIZE(tvnorms); n++) - i->std |= tvnorms[n].id; - return 0; - } - case VIDIOC_G_INPUT: - { - unsigned int *i = arg; + mutex_lock(&core->lock); + cx88_newstation(core); + cx88_video_mux(core,i); + mutex_unlock(&core->lock); + return 0; +} - *i = core->input; - return 0; - } - case VIDIOC_S_INPUT: - { - unsigned int *i = arg; - if (*i >= 4) - return -EINVAL; - mutex_lock(&core->lock); - cx88_newstation(core); - video_mux(core,*i); - mutex_unlock(&core->lock); - return 0; - } +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (unlikely(qctrl->id == 0)) + return -EINVAL; + return cx8800_ctrl_query(qctrl); +} +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return + cx88_get_control(core,ctl); +} - /* --- controls ---------------------------------------------- */ - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + return + cx88_set_control(core,ctl); +} - return cx88_queryctrl(c); - } - case VIDIOC_G_CTRL: - return get_control(core,arg); - case VIDIOC_S_CTRL: - return set_control(core,arg); +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; + u32 reg; - /* --- tuner ioctls ------------------------------------------ */ - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; - u32 reg; - - if (UNSET == core->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - - memset(t,0,sizeof(*t)); - strcpy(t->name, "Television"); - t->type = V4L2_TUNER_ANALOG_TV; - t->capability = V4L2_TUNER_CAP_NORM; - t->rangehigh = 0xffffffffUL; - - cx88_get_stereo(core ,t); - reg = cx_read(MO_DEVICE_STATUS); - t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; - if (UNSET == core->tuner_type) - return -EINVAL; - if (0 != t->index) - return -EINVAL; - cx88_set_stereo(core, t->audmode, 1); - return 0; - } - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; - memset(f,0,sizeof(*f)); + cx88_get_stereo(core ,t); + reg = cx_read(MO_DEVICE_STATUS); + t->signal = (reg & (1<<5)) ? 0xffff : 0x0000; + return 0; +} - if (UNSET == core->tuner_type) - return -EINVAL; +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ - f->type = radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = core->freq; + if (UNSET == core->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + cx88_set_stereo(core, t->audmode, 1); + return 0; +} - return 0; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - if (UNSET == core->tuner_type) - return -EINVAL; - if (f->tuner != 0) - return -EINVAL; - if (0 == radio && f->type != V4L2_TUNER_ANALOG_TV) - return -EINVAL; - if (1 == radio && f->type != V4L2_TUNER_RADIO) - return -EINVAL; - mutex_lock(&core->lock); - core->freq = f->frequency; - cx88_newstation(core); - cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); - - /* When changing channels it is required to reset TVAUDIO */ - msleep (10); - cx88_set_tvaudio(core); +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8800_fh *fh = priv; + struct cx88_core *core = fh->dev->core; - mutex_unlock(&core->lock); - return 0; - } -#ifdef CONFIG_VIDEO_ADV_DEBUG - /* ioctls to allow direct acces to the cx2388x registers */ - case VIDIOC_INT_G_REGISTER: - { - struct v4l2_register *reg = arg; + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; - if (reg->i2c_id != 0) - return -EINVAL; - /* cx2388x has a 24-bit register space */ - reg->val = cx_read(reg->reg&0xffffff); - return 0; - } - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; + /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */ + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = core->freq; - if (reg->i2c_id != 0) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - cx_write(reg->reg&0xffffff, reg->val); - return 0; - } -#endif + cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - driver_ioctl); - } return 0; } -static int video_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +int cx88_set_freq (struct cx88_core *core, + struct v4l2_frequency *f) { - int retval; + if (unlikely(UNSET == core->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; - retval=video_usercopy(inode, file, cmd, arg, video_do_ioctl); + mutex_lock(&core->lock); + core->freq = f->frequency; + cx88_newstation(core); + cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); + + /* When changing channels it is required to reset TVAUDIO */ + msleep (10); + cx88_set_tvaudio(core); - if (video_debug > 1) { - if (retval < 0) { - v4l_print_ioctl("cx88(err)", cmd); - printk(KERN_DEBUG "cx88(err): errcode=%d\n",retval); - } else if (_IOC_DIR(cmd) & _IOC_READ) - v4l_printk_ioctl_arg("cx88(r)",cmd, (void *)arg); - } + mutex_unlock(&core->lock); + + return 0; +} +EXPORT_SYMBOL(cx88_set_freq); + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx8800_fh *fh = priv; + struct cx88_core *core = fh->dev->core; - return retval; + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + return + cx88_set_freq (core,f); +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; + + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + /* cx2388x has a 24-bit register space */ + reg->val = cx_read(reg->reg&0xffffff); + return 0; } +static int vidioc_s_register (struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; + + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return -EINVAL; + cx_write(reg->reg&0xffffff, reg->val); + return 0; +} +#endif + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *arg) +static int radio_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) { - struct cx8800_fh *fh = file->private_data; - struct cx8800_dev *dev = fh->dev; + struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev; struct cx88_core *core = dev->core; - if (video_debug > 1) - v4l_print_ioctl(core->name,cmd); - - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; - - memset(cap,0,sizeof(*cap)); - strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[core->board].name, - sizeof(cap->card)); - sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); - cap->version = CX88_VERSION_CODE; - cap->capabilities = V4L2_CAP_TUNER; - return 0; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *t = arg; + strcpy(cap->driver, "cx8800"); + strlcpy(cap->card, cx88_boards[core->board].name, + sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); + cap->version = CX88_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} - if (t->index > 0) - return -EINVAL; +static int radio_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - memset(t,0,sizeof(*t)); - strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; + if (unlikely(t->index > 0)) + return -EINVAL; - cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); - return 0; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *i = arg; + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; - if (i->index != 0) - return -EINVAL; - strcpy(i->name,"Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - return 0; - } - case VIDIOC_G_INPUT: - { - int *i = arg; - *i = 0; - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *a = arg; + cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); + return 0; +} - memset(a,0,sizeof(*a)); - strcpy(a->name,"Radio"); - return 0; - } - case VIDIOC_G_STD: - { - v4l2_std_id *id = arg; - *id = 0; - return 0; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; +static int radio_enum_input (struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name,"Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; - if (v->tuner) /* Only tuner 0 */ - return -EINVAL; + return 0; +} - cx88_call_i2c_clients(core,VIDIOCSTUNER,v); - return 0; - } -#endif - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *t = arg; +static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; - if (0 != t->index) - return -EINVAL; + memset(a,0,sizeof(*a)); + strcpy(a->name,"Radio"); + return 0; +} - cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); +/* FIXME: Should add a standard for radio */ - return 0; - } +static int radio_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core; - case VIDIOC_S_AUDIO: - case VIDIOC_S_INPUT: - case VIDIOC_S_STD: - return 0; + if (0 != t->index) + return -EINVAL; - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = arg; - int i; - - if (c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) - return -EINVAL; - if (c->id == V4L2_CID_AUDIO_MUTE) { - for (i = 0; i < CX8800_CTLS; i++) - if (cx8800_ctls[i].v.id == c->id) - break; - *c = cx8800_ctls[i].v; - } else - *c = no_ctl; - return 0; - } + cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); + return 0; +} - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - return video_do_ioctl(inode,file,cmd,arg); +static int radio_s_audio (struct file *file, void *fh, + struct v4l2_audio *a) +{ + return 0; +} - default: - return v4l_compat_translate_ioctl(inode,file,cmd,arg, - radio_do_ioctl); - } +static int radio_s_input (struct file *file, void *fh, unsigned int i) +{ return 0; -}; +} -static int radio_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static int radio_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *c) { - return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); -}; + int i; + + if (c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + for (i = 0; i < CX8800_CTLS; i++) + if (cx8800_ctls[i].v.id == c->id) + break; + *c = cx8800_ctls[i].v; + } else + *c = no_ctl; + return 0; +} /* ----------------------------------------------------------- */ @@ -1733,7 +1555,8 @@ static void cx8800_vid_irq(struct cx8800_dev *dev) cx_write(MO_VID_INTSTAT, status); if (irq_debug || (status & mask & ~0xff)) cx88_print_irqbits(core->name, "irq vid", - cx88_vid_irqs, status, mask); + cx88_vid_irqs, ARRAY_SIZE(cx88_vid_irqs), + status, mask); /* risc op code error */ if (status & (1 << 16)) { @@ -1816,27 +1639,52 @@ static const struct file_operations video_fops = .read = video_read, .poll = video_poll, .mmap = video_mmap, - .ioctl = video_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; +static struct video_device cx8800_vbi_template; static struct video_device cx8800_video_template = { - .name = "cx8800-video", - .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, - .hardware = 0, - .fops = &video_fops, - .minor = -1, -}; - -static struct video_device cx8800_vbi_template = -{ - .name = "cx8800-vbi", - .type = VID_TYPE_TELETEXT|VID_TYPE_TUNER, - .hardware = 0, - .fops = &video_fops, - .minor = -1, + .name = "cx8800-video", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES, + .fops = &video_fops, + .minor = -1, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap, + .vidioc_g_fmt_cap = vidioc_g_fmt_cap, + .vidioc_try_fmt_cap = vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = vidioc_s_fmt_cap, + .vidioc_g_fmt_vbi = cx8800_vbi_fmt, + .vidioc_try_fmt_vbi = cx8800_vbi_fmt, + .vidioc_s_fmt_vbi = cx8800_vbi_fmt, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .tvnorms = CX88_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; static const struct file_operations radio_fops = @@ -1844,18 +1692,30 @@ static const struct file_operations radio_fops = .owner = THIS_MODULE, .open = video_open, .release = video_release, - .ioctl = radio_ioctl, + .ioctl = video_ioctl2, .compat_ioctl = v4l_compat_ioctl32, .llseek = no_llseek, }; static struct video_device cx8800_radio_template = { - .name = "cx8800-radio", - .type = VID_TYPE_TUNER, - .hardware = 0, - .fops = &radio_fops, - .minor = -1, + .name = "cx8800-radio", + .type = VID_TYPE_TUNER, + .hardware = 0, + .fops = &radio_fops, + .minor = -1, + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, }; /* ----------------------------------------------------------- */ @@ -1890,6 +1750,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, { struct cx8800_dev *dev; struct cx88_core *core; + int err; dev = kzalloc(sizeof(*dev),GFP_KERNEL); @@ -1918,15 +1779,21 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,0xffffffff)) { + if (!pci_dma_supported(pci_dev,DMA_32BIT_MASK)) { printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; goto fail_core; } + /* Initialize VBI template */ + memcpy( &cx8800_vbi_template, &cx8800_video_template, + sizeof(cx8800_vbi_template) ); + strcpy(cx8800_vbi_template.name,"cx8800-vbi"); + cx8800_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER; + /* initialize driver struct */ spin_lock_init(&dev->slock); - core->tvnorm = tvnorms; + core->tvnorm = cx8800_video_template.current_norm; /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); @@ -2007,9 +1874,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* initial device configuration */ mutex_lock(&core->lock); - cx88_set_tvnorm(core,tvnorms); + cx88_set_tvnorm(core,core->tvnorm); init_controls(core); - video_mux(core,0); + cx88_video_mux(core,0); mutex_unlock(&core->lock); /* start tvaudio thread */ @@ -2178,8 +2045,6 @@ static void cx8800_fini(void) module_init(cx8800_init); module_exit(cx8800_fini); -EXPORT_SYMBOL(cx88_do_ioctl); - /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index a9575ad..738d4f2 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -31,7 +31,9 @@ #include <media/video-buf.h> #include <media/cx2341x.h> #include <media/audiochip.h> +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) #include <media/video-buf-dvb.h> +#endif #include "btcx-risc.h" #include "cx88-reg.h" @@ -50,6 +52,13 @@ /* ----------------------------------------------------------- */ /* defines and enums */ +/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ +#define CX88_NORMS (\ + V4L2_STD_NTSC_M| V4L2_STD_NTSC_M_JP| V4L2_STD_NTSC_443 | \ + V4L2_STD_PAL_BG| V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \ + V4L2_STD_PAL_60| V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK ) + #define FORMAT_FLAGS_PACKED 0x01 #define FORMAT_FLAGS_PLANAR 0x02 @@ -82,22 +91,15 @@ enum cx8802_board_access { /* ----------------------------------------------------------- */ /* tv norms */ -struct cx88_tvnorm { - char *name; - v4l2_std_id id; - u32 cxiformat; - u32 cxoformat; -}; - -static unsigned int inline norm_maxw(struct cx88_tvnorm *norm) +static unsigned int inline norm_maxw(v4l2_std_id norm) { - return (norm->id & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; + return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768; } -static unsigned int inline norm_maxh(struct cx88_tvnorm *norm) +static unsigned int inline norm_maxh(v4l2_std_id norm) { - return (norm->id & V4L2_STD_625_50) ? 576 : 480; + return (norm & V4L2_STD_625_50) ? 576 : 480; } /* ----------------------------------------------------------- */ @@ -313,13 +315,15 @@ struct cx88_core { unsigned int tuner_formats; /* config info -- dvb */ +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) struct dvb_pll_desc *pll_desc; unsigned int pll_addr; int (*prev_set_voltage)(struct dvb_frontend* fe, fe_sec_voltage_t voltage); +#endif /* state info */ struct task_struct *kthread; - struct cx88_tvnorm *tvnorm; + v4l2_std_id tvnorm; u32 tvaudio; u32 audiomode_manual; u32 audiomode_current; @@ -459,13 +463,16 @@ struct cx8802_dev { u32 mailbox; int width; int height; + int fw_size; +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) /* for dvb only */ struct videobuf_dvb dvb; void* fe_handle; int (*fe_release)(void *handle); void *card_priv; +#endif /* for switching modulation types */ unsigned char ts_gen_cntrl; @@ -474,6 +481,8 @@ struct cx8802_dev { /* List of attached drivers */ struct cx8802_driver drvlist; + struct work_struct request_module_wk; + }; /* ----------------------------------------------------------- */ @@ -503,7 +512,7 @@ struct cx8802_dev { /* cx88-core.c */ extern void cx88_print_irqbits(char *name, char *tag, char **strings, - u32 bits, u32 mask); + int len, u32 bits, u32 mask); extern int cx88_core_irq(struct cx88_core *core, u32 status); extern void cx88_wakeup(struct cx88_core *core, @@ -536,7 +545,7 @@ extern void cx88_sram_channel_dump(struct cx88_core *core, extern int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height, enum v4l2_field field); -extern int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm); +extern int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm); extern struct video_device *cx88_vdev_init(struct cx88_core *core, struct pci_dev *pci, @@ -553,7 +562,10 @@ extern int cx88_stop_audio_dma(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-vbi.c */ -void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f); +/* Can be used as g_vbi_fmt, try_vbi_fmt and s_vbi_fmt */ +int cx8800_vbi_fmt (struct file *file, void *priv, + struct v4l2_format *f); + /* int cx8800_start_vbi_dma(struct cx8800_dev *dev, struct cx88_dmaqueue *q, @@ -633,19 +645,14 @@ int cx8802_suspend_common(struct pci_dev *pci_dev, pm_message_t state); int cx8802_resume_common(struct pci_dev *pci_dev); /* ----------------------------------------------------------- */ -/* cx88-video.c */ -extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio, - struct cx88_core *core, unsigned int cmd, - void *arg, v4l2_kioctl driver_ioctl); +/* cx88-video.c*/ extern const u32 cx88_user_ctrls[]; extern int cx8800_ctrl_query(struct v4l2_queryctrl *qctrl); - -/* ----------------------------------------------------------- */ -/* cx88-blackbird.c */ -/* used by cx88-ivtv ioctl emulation layer */ -extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg); -extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd); +int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i); +int cx88_set_freq (struct cx88_core *core,struct v4l2_frequency *f); +int cx88_get_control(struct cx88_core *core, struct v4l2_control *ctl); +int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl); +int cx88_video_mux(struct cx88_core *core, unsigned int input); /* * Local variables: diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index d829d8f..563a831 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -523,7 +523,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c) unsigned char buf; int i, rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c, &buf, 0); if (rc < 0) diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h index 2e5ca40..262f98e 100644 --- a/drivers/media/video/et61x251/et61x251.h +++ b/drivers/media/video/et61x251/et61x251.h @@ -171,10 +171,7 @@ struct et61x251_device { struct et61x251_device* et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) { - if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) - return cam; - - return NULL; + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; } diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c index 49792ae..a652551 100644 --- a/drivers/media/video/et61x251/et61x251_core.c +++ b/drivers/media/video/et61x251/et61x251_core.c @@ -1,7 +1,7 @@ /*************************************************************************** * V4L2 driver for ET61X[12]51 PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -48,8 +48,8 @@ #define ET61X251_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ET61X251_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1:1.02" -#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 2) +#define ET61X251_MODULE_VERSION "1:1.04" +#define ET61X251_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 4) /*****************************************************************************/ @@ -85,7 +85,7 @@ MODULE_PARM_DESC(force_munmap, "\ndetected camera." "\n 0 = do not force memory unmapping" "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"." "\n"); static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = @@ -133,7 +133,8 @@ et61x251_request_buffers(struct et61x251_device* cam, u32 count, cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -543,10 +544,11 @@ static int et61x251_start_transfer(struct et61x251_device* cam) { struct usb_device *udev = cam->usbdev; struct urb* urb; - const unsigned int wMaxPacketSize[] = {0, 256, 384, 512, 640, 768, 832, - 864, 896, 920, 956, 980, 1000, - 1022}; - const unsigned int psz = wMaxPacketSize[ET61X251_ALTERNATE_SETTING]; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + ET61X251_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); s8 i, j; int err = 0; @@ -976,29 +978,31 @@ static CLASS_DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, static int et61x251_create_sysfs(struct et61x251_device* cam) { struct video_device *v4ldev = cam->v4ldev; - int rc; + int err = 0; + + if ((err = video_device_create_file(v4ldev, &class_device_attr_reg))) + goto err_out; + if ((err = video_device_create_file(v4ldev, &class_device_attr_val))) + goto err_reg; - rc = video_device_create_file(v4ldev, &class_device_attr_reg); - if (rc) goto err; - rc = video_device_create_file(v4ldev, &class_device_attr_val); - if (rc) goto err_reg; if (cam->sensor.sysfs_ops) { - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_reg); - if (rc) goto err_val; - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_val); - if (rc) goto err_i2c_reg; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_reg))) + goto err_val; + if ((err = video_device_create_file(v4ldev, + &class_device_attr_i2c_val))) + goto err_i2c_reg; } - return 0; - err_i2c_reg: + if (cam->sensor.sysfs_ops) video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); err_val: video_device_remove_file(v4ldev, &class_device_attr_val); err_reg: video_device_remove_file(v4ldev, &class_device_attr_reg); -err: - return rc; +err_out: + return err; } #endif /* CONFIG_VIDEO_ADV_DEBUG */ @@ -1767,10 +1771,10 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - if (rect->width < 4) - rect->width = 4; - if (rect->height < 4) - rect->height = 4; + if (rect->width < 16) + rect->width = 16; + if (rect->height < 16) + rect->height = 16; if (rect->width > bounds->width) rect->width = bounds->width; if (rect->height > bounds->height) @@ -1784,8 +1788,8 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) if (rect->top + rect->height > bounds->top + bounds->height) rect->top = bounds->top+bounds->height - rect->height; - rect->width &= ~3L; - rect->height &= ~3L; + rect->width &= ~15L; + rect->height &= ~15L; if (ET61X251_PRESERVE_IMGSCALE) { /* Calculate the actual scaling factor */ @@ -1846,6 +1850,35 @@ et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) static int +et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0) + return -EINVAL; + + if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + + frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; + frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; + frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; + frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; + frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + +static int et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) { struct v4l2_fmtdesc fmtd; @@ -1853,6 +1886,9 @@ et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "bayer rgb"); fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; @@ -1934,17 +1970,17 @@ et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, rect.width = scale * pix->width; rect.height = scale * pix->height; - if (rect.width < 4) - rect.width = 4; - if (rect.height < 4) - rect.height = 4; + if (rect.width < 16) + rect.width = 16; + if (rect.height < 16) + rect.height = 16; if (rect.width > bounds->left + bounds->width - rect.left) rect.width = bounds->left + bounds->width - rect.left; if (rect.height > bounds->top + bounds->height - rect.top) rect.height = bounds->top + bounds->height - rect.top; - rect.width &= ~3L; - rect.height &= ~3L; + rect.width &= ~15L; + rect.height &= ~15L; { /* adjust the scaling factor */ u32 a, b; @@ -2378,6 +2414,9 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_FMT: return et61x251_vidioc_try_s_fmt(cam, cmd, arg); + case VIDIOC_ENUM_FRAMESIZES: + return et61x251_vidioc_enum_framesizes(cam, arg); + case VIDIOC_G_JPEGCOMP: return et61x251_vidioc_g_jpegcomp(cam, arg); @@ -2413,6 +2452,7 @@ static int et61x251_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -2459,6 +2499,7 @@ static const struct file_operations et61x251_fops = { .open = et61x251_open, .release = et61x251_release, .ioctl = et61x251_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = et61x251_read, .poll = et61x251_poll, .mmap = et61x251_mmap, @@ -2497,7 +2538,7 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); DBG(2, "ET61X[12]51 PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); for (i = 0; et61x251_sensor_table[i]; i++) { err = et61x251_sensor_table[i](cam); @@ -2550,9 +2591,14 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) #ifdef CONFIG_VIDEO_ADV_DEBUG err = et61x251_create_sysfs(cam); - if (err) - goto fail2; - DBG(2, "Optional device control through 'sysfs' interface ready"); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create 'sysfs' interface for optional " + "device controlling. Error #%d", err); +#else + DBG(2, "Optional device control through 'sysfs' interface disabled"); #endif usb_set_intfdata(intf, cam); @@ -2561,13 +2607,6 @@ et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) return 0; -#ifdef CONFIG_VIDEO_ADV_DEBUG -fail2: - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); - video_unregister_device(cam->v4ldev); -#endif fail: if (cam) { kfree(cam->control_buffer); diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h index 65edd08..5fadb5d 100644 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ b/drivers/media/video/et61x251/et61x251_sensor.h @@ -1,7 +1,7 @@ /*************************************************************************** * API for image sensors connected to ET61X[12]51 PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -82,7 +82,7 @@ enum et61x251_i2c_rsta { ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ }; -#define ET61X251_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) struct et61x251_sensor { char name[32]; diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c index a7d65b8..b066434 100644 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ b/drivers/media/video/et61x251/et61x251_tas5130d1b.c @@ -2,7 +2,7 @@ * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * * PC Camera Controllers * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 210582d..ed92b6f 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -173,7 +173,7 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, return -EIO; } - for (start = 0; start<4; start++) { + for (start = 0; start < ARRAY_SIZE(b); start++) { if (b[start] == marker) { code=b[(start+parity_offset+1)%4]; parity=b[(start+parity_offset)%4]; diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig new file mode 100644 index 0000000..e854f3f --- /dev/null +++ b/drivers/media/video/ivtv/Kconfig @@ -0,0 +1,26 @@ +config VIDEO_IVTV + tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" + depends on VIDEO_V4L1 && VIDEO_V4L2 && USB && I2C && EXPERIMENTAL + select FW_LOADER + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_CX2341X + select VIDEO_CX25840 + select VIDEO_MSP3400 + select VIDEO_SAA711X + select VIDEO_SAA7127 + select VIDEO_TVAUDIO + select VIDEO_CS53L32A + select VIDEO_WM8775 + select VIDEO_WM8739 + select VIDEO_UPD64031A + select VIDEO_UPD64083 + ---help--- + This is a video4linux driver for Conexant cx23416 or cx23416 based + PCI personal video recorder devices. + + This is used in devices such as the Hauppauge PVR-150/250/350/500 + cards. + + To compile this driver as a module, choose M here: the + module will be called ivtv. diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile new file mode 100644 index 0000000..7e95148 --- /dev/null +++ b/drivers/media/video/ivtv/Makefile @@ -0,0 +1,7 @@ +ivtv-objs := ivtv-audio.o ivtv-cards.o ivtv-controls.o \ + ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \ + ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \ + ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \ + ivtv-vbi.o ivtv-video.o ivtv-yuv.o + +obj-$(CONFIG_VIDEO_IVTV) += ivtv.o diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c new file mode 100644 index 0000000..d702b8b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.c @@ -0,0 +1,74 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include "ivtv-audio.h" +#include <media/msp3400.h> +#include <linux/videodev.h> + +/* Selects the audio input and output according to the current + settings. */ +int ivtv_audio_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + u32 audio_input; + int mux_input; + + /* Determine which input to use */ + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + audio_input = itv->card->radio_input.audio_input; + mux_input = itv->card->radio_input.muxer_input; + } else { + audio_input = itv->card->audio_inputs[itv->audio_input].audio_input; + mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input; + } + + /* handle muxer chips */ + route.input = mux_input; + route.output = 0; + ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + + route.input = audio_input; + if (itv->card->hw_audio & IVTV_HW_MSP34XX) { + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + } + return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); +} + +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route) +{ + ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); +} + +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq) +{ + static u32 freqs[3] = { 44100, 48000, 32000 }; + + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (freq > 2) + return; + ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); +} + diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h new file mode 100644 index 0000000..9c42846 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.h @@ -0,0 +1,23 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_audio_set_io(struct ivtv *itv); +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route); +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq); diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c new file mode 100644 index 0000000..8eab0208 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -0,0 +1,964 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-i2c.h" + +#include <media/msp3400.h> +#include <media/wm8775.h> +#include <media/cs53l32a.h> +#include <media/cx25840.h> +#include <media/upd64031a.h> + +#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER) +#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) + +/********************** card configuration *******************************/ + +/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii + This keeps the PCI ID database up to date. Note that the entries + must be added under vendor 0x4444 (Conexant) as subsystem IDs. + New vendor IDs should still be added to the vendor ID list. */ + +/* Hauppauge PVR-250 cards */ + +/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */ +static const struct ivtv_card ivtv_card_pvr250 = { + .type = IVTV_CARD_PVR_250, + .name = "Hauppauge WinTV PVR-250", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-350 cards */ + +/* Outputs for Hauppauge PVR350 cards */ +static struct ivtv_card_output ivtv_pvr350_outputs[] = { + { + .name = "S-Video + Composite", + .video_output = 0, + }, { + .name = "Composite", + .video_output = 1, + }, { + .name = "S-Video", + .video_output = 2, + }, { + .name = "RGB", + .video_output = 3, + }, { + .name = "YUV C", + .video_output = 4, + }, { + .name = "YUV V", + .video_output = 5, + } +}; + +static const struct ivtv_card ivtv_card_pvr350 = { + .type = IVTV_CARD_PVR_350, + .name = "Hauppauge WinTV PVR-350", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* PVR-350 V1 boards have a different audio tuner input and use a + saa7114 instead of a saa7115. + Note that the info below comes from a pre-production model so it may + not be correct. Especially the audio behaves strangely (mono only it seems) */ +static const struct ivtv_card ivtv_card_pvr350_v1 = { + .type = IVTV_CARD_PVR_350_V1, + .name = "Hauppauge WinTV PVR-350 (V1)", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-150/PVR-500 cards */ + +static const struct ivtv_card ivtv_card_pvr150 = { + .type = IVTV_CARD_PVR_150, + .name = "Hauppauge WinTV PVR-150", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_WM8775, + .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO8, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN1, + CX25840_AUDIO_SERIAL, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN2, + CX25840_AUDIO_SERIAL, WM8775_AIN3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO_SERIAL, WM8775_AIN4 }, + /* apparently needed for the IR blaster */ + .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* AVerMedia M179 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_m179[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_m179 = { + .type = IVTV_CARD_M179, + .name = "AVerMedia M179", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 }, + .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 }, + .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 }, + .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000, + .f44100 = 0x0008, .f48000 = 0x0010 }, + .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 }, + .tuners = { + /* As far as we know all M179 cards use this tuner */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC }, + }, + .pci_list = ivtv_pci_m179, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg600 = { + .type = IVTV_CARD_MPG600, + .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + /* The PAL tuner is confirmed */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg160 = { + .type = IVTV_CARD_MPG160, + .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x7080, .initial_value = 0x400c }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg160, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan PG600/Diamond PVR-550 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600 = { + .type = IVTV_CARD_PG600, + .name = "Yuan PG600, Diamond PVR-550", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_pg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2410 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2410 = { + .type = IVTV_CARD_AVC2410, + .name = "Adaptec VideOh! AVC-2410", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_muxer = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A | + IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + MSP_TUNER, CS53L32A_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, + MSP_SCART1, CS53L32A_IN2 }, + }, + /* This card has no eeprom and in fact the Windows driver relies + on the country/region setting of the user to decide which tuner + is available. */ + .tuners = { + /* This tuner has been verified for the AVC2410 */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + /* This is a good guess, but I'm not totally sure this is + the correct tuner for NTSC. */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_avc2410, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2010 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2010 = { + .type = IVTV_CARD_AVC2010, + .name = "Adaptec VideOh! AVC-2010", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_CS53L32A, + .hw_audio_ctrl = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115, + .video_inputs = { + { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 }, + }, + /* Does not have a tuner */ + .pci_list = ivtv_pci_avc2010, +}; + +/* ------------------------------------------------------------------------- */ + +/* Nagase Transgear 5000TV card */ + +static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_tg5000tv = { + .type = IVTV_CARD_TG5000TV, + .name = "Nagase Transgear 5000TV", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020 }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_tg5000tv, +}; + +/* ------------------------------------------------------------------------- */ + +/* AOpen VA2000MAX-SNT6 card */ + +static const struct ivtv_card_pci_info ivtv_pci_va2000[] = { + { PCI_DEVICE_ID_IVTV16, 0, 0xff5f }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_va2000 = { + .type = IVTV_CARD_VA2000MAX_SNT6, + .name = "AOpen VA2000MAX-SNT6", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_UPD6408X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_va2000, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_cx23416gyc = { + .type = IVTV_CARD_CX23416GYC, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_cx23416gyc, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { + .type = IVTV_CARD_CX23416GYC_NOGR, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { + .type = IVTV_CARD_CX23416GYC_NOGRYCS, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */ + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gv_mvprx = { + .type = IVTV_CARD_GV_MVPRX, + .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO | + IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX2E card */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 }, + {0, 0, 0} +}; + +static const struct ivtv_card ivtv_card_gv_mvprx2e = { + .type = IVTV_CARD_GV_MVPRX2E, + .name = "I/O Data GV-MVP/RX2E", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_TVAUDIO | IVTV_HW_WM8739, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx2e, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD, + .name = "GotView PCI DVD", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */ + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */ + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */ + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 }, + }, + .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, + .tuners = { + /* This card has a Philips FQ1216ME MK3 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD2 Deluxe card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD2, + .name = "GotView PCI DVD2 Deluxe", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_GPIO, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + .gpio_init = { .direction = 0x0800, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + .tuners = { + /* This card has a Philips FQ1216ME MK5 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd2, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPC622 miniPCI card */ + +static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_yuan_mpc622 = { + .type = IVTV_CARD_YUAN_MPC622, + .name = "Yuan MPC622", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 }, + .tuners = { + /* This card has the TDA8290/TDA8275 tuner chips */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 }, + }, + .pci_list = ivtv_pci_yuan_mpc622, +}; + +/* ------------------------------------------------------------------------- */ + +/* DIGITAL COWBOY DCT-MTVP1 card */ + +static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_dctmvtvp1 = { + .type = IVTV_CARD_DCTMTVP1, + .name = "Digital Cowboy DCT-MTVP1", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020}, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_dctmvtvp1, +}; + +/* ------------------------------------------------------------------------- */ + +#ifdef HAVE_XC3028 + +/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600v2 = { + .type = IVTV_CARD_PG600V2, + .name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 }, + }, + .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ + .pci_list = ivtv_pci_pg600v2, +}; +#endif + +static const struct ivtv_card *ivtv_card_list[] = { + &ivtv_card_pvr250, + &ivtv_card_pvr350, + &ivtv_card_pvr150, + &ivtv_card_m179, + &ivtv_card_mpg600, + &ivtv_card_mpg160, + &ivtv_card_pg600, + &ivtv_card_avc2410, + &ivtv_card_avc2010, + &ivtv_card_tg5000tv, + &ivtv_card_va2000, + &ivtv_card_cx23416gyc, + &ivtv_card_gv_mvprx, + &ivtv_card_gv_mvprx2e, + &ivtv_card_gotview_pci_dvd, + &ivtv_card_gotview_pci_dvd2, + &ivtv_card_yuan_mpc622, + &ivtv_card_dctmvtvp1, +#ifdef HAVE_XC3028 + &ivtv_card_pg600v2, +#endif + + /* Variations of standard cards but with the same PCI IDs. + These cards must come last in this list. */ + &ivtv_card_pvr350_v1, + &ivtv_card_cx23416gyc_nogr, + &ivtv_card_cx23416gyc_nogrycs, +}; + +const struct ivtv_card *ivtv_get_card(u16 index) +{ + if (index >= ARRAY_SIZE(ivtv_card_list)) + return NULL; + return ivtv_card_list[index]; +} + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input) +{ + const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "S-Video 1", + "S-Video 2", + "Composite 1", + "Composite 2", + "Composite 3" + }; + + memset(input, 0, sizeof(*input)); + if (index >= itv->nof_inputs) + return -EINVAL; + input->index = index; + strcpy(input->name, input_strs[card_input->video_type - 1]); + input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ? + V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); + input->audioset = (1 << itv->nof_audio_inputs) - 1; + input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? + itv->tuner_std : V4L2_STD_ALL; + return 0; +} + +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output) +{ + const struct ivtv_card_output *card_output = itv->card->video_outputs + index; + + memset(output, 0, sizeof(*output)); + if (index >= itv->card->nof_outputs) + return -EINVAL; + output->index = index; + strcpy(output->name, card_output->name); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->audioset = 1; + output->std = V4L2_STD_ALL; + return 0; +} + +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio) +{ + const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "Line In 1", + "Line In 2" + }; + + memset(audio, 0, sizeof(*audio)); + if (index >= itv->nof_audio_inputs) + return -EINVAL; + strcpy(audio->name, input_strs[aud_input->audio_type - 1]); + audio->index = index; + audio->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output) +{ + memset(aud_output, 0, sizeof(*aud_output)); + if (itv->card->video_outputs == NULL || index != 0) + return -EINVAL; + strcpy(aud_output->name, "A/V Audio Out"); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h new file mode 100644 index 0000000..15012f8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -0,0 +1,207 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* hardware flags */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_TVAUDIO (1 << 9) +#define IVTV_HW_UPD64031A (1 << 10) +#define IVTV_HW_UPD6408X (1 << 11) +#define IVTV_HW_SAA717X (1 << 12) +#define IVTV_HW_WM8739 (1 << 13) +#define IVTV_HW_GPIO (1 << 14) + +#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) + +/* video inputs */ +#define IVTV_CARD_INPUT_VID_TUNER 1 +#define IVTV_CARD_INPUT_SVIDEO1 2 +#define IVTV_CARD_INPUT_SVIDEO2 3 +#define IVTV_CARD_INPUT_COMPOSITE1 4 +#define IVTV_CARD_INPUT_COMPOSITE2 5 +#define IVTV_CARD_INPUT_COMPOSITE3 6 + +/* audio inputs */ +#define IVTV_CARD_INPUT_AUD_TUNER 1 +#define IVTV_CARD_INPUT_LINE_IN1 2 +#define IVTV_CARD_INPUT_LINE_IN2 3 + +#define IVTV_CARD_MAX_VIDEO_INPUTS 6 +#define IVTV_CARD_MAX_AUDIO_INPUTS 3 +#define IVTV_CARD_MAX_TUNERS 2 + +/* SAA71XX HW inputs */ +#define IVTV_SAA71XX_COMPOSITE0 0 +#define IVTV_SAA71XX_COMPOSITE1 1 +#define IVTV_SAA71XX_COMPOSITE2 2 +#define IVTV_SAA71XX_COMPOSITE3 3 +#define IVTV_SAA71XX_COMPOSITE4 4 +#define IVTV_SAA71XX_COMPOSITE5 5 +#define IVTV_SAA71XX_SVIDEO0 6 +#define IVTV_SAA71XX_SVIDEO1 7 +#define IVTV_SAA71XX_SVIDEO2 8 +#define IVTV_SAA71XX_SVIDEO3 9 + +/* SAA717X needs to mark the tuner input by ORing with this flag */ +#define IVTV_SAA717X_TUNER_FLAG 0x80 + +/* Dummy HW input */ +#define IVTV_DUMMY_AUDIO 0 + +/* GPIO HW inputs */ +#define IVTV_GPIO_TUNER 0 +#define IVTV_GPIO_LINE_IN 1 + +/* SAA717X HW inputs */ +#define IVTV_SAA717X_IN0 0 +#define IVTV_SAA717X_IN1 1 +#define IVTV_SAA717X_IN2 2 + +/* V4L2 capability aliases */ +#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ + V4L2_CAP_SLICED_VBI_CAPTURE) +#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \ + V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS) + +struct ivtv_card_video_input { + u8 video_type; /* video input type */ + u8 audio_index; /* index in ivtv_card_audio_input array */ + u16 video_input; /* hardware video input */ +}; + +struct ivtv_card_audio_input { + u8 audio_type; /* audio input type */ + u32 audio_input; /* hardware audio input */ + u16 muxer_input; /* hardware muxer input for boards with a + multiplexer chip */ +}; + +struct ivtv_card_output { + u8 name[32]; + u16 video_output; /* hardware video output */ +}; + +struct ivtv_card_pci_info { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; +}; + +/* GPIO definitions */ + +/* The mask is the set of bits used by the operation */ + +struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */ + u16 direction; /* DIR setting. Leave to 0 if no init is needed */ + u16 initial_value; +}; + +struct ivtv_gpio_video_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 composite; + u16 svideo; +}; + +struct ivtv_gpio_audio_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 linein; + u16 radio; +}; + +struct ivtv_gpio_audio_mute { + u16 mask; /* leave to 0 if not supported */ + u16 mute; /* set this value to mute, 0 to unmute */ +}; + +struct ivtv_gpio_audio_mode { + u16 mask; /* leave to 0 if not supported */ + u16 mono; /* set audio to mono */ + u16 stereo; /* set audio to stereo */ + u16 lang1; /* set audio to the first language */ + u16 lang2; /* set audio to the second language */ + u16 both; /* both languages are output */ +}; + +struct ivtv_gpio_audio_freq { + u16 mask; /* leave to 0 if not supported */ + u16 f32000; + u16 f44100; + u16 f48000; +}; + +struct ivtv_gpio_audio_detect { + u16 mask; /* leave to 0 if not supported */ + u16 stereo; /* if the input matches this value then + stereo is detected */ +}; + +struct ivtv_card_tuner { + v4l2_std_id std; /* standard for which the tuner is suitable */ + int tuner; /* tuner ID (from tuner.h) */ +}; + +/* for card information/parameters */ +struct ivtv_card { + int type; + char *name; + u32 v4l2_capabilities; + u32 hw_video; /* hardware used to process video */ + u32 hw_audio; /* hardware used to process audio */ + u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */ + u32 hw_muxer; /* hardware used to multiplex audio input */ + u32 hw_all; /* all hardware used by the board */ + struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS]; + struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS]; + struct ivtv_card_audio_input radio_input; + int nof_outputs; + const struct ivtv_card_output *video_outputs; + u8 gr_config; /* config byte for the ghost reduction device */ + + /* GPIO card-specific settings */ + struct ivtv_gpio_init gpio_init; + struct ivtv_gpio_video_input gpio_video_input; + struct ivtv_gpio_audio_input gpio_audio_input; + struct ivtv_gpio_audio_mute gpio_audio_mute; + struct ivtv_gpio_audio_mode gpio_audio_mode; + struct ivtv_gpio_audio_freq gpio_audio_freq; + struct ivtv_gpio_audio_detect gpio_audio_detect; + + struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS]; + + /* list of device and subsystem vendor/devices that + correspond to this card type. */ + const struct ivtv_card_pci_info *pci_list; +}; + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input); +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output); +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input); +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output); +const struct ivtv_card *ivtv_get_card(u16 index); diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c new file mode 100644 index 0000000..7a876c3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -0,0 +1,303 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-ioctl.h" +#include "ivtv-audio.h" +#include "ivtv-i2c.h" +#include "ivtv-mailbox.h" +#include "ivtv-controls.h" + +static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_BALANCE, + V4L2_CID_AUDIO_BASS, + V4L2_CID_AUDIO_TREBLE, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_AUDIO_LOUDNESS, + 0 +}; + +static const u32 *ctrl_classes[] = { + user_ctrls, + cx2341x_mpeg_ctrls, + NULL +}; + +static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl) +{ + const char *name; + + IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); + + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + switch (qctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + default: + if (cx2341x_ctrl_query(&itv->params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + } + strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); + qctrl->name[sizeof(qctrl->name) - 1] = 0; + return 0; +} + +static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + ivtv_queryctrl(itv, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); +} + +static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + s32 v = vctrl->value; + + IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt) +{ + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + /* First try to allocate sliced VBI buffers if needed. */ + if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { + int i; + + for (i = 0; i < IVTV_VBI_FRAMES; i++) { + /* Yuck, hardcoded. Needs to be a define */ + itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + if (itv->vbi.sliced_mpeg_data[i] == NULL) { + while (--i >= 0) { + kfree(itv->vbi.sliced_mpeg_data[i]); + itv->vbi.sliced_mpeg_data[i] = NULL; + } + return -ENOMEM; + } + } + } + + itv->vbi.insert_mpeg = fmt; + + if (itv->vbi.insert_mpeg == 0) { + return 0; + } + /* Need sliced data for mpeg insertion */ + if (get_service_set(itv->vbi.sliced_in) == 0) { + if (itv->is_60hz) + itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + else + itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; + expand_service_set(itv->vbi.sliced_in, itv->is_50hz); + } + return 0; +} + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_control ctrl; + + switch (cmd) { + case VIDIOC_QUERYMENU: + IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); + return ivtv_querymenu(itv, arg); + + case VIDIOC_QUERYCTRL: + return ivtv_queryctrl(itv, arg); + + case VIDIOC_S_CTRL: + return ivtv_s_ctrl(itv, arg); + + case VIDIOC_G_CTRL: + return ivtv_g_ctrl(itv, arg); + + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_s_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx2341x_mpeg_params p = itv->params; + int err = cx2341x_ext_ctrls(&p, arg, cmd); + + if (err) + return err; + + if (p.video_encoding != itv->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = itv->params.height; + itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + } + err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); + if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) { + err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); + } + itv->params = p; + itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; + ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03); + return err; + } + return -EINVAL; + } + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_g_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h new file mode 100644 index 0000000..5a11149 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -0,0 +1,21 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c new file mode 100644 index 0000000..45b9328 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -0,0 +1,1374 @@ +/* + ivtv driver initialization and card probing + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Main Driver file for the ivtv project: + * Driver for the Conexant CX23415/CX23416 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com> + * and Takeru KOMORIYA<komoriya@paken.org> + * + * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org> + * using information provided by Jiun-Kuei Jung @ AVerMedia. + * + * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta <alpha292@bremen.or.jp> + * using information from T.Adachi,Takeru KOMORIYA and others :-) + * + * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX + * version by T.Adachi. Special thanks Mr.Suzuki + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-firmware.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-mailbox.h" +#include "ivtv-streams.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-gpio.h" +#include "ivtv-yuv.h" + +#include <linux/vermagic.h> +#include <media/tveeprom.h> +#include <media/v4l2-chip-ident.h> + +/* var to keep track of the number of array elements in use */ +int ivtv_cards_active = 0; + +/* If you have already X v4l cards, then set this to X. This way + the device numbers stay matched. Example: you have a WinTV card + without radio and a PVR-350 with. Normally this would give a + video1 device together with a radio0 device for the PVR. By + setting this to 1 you ensure that radio0 is now also radio1. */ +int ivtv_first_minor = 0; + +/* Master variable for all ivtv info */ +struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; + +/* Protects ivtv_cards_active */ +spinlock_t ivtv_cards_lock = SPIN_LOCK_UNLOCKED; + +/* add your revision and whatnot here */ +static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); + +const u32 yuv_offset[4] = { + IVTV_YUV_BUFFER_OFFSET, + IVTV_YUV_BUFFER_OFFSET_1, + IVTV_YUV_BUFFER_OFFSET_2, + IVTV_YUV_BUFFER_OFFSET_3 +}; + +/* Parameter declarations */ +static int cardtype[IVTV_MAX_CARDS]; +static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static int cardtype_c = 1; +static int tuner_c = 1; +static int radio_c = 1; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +/* Buffers */ +static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS; +static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS; +static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS; +static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS; +static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; +static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; +static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS; + +static int ivtv_yuv_mode = 0; +static int ivtv_yuv_threshold=-1; +static int ivtv_pci_latency = 1; + +int ivtv_debug = 0; + +static int newi2c = -1; + +module_param_array(tuner, int, &tuner_c, 0644); +module_param_array(radio, bool, &radio_c, 0644); +module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); +module_param_named(debug,ivtv_debug, int, 0644); +module_param(ivtv_pci_latency, int, 0644); +module_param(ivtv_yuv_mode, int, 0644); +module_param(ivtv_yuv_threshold, int, 0644); +module_param(ivtv_first_minor, int, 0644); + +module_param(enc_mpg_buffers, int, 0644); +module_param(enc_yuv_buffers, int, 0644); +module_param(enc_vbi_buffers, int, 0644); +module_param(enc_pcm_buffers, int, 0644); +module_param(dec_mpg_buffers, int, 0644); +module_param(dec_yuv_buffers, int, 0644); +module_param(dec_vbi_buffers, int, 0644); + +module_param(newi2c, int, 0644); + +MODULE_PARM_DESC(tuner, "Tuner type selection,\n" + "\t\t\tsee tuner.h for values"); +MODULE_PARM_DESC(radio, + "Enable or disable the radio. Use only if autodetection\n" + "\t\t\tfails. 0 = disable, 1 = enable"); +MODULE_PARM_DESC(cardtype, + "Only use this option if your card is not detected properly.\n" + "\t\tSpecify card type:\n" + "\t\t\t 1 = WinTV PVR 250\n" + "\t\t\t 2 = WinTV PVR 350\n" + "\t\t\t 3 = WinTV PVR-150 or PVR-500\n" + "\t\t\t 4 = AVerMedia M179\n" + "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n" + "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n" + "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n" + "\t\t\t 8 = Adaptec AVC-2410\n" + "\t\t\t 9 = Adaptec AVC-2010\n" + "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n" + "\t\t\t11 = AOpen VA2000MAX-STN6\n" + "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n" + "\t\t\t13 = I/O Data GV-MVP/RX\n" + "\t\t\t14 = I/O Data GV-MVP/RX2E\n" + "\t\t\t15 = GOTVIEW PCI DVD\n" + "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n" + "\t\t\t17 = Yuan MPC622\n" + "\t\t\t18 = Digital Cowboy DCT-MTVP1\n" +#ifdef HAVE_XC3028 + "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n" +#endif + "\t\t\t 0 = Autodetect (default)\n" + "\t\t\t-1 = Ignore this card\n\t\t"); +MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); +MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); +MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: errors only\n" + "\t\t\t(debug = 511 gives full debugging)"); +MODULE_PARM_DESC(ivtv_pci_latency, + "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" + "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(ivtv_yuv_mode, + "Specify the yuv playback mode:\n" + "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n" + "\t\t\tDefault: 0 (interlaced)"); +MODULE_PARM_DESC(ivtv_yuv_threshold, + "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" + "\t\t\tDefault: 480");; +MODULE_PARM_DESC(enc_mpg_buffers, + "Encoder MPG Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); +MODULE_PARM_DESC(enc_yuv_buffers, + "Encoder YUV Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS)); +MODULE_PARM_DESC(enc_vbi_buffers, + "Encoder VBI Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS)); +MODULE_PARM_DESC(enc_pcm_buffers, + "Encoder PCM buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS)); +MODULE_PARM_DESC(dec_mpg_buffers, + "Decoder MPG buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS)); +MODULE_PARM_DESC(dec_yuv_buffers, + "Decoder YUV buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS)); +MODULE_PARM_DESC(dec_vbi_buffers, + "Decoder VBI buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS)); +MODULE_PARM_DESC(newi2c, + "Use new I2C implementation\n" + "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" + "\t\t\tDefault is autodetect"); + +MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card"); + +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_DESCRIPTION("CX23415/CX23416 driver"); +MODULE_SUPPORTED_DEVICE + ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n" + "\t\t\tYuan MPG series and similar)"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(IVTV_VERSION); + +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask &= ~mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask |= mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +int ivtv_set_output_mode(struct ivtv *itv, int mode) +{ + int old_mode; + + spin_lock(&itv->lock); + old_mode = itv->output_mode; + if (old_mode == 0) + itv->output_mode = old_mode = mode; + spin_unlock(&itv->lock); + return old_mode; +} + +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv) +{ + switch (itv->output_mode) { + case OUT_MPG: + return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + case OUT_YUV: + return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + default: + return NULL; + } +} + +int ivtv_waitq(wait_queue_head_t *waitq) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(waitq, &wait); + return signal_pending(current) ? -EINTR : 0; +} + +/* Generic utility functions */ +int ivtv_sleep_timeout(int timeout, int intr) +{ + int ret; + + do { + set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); + if (intr && (ret = signal_pending(current))) + return ret; + } while (timeout); + return 0; +} + +/* Release ioremapped memory */ +static void ivtv_iounmap(struct ivtv *itv) +{ + if (itv == NULL) + return; + + /* Release registers memory */ + if (itv->reg_mem != NULL) { + IVTV_DEBUG_INFO("releasing reg_mem\n"); + iounmap(itv->reg_mem); + itv->reg_mem = NULL; + } + /* Release io memory */ + if (itv->has_cx23415 && itv->dec_mem != NULL) { + IVTV_DEBUG_INFO("releasing dec_mem\n"); + iounmap(itv->dec_mem); + } + itv->dec_mem = NULL; + + /* Release io memory */ + if (itv->enc_mem != NULL) { + IVTV_DEBUG_INFO("releasing enc_mem\n"); + iounmap(itv->enc_mem); + itv->enc_mem = NULL; + } +} + +/* Hauppauge card? get values from tveeprom */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) +{ + u8 eedata[256]; + + itv->i2c_client.addr = 0xA0 >> 1; + tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata)); + tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata); +} + +static void ivtv_process_eeprom(struct ivtv *itv) +{ + struct tveeprom tv; + int pci_slot = PCI_SLOT(itv->dev->devfn); + + ivtv_read_eeprom(itv, &tv); + + /* Many thanks to Steven Toth from Hauppauge for providing the + model numbers */ + switch (tv.model) { + /* In a few cases the PCI subsystem IDs do not correctly + identify the card. A better method is to check the + model number from the eeprom instead. */ + case 32000 ... 32999: + case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */ + case 48400 ... 48599: + itv->card = ivtv_get_card(IVTV_CARD_PVR_250); + break; + case 48100 ... 48399: + case 48600 ... 48999: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350); + break; + case 23000 ... 23999: /* PVR500 */ + case 25000 ... 25999: /* Low profile PVR150 */ + case 26000 ... 26999: /* Regular PVR150 */ + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + case 0: + IVTV_ERR("Invalid EEPROM\n"); + return; + default: + IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model); + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + } + + switch (tv.model) { + /* Old style PVR350 (with an saa7114) uses this input for + the tuner. */ + case 48254: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1); + break; + default: + break; + } + + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; + + /* If this is a PVR500 then it should be possible to detect whether it is the + first or second unit by looking at the subsystem device ID: is bit 4 is + set, then it is the second unit (according to info from Hauppauge). + + However, while this works for most cards, I have seen a few PVR500 cards + where both units have the same subsystem ID. + + So instead I look at the reported 'PCI slot' (which is the slot on the PVR500 + PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise + it is the second unit. It is possible that it is a different slot when ivtv is + used in Xen, in that case I ignore this card here. The worst that can happen + is that the card presents itself with a non-working radio device. + + This detection is needed since the eeprom reports incorrectly that a radio is + present on the second unit. */ + if (tv.model / 1000 == 23) { + itv->card_name = "WinTV PVR 500"; + if (pci_slot == 8 || pci_slot == 9) { + int is_first = (pci_slot & 1) == 0; + + itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" : + "WinTV PVR 500 (unit #2)"; + if (!is_first) { + IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n"); + tv.has_radio = 0; + } + } + } + IVTV_INFO("Autodetected %s\n", itv->card_name); + + switch (tv.tuner_hauppauge_model) { + case 85: + case 99: + case 112: + itv->pvr150_workaround = 1; + break; + default: + break; + } + if (tv.tuner_type == TUNER_ABSENT) + IVTV_ERR("tveeprom cannot autodetect tuner!"); + + if (itv->options.tuner == -1) + itv->options.tuner = tv.tuner_type; + if (itv->options.radio == -1) + itv->options.radio = (tv.has_radio != 0); + /* only enable newi2c if an IR blaster is present */ + /* FIXME: for 2.6.20 the test against 2 should be removed */ + if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) { + itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0; + if (itv->options.newi2c) { + IVTV_INFO("reopen i2c bus for IR-blaster support\n"); + exit_ivtv_i2c(itv); + init_ivtv_i2c(itv); + } + } + + if (itv->std != 0) + /* user specified tuner standard */ + return; + + /* autodetect tuner standard */ + if (tv.tuner_formats & V4L2_STD_PAL) { + IVTV_DEBUG_INFO("PAL tuner detected\n"); + itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + } else if (tv.tuner_formats & V4L2_STD_NTSC) { + IVTV_DEBUG_INFO("NTSC tuner detected\n"); + itv->std |= V4L2_STD_NTSC_M; + } else if (tv.tuner_formats & V4L2_STD_SECAM) { + IVTV_DEBUG_INFO("SECAM tuner detected\n"); + itv->std |= V4L2_STD_SECAM_L; + } else { + IVTV_INFO("No tuner detected, default to NTSC-M\n"); + itv->std |= V4L2_STD_NTSC_M; + } +} + +static v4l2_std_id ivtv_parse_std(struct ivtv *itv) +{ + switch (pal[0]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + default: + IVTV_WARN("pal= argument not recognised\n"); + return 0; + } + + switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[1] == 'C' || secam[1] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + default: + IVTV_WARN("secam= argument not recognised\n"); + return 0; + } + + switch (ntsc[0]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + default: + IVTV_WARN("ntsc= argument not recognised\n"); + return 0; + } + + /* no match found */ + return 0; +} + +static void ivtv_process_options(struct ivtv *itv) +{ + const char *chipname; + int i, j; + + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; + itv->options.cardtype = cardtype[itv->num]; + itv->options.tuner = tuner[itv->num]; + itv->options.radio = radio[itv->num]; + itv->options.newi2c = newi2c; + + itv->std = ivtv_parse_std(itv); + itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15); + chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; + if (itv->options.cardtype == -1) { + IVTV_INFO("Ignore card (detected %s based chip)\n", chipname); + return; + } + if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) { + IVTV_INFO("User specified %s card (detected %s based chip)\n", + itv->card->name, chipname); + } else if (itv->options.cardtype != 0) { + IVTV_ERR("Unknown user specified type, trying to autodetect card\n"); + } + if (itv->card == NULL) { + if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { + itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150); + IVTV_INFO("Autodetected Hauppauge card (%s based)\n", + chipname); + } + } + if (itv->card == NULL) { + for (i = 0; (itv->card = ivtv_get_card(i)); i++) { + if (itv->card->pci_list == NULL) + continue; + for (j = 0; itv->card->pci_list[j].device; j++) { + if (itv->dev->device != + itv->card->pci_list[j].device) + continue; + if (itv->dev->subsystem_vendor != + itv->card->pci_list[j].subsystem_vendor) + continue; + if (itv->dev->subsystem_device != + itv->card->pci_list[j].subsystem_device) + continue; + IVTV_INFO("Autodetected %s card (%s based)\n", + itv->card->name, chipname); + goto done; + } + } + } +done: + + if (itv->card == NULL) { + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n", + itv->dev->vendor, itv->dev->device); + IVTV_ERR(" subsystem vendor/device: %04x/%04x\n", + itv->dev->subsystem_vendor, itv->dev->subsystem_device); + IVTV_ERR(" %s based\n", chipname); + IVTV_ERR("Defaulting to %s card\n", itv->card->name); + IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); + IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); + IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n"); + } + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; +} + +/* Precondition: the ivtv structure has been memset to 0. Only + the dev and num fields have been filled in. + No assumptions on the card type may be made here (see ivtv_init_struct2 + for that). + */ +static int __devinit ivtv_init_struct1(struct ivtv *itv) +{ + itv->base_addr = pci_resource_start(itv->dev, 0); + itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ + itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ + + mutex_init(&itv->i2c_bus_lock); + mutex_init(&itv->udma.lock); + + spin_lock_init(&itv->lock); + spin_lock_init(&itv->dma_reg_lock); + + itv->irq_work_queues = create_workqueue(itv->name); + if (itv->irq_work_queues == NULL) { + IVTV_ERR("Could not create ivtv workqueue\n"); + return -1; + } + + INIT_WORK(&itv->irq_work_queue, ivtv_irq_work_handler); + + /* start counting open_id at 1 */ + itv->open_id = 1; + + /* Initial settings */ + cx2341x_fill_defaults(&itv->params); + itv->params.port = CX2341X_PORT_MEMORY; + itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + init_waitqueue_head(&itv->cap_w); + init_waitqueue_head(&itv->event_waitq); + init_waitqueue_head(&itv->vsync_waitq); + init_waitqueue_head(&itv->dma_waitq); + init_timer(&itv->dma_timer); + itv->dma_timer.function = ivtv_unfinished_dma; + itv->dma_timer.data = (unsigned long)itv; + + itv->cur_dma_stream = -1; + itv->audio_stereo_mode = AUDIO_STEREO; + itv->audio_bilingual_mode = AUDIO_MONO_LEFT; + + /* Ctrls */ + itv->speed = 1000; + + /* VBI */ + itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced; + + /* OSD */ + itv->osd_global_alpha_state = 1; + itv->osd_global_alpha = 255; + + /* YUV */ + atomic_set(&itv->yuv_info.next_dma_frame, -1); + itv->yuv_info.lace_mode = ivtv_yuv_mode; + itv->yuv_info.lace_threshold = ivtv_yuv_threshold; + return 0; +} + +/* Second initialization part. Here the card type has been + autodetected. */ +static void __devinit ivtv_init_struct2(struct ivtv *itv) +{ + int i; + + for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++) + if (itv->card->video_inputs[i].video_type == 0) + break; + itv->nof_inputs = i; + for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++) + if (itv->card->audio_inputs[i].audio_type == 0) + break; + itv->nof_audio_inputs = i; + + /* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */ + if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) + itv->digitizer = 0xF1; + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->digitizer = 0xEF; + else /* cx25840 */ + itv->digitizer = 0x140; + + if (itv->card->hw_all & IVTV_HW_CX25840) { + itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */ + } else { + itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */ + } + + /* Find tuner input */ + for (i = 0; i < itv->nof_inputs; i++) { + if (itv->card->video_inputs[i].video_type == + IVTV_CARD_INPUT_VID_TUNER) + break; + } + if (i == itv->nof_inputs) + i = 0; + itv->active_input = i; + itv->audio_input = itv->card->video_inputs[i].audio_index; + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->video_dec_func = ivtv_cx25840; + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->video_dec_func = ivtv_saa717x; + else + itv->video_dec_func = ivtv_saa7115; +} + +static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + u16 cmd; + unsigned char pci_latency; + + IVTV_DEBUG_INFO("Enabling pci device\n"); + + if (pci_enable_device(dev)) { + IVTV_ERR("Can't enable device %d!\n", itv->num); + return -EIO; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + return -EIO; + } + if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { + IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + return -EIO; + } + + if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE, "ivtv registers")) { + IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + return -EIO; + } + + if (itv->has_cx23415 && + !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE, "ivtv decoder")) { + IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + return -EIO; + } + + /* Check for bus mastering */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n"); + pci_set_master(dev); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_ERR("Bus Mastering is not enabled\n"); + return -ENXIO; + } + } + IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); + + pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 64 && ivtv_pci_latency) { + IVTV_INFO("Unreasonably low latency timer, " + "setting to 64 (was %d)\n", pci_latency); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + } + /* This config space value relates to DMA latencies. The + default value 0x8080 is too low however and will lead + to DMA errors. 0xffff is the max value which solves + these problems. */ + pci_write_config_dword(dev, 0x40, 0xffff); + + IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " + "irq: %d, latency: %d, memory: 0x%lx\n", + itv->dev->device, itv->card_rev, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + itv->dev->irq, pci_latency, (unsigned long)itv->base_addr); + + return 0; +} + +static void ivtv_request_module(struct ivtv *itv, const char *name) +{ + if (request_module(name) != 0) { + IVTV_ERR("Failed to load module %s\n", name); + } else { + IVTV_DEBUG_INFO("Loaded module %s\n", name); + } +} + +static void ivtv_load_and_init_modules(struct ivtv *itv) +{ + struct v4l2_control ctrl; + u32 hw = itv->card->hw_all; + int i; + + /* load modules */ +#ifndef CONFIG_VIDEO_TUNER + if (hw & IVTV_HW_TUNER) { + ivtv_request_module(itv, "tuner"); +#ifdef HAVE_XC3028 + if (itv->options.tuner == TUNER_XCEIVE_XC3028) + ivtv_request_module(itv, "xc3028-tuner"); +#endif + } +#endif +#ifndef CONFIG_VIDEO_CX25840 + if (hw & IVTV_HW_CX25840) + ivtv_request_module(itv, "cx25840"); +#endif +#ifndef CONFIG_VIDEO_SAA711X + if (hw & IVTV_HW_SAA711X) + ivtv_request_module(itv, "saa7115"); +#endif +#ifndef CONFIG_VIDEO_SAA7127 + if (hw & IVTV_HW_SAA7127) + ivtv_request_module(itv, "saa7127"); +#endif + if (hw & IVTV_HW_SAA717X) + ivtv_request_module(itv, "saa717x"); +#ifndef CONFIG_VIDEO_UPD64031A + if (hw & IVTV_HW_UPD64031A) + ivtv_request_module(itv, "upd64031a"); +#endif +#ifndef CONFIG_VIDEO_UPD64083 + if (hw & IVTV_HW_UPD6408X) + ivtv_request_module(itv, "upd64083"); +#endif +#ifndef CONFIG_VIDEO_MSP3400 + if (hw & IVTV_HW_MSP34XX) + ivtv_request_module(itv, "msp3400"); +#endif + if (hw & IVTV_HW_TVAUDIO) + ivtv_request_module(itv, "tvaudio"); +#ifndef CONFIG_VIDEO_WM8775 + if (hw & IVTV_HW_WM8775) + ivtv_request_module(itv, "wm8775"); +#endif +#ifndef CONFIG_VIDEO_WM8739 + if (hw & IVTV_HW_WM8739) + ivtv_request_module(itv, "wm8739"); +#endif +#ifndef CONFIG_VIDEO_CS53L32A + if (hw & IVTV_HW_CS53L32A) + ivtv_request_module(itv, "cs53l32a"); +#endif + + /* check which i2c devices are actually found */ + for (i = 0; i < 32; i++) { + u32 device = 1 << i; + + if (!(device & hw)) + continue; + if (device == IVTV_HW_GPIO) { + /* GPIO is always available */ + itv->hw_flags |= IVTV_HW_GPIO; + continue; + } + if (ivtv_i2c_hw_addr(itv, device) > 0) + itv->hw_flags |= device; + } + + hw = itv->hw_flags; + + if (itv->card->type == IVTV_CARD_CX23416GYC) { + /* Several variations of this card exist, detect which card + type should be used. */ + if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS); + else if ((hw & IVTV_HW_UPD64031A) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR); + } + + if (hw & IVTV_HW_CX25840) { + /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ + ctrl.id = V4L2_CID_PRIVATE_BASE; + ctrl.value = itv->pvr150_workaround; + itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + + itv->vbi.raw_decoder_line_size = 1444; + itv->vbi.raw_decoder_sav_odd_field = 0x20; + itv->vbi.raw_decoder_sav_even_field = 0x60; + itv->vbi.sliced_decoder_line_size = 272; + itv->vbi.sliced_decoder_sav_odd_field = 0xB0; + itv->vbi.sliced_decoder_sav_even_field = 0xF0; + } + + if (hw & IVTV_HW_SAA711X) { + struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X }; + + /* determine the exact saa711x model */ + itv->hw_flags &= ~IVTV_HW_SAA711X; + + ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + if (v.ident == V4L2_IDENT_SAA7114) { + itv->hw_flags |= IVTV_HW_SAA7114; + /* VBI is not yet supported by the saa7114 driver. */ + itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE); + } + else { + itv->hw_flags |= IVTV_HW_SAA7115; + } + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } + + if (hw & IVTV_HW_SAA717X) { + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } +} + +static int __devinit ivtv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int retval = 0; + int video_input; + int yuv_buf_size; + int vbi_buf_size; + int fw_retry_count = 3; + struct ivtv *itv; + struct v4l2_frequency vf; + + spin_lock(&ivtv_cards_lock); + + /* Make sure we've got a place for this card */ + if (ivtv_cards_active == IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: Maximum number of cards detected (%d).\n", + ivtv_cards_active); + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + + itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); + if (itv == 0) { + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + ivtv_cards[ivtv_cards_active] = itv; + itv->dev = dev; + itv->num = ivtv_cards_active++; + snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num); + if (itv->num) { + printk(KERN_INFO "ivtv: ====================== NEXT CARD ======================\n"); + } + + spin_unlock(&ivtv_cards_lock); + + ivtv_process_options(itv); + if (itv->options.cardtype == -1) { + retval = -ENODEV; + goto err; + } + if (ivtv_init_struct1(itv)) { + retval = -ENOMEM; + goto err; + } + + IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr); + + /* PCI Device Setup */ + if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) { + if (retval == -EIO) + goto free_workqueue; + else if (retval == -ENXIO) + goto free_mem; + } + /* save itv in the pci struct for later use */ + pci_set_drvdata(dev, itv); + + /* map io memory */ + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); + itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET, + IVTV_ENCODER_SIZE); + if (!itv->enc_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); + if (!itv->dec_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + } + else { + itv->dec_mem = itv->enc_mem; + } + + /* map registers memory */ + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + itv->reg_mem = + ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (!itv->reg_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_io; + } + + while (--fw_retry_count > 0) { + /* load firmware */ + if (ivtv_firmware_init(itv) == 0) + break; + if (fw_retry_count > 1) + IVTV_WARN("Retry loading firmware\n"); + } + if (fw_retry_count == 0) { + IVTV_ERR("Error initializing firmware\n"); + goto free_i2c; + } + + /* Try and get firmware versions */ + IVTV_DEBUG_INFO("Getting firmware version..\n"); + ivtv_firmware_versions(itv); + + /* Check yuv output filter table */ + if (itv->has_cx23415) ivtv_yuv_filter_check(itv); + + ivtv_gpio_init(itv); + + /* active i2c */ + IVTV_DEBUG_INFO("activating i2c...\n"); + if (init_ivtv_i2c(itv)) { + IVTV_ERR("Could not initialize i2c\n"); + goto free_irq; + } + + IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); + + if (itv->card->hw_all & IVTV_HW_TVEEPROM) { +#ifdef CONFIG_VIDEO_TVEEPROM_MODULE + ivtv_request_module(itv, "tveeprom"); +#endif + /* Based on the model number the cardtype may be changed. + The PCI IDs are not always reliable. */ + ivtv_process_eeprom(itv); + } + + if (itv->std == 0) { + itv->std = V4L2_STD_NTSC_M; + } + + if (itv->options.tuner == -1) { + int i; + + for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) { + if ((itv->std & itv->card->tuners[i].std) == 0) + continue; + itv->options.tuner = itv->card->tuners[i].tuner; + break; + } + } + /* if no tuner was found, then pick the first tuner in the card list */ + if (itv->options.tuner == -1 && itv->card->tuners[0].std) { + itv->std = itv->card->tuners[0].std; + itv->options.tuner = itv->card->tuners[0].tuner; + } + if (itv->options.radio == -1) + itv->options.radio = (itv->card->radio_input.audio_type != 0); + + /* The card is now fully identified, continue with card-specific + initialization. */ + ivtv_init_struct2(itv); + + ivtv_load_and_init_modules(itv); + + if (itv->std & V4L2_STD_525_60) { + itv->is_60hz = 1; + itv->is_out_60hz = 1; + } else { + itv->is_50hz = 1; + itv->is_out_50hz = 1; + } + itv->params.video_gop_size = itv->is_60hz ? 15 : 12; + + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000; + + /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */ + yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8; + + /* Setup VBI Raw Size. Should be big enough to hold PAL. + It is possible to switch between PAL and NTSC, so we need to + take the largest size here. */ + /* 1456 is multiple of 16, real size = 1444 */ + itv->vbi.raw_size = 1456; + /* We use a buffer size of 1/2 of the total size needed for a + frame. This is actually very useful, since we now receive + a field at a time and that makes 'compressing' the raw data + down to size by stripping off the SAV codes a lot easier. + Note: having two different buffer sizes prevents standard + switching on the fly. We need to find a better solution... */ + vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36; + + if (itv->options.radio > 0) + itv->v4l2_cap |= V4L2_CAP_RADIO; + + if (itv->options.tuner > -1) { + struct tuner_setup setup; + + setup.addr = ADDR_UNSET; + setup.type = itv->options.tuner; + setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ +#ifdef HAVE_XC3028 + setup.initmode = V4L2_TUNER_ANALOG_TV; + if (itv->options.tuner == TUNER_XCEIVE_XC3028) { + setup.gpio_write = ivtv_reset_tuner_gpio; + setup.gpio_priv = itv; + } +#endif + ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + } + + vf.tuner = 0; + vf.type = V4L2_TUNER_ANALOG_TV; + vf.frequency = 6400; /* the tuner 'baseline' frequency */ + if (itv->std & V4L2_STD_NTSC_M) { + /* Why on earth? */ + vf.frequency = 1076; /* ch. 4 67250*16/1000 */ + } + + /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) + are not. */ + itv->tuner_std = itv->std; + + video_input = itv->active_input; + itv->active_input++; /* Force update of input */ + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); + + /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code + in one place. */ + itv->std++; /* Force full standard initialization */ + itv->std_out = itv->std; + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); + + retval = ivtv_streams_setup(itv); + if (retval) { + IVTV_ERR("Error %d setting up streams\n", retval); + goto free_i2c; + } + + if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + } + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); + + IVTV_DEBUG_IRQ("Masking interrupts\n"); + /* clear interrupt mask, effectively disabling interrupts */ + ivtv_set_irq_mask(itv, 0xffffffff); + + /* Register IRQ */ + retval = request_irq(itv->dev->irq, ivtv_irq_handler, + IRQF_SHARED | IRQF_DISABLED, itv->name, (void *)itv); + if (retval) { + IVTV_ERR("Failed to register irq %d\n", retval); + goto free_streams; + } + + /* On a cx23416 this seems to be able to enable DMA to the chip? */ + if (!itv->has_cx23415) + write_reg_sync(0x03, IVTV_REG_DMACONTROL); + + /* Default interrupts enabled. For the PVR350 this includes the + decoder VSYNC interrupt, which is always on. It is not only used + during decoding but also by the OSD. + Some old PVR250 cards had a cx23415, so testing for that is too + general. Instead test if the card has video output capability. */ + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + + if (itv->has_cx23415) + ivtv_set_osd_alpha(itv); + + IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num); + + return 0; + + free_irq: + free_irq(itv->dev->irq, (void *)itv); + free_streams: + ivtv_streams_cleanup(itv); + free_i2c: + exit_ivtv_i2c(itv); + free_io: + ivtv_iounmap(itv); + free_mem: + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + free_workqueue: + destroy_workqueue(itv->irq_work_queues); + err: + if (retval == 0) + retval = -ENODEV; + IVTV_ERR("Error %d on initialization\n", retval); + + kfree(ivtv_cards[ivtv_cards_active]); + ivtv_cards[ivtv_cards_active] = NULL; + return retval; +} + +static void ivtv_remove(struct pci_dev *pci_dev) +{ + struct ivtv *itv = pci_get_drvdata(pci_dev); + + IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num); + + /* Stop all captures */ + IVTV_DEBUG_INFO(" Stopping all streams.\n"); + if (atomic_read(&itv->capturing) > 0) + ivtv_stop_all_captures(itv); + + /* Stop all decoding */ + IVTV_DEBUG_INFO(" Stopping decoding.\n"); + if (atomic_read(&itv->decoding) > 0) { + int type; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + type = IVTV_DEC_STREAM_TYPE_YUV; + else + type = IVTV_DEC_STREAM_TYPE_MPG; + ivtv_stop_v4l2_decode_stream(&itv->streams[type], + VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } + + /* Interrupts */ + IVTV_DEBUG_INFO(" Disabling interrupts.\n"); + ivtv_set_irq_mask(itv, 0xffffffff); + del_timer_sync(&itv->dma_timer); + + /* Stop all Work Queues */ + IVTV_DEBUG_INFO(" Stop Work Queues.\n"); + flush_workqueue(itv->irq_work_queues); + destroy_workqueue(itv->irq_work_queues); + + IVTV_DEBUG_INFO(" Stopping Firmware.\n"); + ivtv_halt_firmware(itv); + + IVTV_DEBUG_INFO(" Unregistering v4l devices.\n"); + ivtv_streams_cleanup(itv); + IVTV_DEBUG_INFO(" Freeing dma resources.\n"); + ivtv_udma_free(itv); + + exit_ivtv_i2c(itv); + + IVTV_DEBUG_INFO(" Releasing irq.\n"); + free_irq(itv->dev->irq, (void *)itv); + + if (itv->dev) { + ivtv_iounmap(itv); + } + + IVTV_DEBUG_INFO(" Releasing mem.\n"); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + + pci_disable_device(itv->dev); + + IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); +} + +/* define a pci_driver for card detection */ +static struct pci_driver ivtv_pci_driver = { + .name = "ivtv", + .id_table = ivtv_pci_tbl, + .probe = ivtv_probe, + .remove = ivtv_remove, +}; + +static int module_start(void) +{ + printk(KERN_INFO "ivtv: ==================== START INIT IVTV ====================\n"); + printk(KERN_INFO "ivtv: version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION); + + memset(ivtv_cards, 0, sizeof(ivtv_cards)); + + /* Validate parameters */ + if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: ivtv_first_minor must be between 0 and %d. Exiting...\n", + IVTV_MAX_CARDS - 1); + return -1; + } + + if (ivtv_debug < 0 || ivtv_debug > 511) { + ivtv_debug = 0; + printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 511!\n"); + } + + if (pci_register_driver(&ivtv_pci_driver)) { + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + return -ENODEV; + } + printk(KERN_INFO "ivtv: ==================== END INIT IVTV ====================\n"); + return 0; +} + +static void module_cleanup(void) +{ + int i, j; + + pci_unregister_driver(&ivtv_pci_driver); + + for (i = 0; i < ivtv_cards_active; i++) { + if (ivtv_cards[i] == NULL) + continue; + for (j = 0; j < IVTV_VBI_FRAMES; j++) { + kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); + } + kfree(ivtv_cards[i]); + } +} + +/* Note: These symbols are exported because they are used by the ivtv-fb + framebuffer module and an infrared module for the IR-blaster. */ +EXPORT_SYMBOL(ivtv_set_irq_mask); +EXPORT_SYMBOL(ivtv_cards_active); +EXPORT_SYMBOL(ivtv_cards); +EXPORT_SYMBOL(ivtv_api); +EXPORT_SYMBOL(ivtv_vapi); +EXPORT_SYMBOL(ivtv_vapi_result); +EXPORT_SYMBOL(ivtv_clear_irq_mask); +EXPORT_SYMBOL(ivtv_debug); +EXPORT_SYMBOL(ivtv_reset_ir_gpio); +EXPORT_SYMBOL(ivtv_udma_setup); +EXPORT_SYMBOL(ivtv_udma_unmap); +EXPORT_SYMBOL(ivtv_udma_alloc); +EXPORT_SYMBOL(ivtv_udma_prepare); + +module_init(module_start); +module_exit(module_cleanup); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h new file mode 100644 index 0000000..9a412d6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -0,0 +1,868 @@ +/* + ivtv driver internal defines and structures + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IVTV_DRIVER_H +#define IVTV_DRIVER_H + +/* Internal header for ivtv project: + * Driver for the cx23415/6 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com> + * and Takeru KOMORIYA<komoriya@paken.org> + * + * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org> + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/fs.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/list.h> +#include <linux/unistd.h> +#include <linux/byteorder/swab.h> +#include <linux/pagemap.h> +#include <linux/workqueue.h> +#include <linux/mutex.h> +#include <asm/uaccess.h> +#include <asm/system.h> + +#include <linux/dvb/video.h> +#include <linux/dvb/audio.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include <media/cx2341x.h> + +/* #define HAVE_XC3028 1 */ + +#include <media/ivtv.h> + +#ifdef CONFIG_LIRC_I2C +# error "This driver is not compatible with the LIRC I2C kernel configuration option." +#endif /* CONFIG_LIRC_I2C */ + +#ifndef CONFIG_PCI +# error "This driver requires kernel PCI support." +#endif /* CONFIG_PCI */ + +#define IVTV_ENCODER_OFFSET 0x00000000 +#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_DECODER_OFFSET 0x01000000 +#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_REG_OFFSET 0x02000000 +#define IVTV_REG_SIZE 0x00010000 + +/* Buffers on hardware offsets */ +#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */ +#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ + +/* Offset to filter table in firmware */ +#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8 +#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358 + +extern const u32 yuv_offset[4]; + +/* Maximum ivtv driver instances. + Based on 6 PVR500s each with two PVR15s... + TODO: make this dynamic. I believe it is only a global in order to support + ivtv-fb. There must be a better way to do that. */ +#define IVTV_MAX_CARDS 12 + +/* Supported cards */ +#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ +#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */ +#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two + PVR150s on one PCI board) */ +#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */ +#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */ +#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160 + cx23415 based, but does not have tv-out */ +#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */ +#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */ +#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */ +#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */ +#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */ +#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */ +#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */ +#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */ +#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */ +#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */ +#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */ +#ifdef HAVE_XC3028 +#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */ +#define IVTV_CARD_LAST 18 +#else +#define IVTV_CARD_LAST 17 +#endif + +/* Variants of existing cards but with the same PCI IDs. The driver + detects these based on other device information. + These cards must always come last. + New cards must be inserted above, and the indices of the cards below + must be adjusted accordingly. */ + +/* PVR-350 V1 (uses saa7114) */ +#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1) +/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2) +#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3) + +#define IVTV_ENC_STREAM_TYPE_MPG 0 +#define IVTV_ENC_STREAM_TYPE_YUV 1 +#define IVTV_ENC_STREAM_TYPE_VBI 2 +#define IVTV_ENC_STREAM_TYPE_PCM 3 +#define IVTV_ENC_STREAM_TYPE_RAD 4 +#define IVTV_DEC_STREAM_TYPE_MPG 5 +#define IVTV_DEC_STREAM_TYPE_VBI 6 +#define IVTV_DEC_STREAM_TYPE_VOUT 7 +#define IVTV_DEC_STREAM_TYPE_YUV 8 +#define IVTV_MAX_STREAMS 9 + +#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ +#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ +#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ +#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ + +#define IVTV_ENC_MEM_START 0x00000000 +#define IVTV_DEC_MEM_START 0x01000000 + +/* system vendor and device IDs */ +#define PCI_VENDOR_ID_ICOMP 0x4444 +#define PCI_DEVICE_ID_IVTV15 0x0803 +#define PCI_DEVICE_ID_IVTV16 0x0016 + +/* subsystem vendor ID */ +#define IVTV_PCI_ID_HAUPPAUGE 0x0070 +#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270 +#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070 +#define IVTV_PCI_ID_ADAPTEC 0x9005 +#define IVTV_PCI_ID_AVERMEDIA 0x1461 +#define IVTV_PCI_ID_YUAN1 0x12ab +#define IVTV_PCI_ID_YUAN2 0xff01 +#define IVTV_PCI_ID_YUAN3 0xffab +#define IVTV_PCI_ID_YUAN4 0xfbab +#define IVTV_PCI_ID_DIAMONDMM 0xff92 +#define IVTV_PCI_ID_IODATA 0x10fc +#define IVTV_PCI_ID_MELCO 0x1154 +#define IVTV_PCI_ID_GOTVIEW1 0xffac +#define IVTV_PCI_ID_GOTVIEW2 0xffad + +/* Decoder Buffer hardware size on Chip */ +#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */ +#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */ + +/* ======================================================================== */ +/* ========================== START USER SETTABLE DMA VARIABLES =========== */ +/* ======================================================================== */ + +#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */ + +/* DMA Buffers, Default size in MB allocated */ +#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4 +#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2 +#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1 +#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1 +#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1 +#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1 +#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1 + +/* ======================================================================== */ +/* ========================== END USER SETTABLE DMA VARIABLES ============= */ +/* ======================================================================== */ + +/* Decoder Status Register */ +#define IVTV_DMA_ERR_LIST 0x00000010 +#define IVTV_DMA_ERR_WRITE 0x00000008 +#define IVTV_DMA_ERR_READ 0x00000004 +#define IVTV_DMA_SUCCESS_WRITE 0x00000002 +#define IVTV_DMA_SUCCESS_READ 0x00000001 +#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ) +#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE) +#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ) + +/* DMA Registers */ +#define IVTV_REG_DMAXFER (0x0000) +#define IVTV_REG_DMASTATUS (0x0004) +#define IVTV_REG_DECDMAADDR (0x0008) +#define IVTV_REG_ENCDMAADDR (0x000c) +#define IVTV_REG_DMACONTROL (0x0010) +#define IVTV_REG_IRQSTATUS (0x0040) +#define IVTV_REG_IRQMASK (0x0048) + +/* Setup Registers */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8) +#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC) +#define IVTV_REG_VDM (0x2800) +#define IVTV_REG_AO (0x2D00) +#define IVTV_REG_BYTEFLUSH (0x2D24) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +#define IVTV_IRQ_ENC_START_CAP (0x1 << 31) +#define IVTV_IRQ_ENC_EOS (0x1 << 30) +#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) +#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) +#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) +#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) +#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) +#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) +#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) +#define IVTV_IRQ_DMA_ERR (0x1 << 18) +#define IVTV_IRQ_DMA_WRITE (0x1 << 17) +#define IVTV_IRQ_DMA_READ (0x1 << 16) +#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) + +/* IRQ Masks */ +#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ) + +#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) +#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 + +/* debugging */ + +#define IVTV_DBGFLG_WARN (1 << 0) +#define IVTV_DBGFLG_INFO (1 << 1) +#define IVTV_DBGFLG_API (1 << 2) +#define IVTV_DBGFLG_DMA (1 << 3) +#define IVTV_DBGFLG_IOCTL (1 << 4) +#define IVTV_DBGFLG_I2C (1 << 5) +#define IVTV_DBGFLG_IRQ (1 << 6) +#define IVTV_DBGFLG_DEC (1 << 7) +#define IVTV_DBGFLG_YUV (1 << 8) + +/* NOTE: extra space before comma in 'itv->num , ## args' is required for + gcc-2.95, otherwise it won't compile. */ +#define IVTV_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args) +#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +#define IVTV_FB_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) +#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +/* Standard kernel messages */ +#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args) + +/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ +#define MPEG_FRAME_TYPE_IFRAME 1 +#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 +#define MPEG_FRAME_TYPE_ALL 7 + +/* output modes (cx23415 only) */ +#define OUT_NONE 0 +#define OUT_MPG 1 +#define OUT_YUV 2 +#define OUT_UDMA_YUV 3 +#define OUT_PASSTHROUGH 4 + +#define IVTV_MAX_PGM_INDEX (400) + +extern int ivtv_debug; + + +struct ivtv_options { + int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */ + int cardtype; /* force card type on load */ + int tuner; /* set tuner on load */ + int radio; /* enable/disable radio */ + int newi2c; /* New I2C algorithm */ +}; + +#define IVTV_MBOX_DMA_START 6 +#define IVTV_MBOX_DMA_END 8 +#define IVTV_MBOX_DMA 9 +#define IVTV_MBOX_FIELD_DISPLAYED 8 + +/* ivtv-specific mailbox template */ +struct ivtv_mailbox { + u32 flags; + u32 cmd; + u32 retval; + u32 timeout; + u32 data[CX2341X_MBOX_MAX_DATA]; +}; + +struct ivtv_api_cache { + unsigned long last_jiffies; /* when last command was issued */ + u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */ +}; + +struct ivtv_mailbox_data { + volatile struct ivtv_mailbox __iomem *mbox; + /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes. + If the bit is set, then the corresponding mailbox is in use by the driver. */ + unsigned long busy; + u8 max_mbox; +}; + +/* per-buffer bit flags */ +#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ + +/* per-stream, s_flags */ +#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */ +#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */ +#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */ + +#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */ +#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ +#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ +#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */ +#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ +#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ + +/* per-ivtv, i_flags */ +#define IVTV_F_I_DMA 0 /* DMA in progress */ +#define IVTV_F_I_UDMA 1 /* UDMA in progress */ +#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */ +#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */ +#define IVTV_F_I_EOS 4 /* End of encoder stream reached */ +#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */ +#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */ +#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */ +#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */ +#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */ +#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */ +#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */ +#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ +#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ +#define IVTV_F_I_WORK_HANDLER_VBI 15 /* there is work to be done for VBI */ +#define IVTV_F_I_WORK_HANDLER_YUV 16 /* there is work to be done for YUV */ + +/* Event notifications */ +#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ +#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */ +#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */ +#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */ + +/* Scatter-Gather array element, used in DMA transfers */ +struct ivtv_SG_element { + u32 src; + u32 dst; + u32 size; +}; + +struct ivtv_user_dma { + struct mutex lock; + int page_count; + struct page *map[IVTV_DMA_SG_OSD_ENT]; + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT]; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT]; +}; + +struct ivtv_dma_page_info { + unsigned long uaddr; + unsigned long first; + unsigned long last; + unsigned int offset; + unsigned int tail; + int page_count; +}; + +struct ivtv_buffer { + struct list_head list; + dma_addr_t dma_handle; + unsigned long b_flags; + char *buf; + + u32 bytesused; + u32 readpos; +}; + +struct ivtv_queue { + struct list_head list; + u32 buffers; + u32 length; + u32 bytesused; +}; + +struct ivtv; /* forward reference */ + +struct ivtv_stream { + /* These first four fields are always set, even if the stream + is not actually created. */ + struct video_device *v4l2dev; /* NULL when stream not created */ + struct ivtv *itv; /* for ease of use */ + const char *name; /* name of the stream */ + int type; /* stream type */ + + u32 id; + spinlock_t qlock; /* locks access to the queues */ + unsigned long s_flags; /* status flags, see above */ + int dma; /* can be PCI_DMA_TODEVICE, + PCI_DMA_FROMDEVICE or + PCI_DMA_NONE */ + u32 dma_offset; + u32 dma_backup; + u64 dma_pts; + + int subtype; + wait_queue_head_t waitq; + u32 dma_last_offset; + + /* Buffer Stats */ + u32 buffers; + u32 buf_size; + u32 buffers_stolen; + + /* Buffer Queues */ + struct ivtv_queue q_free; /* free buffers */ + struct ivtv_queue q_full; /* full buffers */ + struct ivtv_queue q_io; /* waiting for I/O */ + struct ivtv_queue q_dma; /* waiting for DMA */ + struct ivtv_queue q_predma; /* waiting for DMA */ + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element *SGarray; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist *SGlist; +}; + +struct ivtv_open_id { + u32 open_id; + int type; + enum v4l2_priority prio; + struct ivtv *itv; +}; + +#define IVTV_YUV_UPDATE_HORIZONTAL 0x01 +#define IVTV_YUV_UPDATE_VERTICAL 0x02 + +struct yuv_frame_info +{ + u32 update; + int src_x; + int src_y; + unsigned int src_w; + unsigned int src_h; + int dst_x; + int dst_y; + unsigned int dst_w; + unsigned int dst_h; + int pan_x; + int pan_y; + u32 vis_w; + u32 vis_h; + u32 interlaced_y; + u32 interlaced_uv; + int tru_x; + u32 tru_w; + u32 tru_h; + u32 offset_y; +}; + +#define IVTV_YUV_MODE_INTERLACED 0x00 +#define IVTV_YUV_MODE_PROGRESSIVE 0x01 +#define IVTV_YUV_MODE_AUTO 0x02 +#define IVTV_YUV_MODE_MASK 0x03 + +#define IVTV_YUV_SYNC_EVEN 0x00 +#define IVTV_YUV_SYNC_ODD 0x04 +#define IVTV_YUV_SYNC_MASK 0x04 + +struct yuv_playback_info +{ + u32 reg_2834; + u32 reg_2838; + u32 reg_283c; + u32 reg_2840; + u32 reg_2844; + u32 reg_2848; + u32 reg_2854; + u32 reg_285c; + u32 reg_2864; + + u32 reg_2870; + u32 reg_2874; + u32 reg_2890; + u32 reg_2898; + u32 reg_289c; + + u32 reg_2918; + u32 reg_291c; + u32 reg_2920; + u32 reg_2924; + u32 reg_2928; + u32 reg_292c; + u32 reg_2930; + + u32 reg_2934; + + u32 reg_2938; + u32 reg_293c; + u32 reg_2940; + u32 reg_2944; + u32 reg_2948; + u32 reg_294c; + u32 reg_2950; + u32 reg_2954; + u32 reg_2958; + u32 reg_295c; + u32 reg_2960; + u32 reg_2964; + u32 reg_2968; + u32 reg_296c; + + u32 reg_2970; + + int v_filter_1; + int v_filter_2; + int h_filter; + + u32 osd_x_offset; + u32 osd_y_offset; + + u32 osd_x_pan; + u32 osd_y_pan; + + u32 osd_vis_w; + u32 osd_vis_h; + + int decode_height; + + int frame_interlaced; + int frame_interlaced_last; + + int lace_mode; + int lace_threshold; + int lace_sync_field; + + atomic_t next_dma_frame; + atomic_t next_fill_frame; + + u32 yuv_forced_update; + int update_frame; + struct yuv_frame_info new_frame_info[4]; + struct yuv_frame_info old_frame_info; + struct yuv_frame_info old_frame_info_args; + + void *blanking_ptr; + dma_addr_t blanking_dmaptr; +}; + +#define IVTV_VBI_FRAMES 32 + +/* VBI data */ +struct vbi_info { + u32 dec_start; + u32 enc_start, enc_size; + int fpi; + u32 frame; + u32 dma_offset; + u8 cc_data_odd[256]; + u8 cc_data_even[256]; + int cc_pos; + u8 cc_no_update; + u8 vps[5]; + u8 vps_found; + int wss; + u8 wss_found; + u8 wss_no_update; + u32 raw_decoder_line_size; + u8 raw_decoder_sav_odd_field; + u8 raw_decoder_sav_even_field; + u32 sliced_decoder_line_size; + u8 sliced_decoder_sav_odd_field; + u8 sliced_decoder_sav_even_field; + struct v4l2_format in; + /* convenience pointer to sliced struct in vbi_in union */ + struct v4l2_sliced_vbi_format *sliced_in; + u32 service_set_in; + u32 service_set_out; + int insert_mpeg; + + /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + One for /dev/vbi0 and one for /dev/vbi8 */ + struct v4l2_sliced_vbi_data sliced_data[36]; + struct v4l2_sliced_vbi_data sliced_dec_data[36]; + + /* Buffer for VBI data inserted into MPEG stream. + The first byte is a dummy byte that's never used. + The next 16 bytes contain the MPEG header for the VBI data, + the remainder is the actual VBI data. + The max size accepted by the MPEG VBI reinsertion turns out + to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, + where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is + a single line header byte and 2 * 18 is the number of VBI lines per frame. + + However, it seems that the data must be 1K aligned, so we have to + pad the data until the 1 or 2 K boundary. + + This pointer array will allocate 2049 bytes to store each VBI frame. */ + u8 *sliced_mpeg_data[IVTV_VBI_FRAMES]; + u32 sliced_mpeg_size[IVTV_VBI_FRAMES]; + struct ivtv_buffer sliced_mpeg_buf; + u32 inserted_frame; + + u32 start[2], count; + u32 raw_size; + u32 sliced_size; +}; + +/* forward declaration of struct defined in ivtv-cards.h */ +struct ivtv_card; + +/* Struct to hold info about ivtv cards */ +struct ivtv { + int num; /* board number, -1 during init! */ + char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ + struct pci_dev *dev; /* PCI device */ + const struct ivtv_card *card; /* card information */ + const char *card_name; /* full name of the card */ + u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */ + u8 is_50hz; + u8 is_60hz; + u8 is_out_50hz; + u8 is_out_60hz; + u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */ + u8 nof_inputs; /* number of video inputs */ + u8 nof_audio_inputs; /* number of audio inputs */ + u32 v4l2_cap; /* V4L2 capabilities of card */ + u32 hw_flags; /* Hardware description of the board */ + + /* controlling Video decoder function */ + int (*video_dec_func)(struct ivtv *, unsigned int, void *); + + struct ivtv_options options; /* User options */ + int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */ + struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */ + int speed; + u8 speed_mute_audio; + unsigned long i_flags; /* global ivtv flags */ + atomic_t capturing; /* count number of active capture streams */ + atomic_t decoding; /* count number of active decoding streams */ + u32 irq_rr_idx; /* Round-robin stream index */ + int cur_dma_stream; /* index of stream doing DMA */ + u32 dma_data_req_offset; + u32 dma_data_req_size; + int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */ + spinlock_t lock; /* lock access to this struct */ + int search_pack_header; + + spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + + /* User based DMA for OSD */ + struct ivtv_user_dma udma; + + int open_id; /* incremented each time an open occurs, used as unique ID. + starts at 1, so 0 can be used as uninitialized value + in the stream->id. */ + + u32 base_addr; + u32 irqmask; + + struct v4l2_prio_state prio; + struct workqueue_struct *irq_work_queues; + struct work_struct irq_work_queue; + struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */ + + struct vbi_info vbi; + + struct ivtv_mailbox_data enc_mbox; + struct ivtv_mailbox_data dec_mbox; + struct ivtv_api_cache api_cache[256]; /* Cached API Commands */ + + u8 card_rev; + volatile void __iomem *enc_mem, *dec_mem, *reg_mem; + + u32 pgm_info_offset; + u32 pgm_info_num; + u32 pgm_info_write_idx; + u32 pgm_info_read_idx; + struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; + + u64 mpg_data_received; + u64 vbi_data_inserted; + + wait_queue_head_t cap_w; + /* when the next decoder event arrives this queue is woken up */ + wait_queue_head_t event_waitq; + /* when the next decoder vsync arrives this queue is woken up */ + wait_queue_head_t vsync_waitq; + /* when the current DMA is finished this queue is woken up */ + wait_queue_head_t dma_waitq; + + /* OSD support */ + unsigned long osd_video_pbase; + int osd_global_alpha_state; /* 0=off : 1=on */ + int osd_local_alpha_state; /* 0=off : 1=on */ + int osd_color_key_state; /* 0=off : 1=on */ + u8 osd_global_alpha; /* Current global alpha */ + u32 osd_color_key; /* Current color key */ + u32 osd_pixelformat; /* Current pixel format */ + struct v4l2_rect osd_rect; /* Current OSD position and size */ + struct v4l2_rect main_rect; /* Current Main window position and size */ + + u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */ + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + struct mutex i2c_bus_lock; + int i2c_state; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* v4l2 and User settings */ + + /* codec settings */ + struct cx2341x_mpeg_params params; + u32 audio_input; + u32 active_input; + u32 active_output; + v4l2_std_id std; + v4l2_std_id std_out; + v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ + u8 audio_stereo_mode; + u8 audio_bilingual_mode; + + /* dualwatch */ + unsigned long dualwatch_jiffies; + u16 dualwatch_stereo_mode; + + /* Digitizer type */ + int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + + u32 lastVsyncFrame; + + struct yuv_playback_info yuv_info; + struct osd_info *osd_info; +}; + +/* Globals */ +extern struct ivtv *ivtv_cards[]; +extern int ivtv_cards_active; +extern int ivtv_first_minor; +extern spinlock_t ivtv_cards_lock; + +/*==============Prototypes==================*/ + +/* Hardware/IRQ */ +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask); +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask); + +/* try to set output mode, return current mode. */ +int ivtv_set_output_mode(struct ivtv *itv, int mode); + +/* return current output stream based on current mode */ +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv); + +/* Return non-zero if a signal is pending */ +int ivtv_sleep_timeout(int timeout, int intr); + +/* Wait on queue, returns -EINTR if interrupted */ +int ivtv_waitq(wait_queue_head_t *waitq); + +/* Read Hauppauge eeprom */ +struct tveeprom; /* forward reference */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); + +/* This is a PCI post thing, where if the pci register is not read, then + the write doesn't always take effect right away. By reading back the + register any pending PCI writes will be performed (in order), and so + you can be sure that the writes are guaranteed to be done. + + Rarely needed, only in some timing sensitive cases. + Apparently if this is not done some motherboards seem + to kill the firmware and get into the broken state until computer is + rebooted. */ +#define write_sync(val, reg) \ + do { writel(val, reg); readl(reg); } while (0) + +#define read_reg(reg) readl(itv->reg_mem + (reg)) +#define write_reg(val, reg) writel(val, itv->reg_mem + (reg)) +#define write_reg_sync(val, reg) \ + do { write_reg(val, reg); read_reg(reg); } while (0) + +#define read_enc(addr) readl(itv->enc_mem + (u32)(addr)) +#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr)) +#define write_enc_sync(val, addr) \ + do { write_enc(val, addr); read_enc(addr); } while (0) + +#define read_dec(addr) readl(itv->dec_mem + (u32)(addr)) +#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr)) +#define write_dec_sync(val, addr) \ + do { write_dec(val, addr); read_dec(addr); } while (0) + +#endif /* IVTV_DRIVER_H */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c new file mode 100644 index 0000000..1637097 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -0,0 +1,921 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-vbi.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-controls.h" +#include "ivtv-ioctl.h" + +/* This function tries to claim the stream for a specific file descriptor. + If no one else is using this stream then the stream is claimed and + associated VBI streams are also automatically claimed. + Possible error returns: -EBUSY if someone else has claimed + the stream or 0 on success. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[type]; + struct ivtv_stream *s_vbi; + int vbi_type; + + if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + /* someone already claimed this stream */ + if (s->id == id->open_id) { + /* yes, this file descriptor did. So that's OK. */ + return 0; + } + if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI || + type == IVTV_ENC_STREAM_TYPE_VBI)) { + /* VBI is handled already internally, now also assign + the file descriptor to this stream for external + reading of the stream. */ + s->id = id->open_id; + IVTV_DEBUG_INFO("Start Read VBI\n"); + return 0; + } + /* someone else is using this stream already */ + IVTV_DEBUG_INFO("Stream %d is busy\n", type); + return -EBUSY; + } + s->id = id->open_id; + if (type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Enable reinsertion interrupt */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + + /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI + (provided VBI insertion is on and sliced VBI is selected), for all + other streams we're done */ + if (type == IVTV_DEC_STREAM_TYPE_MPG) { + vbi_type = IVTV_DEC_STREAM_TYPE_VBI; + } else if (type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) { + vbi_type = IVTV_ENC_STREAM_TYPE_VBI; + } else { + return 0; + } + s_vbi = &itv->streams[vbi_type]; + + if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) { + /* Enable reinsertion interrupt */ + if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + /* mark that it is used internally */ + set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags); + return 0; +} + +/* This function releases a previously claimed stream. It will take into + account associated VBI streams. */ +void ivtv_release_stream(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi; + + s->id = -1; + if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* this stream is still in use internally */ + return; + } + if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name); + return; + } + + ivtv_flush_queues(s); + + /* disable reinsertion interrupt */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + + /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI, + for all other streams we're done */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + else if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + else + return; + + /* clear internal use flag */ + if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) { + /* was already cleared */ + return; + } + if (s_vbi->id != -1) { + /* VBI stream still claimed by a file descriptor */ + return; + } + /* disable reinsertion interrupt */ + if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags); + ivtv_flush_queues(s_vbi); +} + +static void ivtv_dualwatch(struct ivtv *itv) +{ + struct v4l2_tuner vt; + u16 new_bitmap; + u16 new_stereo_mode; + const u16 stereo_mask = 0x0300; + const u16 dual = 0x0200; + + new_stereo_mode = itv->params.audio_properties & stereo_mask; + memset(&vt, 0, sizeof(vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) + new_stereo_mode = dual; + + if (new_stereo_mode == itv->dualwatch_stereo_mode) + return; + + new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask); + + IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", + itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + + if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) { + itv->dualwatch_stereo_mode = new_stereo_mode; + return; + } + IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); +} + +static void ivtv_update_pgm_info(struct ivtv *itv) +{ + u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24; + int cnt; + int i = 0; + + if (wr_idx >= itv->pgm_info_num) { + IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num); + return; + } + cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num; + while (i < cnt) { + int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; + struct v4l2_enc_idx_entry *e = itv->pgm_info + idx; + u32 addr = itv->pgm_info_offset + 4 + idx * 24; + const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; + + e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + if (e->offset > itv->mpg_data_received) { + break; + } + e->offset += itv->vbi_data_inserted; + e->length = read_enc(addr); + e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); + e->flags = mapping[read_enc(addr + 12) & 3]; + i++; + } + itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; +} + +static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + struct ivtv_buffer *buf; + DEFINE_WAIT(wait); + + *err = 0; + while (1) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) { + /* Process pending program info updates and pending VBI data */ + ivtv_update_pgm_info(itv); + + if (jiffies - itv->dualwatch_jiffies > HZ) { + itv->dualwatch_jiffies = jiffies; + ivtv_dualwatch(itv); + } + + if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type); + ivtv_enqueue(s_vbi, buf, &s_vbi->q_free); + } + } + buf = &itv->vbi.sliced_mpeg_buf; + if (buf->readpos != buf->bytesused) { + return buf; + } + } + + /* do we have leftover data? */ + buf = ivtv_dequeue(s, &s->q_io); + if (buf) + return buf; + + /* do we have new data? */ + buf = ivtv_dequeue(s, &s->q_full); + if (buf) { + if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags)) + return buf; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + /* byteswap MPG data */ + ivtv_buf_swap(buf); + else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type); + } + return buf; + } + /* return if file was opened with O_NONBLOCK */ + if (non_block) { + *err = -EAGAIN; + return NULL; + } + + /* return if end of stream */ + if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + IVTV_DEBUG_INFO("EOS %s\n", s->name); + return NULL; + } + + /* wait for more data to arrive */ + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become available before we were added to the waitqueue */ + if (!s->q_full.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + *err = -EINTR; + return NULL; + } + } +} + +static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv) +{ + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + + itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx]; + itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx]; + itv->vbi.sliced_mpeg_buf.readpos = 0; +} + +static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf, + char __user *ubuf, size_t ucount) +{ + struct ivtv *itv = s->itv; + size_t len = buf->bytesused - buf->readpos; + + if (len > ucount) len = ucount; + if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) { + const char *start = buf->buf + buf->readpos; + const char *p = start + 1; + const u8 *q; + u8 ch = itv->search_pack_header ? 0xba : 0xe0; + int stuffing, i; + + while (start + len > p && (q = memchr(p, 0, start + len - p))) { + p = q + 1; + if ((char *)q + 15 >= buf->buf + buf->bytesused || + q[1] != 0 || q[2] != 1 || q[3] != ch) { + continue; + } + if (!itv->search_pack_header) { + if ((q[6] & 0xc0) != 0x80) + continue; + if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) || + ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) { + ch = 0xba; + itv->search_pack_header = 1; + p = q + 9; + } + continue; + } + stuffing = q[13] & 7; + /* all stuffing bytes must be 0xff */ + for (i = 0; i < stuffing; i++) + if (q[14 + i] != 0xff) + break; + if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 && + q[14 + stuffing] == 0 && q[15 + stuffing] == 0 && + q[16 + stuffing] == 1) { + itv->search_pack_header = 0; + len = (char *)q - start; + ivtv_setup_sliced_vbi_buf(itv); + break; + } + } + } + if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { + IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); + return -EFAULT; + } + /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount, + buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len, + buf == &itv->vbi.sliced_mpeg_buf); */ + buf->readpos += len; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf) + itv->mpg_data_received += len; + return len; +} + +static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block) +{ + struct ivtv *itv = s->itv; + size_t tot_written = 0; + int single_frame = 0; + + if (atomic_read(&itv->capturing) == 0 && s->id == -1) { + /* shouldn't happen */ + IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name); + return -EIO; + } + + /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should + arrive one-by-one, so make sure we never output more than one VBI frame at a time */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI || + (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set)) + single_frame = 1; + + for (;;) { + struct ivtv_buffer *buf; + int rc; + + buf = ivtv_get_buffer(s, non_block, &rc); + if (buf == NULL && rc == -EAGAIN && tot_written) + break; + if (buf == NULL) + return rc; + rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); + if (buf != &itv->vbi.sliced_mpeg_buf) { + ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io); + } + else if (buf->readpos == buf->bytesused) { + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + itv->vbi.sliced_mpeg_size[idx] = 0; + itv->vbi.inserted_frame++; + itv->vbi_data_inserted += buf->bytesused; + } + if (rc < 0) + return rc; + tot_written += rc; + + if (tot_written == tot_count || single_frame) + break; + } + return tot_written; +} + +static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count, + loff_t *pos, int non_block) +{ + ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0; + struct ivtv *itv = s->itv; + + IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc); + if (rc > 0) + pos += rc; + return rc; +} + +int ivtv_start_capture(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_stream *s_vbi; + + if (s->type == IVTV_ENC_STREAM_TYPE_RAD || + s->type == IVTV_DEC_STREAM_TYPE_MPG || + s->type == IVTV_DEC_STREAM_TYPE_YUV || + s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + /* you cannot read from these stream types. */ + return -EPERM; + } + + /* Try to claim this stream. */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start capturing */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* If capture is already in progress, then we also have to + do nothing extra. */ + if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* Start VBI capture if required */ + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed + automatically when the MPG stream is claimed. + We only need to start the VBI capturing. */ + if (ivtv_start_v4l2_encode_stream(s_vbi)) { + IVTV_DEBUG_WARN("VBI capture start failed\n"); + + /* Failure, clean up and return an error */ + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + /* also releases the associated VBI stream */ + ivtv_release_stream(s); + return -EIO; + } + IVTV_DEBUG_INFO("VBI insertion started\n"); + } + + /* Tell the card to start capturing */ + if (!ivtv_start_v4l2_encode_stream(s)) { + /* We're done */ + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + /* Resume a possibly paused encoder */ + if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + return 0; + } + + /* failure, clean up */ + IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); + + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released + automatically when the MPG stream is released. + We only need to stop the VBI capturing. */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + } + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_release_stream(s); + return -EIO; +} + +ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int rc; + + IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name); + + rc = ivtv_start_capture(id); + if (rc) + return rc; + return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); +} + +int ivtv_start_decoding(struct ivtv_open_id *id, int speed) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + if (atomic_read(&itv->decoding) == 0) { + if (ivtv_claim_stream(id, s->type)) { + /* someone else is using this stream already */ + IVTV_DEBUG_WARN("start decode, stream already claimed\n"); + return -EBUSY; + } + ivtv_start_v4l2_decode_stream(s, 0); + } + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + return ivtv_set_speed(itv, speed); + return 0; +} + +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_buffer *buf; + struct ivtv_queue q; + int bytes_written = 0; + int mode; + int rc; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name); + + if (s->type != IVTV_DEC_STREAM_TYPE_MPG && + s->type != IVTV_DEC_STREAM_TYPE_YUV && + s->type != IVTV_DEC_STREAM_TYPE_VOUT) + /* not decoder streams */ + return -EPERM; + + /* Try to claim this stream */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start any decoding */ + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return ivtv_write_vbi(itv, user_buf, count); + } + + mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV; + + if (ivtv_set_output_mode(itv, mode) != mode) { + ivtv_release_stream(s); + return -EBUSY; + } + ivtv_queue_init(&q); + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + +retry: + for (;;) { + /* Gather buffers */ + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io))) + ivtv_enqueue(s, buf, &q); + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) { + ivtv_enqueue(s, buf, &q); + } + if (q.buffers) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become free before we were added to the waitqueue */ + if (!s->q_free.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + return -EINTR; + } + } + + /* copy user data into buffers */ + while ((buf = ivtv_dequeue(s, &q))) { + /* Make sure we really got all the user data */ + rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); + + if (rc < 0) { + ivtv_queue_move(s, &q, NULL, &s->q_free, 0); + return rc; + } + user_buf += rc; + count -= rc; + bytes_written += rc; + + if (buf->bytesused != s->buf_size) { + /* incomplete, leave in q_io for next time */ + ivtv_enqueue(s, buf, &s->q_io); + break; + } + /* Byteswap MPEG buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + ivtv_buf_swap(buf); + ivtv_enqueue(s, buf, &s->q_full); + } + + /* Start decoder (returns 0 if already started) */ + rc = ivtv_start_decoding(id, itv->speed); + if (rc) { + IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); + + /* failure, clean up */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return rc; + } + if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { + if (s->q_full.length >= itv->dma_data_req_size) { + int got_sig; + + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (!(got_sig = signal_pending(current)) && + test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) { + IVTV_DEBUG_INFO("User interrupted %s\n", s->name); + return -EINTR; + } + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1); + } + } + /* more user data is available, wait until buffers become free + to transfer the rest. */ + if (count && !(filp->f_flags & O_NONBLOCK)) + goto retry; + IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); + return bytes_written; +} + +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int res = 0; + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || + test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + res = POLLPRI; + + /* Allow write if buffers are available for writing */ + if (s->q_free.buffers) + res |= POLLOUT | POLLWRNORM; + return res; +} + +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Start a capture if there is none */ + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + int rc = ivtv_start_capture(id); + + if (rc) { + IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n", + s->name, rc); + return POLLERR; + } + } + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + if (eof || s->q_full.length) + return POLLIN | POLLRDNORM; + return 0; +} + +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* 'Unclaim' this stream */ + + /* Stop capturing */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_INFO("close stopping capture\n"); + /* Special case: a running VBI capture for VBI insertion + in the mpeg stream. Need to stop that too. */ + if (id->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + IVTV_DEBUG_INFO("close stopping embedded VBI capture\n"); + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + } + if ((id->type == IVTV_DEC_STREAM_TYPE_VBI || + id->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* Also used internally, don't stop capturing */ + s->id = -1; + } + else { + ivtv_stop_v4l2_encode_stream(s, gop_end); + } + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + ivtv_release_stream(s); +} + +static void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* Stop decoding */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + IVTV_DEBUG_INFO("close stopping decode\n"); + + ivtv_stop_v4l2_decode_stream(s, flags, pts); + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) { + /* Restore registers we've changed & clean up any mess we've made */ + ivtv_yuv_close(itv); + } + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV) + itv->output_mode = OUT_NONE; + else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG) + itv->output_mode = OUT_NONE; + + itv->speed = 0; + ivtv_release_stream(s); +} + +int ivtv_v4l2_close(struct inode *inode, struct file *filp) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + v4l2_prio_close(&itv->prio, &id->prio); + + /* Easy case first: this stream was never claimed by us */ + if (s->id != id->open_id) { + kfree(id); + return 0; + } + + /* 'Unclaim' this stream */ + + /* Stop radio */ + if (id->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Closing radio device, return to TV mode */ + ivtv_mute(itv); + /* Mark that the radio is no longer in use */ + clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Switch tuner to TV */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + /* Select correct audio input (i.e. TV tuner or Line in) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + ivtv_release_stream(s); + } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { + ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } else { + ivtv_stop_capture(id, 0); + } + kfree(id); + return 0; +} + +int ivtv_v4l2_open(struct inode *inode, struct file *filp) +{ + int x, y = 0; + struct ivtv_open_id *item; + struct ivtv *itv = NULL; + struct ivtv_stream *s = NULL; + int minor = MINOR(inode->i_rdev); + + /* Find which card this open was on */ + spin_lock(&ivtv_cards_lock); + for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { + /* find out which stream this open was on */ + for (y = 0; y < IVTV_MAX_STREAMS; y++) { + s = &ivtv_cards[x]->streams[y]; + if (s->v4l2dev && s->v4l2dev->minor == minor) { + itv = ivtv_cards[x]; + break; + } + } + } + spin_unlock(&ivtv_cards_lock); + + if (itv == NULL) { + /* Couldn't find a device registered + on that minor, shouldn't happen! */ + printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor); + return -ENXIO; + } + + if (y == IVTV_DEC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV) { + if (read_reg(0x82c) == 0) { + IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n"); + /* return -ENODEV; */ + } + ivtv_udma_alloc(itv); + } + + /* Allocate memory */ + item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); + if (NULL == item) { + IVTV_DEBUG_WARN("nomem on v4l2 open\n"); + return -ENOMEM; + } + item->itv = itv; + item->type = y; + v4l2_prio_open(&itv->prio, &item->prio); + + item->open_id = itv->open_id++; + filp->private_data = item; + + if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Try to claim this stream */ + if (ivtv_claim_stream(item, item->type)) { + /* No, it's already in use */ + kfree(item); + return -EBUSY; + } + + /* We have the radio */ + ivtv_mute(itv); + /* Switch tuner to radio */ + ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + /* Mark that the radio is being used. */ + set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Select the correct audio input (i.e. radio tuner) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + } + + /* YUV or MPG Decoding Mode? */ + if (y == IVTV_DEC_STREAM_TYPE_MPG) + clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + else if (y == IVTV_DEC_STREAM_TYPE_YUV) + { + set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + } + + return 0; +} + +void ivtv_mute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 }; + + /* Mute sound to avoid pop */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + + if (atomic_read(&itv->capturing)) + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1); + + IVTV_DEBUG_INFO("Mute\n"); +} + +void ivtv_unmute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 }; + + /* initialize or refresh input */ + if (atomic_read(&itv->capturing) == 0) + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + + if (atomic_read(&itv->capturing)) { + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0); + } + + /* Unmute */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + IVTV_DEBUG_INFO("Unmute\n"); +} diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h new file mode 100644 index 0000000..74a1745 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.h @@ -0,0 +1,44 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Testing/Debugging */ +int ivtv_v4l2_open(struct inode *inode, struct file *filp); +ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t * pos); +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count, + loff_t * pos); +int ivtv_v4l2_close(struct inode *inode, struct file *filp); +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait); +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait); +int ivtv_start_capture(struct ivtv_open_id *id); +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end); +int ivtv_start_decoding(struct ivtv_open_id *id, int speed); +void ivtv_mute(struct ivtv *itv); +void ivtv_unmute(struct ivtv *itv); + +/* Utilities */ + +/* Try to claim a stream for the filehandle. Return 0 on success, + -EBUSY if stream already claimed. Once a stream is claimed, it + remains claimed until the associated filehandle is closed. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type); + +/* Release a previously claimed stream. */ +void ivtv_release_stream(struct ivtv_stream *s); diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c new file mode 100644 index 0000000..d4c910b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -0,0 +1,272 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-firmware.h" +#include <linux/firmware.h> + +#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE +#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 +#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB +#define IVTV_CMD_VDM_STOP 0x00000000 +#define IVTV_CMD_AO_STOP 0x00000005 +#define IVTV_CMD_APU_PING 0x00000000 +#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE +#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF +#define IVTV_CMD_SPU_STOP 0x00000001 +#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A +#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 +#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100) /* 600 ms */ + +#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" +#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) + +/* Encoder/decoder firmware sizes */ +#define IVTV_FW_ENC_SIZE (376836) +#define IVTV_FW_DEC_SIZE (256*1024) + +static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) +{ + const struct firmware *fw = NULL; + int retries = 3; + +retry: + if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) { + int i; + volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; + const u32 *src = (const u32 *)fw->data; + + /* temporarily allow 256 KB encoding firmwares as well for + compatibility with blackbird cards */ + if (fw->size != size && fw->size != 256 * 1024) { + /* Due to race conditions in firmware loading (esp. with udev <0.95) + the wrong file was sometimes loaded. So we check filesizes to + see if at least the right-sized file was loaded. If not, then we + retry. */ + IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); + release_firmware(fw); + retries--; + goto retry; + } + for (i = 0; i < fw->size; i += 4) { + /* no need for endianness conversion on the ppc */ + __raw_writel(*src, dst); + dst++; + src++; + } + release_firmware(fw); + IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + return size; + } + IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size); + IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n"); + return -ENOMEM; +} + +void ivtv_halt_firmware(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); + if (itv->has_cx23415 && itv->dec_mbox.mbox) + ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); + if (itv->enc_mbox.mbox) + ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); + + ivtv_sleep_timeout(HZ / 100, 0); + itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; + + IVTV_DEBUG_INFO("Stopping VDM\n"); + write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); + + IVTV_DEBUG_INFO("Stopping AO\n"); + write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); + + IVTV_DEBUG_INFO("pinging (?) APU\n"); + write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); + + IVTV_DEBUG_INFO("Stopping VPU\n"); + if (!itv->has_cx23415) + write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); + else + write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); + + IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); + write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); + + IVTV_DEBUG_INFO("Stopping SPU\n"); + write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); + + ivtv_sleep_timeout(HZ / 100, 0); + + IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); + } + + IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n", + (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ)); + ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); +} + +void ivtv_firmware_versions(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + /* Encoder */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); + IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); + + if (data[0] != 0x02060039) + IVTV_WARN("Recommended firmware version is 0x02060039.\n"); + + if (itv->has_cx23415) { + /* Decoder */ + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); + IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); + } +} + +static int ivtv_firmware_copy(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Loading encoder image\n"); + if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, + itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { + IVTV_DEBUG_WARN("failed loading encoder firmware\n"); + return -3; + } + if (!itv->has_cx23415) + return 0; + + IVTV_DEBUG_INFO("Loading decoder image\n"); + if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, + itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { + IVTV_DEBUG_WARN("failed loading decoder firmware\n"); + return -1; + } + return 0; +} + +static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) +{ + int i; + + /* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte + address boundary */ + for (i = 0; i < size; i += 0x100) { + if (readl(mem + i) == 0x12345678 && + readl(mem + i + 4) == 0x34567812 && + readl(mem + i + 8) == 0x56781234 && + readl(mem + i + 12) == 0x78123456) { + return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); + } + } + return NULL; +} + +int ivtv_firmware_init(struct ivtv *itv) +{ + int err; + + ivtv_halt_firmware(itv); + + /* load firmware */ + err = ivtv_firmware_copy(itv); + if (err) { + IVTV_DEBUG_WARN("Error %d loading firmware\n", err); + return err; + } + + /* start firmware */ + write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); + ivtv_sleep_timeout(HZ / 10, 0); + if (itv->has_cx23415) + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); + else + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); + ivtv_sleep_timeout(HZ / 10, 0); + + /* find mailboxes and ping firmware */ + itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); + if (itv->enc_mbox.mbox == NULL) + IVTV_ERR("Encoder mailbox not found\n"); + else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { + IVTV_ERR("Encoder firmware dead!\n"); + itv->enc_mbox.mbox = NULL; + } + if (itv->enc_mbox.mbox == NULL) + return -ENODEV; + + if (!itv->has_cx23415) + return 0; + + itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); + if (itv->dec_mbox.mbox == NULL) + IVTV_ERR("Decoder mailbox not found\n"); + else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { + IVTV_ERR("Decoder firmware dead!\n"); + itv->dec_mbox.mbox = NULL; + } + return itv->dec_mbox.mbox ? 0 : -ENODEV; +} + +void ivtv_init_mpeg_decoder(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + long readbytes; + volatile u8 __iomem *mem_offset; + + data[0] = 0; + data[1] = itv->params.width; /* YUV source width */ + data[2] = itv->params.height; + data[3] = itv->params.audio_properties; /* Audio settings to use, + bitmap. see docs. */ + if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); + return; + } + + if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); + return; + } + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + mem_offset = itv->dec_mem + data[1]; + + if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, + mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { + IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", + IVTV_DECODE_INIT_MPEG_FILENAME); + } else { + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); + ivtv_sleep_timeout(HZ / 10, 0); + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); +} diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h new file mode 100644 index 0000000..8b2ffe6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.h @@ -0,0 +1,25 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_firmware_init(struct ivtv *itv); +void ivtv_firmware_versions(struct ivtv *itv); +void ivtv_halt_firmware(struct ivtv *itv); +void ivtv_init_mpeg_decoder(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c new file mode 100644 index 0000000..bc8f8ca --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -0,0 +1,307 @@ +/* + gpio functions. + Merging GPIO support into driver: + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" +#include <media/tuner.h> + +/* + * GPIO assignment of Yuan MPG600/MPG160 + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0 + * INPUT DM1 DM0 + * + * IN* : Input selection + * IN1 IN0 + * 1 1 N/A + * 1 0 Line + * 0 1 N/A + * 0 0 Tuner + * + * AM* : Audio Mode + * AM3 0: Normal 1: Mixed(Sub+Main channel) + * AM2 0: Subchannel 1: Main channel + * AM1 0: Stereo 1: Mono + * AM0 0: Normal 1: Mute + * + * DM* : Detected tuner audio Mode + * DM1 0: Stereo 1: Mono + * DM0 0: Multiplex 1: Normal + * + * GPIO Initial Settings + * MPG600 MPG160 + * DIR 0x3080 0x7080 + * OUTPUT 0x000C 0x400C + * + * Special thanks to Makoto Iguchi <iguchi@tahoo.org> and Mr. Anonymous + * for analyzing GPIO of MPG160. + * + ***************************************************************************** + * + * GPIO assignment of Avermedia M179 (per information direct from AVerMedia) + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1 + * INPUT + * + * IN* : Input selection + * IN0 IN1 IN2 + * * 1 * Mute + * 0 0 0 Line-In + * 1 0 0 TV Tuner Audio + * 0 0 1 FM Audio + * 1 0 1 Mute + * + * AM* : Audio Mode + * AM0 AM1 AM2 + * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP + * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP) + * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo) + * 0 1 1 TV Tuner Audio: mute + * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono) + * + * BR* : Audio Sample Rate (BR stands for bitrate for some reason) + * BR0 BR1 + * 0 0 32 kHz + * 0 1 44.1 kHz + * 1 0 48 kHz + * + * DM* : Detected tuner audio Mode + * Unknown currently + * + * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at + * AVerMedia for providing the GPIO information used to add support + * for the M179 cards. + */ + +/********************* GPIO stuffs *********************/ + +/* GPIO registers */ +#define IVTV_REG_GPIO_IN 0x9008 +#define IVTV_REG_GPIO_OUT 0x900c +#define IVTV_REG_GPIO_DIR 0x9020 + +void ivtv_reset_ir_gpio(struct ivtv *itv) +{ + int curdir, curout; + + if (itv->card->type != IVTV_CARD_PVR_150) + return; + IVTV_DEBUG_INFO("Resetting PVR150 IR\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= 0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); + curout = (curout & ~0xF) | 1; + write_reg(curout, IVTV_REG_GPIO_OUT); + /* We could use something else for smaller time */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + curout |= 2; + write_reg(curout, IVTV_REG_GPIO_OUT); + curdir &= ~0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); +} + +#ifdef HAVE_XC3028 +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr) +{ + int curdir, curout; + struct ivtv *itv = (struct ivtv *) priv; + + if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028) + return -EINVAL; + IVTV_INFO("Resetting tuner.\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= (1 << 12); /* GPIO bit 12 */ + + curout &= ~(1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + curout |= (1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + return 0; +} +#endif + +void ivtv_gpio_init(struct ivtv *itv) +{ + if (itv->card->gpio_init.direction == 0) + return; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR); +} + +static struct v4l2_queryctrl gpio_ctrl_mute = { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, +}; + +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +{ + struct v4l2_tuner *tuner = arg; + struct v4l2_control *ctrl = arg; + struct v4l2_routing *route = arg; + u16 mask, data; + + switch (command) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + mask = itv->card->gpio_audio_freq.mask; + switch (*(u32 *)arg) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; + break; + } + break; + + case VIDIOC_G_TUNER: + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; + + case VIDIOC_S_TUNER: + mask = itv->card->gpio_audio_mode.mask; + switch (tuner->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + break; + + case AUDC_SET_RADIO: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + break; + + case VIDIOC_S_STD: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: + data = itv->card->gpio_audio_input.tuner; + break; + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; + break; + } + break; + + case VIDIOC_G_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; + + case VIDIOC_S_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + break; + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; + } + + case VIDIOC_LOG_STATUS: + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), + read_reg(IVTV_REG_GPIO_IN)); + return 0; + + case VIDIOC_INT_S_VIDEO_ROUTING: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ + return -EINVAL; + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; + break; + + default: + return -EINVAL; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h new file mode 100644 index 0000000..c301d2a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.h @@ -0,0 +1,25 @@ +/* + gpio functions. + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* GPIO stuff */ +void ivtv_gpio_init(struct ivtv *itv); +void ivtv_reset_ir_gpio(struct ivtv *itv); +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr); +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c new file mode 100644 index 0000000..50624c6 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -0,0 +1,748 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + This file includes an i2c implementation that was reverse engineered + from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit, + which whilst fine under most circumstances, had trouble with the Zilog + CPU on the PVR-150 which handles IR functions (occasional inability to + communicate with the chip until it was reset) and also with the i2c + bus being completely unreachable when multiple PVR cards were present. + + The implementation is very similar to i2c-algo-bit, but there are enough + subtle differences that the two are hard to merge. The general strategy + employed by i2c-algo-bit is to use udelay() to implement the timing + when putting out bits on the scl/sda lines. The general strategy taken + here is to poll the lines for state changes (see ivtv_waitscl and + ivtv_waitsda). In addition there are small delays at various locations + which poll the SCL line 5 times (ivtv_scldelay). I would guess that + since this is memory mapped I/O that the length of those delays is tied + to the PCI bus clock. There is some extra code to do with recovery + and retries. Since it is not known what causes the actual i2c problems + in the first place, the only goal if one was to attempt to use + i2c-algo-bit would be to try to make it follow the same code path. + This would be a lot of work, and I'm also not convinced that it would + provide a generic benefit to i2c-algo-bit. Therefore consider this + an engineering solution -- not pretty, but it works. + + Some more general comments about what we are doing: + + The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA) + lines. To communicate on the bus (as a master, we don't act as a slave), + we first initiate a start condition (ivtv_start). We then write the + address of the device that we want to communicate with, along with a flag + that indicates whether this is a read or a write. The slave then issues + an ACK signal (ivtv_ack), which tells us that it is ready for reading / + writing. We then proceed with reading or writing (ivtv_read/ivtv_write), + and finally issue a stop condition (ivtv_stop) to make the bus available + to other masters. + + There is an additional form of transaction where a write may be + immediately followed by a read. In this case, there is no intervening + stop condition. (Only the msp3400 chip uses this method of data transfer). + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" +#include "ivtv-i2c.h" + +#include <media/ir-kbd-i2c.h> + +/* i2c implementation for cx23415/6 chip, ivtv project. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + */ +/* i2c stuff */ +#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000 +#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004 +#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008 +#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c + +#ifndef I2C_ADAP_CLASS_TV_ANALOG +#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG +#endif /* I2C_ADAP_CLASS_TV_ANALOG */ + +#define IVTV_CS53L32A_I2C_ADDR 0x11 +#define IVTV_CX25840_I2C_ADDR 0x44 +#define IVTV_SAA7115_I2C_ADDR 0x21 +#define IVTV_SAA7127_I2C_ADDR 0x44 +#define IVTV_SAA717x_I2C_ADDR 0x21 +#define IVTV_MSP3400_I2C_ADDR 0x40 +#define IVTV_HAUPPAUGE_I2C_ADDR 0x50 +#define IVTV_WM8739_I2C_ADDR 0x1a +#define IVTV_WM8775_I2C_ADDR 0x1b +#define IVTV_TEA5767_I2C_ADDR 0x60 +#define IVTV_UPD64031A_I2C_ADDR 0x12 +#define IVTV_UPD64083_I2C_ADDR 0x5c +#define IVTV_TDA985X_I2C_ADDR 0x5b + +/* This array should match the IVTV_HW_ defines */ +static const u8 hw_driverids[] = { + I2C_DRIVERID_CX25840, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_SAA7127, + I2C_DRIVERID_MSP3400, + I2C_DRIVERID_TUNER, + I2C_DRIVERID_WM8775, + I2C_DRIVERID_CS53L32A, + I2C_DRIVERID_TVEEPROM, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_TVAUDIO, + I2C_DRIVERID_UPD64031A, + I2C_DRIVERID_UPD64083, + I2C_DRIVERID_SAA717X, + I2C_DRIVERID_WM8739, + 0 /* IVTV_HW_GPIO dummy driver ID */ +}; + +/* This array should match the IVTV_HW_ defines */ +static const char * const hw_drivernames[] = { + "cx2584x", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + "tveeprom", + "saa7114", + "tvaudio", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "gpio", +}; + +static int attach_inform(struct i2c_client *client) +{ + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + int i; + + IVTV_DEBUG_I2C("i2c client attach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == NULL) { + itv->i2c_clients[i] = client; + break; + } + } + if (i == I2C_CLIENTS_MAX) { + IVTV_ERR("insufficient room for new I2C client!\n"); + } + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + int i; + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + + IVTV_DEBUG_I2C("i2c client detach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == client) { + itv->i2c_clients[i] = NULL; + break; + } + } + IVTV_DEBUG_I2C("i2c detach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +/* Set the serial clock line to the desired state */ +static void ivtv_setscl(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +/* Set the serial data line to the desired state */ +static void ivtv_setsda(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET); +} + +/* Read the serial clock line */ +static int ivtv_getscl(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +/* Read the serial data line */ +static int ivtv_getsda(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* Implement a short delay by polling the serial clock line */ +static void ivtv_scldelay(struct ivtv *itv) +{ + int i; + + for (i = 0; i < 5; ++i) + ivtv_getscl(itv); +} + +/* Wait for the serial clock line to become set to a specific value */ +static int ivtv_waitscl(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getscl(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the serial data line to become set to a specific value */ +static int ivtv_waitsda(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getsda(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the slave to issue an ACK */ +static int ivtv_ack(struct ivtv *itv) +{ + int ret = 0; + + if (ivtv_getscl(itv) == 1) { + IVTV_DEBUG_I2C("SCL was high starting an ack\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitsda(itv, 0)) { + IVTV_DEBUG_I2C("Slave did not ack\n"); + ret = -EREMOTEIO; + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n"); + ret = -EREMOTEIO; + } + return ret; +} + +/* Write a single byte to the i2c bus and wait for the slave to ACK */ +static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte) +{ + int i, bit; + + IVTV_DEBUG_I2C("write %x\n",byte); + for (i = 0; i < 8; ++i, byte<<=1) { + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + bit = (byte>>7)&1; + ivtv_setsda(itv, bit); + if (!ivtv_waitsda(itv, bit)) { + IVTV_DEBUG_I2C("Error setting SDA\n"); + return -EREMOTEIO; + } + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Slave not ready for bit\n"); + return -EREMOTEIO; + } + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + return ivtv_ack(itv); +} + +/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the + final byte) */ +static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack) +{ + int i; + + *byte = 0; + + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + for (i = 0; i < 8; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Error setting SCL high\n"); + return -EREMOTEIO; + } + *byte = ((*byte)<<1)|ivtv_getsda(itv); + } + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setsda(itv, nack); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + IVTV_DEBUG_I2C("read %x\n",*byte); + return 0; +} + +/* Issue a start condition on the i2c bus to alert slaves to prepare for + an address write */ +static int ivtv_start(struct ivtv *itv) +{ + int sda; + + sda = ivtv_getsda(itv); + if (sda != 1) { + IVTV_DEBUG_I2C("SDA was low at start\n"); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("SDA stuck low\n"); + return -EREMOTEIO; + } + } + if (ivtv_getscl(itv) != 1) { + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL stuck low at start\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + return 0; +} + +/* Issue a stop condition on the i2c bus to release it */ +static int ivtv_stop(struct ivtv *itv) +{ + int i; + + if (ivtv_getscl(itv) != 0) { + IVTV_DEBUG_I2C("SCL not low when stopping\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("SCL could not be set low\n"); + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL could not be set high\n"); + return -EREMOTEIO; + } + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("resetting I2C\n"); + for (i = 0; i < 16; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + } + ivtv_waitsda(itv, 1); + return -EREMOTEIO; + } + return 0; +} + +/* Write a message to the given i2c slave. do_stop may be 0 to prevent + issuing the i2c stop condition (when following with a read) */ +static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + + if (ret == 0) { + ret = ivtv_sendbyte(itv, addr<<1); + for (i = 0; ret == 0 && i < len; ++i) + ret = ivtv_sendbyte(itv, data[i]); + } + if (ret != 0 || do_stop) { + ivtv_stop(itv); + } + } + if (ret) + IVTV_DEBUG_I2C("i2c write to %x failed\n", addr); + return ret; +} + +/* Read data from the given i2c slave. A stop condition is always issued. */ +static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + if (ret == 0) + ret = ivtv_sendbyte(itv, (addr << 1) | 1); + for (i = 0; ret == 0 && i < len; ++i) { + ret = ivtv_readbyte(itv, &data[i], i == len - 1); + } + ivtv_stop(itv); + } + if (ret) + IVTV_DEBUG_I2C("i2c read from %x failed\n", addr); + return ret; +} + +/* Kernel i2c transfer implementation. Takes a number of messages to be read + or written. If a read follows a write, this will occur without an + intervening stop condition */ +static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct ivtv *itv = i2c_get_adapdata(i2c_adap); + int retval; + int i; + + mutex_lock(&itv->i2c_bus_lock); + for (i = retval = 0; retval == 0 && i < num; i++) { + if (msgs[i].flags & I2C_M_RD) + retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len); + else { + /* if followed by a read, don't stop */ + int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD); + + retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop); + } + } + mutex_unlock(&itv->i2c_bus_lock); + return retval ? retval : num; +} + +/* Kernel i2c capabilities */ +static u32 ivtv_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ivtv_algo = { + .master_xfer = ivtv_xfer, + .functionality = ivtv_functionality, +}; + +/* template for our-bit banger */ +static struct i2c_adapter ivtv_i2c_adap_hw_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, + .algo = &ivtv_algo, + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static void ivtv_setscl_old(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +static void ivtv_setsda_old(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET); +} + +static int ivtv_getscl_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +static int ivtv_getsda_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* template for i2c-bit-algo */ +static struct i2c_adapter ivtv_i2c_adap_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, /* algo-bit is OR'd with this */ + .algo = NULL, /* set by i2c-algo-bit */ + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_algo_bit_data ivtv_i2c_algo_template = { + NULL, /* ?? */ + ivtv_setsda_old, /* setsda function */ + ivtv_setscl_old, /* " */ + ivtv_getsda_old, /* " */ + ivtv_getscl_old, /* " */ + 10, /* udelay */ + 200 /* timeout */ +}; + +static struct i2c_client ivtv_i2c_client_template = { + .name = "ivtv internal", +}; + +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) +{ + struct i2c_client *client; + int retval; + int i; + + IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) { + continue; + } + if (client->driver->command == NULL) { + continue; + } + if (addr == client->addr) { + retval = client->driver->command(client, cmd, arg); + return retval; + } + } + if (cmd != VIDIOC_G_CHIP_IDENT) + IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd); + return -ENODEV; +} + +/* Find the i2c device based on the driver ID and return + its i2c address or -ENODEV if no matching device was found. */ +static int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) +{ + struct i2c_client *client; + int retval = -ENODEV; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) + continue; + if (id == client->driver->id) { + retval = client->addr; + break; + } + } + return retval; +} + +/* Find the i2c device name matching the DRIVERID */ +static const char *ivtv_i2c_id_name(u32 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (hw_driverids[i] == id) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device name matching the IVTV_HW_ flag */ +static const char *ivtv_i2c_hw_name(u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device matching the IVTV_HW_ flag and return + its i2c address or -ENODEV if no matching device was found. */ +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return ivtv_i2c_id_addr(itv, hw_driverids[i]); + return -ENODEV; +} + +/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. + If hw == IVTV_HW_GPIO then call the gpio handler. */ +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) +{ + int addr; + + if (hw == IVTV_HW_GPIO) + return ivtv_gpio(itv, cmd, arg); + if (hw == 0) + return 0; + + addr = ivtv_i2c_hw_addr(itv, hw); + if (addr < 0) { + IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n", + hw, ivtv_i2c_hw_name(hw), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +/* Calls i2c device based on I2C driver ID. */ +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) +{ + int addr; + + addr = ivtv_i2c_id_addr(itv, id); + if (addr < 0) { + if (cmd != VIDIOC_G_CHIP_IDENT) + IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n", + id, ivtv_i2c_id_name(id), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); +} + +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); +} + +/* broadcast cmd for all I2C clients and for the gpio subsystem */ +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) +{ + if (itv->i2c_adap.algo == NULL) { + IVTV_ERR("adapter is not set"); + return; + } + i2c_clients_command(&itv->i2c_adap, cmd, arg); + if (itv->hw_flags & IVTV_HW_GPIO) + ivtv_gpio(itv, cmd, arg); +} + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c init\n"); + + if (itv->options.newi2c > 0) { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, + sizeof(struct i2c_adapter)); + } else { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, + sizeof(struct i2c_algo_bit_data)); + itv->i2c_algo.data = itv; + itv->i2c_adap.algo_data = &itv->i2c_algo; + } + + sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", + itv->num); + i2c_set_adapdata(&itv->i2c_adap, itv); + + memcpy(&itv->i2c_client, &ivtv_i2c_client_template, + sizeof(struct i2c_client)); + itv->i2c_client.adapter = &itv->i2c_adap; + itv->i2c_adap.dev.parent = &itv->dev->dev; + + IVTV_DEBUG_I2C("setting scl and sda to 1\n"); + ivtv_setscl(itv, 1); + ivtv_setsda(itv, 1); + + if (itv->options.newi2c > 0) + return i2c_add_adapter(&itv->i2c_adap); + else + return i2c_bit_add_bus(&itv->i2c_adap); +} + +void __devexit exit_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c exit\n"); + + i2c_del_adapter(&itv->i2c_adap); +} diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h new file mode 100644 index 0000000..5d210ad --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -0,0 +1,36 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); + +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv); +void __devexit exit_ivtv_i2c(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c new file mode 100644 index 0000000..794a6a0 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -0,0 +1,1567 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-fileops.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-gpio.h" +#include "ivtv-controls.h" +#include "ivtv-cards.h" +#include <media/saa7127.h> +#include <media/tveeprom.h> +#include <media/v4l2-chip-ident.h> +#include <linux/dvb/audio.h> +#include <linux/i2c-id.h> + +u16 service2vbi(int type) +{ + switch (type) { + case V4L2_SLICED_TELETEXT_B: + return IVTV_SLICED_TYPE_TELETEXT_B; + case V4L2_SLICED_CAPTION_525: + return IVTV_SLICED_TYPE_CAPTION_525; + case V4L2_SLICED_WSS_625: + return IVTV_SLICED_TYPE_WSS_625; + case V4L2_SLICED_VPS: + return IVTV_SLICED_TYPE_VPS; + default: + return 0; + } +} + +static int valid_service_line(int field, int line, int is_pal) +{ + return (is_pal && line >= 6 && (line != 23 || field == 0)) || + (!is_pal && line >= 10 && line < 22); +} + +static u16 select_service_from_set(int field, int line, u16 set, int is_pal) +{ + u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); + int i; + + set = set & valid_set; + if (set == 0 || !valid_service_line(field, line, is_pal)) { + return 0; + } + if (!is_pal) { + if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) + return V4L2_SLICED_CAPTION_525; + } + else { + if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) + return V4L2_SLICED_VPS; + if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) + return V4L2_SLICED_WSS_625; + if (line == 23) + return 0; + } + for (i = 0; i < 32; i++) { + if ((1 << i) & set) + return 1 << i; + } + return 0; +} + +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + u16 set = fmt->service_set; + int f, l; + + fmt->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); + } + } +} + +static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); + set |= fmt->service_lines[f][l]; + } + } + return set != 0; +} + +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + set |= fmt->service_lines[f][l]; + } + } + return set; +} + +static const struct { + v4l2_std_id std; + char *name; +} enum_stds[] = { + { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-L'" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-J" }, + { V4L2_STD_NTSC_M_KR, "NTSC-K" }, +}; + +static const struct v4l2_standard ivtv_std_60hz = +{ + .frameperiod = {.numerator = 1001, .denominator = 30000}, + .framelines = 525, +}; + +static const struct v4l2_standard ivtv_std_50hz = +{ + .frameperiod = {.numerator = 1, .denominator = 25}, + .framelines = 625, +}; + +void ivtv_set_osd_alpha(struct ivtv *itv) +{ + ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3, + itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state); + ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key); +} + +int ivtv_set_speed(struct ivtv *itv, int speed) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + int single_step = (speed == 1 || speed == -1); + DEFINE_WAIT(wait); + + if (speed == 0) speed = 1000; + + /* No change? */ + if (speed == itv->speed && !single_step) + return 0; + + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (single_step && (speed < 0) == (itv->speed < 0)) { + /* Single step video and no need to change direction */ + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + itv->speed = speed; + return 0; + } + if (single_step) + /* Need to change direction */ + speed = speed < 0 ? -1000 : 1000; + + data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0; + data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0; + data[1] = (speed < 0); + data[2] = speed < 0 ? 3 : 7; + data[3] = itv->params.video_b_frames; + data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0; + data[5] = 0; + data[6] = 0; + + if (speed == 1500 || speed == -1500) data[0] |= 1; + else if (speed == 2000 || speed == -2000) data[0] |= 2; + else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed); + else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed); + + /* If not decoding, just change speed setting */ + if (atomic_read(&itv->decoding) > 0) { + int got_sig = 0; + + /* Stop all DMA and decoding activity */ + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0); + + /* Wait for any DMA to finish */ + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (itv->i_flags & IVTV_F_I_DMA) { + got_sig = signal_pending(current); + if (got_sig) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) + return -EINTR; + + /* Change Speed safely */ + ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data); + IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + } + if (single_step) { + speed = (speed < 0) ? -1 : 1; + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + } + itv->speed = speed; + return 0; +} + +static int ivtv_validate_speed(int cur_speed, int new_speed) +{ + int fact = new_speed < 0 ? -1 : 1; + int s; + + if (new_speed < 0) new_speed = -new_speed; + if (cur_speed < 0) cur_speed = -cur_speed; + + if (cur_speed <= new_speed) { + if (new_speed > 1500) return fact * 2000; + if (new_speed > 1000) return fact * 1500; + } + else { + if (new_speed >= 2000) return fact * 2000; + if (new_speed >= 1500) return fact * 1500; + if (new_speed >= 1000) return fact * 1000; + } + if (new_speed == 0) return 1000; + if (new_speed == 1 || new_speed == 1000) return fact * new_speed; + + s = new_speed; + new_speed = 1000 / new_speed; + if (1000 / cur_speed == new_speed) + new_speed += (cur_speed < s) ? -1 : 1; + if (new_speed > 60) return 1000 / (fact * 60); + return 1000 / (fact * new_speed); +} + +static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, + struct video_command *vc, int try) +{ + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + switch (vc->cmd) { + case VIDEO_CMD_PLAY: { + vc->flags = 0; + vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed); + if (vc->play.speed < 0) + vc->play.format = VIDEO_PLAY_FMT_GOP; + if (try) break; + + if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) + return -EBUSY; + return ivtv_start_decoding(id, vc->play.speed); + } + + case VIDEO_CMD_STOP: + vc->flags &= VIDEO_CMD_STOP_IMMEDIATELY|VIDEO_CMD_STOP_TO_BLACK; + if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY) + vc->stop.pts = 0; + if (try) break; + if (atomic_read(&itv->decoding) == 0) + return 0; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + + itv->output_mode = OUT_NONE; + return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts); + + case VIDEO_CMD_FREEZE: + vc->flags &= VIDEO_CMD_FREEZE_TO_BLACK; + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, + (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0); + } + break; + + case VIDEO_CMD_CONTINUE: + vc->flags = 0; + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_register *regs = arg; + unsigned long flags; + volatile u8 __iomem *reg_start; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) + reg_start = itv->reg_mem - IVTV_REG_OFFSET; + else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && + regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) + reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; + else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) + reg_start = itv->enc_mem; + else + return -EINVAL; + + spin_lock_irqsave(&ivtv_cards_lock, flags); + if (cmd == VIDIOC_DBG_G_REGISTER) { + regs->val = readl(regs->reg + reg_start); + } else { + writel(regs->val, regs->reg + reg_start); + } + spin_unlock_irqrestore(&ivtv_cards_lock, flags); + return 0; +} + +static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.pix.left = itv->main_rect.left; + fmt->fmt.pix.top = itv->main_rect.top; + fmt->fmt.pix.width = itv->main_rect.width; + fmt->fmt.pix.height = itv->main_rect.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (itv->output_mode == OUT_UDMA_YUV) { + switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { + case IVTV_YUV_MODE_INTERLACED: + fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? + V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; + break; + case IVTV_YUV_MODE_PROGRESSIVE: + fmt->fmt.pix.field = V4L2_FIELD_NONE; + break; + default: + fmt->fmt.pix.field = V4L2_FIELD_ANY; + break; + } + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } + else if (itv->output_mode == OUT_YUV || + streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt->fmt.pix.left = 0; + fmt->fmt.pix.top = 0; + fmt->fmt.pix.width = itv->params.width; + fmt->fmt.pix.height = itv->params.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.win.chromakey = itv->osd_color_key; + fmt->fmt.win.global_alpha = itv->osd_global_alpha; + break; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + fmt->fmt.vbi.sampling_rate = 27000000; + fmt->fmt.vbi.offset = 248; + fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4; + fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt->fmt.vbi.start[0] = itv->vbi.start[0]; + fmt->fmt.vbi.start[1] = itv->vbi.start[1]; + fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count; + break; + + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + if (itv->is_60hz) { + vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; + } + vbifmt->service_set = get_service_set(vbifmt); + break; + } + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : + V4L2_SLICED_VBI_525; + expand_service_set(vbifmt, itv->is_50hz); + break; + } + + itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + vbifmt->service_set = get_service_set(vbifmt); + break; + } + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, + struct v4l2_format *fmt, int set_fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + u16 set; + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect r; + int field; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + field = fmt->fmt.pix.field; + r.top = fmt->fmt.pix.top; + r.left = fmt->fmt.pix.left; + r.width = fmt->fmt.pix.width; + r.height = fmt->fmt.pix.height; + ivtv_get_fmt(itv, streamtype, fmt); + if (itv->output_mode != OUT_UDMA_YUV) { + /* TODO: would setting the rect also be valid for this mode? */ + fmt->fmt.pix.top = r.top; + fmt->fmt.pix.left = r.left; + fmt->fmt.pix.width = r.width; + fmt->fmt.pix.height = r.height; + } + if (itv->output_mode == OUT_UDMA_YUV) { + /* TODO: add checks for validity */ + fmt->fmt.pix.field = field; + } + if (set_fmt) { + if (itv->output_mode == OUT_UDMA_YUV) { + switch (field) { + case V4L2_FIELD_NONE: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE; + break; + case V4L2_FIELD_ANY: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO; + break; + case V4L2_FIELD_INTERLACED_BT: + itv->yuv_info.lace_mode = + IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; + break; + case V4L2_FIELD_INTERLACED_TB: + default: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED; + break; + } + itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + return 0; + } + if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + r.width, r.height, r.left, r.top)) + itv->main_rect = r; + else + return -EINVAL; + } + return 0; + } + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) { + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (set_fmt) { + itv->osd_color_key = fmt->fmt.win.chromakey; + itv->osd_global_alpha = fmt->fmt.win.global_alpha; + ivtv_set_osd_alpha(itv); + } + return 0; + } + + /* set window size */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + if (w > 720) w = 720; + else if (w < 1) w = 1; + if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480); + else if (h < 2) h = 2; + ivtv_get_fmt(itv, streamtype, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + + if (!set_fmt || (itv->params.width == w && itv->params.height == h)) + return 0; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + itv->params.width = w; + itv->params.height = h; + if (w != 720 || h != (itv->is_50hz ? 576 : 480)) + itv->params.video_temporal_filter = 0; + else + itv->params.video_temporal_filter = 8; + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set raw VBI format */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI && + itv->vbi.sliced_in->service_set && + atomic_read(&itv->capturing) > 0) { + return -EBUSY; + } + if (set_fmt) { + itv->vbi.sliced_in->service_set = 0; + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + } + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set sliced VBI output + In principle the user could request that only certain + VBI types are output and that the others are ignored. + I.e., suppress CC in the even fields or only output + WSS and no VPS. Currently though there is no choice. */ + if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* any else but sliced VBI capture is an error */ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* set sliced VBI capture format */ + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + + if (vbifmt->service_set) + expand_service_set(vbifmt, itv->is_50hz); + set = check_service_set(vbifmt, itv->is_50hz); + vbifmt->service_set = get_service_set(vbifmt); + + if (!set_fmt) + return 0; + if (set == 0) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) { + return -EBUSY; + } + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); + return 0; +} + +static int ivtv_debug_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + struct v4l2_register *reg = arg; + + switch (cmd) { + /* ioctls to allow direct access to the encoder registers for testing */ + case VIDIOC_DBG_G_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_DBG_S_REGISTER: + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_G_CHIP_IDENT: { + struct v4l2_chip_ident *chip = arg; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (reg->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { + struct v4l2_chip_ident *chip = arg; + + chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; + } + return 0; + } + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + return -EINVAL; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + ivtv_audio_set_route(itv, route); + break; + } + + case VIDIOC_INT_RESET: + ivtv_reset_ir_gpio(itv); + break; + + default: + return -EINVAL; + } + return 0; +} + +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = NULL; + + if (filp) id = (struct ivtv_open_id *)filp->private_data; + + switch (cmd) { + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *p = arg; + + *p = v4l2_prio_max(&itv->prio); + break; + } + + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *prio = arg; + + return v4l2_prio_change(&itv->prio, &id->prio, *prio); + } + + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *vcap = arg; + + memset(vcap, 0, sizeof(*vcap)); + strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */ + strcpy(vcap->card, itv->card_name); /* card type */ + strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */ + vcap->version = IVTV_DRIVER_VERSION; /* version */ + vcap->capabilities = itv->v4l2_cap; /* capabilities */ + + /* reserved.. must set to 0! */ + vcap->reserved[0] = vcap->reserved[1] = + vcap->reserved[2] = vcap->reserved[3] = 0; + break; + } + + case VIDIOC_ENUMAUDIO:{ + struct v4l2_audio *vin = arg; + + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_G_AUDIO:{ + struct v4l2_audio *vin = arg; + + vin->index = itv->audio_input; + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_S_AUDIO:{ + struct v4l2_audio *vout = arg; + + if (vout->index >= itv->nof_audio_inputs) + return -EINVAL; + itv->audio_input = vout->index; + ivtv_audio_set_io(itv); + break; + } + + case VIDIOC_ENUMAUDOUT:{ + struct v4l2_audioout *vin = arg; + + /* set it to defaults from our table */ + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_G_AUDOUT:{ + struct v4l2_audioout *vin = arg; + + vin->index = 0; + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_S_AUDOUT:{ + struct v4l2_audioout *vout = arg; + + return ivtv_get_audio_output(itv, vout->index, vout); + } + + case VIDIOC_ENUMINPUT:{ + struct v4l2_input *vin = arg; + + /* set it to defaults from our table */ + return ivtv_get_input(itv, vin->index, vin); + } + + case VIDIOC_ENUMOUTPUT:{ + struct v4l2_output *vout = arg; + + return ivtv_get_output(itv, vout->index, vout); + } + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: { + struct v4l2_format *fmt = arg; + + return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT); + } + + case VIDIOC_G_FMT: { + struct v4l2_format *fmt = arg; + int type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = type; + return ivtv_get_fmt(itv, id->type, fmt); + } + + case VIDIOC_S_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_S_CROP, arg); + } + + case VIDIOC_G_CROP: { + struct v4l2_crop *crop = arg; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_G_CROP, arg); + } + + case VIDIOC_ENUM_FMT: { + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } + } + }; + struct v4l2_fmtdesc *fmt = arg; + enum v4l2_buf_type type = fmt->type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + break; + default: + return -EINVAL; + } + if (fmt->index > 1) + return -EINVAL; + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; + } + + case VIDIOC_G_INPUT:{ + *(int *)arg = itv->active_input; + break; + } + + case VIDIOC_S_INPUT:{ + int inp = *(int *)arg; + + if (inp < 0 || inp >= itv->nof_inputs) + return -EINVAL; + + if (inp == itv->active_input) { + IVTV_DEBUG_INFO("Input unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing input from %d to %d\n", + itv->active_input, inp); + + itv->active_input = inp; + /* Set the audio input to whatever is appropriate for the + input type. */ + itv->audio_input = itv->card->video_inputs[inp].audio_index; + + /* prevent others from messing with the streams until + we're finished changing inputs. */ + ivtv_mute(itv); + ivtv_video_set_io(itv); + ivtv_audio_set_io(itv); + ivtv_unmute(itv); + break; + } + + case VIDIOC_G_OUTPUT:{ + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + *(int *)arg = itv->active_output; + break; + } + + case VIDIOC_S_OUTPUT:{ + int outp = *(int *)arg; + struct v4l2_routing route; + + if (outp >= itv->card->nof_outputs) + return -EINVAL; + + if (outp == itv->active_output) { + IVTV_DEBUG_INFO("Output unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing output from %d to %d\n", + itv->active_output, outp); + + itv->active_output = outp; + route.input = SAA7127_INPUT_TYPE_NORMAL; + route.output = itv->card->video_outputs[outp].video_output; + ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + break; + } + + case VIDIOC_G_FREQUENCY:{ + struct v4l2_frequency *vf = arg; + + if (vf->tuner != 0) + return -EINVAL; + ivtv_call_i2c_clients(itv, cmd, arg); + break; + } + + case VIDIOC_S_FREQUENCY:{ + struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; + + if (vf.tuner != 0) + return -EINVAL; + + ivtv_mute(itv); + IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); + ivtv_call_i2c_clients(itv, cmd, &vf); + ivtv_unmute(itv); + break; + } + + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *vs = arg; + int idx = vs->index; + + if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) + return -EINVAL; + + *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? + ivtv_std_60hz : ivtv_std_50hz; + vs->index = idx; + vs->id = enum_stds[idx].std; + strcpy(vs->name, enum_stds[idx].name); + break; + } + + case VIDIOC_G_STD:{ + *(v4l2_std_id *) arg = itv->std; + break; + } + + case VIDIOC_S_STD: { + v4l2_std_id std = *(v4l2_std_id *) arg; + + if ((std & V4L2_STD_ALL) == 0) + return -EINVAL; + + if (std == itv->std) + break; + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || + atomic_read(&itv->capturing) > 0 || + atomic_read(&itv->decoding) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; + } + + itv->std = std; + itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; + itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; + itv->params.width = 720; + itv->params.height = itv->is_50hz ? 576 : 480; + itv->vbi.count = itv->is_50hz ? 18 : 12; + itv->vbi.start[0] = itv->is_50hz ? 6 : 10; + itv->vbi.start[1] = itv->is_50hz ? 318 : 273; + if (itv->hw_flags & IVTV_HW_CX25840) { + itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; + } + IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std); + + /* Tuner */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* set display standard */ + itv->std_out = std; + itv->is_out_60hz = itv->is_60hz; + itv->is_out_50hz = itv->is_50hz; + ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); + itv->main_rect.left = itv->main_rect.top = 0; + itv->main_rect.width = 720; + itv->main_rect.height = itv->params.height; + ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + 720, itv->main_rect.height, 0, 0); + } + break; + } + + case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = arg; + + if (vt->index != 0) + return -EINVAL; + + memset(vt, 0, sizeof(*vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + strcpy(vt->name, "ivtv Radio Tuner"); + vt->type = V4L2_TUNER_RADIO; + } else { + strcpy(vt->name, "ivtv TV Tuner"); + vt->type = V4L2_TUNER_ANALOG_TV; + } + break; + } + + case VIDIOC_G_SLICED_VBI_CAP: { + struct v4l2_sliced_vbi_cap *cap = arg; + int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; + enum v4l2_buf_type type = cap->type; + + memset(cap, 0, sizeof(*cap)); + cap->type = type; + if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, itv->is_50hz)) { + cap->service_lines[f][l] = set; + } + } + } + return 0; + } + if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + if (itv->is_60hz) { + cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + cap->service_lines[0][16] = V4L2_SLICED_VPS; + } + return 0; + } + return -EINVAL; + } + + case VIDIOC_G_ENC_INDEX: { + struct v4l2_enc_idx *idx = arg; + int i; + + idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % + IVTV_MAX_PGM_INDEX; + if (idx->entries > V4L2_ENC_IDX_ENTRIES) + idx->entries = V4L2_ENC_IDX_ENTRIES; + for (i = 0; i < idx->entries; i++) { + idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; + } + itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; + break; + } + + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: { + struct v4l2_encoder_cmd *enc = arg; + int try = cmd == VIDIOC_TRY_ENCODER_CMD; + + memset(&enc->raw, 0, sizeof(enc->raw)); + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + enc->flags = 0; + if (try) + return 0; + return ivtv_start_capture(id); + + case V4L2_ENC_CMD_STOP: + enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END; + if (try) + return 0; + ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + return 0; + + case V4L2_ENC_CMD_PAUSE: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_mute(itv); + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); + break; + + case V4L2_ENC_CMD_RESUME: + enc->flags = 0; + if (try) + return 0; + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + ivtv_unmute(itv); + break; + default: + return -EINVAL; + } + break; + } + + case VIDIOC_G_FBUF: { + struct v4l2_framebuffer *fb = arg; + + memset(fb, 0, sizeof(*fb)); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA; + fb->fmt.pixelformat = itv->osd_pixelformat; + fb->fmt.width = itv->osd_rect.width; + fb->fmt.height = itv->osd_rect.height; + fb->fmt.left = itv->osd_rect.left; + fb->fmt.top = itv->osd_rect.top; + fb->base = (void *)itv->osd_video_pbase; + if (itv->osd_global_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; + if (itv->osd_local_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + if (itv->osd_color_key_state) + fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + break; + } + + case VIDIOC_S_FBUF: { + struct v4l2_framebuffer *fb = arg; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; + itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0; + itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; + break; + } + + case VIDIOC_LOG_STATUS: + { + int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + if (itv->hw_flags & IVTV_HW_TVEEPROM) { + struct tveeprom tv; + + ivtv_read_eeprom(itv, &tv); + } + ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_get_input(itv, itv->active_input, &vidin); + ivtv_get_audio_input(itv, itv->audio_input, &audin); + IVTV_INFO("Video Input: %s\n", vidin.name); + IVTV_INFO("Audio Input: %s\n", audin.name); + if (has_output) { + struct v4l2_output vidout; + struct v4l2_audioout audout; + int mode = itv->output_mode; + static const char * const output_modes[] = { + "None", + "MPEG Streaming", + "YUV Streaming", + "YUV Frames", + "Passthrough", + }; + + ivtv_get_output(itv, itv->active_output, &vidout); + ivtv_get_audio_output(itv, 0, &audout); + IVTV_INFO("Video Output: %s\n", vidout.name); + IVTV_INFO("Audio Output: %s\n", audout.name); + if (mode < 0 || mode > OUT_PASSTHROUGH) + mode = OUT_NONE; + IVTV_INFO("Output Mode: %s\n", output_modes[mode]); + } + IVTV_INFO("Tuner: %s\n", + test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); + cx2341x_log_status(&itv->params, itv->name); + IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); + } + IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + int nonblocking = filp->f_flags & O_NONBLOCK; + struct ivtv_stream *s = &itv->streams[id->type]; + + switch (cmd) { + case IVTV_IOC_DMA_FRAME: { + struct ivtv_dma_frame *args = arg; + + IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL) + return 0; + if (ivtv_claim_stream(id, id->type)) { + return -EBUSY; + } + if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) { + ivtv_release_stream(s); + return -EBUSY; + } + if (args->y_source == NULL) + return 0; + return ivtv_yuv_prep_frame(itv, args); + } + + case VIDEO_GET_PTS: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *pts = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *pts = s->dma_pts; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) | + (u64)itv->last_dec_timing[1]; + break; + } + *pts = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *pts = (u64) ((u64) data[2] << 32) | (u64) data[1]; + /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ + } + break; + } + + case VIDEO_GET_FRAME_COUNT: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *frame = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *frame = 0; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *frame = itv->last_dec_timing[0]; + break; + } + *frame = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *frame = data[0]; + } + break; + } + + case VIDEO_PLAY: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_PLAY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_STOP: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_STOP; + vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_FREEZE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_FREEZE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_CONTINUE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_CONTINUE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: { + struct video_command *vc = arg; + int try = (cmd == VIDEO_TRY_COMMAND); + + if (try) + IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n"); + else + IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n"); + return ivtv_video_command(itv, id, vc, try); + } + + case VIDEO_GET_EVENT: { + struct video_event *ev = arg; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + memset(ev, 0, sizeof(*ev)); + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + + while (1) { + if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + ev->type = VIDEO_EVENT_DECODER_STOPPED; + else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) { + ev->type = VIDEO_EVENT_VSYNC; + ev->u.vsync_field = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ? + VIDEO_VSYNC_FIELD_ODD : VIDEO_VSYNC_FIELD_EVEN; + if (itv->output_mode == OUT_UDMA_YUV && + (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) == + IVTV_YUV_MODE_PROGRESSIVE) { + ev->u.vsync_field = VIDEO_VSYNC_FIELD_PROGRESSIVE; + } + } + if (ev->type) + return 0; + if (nonblocking) + return -EAGAIN; + /* wait for event */ + prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE); + if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0) + schedule(); + finish_wait(&itv->event_waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped wait for event\n"); + return -EINTR; + } + } + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + int ret; + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + case VIDIOC_S_FMT: + case VIDIOC_S_CROP: + case VIDIOC_S_AUDIO: + case VIDIOC_S_AUDOUT: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_S_FBUF: + ret = v4l2_prio_check(&itv->prio, &id->prio); + if (ret) + return ret; + } + + switch (cmd) { + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_INT_RESET: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_debug_ioctls(filp, cmd, arg); + + case VIDIOC_G_PRIORITY: + case VIDIOC_S_PRIORITY: + case VIDIOC_QUERYCAP: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_OUTPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_ENUM_FMT: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_S_TUNER: + case VIDIOC_G_TUNER: + case VIDIOC_ENUMAUDIO: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_G_FBUF: + case VIDIOC_S_FBUF: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_v4l2_ioctls(itv, filp, cmd, arg); + + case VIDIOC_QUERYMENU: + case VIDIOC_QUERYCTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + if (ivtv_debug & IVTV_DBGFLG_IOCTL) { + printk(KERN_INFO "ivtv%d ioctl: ", itv->num); + v4l_printk_ioctl(cmd); + } + return ivtv_control_ioctls(itv, cmd, arg); + + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + return ivtv_decoder_ioctls(filp, cmd, arg); + + case 0x00005401: /* Handle isatty() calls */ + return -EINVAL; + default: + return v4l_compat_translate_ioctl(inode, filp, cmd, arg, + ivtv_v4l2_do_ioctl); + } + return 0; +} + +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + + /* Filter dvb ioctls that cannot be handled by video_usercopy */ + switch (cmd) { + case VIDEO_SELECT_SOURCE: + IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX); + + case AUDIO_SET_MUTE: + IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); + itv->speed_mute_audio = arg; + return 0; + + case AUDIO_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_stereo_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + case AUDIO_BILINGUAL_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_bilingual_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + default: + break; + } + return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl); +} diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h new file mode 100644 index 0000000..cbccf7a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.h @@ -0,0 +1,28 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +u16 service2vbi(int type); +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt); +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg); +void ivtv_set_osd_alpha(struct ivtv *itv); +int ivtv_set_speed(struct ivtv *itv, int speed); diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c new file mode 100644 index 0000000..c3a047b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -0,0 +1,838 @@ +/* interrupt handling + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-firmware.h" +#include "ivtv-fileops.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-ioctl.h" +#include "ivtv-mailbox.h" +#include "ivtv-vbi.h" +#include "ivtv-yuv.h" + +#define DMA_MAGIC_COOKIE 0x000001fe + +#define SLICED_VBI_PIO 1 + +static void ivtv_dma_dec_start(struct ivtv_stream *s); + +static const int ivtv_stream_map[] = { + IVTV_ENC_STREAM_TYPE_MPG, + IVTV_ENC_STREAM_TYPE_YUV, + IVTV_ENC_STREAM_TYPE_PCM, + IVTV_ENC_STREAM_TYPE_VBI, +}; + +static inline int ivtv_use_pio(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + return s->dma == PCI_DMA_NONE || + (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); +} + +void ivtv_irq_work_handler(struct work_struct *work) +{ + struct ivtv *itv = container_of(work, struct ivtv, irq_work_queue); + + DEFINE_WAIT(wait); + + if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags)) + vbi_work_handler(itv); + + if (test_and_clear_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags)) + ivtv_yuv_work_handler(itv); +} + +/* Determine the required DMA size, setup enough buffers in the predma queue and + actually copy the data from the card to the buffers in case a PIO transfer is + required for this stream. + */ +static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 bytes_needed = 0; + u32 offset, size; + u32 UVoffset = 0, UVsize = 0; + int skip_bufs = s->q_predma.buffers; + int idx = s->SG_length; + int rc; + + /* sanity checks */ + if (s->v4l2dev == NULL) { + IVTV_DEBUG_WARN("Stream %s not started\n", s->name); + return -1; + } + if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Stream %s not open\n", s->name); + return -1; + } + + /* determine offset, size and PTS for the various streams */ + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + offset = data[1]; + size = data[2]; + s->dma_pts = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + offset = data[1]; + size = data[2]; + UVoffset = data[3]; + UVsize = data[4]; + s->dma_pts = ((u64) data[5] << 32) | data[6]; + break; + + case IVTV_ENC_STREAM_TYPE_PCM: + offset = data[1] + 12; + size = data[2] - 12; + s->dma_pts = read_dec(offset - 8) | + ((u64)(read_dec(offset - 12)) << 32); + if (itv->has_cx23415) + offset += IVTV_DECODER_OFFSET; + break; + + case IVTV_ENC_STREAM_TYPE_VBI: + size = itv->vbi.enc_size * itv->vbi.fpi; + offset = read_enc(itv->vbi.enc_start - 4) + 12; + if (offset == 12) { + IVTV_DEBUG_INFO("VBI offset == 0\n"); + return -1; + } + s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32); + break; + + case IVTV_DEC_STREAM_TYPE_VBI: + size = read_dec(itv->vbi.dec_start + 4) + 8; + offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start; + s->dma_pts = 0; + offset += IVTV_DECODER_OFFSET; + break; + default: + /* shouldn't happen */ + return -1; + } + + /* if this is the start of the DMA then fill in the magic cookie */ + if (s->SG_length == 0) { + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET); + write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); + } + else { + s->dma_backup = read_enc(offset); + write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); + } + s->dma_offset = offset; + } + + bytes_needed = size; + if (s->type == IVTV_ENC_STREAM_TYPE_YUV) { + /* The size for the Y samples needs to be rounded upwards to a + multiple of the buf_size. The UV samples then start in the + next buffer. */ + bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size); + bytes_needed += UVsize; + } + + IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n", + ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); + + rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); + if (rc < 0) { /* Insufficient buffers */ + IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n", + bytes_needed, s->name); + return -1; + } + if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) { + IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); + IVTV_WARN("Cause: the application is not reading fast enough.\n"); + } + s->buffers_stolen = rc; + + /* got the buffers, now fill in SGarray (DMA) or copy the data from the card + to the buffers (PIO). */ + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + memset(buf->buf, 0, 128); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + if (skip_bufs-- > 0) + continue; + if (!ivtv_use_pio(s)) { + s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].src = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(s->buf_size); + } + buf->bytesused = (size < s->buf_size) ? size : s->buf_size; + + /* If PIO, then copy the data from the card to the buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused); + } + else if (ivtv_use_pio(s)) { + memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused); + } + + s->q_predma.bytesused += buf->bytesused; + size -= buf->bytesused; + offset += s->buf_size; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + + if (size == 0) { /* YUV */ + /* process the UV section */ + offset = UVoffset; + size = UVsize; + } + idx++; + } + s->SG_length = idx; + return 0; +} + +static void dma_post(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf = NULL; + struct list_head *p; + u32 offset; + u32 *u32buf; + int x = 0; + + if (ivtv_use_pio(s)) { + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + } + IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", + s->name, s->dma_offset); + list_for_each(p, &s->q_dma.list) { + buf = list_entry(p, struct ivtv_buffer, list); + u32buf = (u32 *)buf->buf; + + /* Sync Buffer */ + ivtv_buf_sync_for_cpu(s, buf); + + if (x == 0) { + offset = s->dma_last_offset; + if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) + { + for (offset = 0; offset < 64; offset++) { + if (u32buf[offset] == DMA_MAGIC_COOKIE) { + break; + } + } + offset *= 4; + if (offset == 256) { + IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); + offset = s->dma_last_offset; + } + if (s->dma_last_offset != offset) + IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset); + s->dma_last_offset = offset; + } + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET); + } + else { + write_enc_sync(0, s->dma_offset); + } + if (offset) { + buf->bytesused -= offset; + memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset); + } + *u32buf = cpu_to_le32(s->dma_backup); + } + x++; + /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG || + s->type == IVTV_ENC_STREAM_TYPE_VBI) + set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags); + } + if (buf) + buf->bytesused += s->dma_last_offset; + if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Parse and Groom VBI Data */ + s->q_dma.bytesused -= buf->bytesused; + ivtv_process_vbi_data(itv, buf, 0, s->type); + s->q_dma.bytesused += buf->bytesused; + if (s->id == -1) { + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + return; + } + } + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); + if (s->id != -1) + wake_up(&s->waitq); +} + +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 y_size = itv->params.height * itv->params.width; + u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; + int y_done = 0; + int bytes_written = 0; + unsigned long flags = 0; + int idx = 0; + + IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + /* YUV UV Offset from Y Buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) { + offset = uv_offset; + y_done = 1; + } + s->SGarray[idx].src = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].dst = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(buf->bytesused); + + offset += buf->bytesused; + bytes_written += buf->bytesused; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + idx++; + } + s->SG_length = idx; + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + if (lock) + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + ivtv_dma_dec_start(s); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + if (lock) + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} + +/* start the encoder DMA */ +static void ivtv_dma_enc_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + int i; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256); + + /* If this is an MPEG stream, and VBI data is also pending, then append the + VBI DMA to the MPEG DMA and transfer both sets of data at once. + + VBI DMA is a second class citizen compared to MPEG and mixing them together + will confuse the firmware (the end of a VBI DMA is seen as the end of a + MPEG DMA, thus effectively dropping an MPEG frame). So instead we make + sure we only use the MPEG DMA to transfer the VBI DMA if both are in + use. This way no conflicts occur. */ + clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length && + s->SG_length + s_vbi->SG_length <= s->buffers) { + ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused); + s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256); + for (i = 0; i < s_vbi->SG_length; i++) { + s->SGarray[s->SG_length++] = s_vbi->SGarray[i]; + } + itv->vbi.dma_offset = s_vbi->dma_offset; + s_vbi->SG_length = 0; + set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + IVTV_DEBUG_DMA("include DMA for %s\n", s->name); + } + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_dma_dec_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + /* put SG Handle into register 0x0c */ + write_reg(s->SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_irq_dma_read(struct ivtv *itv) +{ + struct ivtv_stream *s = NULL; + struct ivtv_buffer *buf; + int hw_stream_type; + + IVTV_DEBUG_IRQ("DEC DMA READ\n"); + del_timer(&itv->dma_timer); + if (read_reg(IVTV_REG_DMASTATUS) & 0x14) { + IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS)); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + } + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + hw_stream_type = 2; + } + else { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + hw_stream_type = 0; + } + IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); + + ivtv_stream_sync_for_cpu(s); + + /* For some reason must kick the firmware, like PIO mode, + I think this tells the firmware we are done and the size + of the xfer so it can calculate what we need next. + I think we can do this part ourselves but would have to + fully calculate xfer info ourselves and not use interrupts + */ + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused, + hw_stream_type); + + /* Free last DMA call */ + while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) { + ivtv_buf_sync_for_cpu(s, buf); + ivtv_enqueue(s, buf, &s->q_free); + } + wake_up(&s->waitq); + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_dma_complete(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]); + if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags)) + data[1] = 3; + else if (data[1] > 2) + return; + s = &itv->streams[ivtv_stream_map[data[1]]]; + if (data[0] & 0x18) { + IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]); + } + s->SG_length = 0; + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + dma_post(s); + ivtv_stream_sync_for_cpu(s); + if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) { + u32 tmp; + + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + tmp = s->dma_offset; + s->dma_offset = itv->vbi.dma_offset; + dma_post(s); + s->dma_offset = tmp; + } + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_dma_err(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1], + read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && + itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) { + struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream]; + + /* retry */ + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + return; + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_start_cap(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* Get DMA destination and size arguments from card */ + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data); + IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); + + if (data[0] > 2 || data[1] == 0 || data[2] == 0) { + IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n", + data[0], data[1], data[2]); + return; + } + clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + s = &itv->streams[ivtv_stream_map[data[0]]]; + if (!stream_enc_dma_append(s, data)) { + if (ivtv_use_pio(s)) { + dma_post(s); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + } +} + +static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) +{ + struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + IVTV_DEBUG_IRQ("ENC START VBI CAP\n"); + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + if (ivtv_use_pio(s)) { + if (stream_enc_dma_append(s, data)) + return; + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + dma_post(s); + return; + } + /* If more than two VBI buffers are pending, then + clear the old ones and start with this new one. + This can happen during transition stages when MPEG capturing is + started, but the first interrupts haven't arrived yet. During + that period VBI requests can accumulate without being able to + DMA the data. Since at most four VBI DMA buffers are available, + we just drop the old requests when there are already three + requests queued. */ + if (s->SG_length > 2) { + struct list_head *p; + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); + s->SG_length = 0; + } + /* if we can append the data, and the MPEG stream isn't capturing, + then start a DMA request for just the VBI data. */ + if (!stream_enc_dma_append(s, data) && + !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { + set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } +} + +static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_IRQ("DEC VBI REINSERT\n"); + if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && + !stream_enc_dma_append(s, data)) { + dma_post(s); + } +} + +static void ivtv_irq_dec_data_req(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* YUV or MPG */ + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2; + itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + } + else { + itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2]; + itv->dma_data_req_offset = data[1]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + } + IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, + itv->dma_data_req_offset, itv->dma_data_req_size); + if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) { + set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + } + else { + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0); + } +} + +static void ivtv_irq_vsync(struct ivtv *itv) +{ + /* The vsync interrupt is unusual in that it won't clear until + * the end of the first line for the current field, at which + * point it clears itself. This can result in repeated vsync + * interrupts, or a missed vsync. Read some of the registers + * to determine the line being displayed and ensure we handle + * one vsync per frame. + */ + unsigned int frame = read_reg(0x28c0) & 1; + int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame); + + if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); + + if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) || + (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) { + int next_dma_frame = last_dma_frame; + + if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) { + write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[next_dma_frame] >> 4, 0x834); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + next_dma_frame = (next_dma_frame + 1) & 0x3; + atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame); + } + } + if (frame != (itv->lastVsyncFrame & 1)) { + struct ivtv_stream *s = ivtv_get_output_stream(itv); + int work = 0; + + itv->lastVsyncFrame += 1; + if (frame == 0) { + clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + else { + set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { + set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); + wake_up(&itv->event_waitq); + } + wake_up(&itv->vsync_waitq); + if (s) + wake_up(&s->waitq); + + /* Send VBI to saa7127 */ + if (frame) { + set_bit(IVTV_F_I_WORK_HANDLER_VBI, &itv->i_flags); + work = 1; + } + + /* Check if we need to update the yuv registers */ + if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { + if (!itv->yuv_info.new_frame_info[last_dma_frame].update) + last_dma_frame = (last_dma_frame - 1) & 3; + + if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) { + itv->yuv_info.update_frame = last_dma_frame; + itv->yuv_info.new_frame_info[last_dma_frame].update = 0; + itv->yuv_info.yuv_forced_update = 0; + set_bit(IVTV_F_I_WORK_HANDLER_YUV, &itv->i_flags); + work = 1; + } + } + if (work) + queue_work(itv->irq_work_queues, &itv->irq_work_queue); + } +} + +#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ) + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id) +{ + struct ivtv *itv = (struct ivtv *)dev_id; + u32 combo; + u32 stat; + int i; + u8 vsync_force = 0; + + spin_lock(&itv->dma_reg_lock); + /* get contents of irq status register */ + stat = read_reg(IVTV_REG_IRQSTATUS); + + combo = ~itv->irqmask & stat; + + /* Clear out IRQ */ + if (combo) write_reg(combo, IVTV_REG_IRQSTATUS); + + if (0 == combo) { + /* The vsync interrupt is unusual and clears itself. If we + * took too long, we may have missed it. Do some checks + */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + /* vsync is enabled, see if we're in a new field */ + if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) { + /* New field, looks like we missed it */ + IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16); + vsync_force = 1; + } + } + + if (!vsync_force) { + /* No Vsync expected, wasn't for us */ + spin_unlock(&itv->dma_reg_lock); + return IRQ_NONE; + } + } + + /* Exclude interrupts noted below from the output, otherwise the log is flooded with + these messages */ + if (combo & ~0xff6d0400) + IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); + + if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { + IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n"); + } + + if (combo & IVTV_IRQ_DMA_READ) { + ivtv_irq_dma_read(itv); + } + + if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { + ivtv_irq_enc_dma_complete(itv); + } + + if (combo & IVTV_IRQ_DMA_ERR) { + ivtv_irq_dma_err(itv); + } + + if (combo & IVTV_IRQ_ENC_START_CAP) { + ivtv_irq_enc_start_cap(itv); + } + + if (combo & IVTV_IRQ_ENC_VBI_CAP) { + ivtv_irq_enc_vbi_cap(itv); + } + + if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { + ivtv_irq_dev_vbi_reinsert(itv); + } + + if (combo & IVTV_IRQ_ENC_EOS) { + IVTV_DEBUG_IRQ("ENC EOS\n"); + set_bit(IVTV_F_I_EOS, &itv->i_flags); + wake_up(&itv->cap_w); + } + + if (combo & IVTV_IRQ_DEC_DATA_REQ) { + ivtv_irq_dec_data_req(itv); + } + + /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + ivtv_irq_vsync(itv); + } + + if (combo & IVTV_IRQ_ENC_VIM_RST) { + IVTV_DEBUG_IRQ("VIM RST\n"); + /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */ + } + + if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) { + IVTV_DEBUG_INFO("Stereo mode changed\n"); + } + + if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS; + struct ivtv_stream *s = &itv->streams[idx]; + + if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) + continue; + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + break; + } + if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) { + ivtv_udma_start(itv); + } + } + + spin_unlock(&itv->dma_reg_lock); + + /* If we've just handled a 'forced' vsync, it's safest to say it + * wasn't ours. Another device may have triggered it at just + * the right time. + */ + return vsync_force ? IRQ_NONE : IRQ_HANDLED; +} + +void ivtv_unfinished_dma(unsigned long arg) +{ + struct ivtv *itv = (struct ivtv *)arg; + + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + return; + IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h new file mode 100644 index 0000000..a43348a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.h @@ -0,0 +1,26 @@ +/* + interrupt handling + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id); + +void ivtv_irq_work_handler(struct work_struct *work); +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock); +void ivtv_unfinished_dma(unsigned long arg); diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c new file mode 100644 index 0000000..6ae42a3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -0,0 +1,360 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <stdarg.h> + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" + +/* Firmware mailbox flags*/ +#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 +#define IVTV_MBOX_DRIVER_DONE 0x00000002 +#define IVTV_MBOX_DRIVER_BUSY 0x00000001 +#define IVTV_MBOX_FREE 0x00000000 + +/* Firmware mailbox standard timeout */ +#define IVTV_API_STD_TIMEOUT 0x02000000 + +#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */ +#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */ +#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */ +#define API_DMA (1 << 3) /* DMA mailbox, has special handling */ +#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */ +#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */ + +struct ivtv_api_info { + int flags; /* Flags, see above */ + const char *name; /* The name of the command */ +}; + +#define API_ENTRY(x, f) [x] = { (f), #x } + +static const struct ivtv_api_info api_info[256] = { + /* MPEG encoder API */ + API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA), + API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT), + API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB), + API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE), + API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT), + API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT), + /* Obsolete PULLDOWN API command */ + API_ENTRY(0xb1, API_CACHE), + + /* MPEG decoder API */ + API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT), + API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA), + API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/), + API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE), + API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE), + API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE), + + /* OSD API */ + API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT), + API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE) +}; + +static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb) +{ + u32 flags = readl(&mbdata->mbox[mb].flags); + int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE); + + /* if the mailbox is free, then try to claim it */ + if (is_free && !test_and_set_bit(mb, &mbdata->busy)) { + write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags); + return 1; + } + return 0; +} + +/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not + attempted here. */ +static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags) +{ + unsigned long then = jiffies; + int i, mb; + int max_mbox = mbdata->max_mbox; + int retries = 100; + + /* All slow commands use the same mailbox, serializing them and also + leaving the other mailbox free for simple fast commands. */ + if ((flags & API_FAST_RESULT) == API_RESULT) + max_mbox = 1; + + /* find free non-DMA mailbox */ + for (i = 0; i < retries; i++) { + for (mb = 1; mb <= max_mbox; mb++) + if (try_mailbox(itv, mbdata, mb)) + return mb; + + /* Sleep before a retry, if not atomic */ + if (!(flags & API_NO_WAIT_MB)) { + if (jiffies - then > retries * HZ / 100) + break; + ivtv_sleep_timeout(HZ / 100, 0); + } + } + return -ENODEV; +} + +static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[]) +{ + int i; + + write_sync(cmd, &mbox->cmd); + write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + write_sync(data[i], &mbox->data[i]); + + write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags); +} + +static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata) +{ + int i; + + for (i = 0; i <= mbdata->max_mbox; i++) { + IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n", + i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags)); + write_sync(0, &mbdata->mbox[i].flags); + clear_bit(i, &mbdata->busy); + } +} + +static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; + volatile struct ivtv_mailbox __iomem *mbox; + int api_timeout = HZ; + int flags, mb, i; + unsigned long then; + + /* sanity checks */ + if (NULL == mbdata) { + IVTV_ERR("No mailbox allocated\n"); + return -ENODEV; + } + if (args < 0 || args > CX2341X_MBOX_MAX_DATA || + cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { + IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args); + return -EINVAL; + } + + IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name); + + /* clear possibly uninitialized part of data array */ + for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = 0; + + /* If this command was issued within the last 30 minutes and with identical + data, then just return 0 as there is no need to issue this command again. + Just an optimization to prevent unnecessary use of mailboxes. */ + if (itv->api_cache[cmd].last_jiffies && + jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 && + !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { + itv->api_cache[cmd].last_jiffies = jiffies; + return 0; + } + + flags = api_info[cmd].flags; + + if (flags & API_DMA) { + for (i = 0; i < 100; i++) { + mb = i % (mbdata->max_mbox + 1); + if (try_mailbox(itv, mbdata, mb)) { + write_mailbox(&mbdata->mbox[mb], cmd, args, data); + clear_bit(mb, &mbdata->busy); + return 0; + } + IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", + api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); + } + IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + + if ((flags & API_FAST_RESULT) == API_FAST_RESULT) + api_timeout = HZ / 10; + + mb = get_mailbox(itv, mbdata, flags); + if (mb < 0) { + IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + mbox = &mbdata->mbox[mb]; + write_mailbox(mbox, cmd, args, data); + if (flags & API_CACHE) { + memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); + itv->api_cache[cmd].last_jiffies = jiffies; + } + if ((flags & API_RESULT) == 0) { + clear_bit(mb, &mbdata->busy); + return 0; + } + + /* Get results */ + then = jiffies; + + while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { + if (jiffies - then > api_timeout) { + IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); + /* reset the mailbox, but it is likely too late already */ + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return -EIO; + } + if (flags & API_NO_WAIT_RES) + mdelay(1); + else + ivtv_sleep_timeout(HZ / 100, 0); + } + if (jiffies - then > HZ / 10) + IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n", + api_info[cmd].name, jiffies - then, HZ); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbox->data[i]); + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return 0; +} + +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + int res = ivtv_api_call(itv, cmd, args, data); + + /* Allow a single retry, probably already too late though. + If there is no free mailbox then that is usually an indication + of a more serious problem. */ + return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res; +} + +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + return ivtv_api(priv, cmd, in, data); +} + +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...) +{ + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +/* This one is for stuff that can't sleep.. irq handlers, etc.. */ +void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[]) +{ + int i; + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbdata->mbox[mb].data[i]); +} diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h new file mode 100644 index 0000000..79b8aec --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -0,0 +1,25 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]); +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c new file mode 100644 index 0000000..ccfcef1 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -0,0 +1,262 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes) +{ + if (s->buf_size - buf->bytesused < copybytes) + copybytes = s->buf_size - buf->bytesused; + if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) { + return -EFAULT; + } + buf->bytesused += copybytes; + return copybytes; +} + +void ivtv_buf_swap(struct ivtv_buffer *buf) +{ + int i; + + for (i = 0; i < buf->bytesused; i += 4) + swab32s((u32 *)(buf->buf + i)); +} + +void ivtv_queue_init(struct ivtv_queue *q) +{ + INIT_LIST_HEAD(&q->list); + q->buffers = 0; + q->length = 0; + q->bytesused = 0; +} + +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q) +{ + unsigned long flags = 0; + + /* clear the buffer if it is going to be enqueued to the free queue */ + if (q == &s->q_free) { + buf->bytesused = 0; + buf->readpos = 0; + buf->b_flags = 0; + } + spin_lock_irqsave(&s->qlock, flags); + list_add_tail(&buf->list, &q->list); + q->buffers++; + q->length += s->buf_size; + q->bytesused += buf->bytesused - buf->readpos; + spin_unlock_irqrestore(&s->qlock, flags); +} + +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q) +{ + struct ivtv_buffer *buf = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&s->qlock, flags); + if (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct ivtv_buffer, list); + list_del_init(q->list.next); + q->buffers--; + q->length -= s->buf_size; + q->bytesused -= buf->bytesused - buf->readpos; + } + spin_unlock_irqrestore(&s->qlock, flags); + return buf; +} + +static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from, + struct ivtv_queue *to, int clear, int full) +{ + struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list); + + list_move_tail(from->list.next, &to->list); + from->buffers--; + from->length -= s->buf_size; + from->bytesused -= buf->bytesused - buf->readpos; + /* special handling for q_free */ + if (clear) + buf->bytesused = buf->readpos = buf->b_flags = 0; + else if (full) { + /* special handling for stolen buffers, assume + all bytes are used. */ + buf->bytesused = s->buf_size; + buf->readpos = buf->b_flags = 0; + } + to->buffers++; + to->length += s->buf_size; + to->bytesused += buf->bytesused - buf->readpos; +} + +/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. + If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. + If 'steal' != NULL, then buffers may also taken from that queue if + needed. + + The buffer is automatically cleared if it goes to the free queue. It is + also cleared if buffers need to be taken from the 'steal' queue and + the 'from' queue is the free queue. + + When 'from' is q_free, then needed_bytes is compared to the total + available buffer length, otherwise needed_bytes is compared to the + bytesused value. For the 'steal' queue the total available buffer + length is always used. + + -ENOMEM is returned if the buffers could not be obtained, 0 if all + buffers where obtained from the 'from' list and if non-zero then + the number of stolen buffers is returned. */ +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes) +{ + unsigned long flags; + int rc = 0; + int from_free = from == &s->q_free; + int to_free = to == &s->q_free; + int bytes_available; + + spin_lock_irqsave(&s->qlock, flags); + if (needed_bytes == 0) { + from_free = 1; + needed_bytes = from->length; + } + + bytes_available = from_free ? from->length : from->bytesused; + bytes_available += steal ? steal->length : 0; + + if (bytes_available < needed_bytes) { + spin_unlock_irqrestore(&s->qlock, flags); + return -ENOMEM; + } + if (from_free) { + u32 old_length = to->length; + + while (to->length - old_length < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, 1, 0); + } + } + else { + u32 old_bytesused = to->bytesused; + + while (to->bytesused - old_bytesused < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, to_free, rc); + } + } + spin_unlock_irqrestore(&s->qlock, flags); + return rc; +} + +void ivtv_flush_queues(struct ivtv_stream *s) +{ + ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); +} + +int ivtv_stream_alloc(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + int SGsize = sizeof(struct ivtv_SG_element) * s->buffers; + int i; + + if (s->buffers == 0) + return 0; + + IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n", + s->dma != PCI_DMA_NONE ? "DMA " : "", + s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); + + /* Allocate DMA SG Arrays */ + if (s->dma != PCI_DMA_NONE) { + s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL); + if (s->SGarray == NULL) { + IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name); + return -ENOMEM; + } + s->SG_length = 0; + s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma); + ivtv_stream_sync_for_cpu(s); + } + + /* allocate stream buffers. Initially all buffers are in q_free. */ + for (i = 0; i < s->buffers; i++) { + struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL); + + if (buf == NULL) + break; + buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL); + if (buf->buf == NULL) { + kfree(buf); + break; + } + INIT_LIST_HEAD(&buf->list); + if (s->dma != PCI_DMA_NONE) { + buf->dma_handle = pci_map_single(s->itv->dev, + buf->buf, s->buf_size + 256, s->dma); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_enqueue(s, buf, &s->q_free); + } + if (i == s->buffers) + return 0; + IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name); + ivtv_stream_free(s); + return -ENOMEM; +} + +void ivtv_stream_free(struct ivtv_stream *s) +{ + struct ivtv_buffer *buf; + + /* move all buffers to q_free */ + ivtv_flush_queues(s); + + /* empty q_free */ + while ((buf = ivtv_dequeue(s, &s->q_free))) { + if (s->dma != PCI_DMA_NONE) + pci_unmap_single(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); + kfree(buf->buf); + kfree(buf); + } + + /* Free SG Array/Lists */ + if (s->SGarray != NULL) { + if (s->SG_handle != IVTV_DMA_UNMAPPED) { + pci_unmap_single(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); + s->SG_handle = IVTV_DMA_UNMAPPED; + } + s->SGarray = NULL; + s->SG_length = 0; + } +} diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h new file mode 100644 index 0000000..903edd4 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.h @@ -0,0 +1,64 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 IVTV_DMA_UNMAPPED ((u32) -1) + +/* ivtv_buffer utility functions */ +static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes); +void ivtv_buf_swap(struct ivtv_buffer *buf); + +/* ivtv_queue utility functions */ +void ivtv_queue_init(struct ivtv_queue *q); +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q); +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q); +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes); +void ivtv_flush_queues(struct ivtv_stream *s); + +/* ivtv_stream utility functions */ +int ivtv_stream_alloc(struct ivtv_stream *s); +void ivtv_stream_free(struct ivtv_stream *s); + +static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} + +static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c new file mode 100644 index 0000000..01a41a8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -0,0 +1,977 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* License: GPL + * Author: Kevin Thayer <nufan_wfk at yahoo dot com> + * + * This file will hold API related functions, both internal (firmware api) + * and external (v4l2, etc) + * + * ----- + * MPG600/MPG160 support by T.Adachi <tadachi@tadachi-net.com> + * and Takeru KOMORIYA<komoriya@paken.org> + * + * AVerMedia M179 GPIO info by Chris Pinkham <cpinkham@bc2va.org> + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-irq.h" +#include "ivtv-streams.h" +#include "ivtv-cards.h" + +static struct file_operations ivtv_v4l2_enc_fops = { + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_enc_poll, +}; + +static struct file_operations ivtv_v4l2_dec_fops = { + .owner = THIS_MODULE, + .read = ivtv_v4l2_read, + .write = ivtv_v4l2_write, + .open = ivtv_v4l2_open, + .ioctl = ivtv_v4l2_ioctl, + .release = ivtv_v4l2_close, + .poll = ivtv_v4l2_dec_poll, +}; + +static struct { + const char *name; + int vfl_type; + int minor_offset; + int dma, pio; + enum v4l2_buf_type buf_type; + struct file_operations *fops; +} ivtv_stream_info[] = { + { /* IVTV_ENC_STREAM_TYPE_MPG */ + "encoder MPEG", + VFL_TYPE_GRABBER, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_YUV */ + "encoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_VBI */ + "encoder VBI", + VFL_TYPE_VBI, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_PCM */ + "encoder PCM audio", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_RAD */ + "encoder radio", + VFL_TYPE_RADIO, 0, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_MPG */ + "decoder MPEG", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VBI */ + "decoder VBI", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VOUT */ + "decoder VOUT", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_YUV */ + "decoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + } +}; + +static void ivtv_stream_init(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + struct video_device *dev = s->v4l2dev; + + /* we need to keep v4l2dev, so restore it afterwards */ + memset(s, 0, sizeof(*s)); + s->v4l2dev = dev; + + /* initialize ivtv_stream fields */ + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + if (ivtv_stream_info[type].pio) + s->dma = PCI_DMA_NONE; + else + s->dma = ivtv_stream_info[type].dma; + s->buf_size = itv->stream_buf_size[type]; + if (s->buf_size) + s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size; + spin_lock_init(&s->qlock); + init_waitqueue_head(&s->waitq); + s->id = -1; + s->SG_handle = IVTV_DMA_UNMAPPED; + ivtv_queue_init(&s->q_free); + ivtv_queue_init(&s->q_full); + ivtv_queue_init(&s->q_dma); + ivtv_queue_init(&s->q_predma); + ivtv_queue_init(&s->q_io); +} + +static int ivtv_reg_dev(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + int vfl_type = ivtv_stream_info[type].vfl_type; + int minor_offset = ivtv_stream_info[type].minor_offset; + int minor; + + /* These four fields are always initialized. If v4l2dev == NULL, then + this stream is not in use. In that case no other fields but these + four can be used. */ + s->v4l2dev = NULL; + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + /* Check whether the radio is supported */ + if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO)) + return 0; + if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return 0; + + if (minor_offset >= 0) + /* card number + user defined offset + device offset */ + minor = itv->num + ivtv_first_minor + minor_offset; + else + minor = -1; + + /* User explicitly selected 0 buffers for these streams, so don't + create them. */ + if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE && + itv->options.megabytes[type] == 0) { + IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name); + return 0; + } + + ivtv_stream_init(itv, type); + + /* allocate and initialize the v4l2 video device structure */ + s->v4l2dev = video_device_alloc(); + if (s->v4l2dev == NULL) { + IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); + return -ENOMEM; + } + + s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | + VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + s->v4l2dev->type |= VID_TYPE_MPEG_DECODER; + } + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", + itv->num, s->name); + + s->v4l2dev->minor = minor; + s->v4l2dev->dev = &itv->dev->dev; + s->v4l2dev->fops = ivtv_stream_info[type].fops; + s->v4l2dev->release = video_device_release; + + if (minor >= 0) { + /* Register device. First try the desired minor, then any free one. */ + if (video_register_device(s->v4l2dev, vfl_type, minor) && + video_register_device(s->v4l2dev, vfl_type, -1)) { + IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n", + s->name, minor); + video_device_release(s->v4l2dev); + s->v4l2dev = NULL; + return -ENOMEM; + } + } + else { + /* Don't register a 'hidden' stream (OSD) */ + IVTV_INFO("Created framebuffer stream for %s\n", s->name); + return 0; + } + + switch (vfl_type) { + case VFL_TYPE_GRABBER: + IVTV_INFO("Registered device video%d for %s (%d MB)\n", + s->v4l2dev->minor, s->name, itv->options.megabytes[type]); + break; + case VFL_TYPE_RADIO: + IVTV_INFO("Registered device radio%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); + break; + case VFL_TYPE_VBI: + if (itv->options.megabytes[type]) + IVTV_INFO("Registered device vbi%d for %s (%d MB)\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, + s->name, itv->options.megabytes[type]); + else + IVTV_INFO("Registered device vbi%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name); + break; + } + return 0; +} + +/* Initialize v4l2 variables and register v4l2 devices */ +int ivtv_streams_setup(struct ivtv *itv) +{ + int type; + + /* Setup V4L2 Devices */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + /* Register Device */ + if (ivtv_reg_dev(itv, type)) + break; + + if (itv->streams[type].v4l2dev == NULL) + continue; + + /* Allocate Stream */ + if (ivtv_stream_alloc(&itv->streams[type])) + break; + } + if (type == IVTV_MAX_STREAMS) { + return 0; + } + + /* One or more streams could not be initialized. Clean 'em all up. */ + ivtv_streams_cleanup(itv); + return -ENOMEM; +} + +/* Unregister v4l2 devices */ +void ivtv_streams_cleanup(struct ivtv *itv) +{ + int type; + + /* Teardown all streams */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + struct video_device *vdev = itv->streams[type].v4l2dev; + + itv->streams[type].v4l2dev = NULL; + if (vdev == NULL) + continue; + + ivtv_stream_free(&itv->streams[type]); + /* Free Device */ + if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */ + video_device_release(vdev); + else /* All others, just unregister. */ + video_unregister_device(vdev); + } +} + +static void ivtv_vbi_setup(struct ivtv *itv) +{ + int raw = itv->vbi.sliced_in->service_set == 0; + u32 data[CX2341X_MBOX_MAX_DATA]; + int lines; + int i; + + /* If Embed then streamtype must be Program */ + /* TODO: should we really do this? */ + if (0 && !raw && itv->vbi.insert_mpeg) { + itv->params.stream_type = 0; + + /* assign stream type */ + ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type); + } + + /* Reset VBI */ + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); + + if (itv->is_60hz) { + itv->vbi.count = 12; + itv->vbi.start[0] = 10; + itv->vbi.start[1] = 273; + } else { /* PAL/SECAM */ + itv->vbi.count = 18; + itv->vbi.start[0] = 6; + itv->vbi.start[1] = 318; + } + + /* setup VBI registers */ + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + + /* determine number of lines and total number of VBI bytes. + A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 + The '- 1' byte is probably an unused U or V byte. Or something... + A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal + header, 42 data bytes + checksum (to be confirmed) */ + if (raw) { + lines = itv->vbi.count * 2; + } else { + lines = itv->is_60hz ? 24 : 38; + if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840)) + lines += 2; + } + + itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + + /* Note: sliced vs raw flag doesn't seem to have any effect + TODO: check mode (0x02) value with older ivtv versions. */ + data[0] = raw | 0x02 | (0xbd << 8); + + /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ + data[1] = 1; + /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ + data[2] = raw ? 4 : 8; + /* The start/stop codes determine which VBI lines end up in the raw VBI data area. + The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line + is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) + code. These values for raw VBI are obtained from a driver disassembly. The sliced + start/stop codes was deduced from this, but they do not appear in the driver. + Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54. + However, I have no idea what these values are for. */ + if (itv->hw_flags & IVTV_HW_CX25840) { + /* Setup VBI for the cx25840 digitizer */ + if (raw) { + data[3] = 0x20602060; + data[4] = 0x30703070; + } else { + data[3] = 0xB0F0B0F0; + data[4] = 0xA0E0A0E0; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + } else { + /* Setup VBI for the saa7115 digitizer */ + if (raw) { + data[3] = 0x25256262; + data[4] = 0x387F7F7F; + } else { + data[3] = 0xABABECEC; + data[4] = 0xB6F1F1F1; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = itv->vbi.enc_size / lines; + } + + IVTV_DEBUG_INFO( + "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n", + data[0], data[1], data[2], data[5], data[6]); + + ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data); + + /* returns the VBI encoder memory area. */ + itv->vbi.enc_start = data[2]; + itv->vbi.fpi = data[0]; + if (!itv->vbi.fpi) + itv->vbi.fpi = 1; + + IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n", + itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer); + + /* select VBI lines. + Note that the sliced argument seems to have no effect. */ + for (i = 2; i <= 24; i++) { + int valid; + + if (itv->is_60hz) { + valid = i >= 10 && i < 22; + } else { + valid = i >= 6 && i < 24; + } + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1, + valid, 0 , 0, 0); + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000, + valid, 0, 0, 0); + } + + /* Remaining VBI questions: + - Is it possible to select particular VBI lines only for inclusion in the MPEG + stream? Currently you can only get the first X lines. + - Is mixed raw and sliced VBI possible? + - What's the meaning of the raw/sliced flag? + - What's the meaning of params 2, 3 & 4 of the Select VBI command? */ +} + +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int captype = 0, subtype = 0; + int enable_passthrough = 0; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + captype = 0; + subtype = 3; + + /* Stop Passthrough */ + if (itv->output_mode == OUT_PASSTHROUGH) { + ivtv_passthrough_mode(itv, 0); + enable_passthrough = 1; + } + itv->mpg_data_received = itv->vbi_data_inserted = 0; + itv->dualwatch_jiffies = jiffies; + itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300; + itv->search_pack_header = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + if (itv->output_mode == OUT_PASSTHROUGH) { + captype = 2; + subtype = 11; /* video+audio+decoder */ + break; + } + captype = 1; + subtype = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + captype = 1; + subtype = 2; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + captype = 1; + subtype = 4; + + itv->vbi.frame = 0; + itv->vbi.inserted_frame = 0; + memset(itv->vbi.sliced_mpeg_size, + 0, sizeof(itv->vbi.sliced_mpeg_size)); + break; + default: + return -EINVAL; + } + s->subtype = subtype; + s->buffers_stolen = 0; + + /* mute/unmute video */ + ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0); + + /* Clear Streamoff flags in case left from last capture */ + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + if (atomic_read(&itv->capturing) == 0) { + /* Always use frame based mode. Experiments have demonstrated that byte + stream based mode results in dropped frames and corruption. Not often, + but occasionally. Many thanks go to Leonard Orb who spent a lot of + effort and time trying to trace the cause of the drop outs. */ + /* 1 frame per DMA */ + /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ + ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); + + /* Stuff from Windows, we don't know what it is */ + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); + /* According to the docs, this should be correct. However, this is + untested. I don't dare enable this without having tested it. + Only very few old cards actually have this hardware combination. + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, + ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); + */ + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + + /* assign placeholder */ + ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer); + + /* Setup VBI */ + if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { + ivtv_vbi_setup(itv); + } + + /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ + ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); + itv->pgm_info_offset = data[0]; + itv->pgm_info_num = data[1]; + itv->pgm_info_write_idx = 0; + itv->pgm_info_read_idx = 0; + + IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", + itv->pgm_info_offset, itv->pgm_info_num); + + /* Setup API for Stream */ + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Vsync Setup */ + if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* event notification (on) */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + + if (atomic_read(&itv->capturing) == 0) { + /* Clear all Pending Interrupts */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + clear_bit(IVTV_F_I_EOS, &itv->i_flags); + + /* Initialize Digitizer for Capture */ + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + } + + /* begin_capture */ + if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) + { + IVTV_DEBUG_WARN( "Error starting capture!\n"); + return -EINVAL; + } + + /* Start Passthrough */ + if (enable_passthrough) { + ivtv_passthrough_mode(itv, 1); + } + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->capturing); + return 0; +} + +static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int datatype; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); + + /* disable VBI signals, if the MPEG stream contains VBI data, + then that data will be processed automatically for you. */ + ivtv_disable_vbi(itv); + + /* set audio mode to left/stereo for dual/stereo mode. */ + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + + /* set number of internal decoder buffers */ + ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); + + /* prebuffering */ + ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); + + /* extract from user packets */ + ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); + itv->vbi.dec_start = data[0]; + + IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", + itv->vbi.dec_start, data[1]); + + /* set decoder source settings */ + /* Data type: 0 = mpeg from host, + 1 = yuv from encoder, + 2 = yuv_from_host */ + switch (s->type) { + case IVTV_DEC_STREAM_TYPE_YUV: + datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2; + IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); + break; + case IVTV_DEC_STREAM_TYPE_MPG: + default: + datatype = 0; + break; + } + if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, + itv->params.width, itv->params.height, itv->params.audio_properties)) { + IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n"); + } + return 0; +} + +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; /* already started */ + + IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); + + /* Clear Streamoff */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { + /* Initialize Decoder */ + /* Reprogram Decoder YUV Buffers for YUV */ + write_reg(yuv_offset[0] >> 4, 0x82c); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[0] >> 4, 0x834); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + + write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24); + + write_reg_sync(0x00108080, 0x2898); + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + } + + ivtv_setup_v4l2_decode_stream(s); + + /* set dma size to 65536 bytes */ + ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); + + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Zero out decoder counters */ + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); + + /* turn on notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + /* start playback */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); + + /* Clear the following Interrupt mask bits for decoding */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->decoding); + return 0; +} + +void ivtv_stop_all_captures(struct ivtv *itv) +{ + int i; + + for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL) + continue; + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + ivtv_stop_v4l2_encode_stream(s, 0); + } + } +} + +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) +{ + struct ivtv *itv = s->itv; + DECLARE_WAITQUEUE(wait, current); + int cap_type; + unsigned long then; + int stopmode; + u32 data[CX2341X_MBOX_MAX_DATA]; + + if (s->v4l2dev == NULL) + return -EINVAL; + + /* This function assumes that you are allowed to stop the capture + and that we are actually capturing */ + + IVTV_DEBUG_INFO("Stop Capture\n"); + + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) + return 0; + if (atomic_read(&itv->capturing) == 0) + return 0; + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_YUV: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_MPG: + default: + cap_type = 0; + break; + } + + /* Stop Capture Mode */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + stopmode = 0; + } else { + stopmode = 1; + } + + /* end_capture */ + /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); + + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->capturing) - 1 == 0) { + /* event notification (off) */ + if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* type: 0 = refresh */ + /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + } + + then = jiffies; + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + /* only run these if we're shutting down the last cap */ + unsigned long duration; + + then = jiffies; + add_wait_queue(&itv->cap_w, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + + /* wait 2s for EOS interrupt */ + while (!test_bit(IVTV_F_I_EOS, &itv->i_flags) && jiffies < then + 2 * HZ) { + schedule_timeout(HZ / 100); + } + + /* To convert jiffies to ms, we must multiply by 1000 + * and divide by HZ. To avoid runtime division, we + * convert this to multiplication by 1000/HZ. + * Since integer division truncates, we get the best + * accuracy if we do a rounding calculation of the constant. + * Think of the case where HZ is 1024. + */ + duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); + + if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { + IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); + IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); + } else { + IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&itv->cap_w, &wait); + } + + then = jiffies; + /* Make sure DMA is complete */ + add_wait_queue(&s->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + do { + /* check if DMA is pending */ + if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */ + (read_reg(IVTV_REG_DMASTATUS) & 0x02)) { + /* Check for last DMA */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0); + + if (data[0] == 1) { + IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]); + break; + } + } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) { + break; + } + + ivtv_sleep_timeout(HZ / 100, 1); + } while (then + HZ * 2 > jiffies); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->waitq, &wait); + } + + atomic_dec(&itv->capturing); + + /* Clear capture and no-read bits */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + + if (atomic_read(&itv->capturing) > 0) { + return 0; + } + + /* Set the following Interrupt mask bits for capture */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + wake_up(&s->waitq); + + return 0; +} + +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) + return -EINVAL; + + if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; + + IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags); + + /* Stop Decoder */ + if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { + u32 tmp = 0; + + /* Wait until the decoder is no longer running */ + if (pts) { + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, + 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); + } + while (1) { + u32 data[CX2341X_MBOX_MAX_DATA]; + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); + if (s->q_full.buffers + s->q_dma.buffers == 0) { + if (tmp == data[3]) + break; + tmp = data[3]; + } + if (ivtv_sleep_timeout(HZ/10, 1)) + break; + } + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); + + /* turn off notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_flush_queues(s); + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + /* disable VBI on TV-out */ + ivtv_disable_vbi(itv); + } + + /* decrement decoding */ + atomic_dec(&itv->decoding); + + set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); + wake_up(&itv->event_waitq); + + /* wake up wait queues */ + wake_up(&s->waitq); + + return 0; +} + +int ivtv_passthrough_mode(struct ivtv *itv, int enable) +{ + struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; + struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + + if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); + + /* Prevent others from starting/stopping streams while we + initiate/terminate passthrough mode */ + if (enable) { + if (itv->output_mode == OUT_PASSTHROUGH) { + return 0; + } + if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) + return -EBUSY; + + /* Fully initialize stream, and then unflag init */ + set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + + /* Setup YUV Decoder */ + ivtv_setup_v4l2_decode_stream(dec_stream); + + /* Start Decoder */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); + atomic_inc(&itv->decoding); + + /* Setup capture if not already done */ + if (atomic_read(&itv->capturing) == 0) { + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Start Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); + atomic_inc(&itv->capturing); + return 0; + } + + if (itv->output_mode != OUT_PASSTHROUGH) + return 0; + + /* Stop Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); + + atomic_dec(&itv->capturing); + atomic_dec(&itv->decoding); + clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + itv->output_mode = OUT_NONE; + + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h new file mode 100644 index 0000000..8597b75 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.h @@ -0,0 +1,31 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_streams_setup(struct ivtv *itv); +void ivtv_streams_cleanup(struct ivtv *itv); + +/* Capture related */ +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s); +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end); +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset); +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts); + +void ivtv_stop_all_captures(struct ivtv *itv); +int ivtv_passthrough_mode(struct ivtv *itv, int enable); diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c new file mode 100644 index 0000000..bd642e1 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -0,0 +1,200 @@ +/* + User DMA + + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-udma.h" + +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) +{ + dma_page->uaddr = first & PAGE_MASK; + dma_page->offset = first & ~PAGE_MASK; + dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK); + dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT; + dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT; + dma_page->page_count = dma_page->last - dma_page->first + 1; + if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset; +} + +int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) +{ + int i, offset; + + offset = dma_page->offset; + + /* Fill SG Array with new values */ + for (i = 0; i < dma_page->page_count; i++) { + if (i == dma_page->page_count - 1) { + dma->SGlist[map_offset].length = dma_page->tail; + } + else { + dma->SGlist[map_offset].length = PAGE_SIZE - offset; + } + dma->SGlist[map_offset].offset = offset; + dma->SGlist[map_offset].page = dma->map[map_offset]; + offset = 0; + map_offset++; + } + return map_offset; +} + +void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) { + int i; + struct scatterlist *sg; + + for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) { + dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg)); + dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg)); + dma->SGarray[i].dst = cpu_to_le32(buffer_offset); + buffer_offset += sg_dma_len(sg); + + split -= sg_dma_len(sg); + if (split == 0) + buffer_offset = buffer_offset_2; + } +} + +/* User DMA Buffers */ +void ivtv_udma_alloc(struct ivtv *itv) +{ + if (itv->udma.SG_handle == 0) { + /* Map DMA Page Array Buffer */ + itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + ivtv_udma_sync_for_cpu(itv); + } +} + +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes) +{ + struct ivtv_dma_page_info user_dma; + struct ivtv_user_dma *dma = &itv->udma; + int err; + + IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes); + + if (user_dma.page_count <= 0) { + IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n", + user_dma.page_count, size_in_bytes, user_dma.offset); + return -EINVAL; + } + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + err = get_user_pages(current, current->mm, + user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL); + up_read(¤t->mm->mmap_sem); + + if (user_dma.page_count != err) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + err, user_dma.page_count); + return -EINVAL; + } + + dma->page_count = user_dma.page_count; + + /* Fill SG List with new values */ + ivtv_udma_fill_sg_list(dma, &user_dma, 0); + + /* Map SG List */ + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return dma->page_count; +} + +void ivtv_udma_unmap(struct ivtv *itv) +{ + struct ivtv_user_dma *dma = &itv->udma; + int i; + + IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n"); + + /* Nothing to free */ + if (dma->page_count == 0) + return; + + /* Unmap Scatterlist */ + if (dma->SG_length) { + pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + dma->SG_length = 0; + } + /* sync DMA */ + ivtv_udma_sync_for_cpu(itv); + + /* Release User Pages */ + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; +} + +void ivtv_udma_free(struct ivtv *itv) +{ + /* Unmap SG Array */ + if (itv->udma.SG_handle) { + pci_unmap_single(itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + } + + /* Unmap Scatterlist */ + if (itv->udma.SG_length) { + pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); + } +} + +void ivtv_udma_start(struct ivtv *itv) +{ + IVTV_DEBUG_DMA("start UDMA\n"); + write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + set_bit(IVTV_F_I_UDMA, &itv->i_flags); +} + +void ivtv_udma_prepare(struct ivtv *itv) +{ + unsigned long flags; + + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_udma_start(itv); + else + set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h new file mode 100644 index 0000000..e131bcc --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> + Copyright (C) 2004 Chris Kennedy <c@groovy.org> + Copyright (C) 2006-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* User DMA functions */ +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size); +int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset); +void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split); +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes); +void ivtv_udma_unmap(struct ivtv *itv); +void ivtv_udma_free(struct ivtv *itv); +void ivtv_udma_alloc(struct ivtv *itv); +void ivtv_udma_prepare(struct ivtv *itv); +void ivtv_udma_start(struct ivtv *itv); + +static inline void ivtv_udma_sync_for_device(struct ivtv *itv) +{ + pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} + +static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) +{ + pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c new file mode 100644 index 0000000..5efa5a8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -0,0 +1,538 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-queue.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +static void passthrough_vbi_data(struct ivtv *itv, int cnt) +{ + int wss = 0; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + u8 vps[13]; + int found_cc = 0; + int found_wss = 0; + int found_vps = 0; + int cc_pos = itv->vbi.cc_pos; + int i; + + for (i = 0; i < cnt; i++) { + struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i; + + if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) { + found_cc = 1; + if (d->field) { + cc[2] = d->data[0]; + cc[3] = d->data[1]; + } else { + cc[0] = d->data[0]; + cc[1] = d->data[1]; + } + } + else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) { + memcpy(vps, d->data, sizeof(vps)); + found_vps = 1; + } + else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) { + wss = d->data[0] | d->data[1] << 8; + found_wss = 1; + } + } + + if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) { + itv->vbi.wss = wss; + itv->vbi.wss_found = found_wss; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + + if (found_vps || itv->vbi.vps_found) { + itv->vbi.vps[0] = vps[2]; + itv->vbi.vps[1] = vps[8]; + itv->vbi.vps[2] = vps[9]; + itv->vbi.vps[3] = vps[10]; + itv->vbi.vps[4] = vps[11]; + itv->vbi.vps_found = found_vps; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } +} + +static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) +{ + int line = 0; + int i; + u32 linemask[2] = { 0, 0 }; + unsigned short size; + static const u8 mpeg_hdr_data[] = { + 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, + 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, + 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + }; + const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ + int idx = itv->vbi.frame % IVTV_VBI_FRAMES; + u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0]; + + for (i = 0; i < lines; i++) { + int f, l; + + if (itv->vbi.sliced_data[i].id == 0) + continue; + + l = itv->vbi.sliced_data[i].line - 6; + f = itv->vbi.sliced_data[i].field; + if (f) + l += 18; + if (l < 32) + linemask[0] |= (1 << l); + else + linemask[1] |= (1 << (l - 32)); + dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id); + memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42); + line++; + } + memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); + if (line == 36) { + /* All lines are used, so there is no space for the linemask + (the max size of the VBI data is 36 * 43 + 4 bytes). + So in this case we use the magic number 'ITV0'. */ + memcpy(dst + sd, "ITV0", 4); + memcpy(dst + sd + 4, dst + sd + 12, line * 43); + size = 4 + ((43 * line + 3) & ~3); + } else { + memcpy(dst + sd, "itv0", 4); + memcpy(dst + sd + 4, &linemask[0], 8); + size = 12 + ((43 * line + 3) & ~3); + } + dst[4+16] = (size + 10) >> 8; + dst[5+16] = (size + 10) & 0xff; + dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); + dst[10+16] = (pts_stamp >> 22) & 0xff; + dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); + dst[12+16] = (pts_stamp >> 7) & 0xff; + dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); + itv->vbi.sliced_mpeg_size[idx] = sd + size; +} + +static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p) +{ + u32 linemask[2]; + int i, l, id2; + int line = 0; + + if (!memcmp(p, "itv0", 4)) { + memcpy(linemask, p + 4, 8); + p += 12; + } else if (!memcmp(p, "ITV0", 4)) { + linemask[0] = 0xffffffff; + linemask[1] = 0xf; + p += 4; + } else { + /* unknown VBI data stream */ + return 0; + } + for (i = 0; i < 36; i++) { + int err = 0; + + if (i < 32 && !(linemask[0] & (1 << i))) + continue; + if (i >= 32 && !(linemask[1] & (1 << (i - 32)))) + continue; + id2 = *p & 0xf; + switch (id2) { + case IVTV_SLICED_TYPE_TELETEXT_B: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case IVTV_SLICED_TYPE_CAPTION_525: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[1]) || !odd_parity(p[2]); + break; + case IVTV_SLICED_TYPE_VPS: + id2 = V4L2_SLICED_VPS; + break; + case IVTV_SLICED_TYPE_WSS_625: + id2 = V4L2_SLICED_WSS_625; + break; + default: + id2 = 0; + break; + } + if (err == 0) { + l = (i < 18) ? i + 6 : i - 18 + 6; + itv->vbi.sliced_dec_data[line].line = l; + itv->vbi.sliced_dec_data[line].field = i >= 18; + itv->vbi.sliced_dec_data[line].id = id2; + memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42); + line++; + } + p += 43; + } + while (line < 36) { + itv->vbi.sliced_dec_data[line].id = 0; + itv->vbi.sliced_dec_data[line].line = 0; + itv->vbi.sliced_dec_data[line].field = 0; + line++; + } + return line * sizeof(itv->vbi.sliced_dec_data[0]); +} + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count) +{ + /* Should be a __user pointer, but sparse doesn't parse this bit correctly. */ + const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + int found_cc = 0; + int cc_pos = itv->vbi.cc_pos; + + if (itv->vbi.service_set_out == 0) + return -EPERM; + + while (count >= sizeof(struct v4l2_sliced_vbi_data)) { + switch (p->id) { + case V4L2_SLICED_CAPTION_525: + if (p->id == V4L2_SLICED_CAPTION_525 && + p->line == 21 && + (itv->vbi.service_set_out & + V4L2_SLICED_CAPTION_525) == 0) { + break; + } + found_cc = 1; + if (p->field) { + cc[2] = p->data[0]; + cc[3] = p->data[1]; + } else { + cc[0] = p->data[0]; + cc[1] = p->data[1]; + } + break; + + case V4L2_SLICED_VPS: + if (p->line == 16 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_VPS)) { + itv->vbi.vps[0] = p->data[2]; + itv->vbi.vps[1] = p->data[8]; + itv->vbi.vps[2] = p->data[9]; + itv->vbi.vps[3] = p->data[10]; + itv->vbi.vps[4] = p->data[11]; + itv->vbi.vps_found = 1; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + break; + + case V4L2_SLICED_WSS_625: + if (p->line == 23 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) { + /* No lock needed for WSS */ + itv->vbi.wss = p->data[0] | (p->data[1] << 8); + itv->vbi.wss_found = 1; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + break; + + default: + break; + } + count -= sizeof(*p); + p++; + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } + + return (const char __user *)p - ubuf; +} + +/* Compress raw VBI format, removes leading SAV codes and surplus space after the + field. + Returns new compressed size. */ +static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size) +{ + u32 line_size = itv->vbi.raw_decoder_line_size; + u32 lines = itv->vbi.count; + u8 sav1 = itv->vbi.raw_decoder_sav_odd_field; + u8 sav2 = itv->vbi.raw_decoder_sav_even_field; + u8 *q = buf; + u8 *p; + int i; + + for (i = 0; i < lines; i++) { + p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) { + break; + } + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } + return lines * (line_size - 4); +} + + +/* Compressed VBI format, all found sliced blocks put next to one another + Returns new compressed size */ +static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav) +{ + u32 line_size = itv->vbi.sliced_decoder_line_size; + struct v4l2_decode_vbi_line vbi; + int i; + + /* find the first valid line */ + for (i = 0; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + break; + } + + size -= i; + if (size < line_size) { + return line; + } + for (i = 0; i < size / line_size; i++) { + u8 *p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) { + continue; + } + vbi.p = p + 4; + itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + if (vbi.type) { + itv->vbi.sliced_data[line].id = vbi.type; + itv->vbi.sliced_data[line].field = vbi.is_second_field; + itv->vbi.sliced_data[line].line = vbi.line; + memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42); + line++; + } + } + return line; +} + +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype) +{ + u8 *p = (u8 *) buf->buf; + u32 size = buf->bytesused; + int y; + + /* Raw VBI data */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) { + u8 type; + + ivtv_buf_swap(buf); + + type = p[3]; + + size = buf->bytesused = compress_raw_buf(itv, p, size); + + /* second field of the frame? */ + if (type == itv->vbi.raw_decoder_sav_even_field) { + /* Dirty hack needed for backwards + compatibility of old VBI software. */ + p += size - 4; + memcpy(p, &itv->vbi.frame, 4); + itv->vbi.frame++; + } + return; + } + + /* Sliced VBI data with data insertion */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) { + int lines; + + ivtv_buf_swap(buf); + + /* first field */ + lines = compress_sliced_buf(itv, 0, p, size / 2, + itv->vbi.sliced_decoder_sav_odd_field); + /* second field */ + /* experimentation shows that the second half does not always begin + at the exact address. So start a bit earlier (hence 32). */ + lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32, + itv->vbi.sliced_decoder_sav_even_field); + /* always return at least one empty line */ + if (lines == 0) { + itv->vbi.sliced_data[0].id = 0; + itv->vbi.sliced_data[0].line = 0; + itv->vbi.sliced_data[0].field = 0; + lines = 1; + } + buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]); + memcpy(p, &itv->vbi.sliced_data[0], size); + + if (itv->vbi.insert_mpeg) { + copy_vbi_data(itv, lines, pts_stamp); + } + itv->vbi.frame++; + return; + } + + /* Sliced VBI re-inserted from an MPEG stream */ + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + /* If the size is not 4-byte aligned, then the starting address + for the swapping is also shifted. After swapping the data the + real start address of the VBI data is exactly 4 bytes after the + original start. It's a bit fiddly but it works like a charm. + Non-4-byte alignment happens when an lseek is done on the input + mpeg file to a non-4-byte aligned position. So on arrival here + the VBI data is also non-4-byte aligned. */ + int offset = size & 3; + int cnt; + + if (offset) { + p += 4 - offset; + } + /* Swap Buffer */ + for (y = 0; y < size; y += 4) { + swab32s((u32 *)(p + y)); + } + + cnt = ivtv_convert_ivtv_vbi(itv, p + offset); + memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt); + buf->bytesused = cnt; + + passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0])); + return; + } +} + +void ivtv_disable_vbi(struct ivtv *itv) +{ + clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + ivtv_set_wss(itv, 0, 0); + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0); + itv->vbi.vps_found = itv->vbi.wss_found = 0; + itv->vbi.wss = 0; + itv->vbi.cc_pos = 0; +} + + +void vbi_work_handler(struct ivtv *itv) +{ + struct v4l2_sliced_vbi_data data; + + /* Lock */ + if (itv->output_mode == OUT_PASSTHROUGH) { + /* Note: currently only the saa7115 is used in a PVR350, + so these commands are for now saa7115 specific. */ + if (itv->is_50hz) { + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + ivtv_set_wss(itv, 1, data.data[0] & 0xf); + itv->vbi.wss_no_update = 0; + } else if (itv->vbi.wss_no_update == 4) { + ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */ + } else { + itv->vbi.wss_no_update++; + } + } + else { + u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0; + int mode = 0; + + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 1; + c1 = data.data[0]; + c2 = data.data[1]; + } + data.field = 1; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 2; + c3 = data.data[0]; + c4 = data.data[1]; + } + if (mode) { + itv->vbi.cc_no_update = 0; + ivtv_set_cc(itv, mode, c1, c2, c3, c4); + } else if (itv->vbi.cc_no_update == 4) { + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + } else { + itv->vbi.cc_no_update++; + } + } + return; + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) { + /* Lock */ + ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf); + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) { + if (itv->vbi.cc_pos == 0) { + ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80); + } + while (itv->vbi.cc_pos) { + u8 cc_odd0 = itv->vbi.cc_data_odd[0]; + u8 cc_odd1 = itv->vbi.cc_data_odd[1]; + u8 cc_even0 = itv->vbi.cc_data_even[0]; + u8 cc_even1 = itv->vbi.cc_data_even[1]; + + memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2); + memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2); + itv->vbi.cc_pos -= 2; + if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80) + continue; + + /* Send to Saa7127 */ + ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1); + if (itv->vbi.cc_pos == 0) + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + break; + } + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) { + /* Lock */ + ivtv_set_vps(itv, itv->vbi.vps_found, + itv->vbi.vps[0], itv->vbi.vps[1], + itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]); + } +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h new file mode 100644 index 0000000..cdaea69 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.h @@ -0,0 +1,26 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count); +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype); +int ivtv_used_line(struct ivtv *itv, int line, int field); +void ivtv_disable_vbi(struct ivtv *itv); +void ivtv_set_vbi(unsigned long arg); +void vbi_work_handler(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h new file mode 100644 index 0000000..85530a3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -0,0 +1,26 @@ +/* + ivtv driver version information + Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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 IVTV_DRIVER_NAME "ivtv" +#define IVTV_DRIVER_VERSION_MAJOR 1 +#define IVTV_DRIVER_VERSION_MINOR 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 + +#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) +#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c new file mode 100644 index 0000000..5858b19 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.c @@ -0,0 +1,142 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include <media/upd64031a.h> +#include <media/upd64083.h> + +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_VPS; + data.field = 0; + data.line = enabled ? 16 : 0; + data.data[4] = vps1; + data.data[10] = vps2; + data.data[11] = vps3; + data.data[12] = vps4; + data.data[13] = vps5; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + data.line = (mode & 1) ? 21 : 0; + data.data[0] = cc1; + data.data[1] = cc2; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + data.field = 1; + data.line = (mode & 2) ? 21 : 0; + data.data[0] = cc3; + data.data[1] = cc4; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + /* When using a 50 Hz system, always turn on the + wide screen signal with 4x3 ratio as the default. + Turning this signal on and off can confuse certain + TVs. As far as I can tell there is no reason not to + transmit this signal. */ + if ((itv->std & V4L2_STD_625_50) && !enabled) { + enabled = 1; + mode = 0x08; /* 4x3 full format */ + } + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + data.line = enabled ? 23 : 0; + data.data[0] = mode & 0xff; + data.data[1] = (mode >> 8) & 0xff; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_video_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + int inp = itv->active_input; + u32 type; + + route.input = itv->card->video_inputs[inp].video_input; + route.output = 0; + itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + type = itv->card->video_inputs[inp].video_type; + + if (type == IVTV_CARD_INPUT_VID_TUNER) { + route.input = 0; /* Tuner */ + } else if (type < IVTV_CARD_INPUT_COMPOSITE1) { + route.input = 2; /* S-Video */ + } else { + route.input = 1; /* Composite */ + } + + if (itv->card->hw_video & IVTV_HW_GPIO) + ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + if (itv->card->hw_video & IVTV_HW_UPD64031A) { + if (type == IVTV_CARD_INPUT_VID_TUNER || + type >= IVTV_CARD_INPUT_COMPOSITE1) { + /* Composite: GR on, connect to 3DYCS */ + route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; + } else { + /* S-Video: GR bypassed, turn it off */ + route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; + } + route.input |= itv->card->gr_config; + + ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } + + if (itv->card->hw_video & IVTV_HW_UPD6408X) { + route.input = UPD64083_YCS_MODE; + if (type > IVTV_CARD_INPUT_VID_TUNER && + type < IVTV_CARD_INPUT_COMPOSITE1) { + /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a + is not used. */ + route.input |= UPD64083_YCNR_MODE; + } + else if (itv->card->hw_video & IVTV_HW_UPD64031A) { + /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */ + if ((type == IVTV_CARD_INPUT_VID_TUNER)|| + (itv->card->type == IVTV_CARD_CX23416GYC)) { + route.input |= UPD64083_EXT_Y_ADC; + } + } + ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } +} diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h new file mode 100644 index 0000000..c8ade5d --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.h @@ -0,0 +1,24 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil <hverkuil@xs4all.nl> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode); +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4); +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5); +void ivtv_video_set_io(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c new file mode 100644 index 0000000..bcea0954 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -0,0 +1,1129 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-yuv.h" + +static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, + struct ivtv_dma_frame *args) +{ + struct ivtv_dma_page_info y_dma; + struct ivtv_dma_page_info uv_dma; + + int i; + int y_pages, uv_pages; + + unsigned long y_buffer_offset, uv_buffer_offset; + int y_decode_height, uv_decode_height, y_size; + int frame = atomic_read(&itv->yuv_info.next_fill_frame); + + y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame]; + uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; + + y_decode_height = uv_decode_height = args->src.height + args->src.top; + + if (y_decode_height < 512-16) + y_buffer_offset += 720 * 16; + + if (y_decode_height & 15) + y_decode_height = (y_decode_height + 16) & ~15; + + if (uv_decode_height & 31) + uv_decode_height = (uv_decode_height + 32) & ~31; + + y_size = 720 * y_decode_height; + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height); + ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL); + up_read(¤t->mm->mmap_sem); + + dma->page_count = y_dma.page_count + uv_dma.page_count; + + if (y_pages + uv_pages != dma->page_count) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + y_pages + uv_pages, dma->page_count); + + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; + return -EINVAL; + } + + /* Fill & map SG List */ + ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)); + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size); + + /* If we've offset the y plane, ensure top area is blanked */ + if (args->src.height + args->src.top < 512-16) { + if (itv->yuv_info.blanking_dmaptr) { + dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); + dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); + dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]); + dma->SG_length++; + } + } + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return 0; +} + +/* We rely on a table held in the firmware - Quick check. */ +int ivtv_yuv_filter_check(struct ivtv *itv) +{ + int i, offset_y, offset_uv; + + for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) { + if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) || + (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) { + IVTV_WARN ("YUV filter table not found in firmware.\n"); + return -1; + } + } + return 0; +} + +static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2) +{ + int filter_index, filter_line; + + /* If any filter is -1, then don't update it */ + if (h_filter > -1) { + if (h_filter > 4) h_filter = 4; + filter_index = h_filter * 384; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c); + filter_index += 8; + write_reg(0, 0x02818); + write_reg(0, 0x02830); + filter_line ++; + } + IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter); + } + + if (v_filter_1 > -1) { + if (v_filter_1 > 4) v_filter_1 = 4; + filter_index = v_filter_1 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904); + filter_index += 8; + write_reg(0, 0x02908); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1); + } + + if (v_filter_2 > -1) { + if (v_filter_2 > 4) v_filter_2 = 4; + filter_index = v_filter_2 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910); + filter_index += 8; + write_reg(0, 0x02914); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2); + } +} + +static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 reg_2834, reg_2838, reg_283c; + u32 reg_2844, reg_2854, reg_285c; + u32 reg_2864, reg_2874, reg_2890; + u32 reg_2870, reg_2870_base, reg_2870_offset; + int x_cutoff; + int h_filter; + u32 master_width; + + IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", + window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x); + + /* How wide is the src image */ + x_cutoff = window->src_w + window->src_x; + + /* Set the display width */ + reg_2834 = window->dst_w; + reg_2838 = reg_2834; + + /* Set the display position */ + reg_2890 = window->dst_x; + + /* Index into the image horizontally */ + reg_2870 = 0; + + /* 2870 is normally fudged to align video coords with osd coords. + If running full screen, it causes an unwanted left shift + Remove the fudge if we almost fill the screen. + Gradually adjust the offset to avoid the video 'snapping' + left/right if it gets dragged through this region. + Only do this if osd is full width. */ + if (window->vis_w == 720) { + if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){ + reg_2870 = 10 - (window->tru_x - window->pan_x) / 4; + } + else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) { + reg_2870 = (10 + (window->tru_x - window->pan_x) / 2); + } + + if (window->dst_w >= window->src_w) + reg_2870 = reg_2870 << 16 | reg_2870; + else + reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1); + } + + if (window->dst_w < window->src_w) + reg_2870 = 0x000d000e - reg_2870; + else + reg_2870 = 0x0012000e - reg_2870; + + /* We're also using 2870 to shift the image left (src_x & negative dst_x) */ + reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19; + + if (window->dst_w >= window->src_w) { + x_cutoff &= ~1; + master_width = (window->src_w * 0x00200000) / (window->dst_w); + if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 2; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + + /* We also need to factor in the scaling + (src_w - dst_w) / (src_w / 4) */ + if (window->dst_w > window->src_w) + reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14); + else + reg_2870_base = 0; + + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base); + reg_2874 = 0; + } + else if (window->dst_w < window->src_w / 2) { + master_width = (window->src_w * 0x00080000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset); + reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000012; + } + else { + master_width = (window->src_w * 0x00100000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1); + reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000001; + } + + /* Select the horizontal filter */ + if (window->src_w == window->dst_w) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((window->src_w << 16) / window->dst_w) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0 */ + if (h_filter == 0) h_filter = 1; + } + + write_reg(reg_2834, 0x02834); + write_reg(reg_2838, 0x02838); + IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838); + + write_reg(reg_283c, 0x0283c); + write_reg(reg_2844, 0x02844); + + IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844); + + write_reg(0x00080514, 0x02840); + write_reg(0x00100514, 0x02848); + IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514); + + write_reg(reg_2854, 0x02854); + IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854); + + write_reg(reg_285c, 0x0285c); + write_reg(reg_2864, 0x02864); + IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864); + + write_reg(reg_2874, 0x02874); + IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874); + + write_reg(reg_2870, 0x02870); + IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870); + + write_reg( reg_2890,0x02890); + IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890); + + /* Only update the filter if we really need to */ + if (h_filter != itv->yuv_info.h_filter) { + ivtv_yuv_filter (itv,h_filter,-1,-1); + itv->yuv_info.h_filter = h_filter; + } +} + +static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 master_height; + u32 reg_2918, reg_291c, reg_2920, reg_2928; + u32 reg_2930, reg_2934, reg_293c; + u32 reg_2940, reg_2944, reg_294c; + u32 reg_2950, reg_2954, reg_2958, reg_295c; + u32 reg_2960, reg_2964, reg_2968, reg_296c; + u32 reg_289c; + u32 src_y_major_y, src_y_minor_y; + u32 src_y_major_uv, src_y_minor_uv; + u32 reg_2964_base, reg_2968_base; + int v_filter_1, v_filter_2; + + IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", + window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y); + + /* What scaling mode is being used... */ + if (window->interlaced_y) { + IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n"); + } + + if (window->interlaced_uv) { + IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n"); + } + + /* What is the source video being treated as... */ + if (itv->yuv_info.frame_interlaced) { + IVTV_DEBUG_WARN("Source video: Interlaced\n"); + } + else { + IVTV_DEBUG_WARN("Source video: Non-interlaced\n"); + } + + /* We offset into the image using two different index methods, so split + the y source coord into two parts. */ + if (window->src_y < 8) { + src_y_minor_uv = window->src_y; + src_y_major_uv = 0; + } + else { + src_y_minor_uv = 8; + src_y_major_uv = window->src_y - 8; + } + + src_y_minor_y = src_y_minor_uv; + src_y_major_y = src_y_major_uv; + + if (window->offset_y) src_y_minor_y += 16; + + if (window->interlaced_y) + reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y); + else + reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1); + + if (window->interlaced_uv) + reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1); + else + reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv); + + reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14; + reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14; + + if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++; + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 3; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_2964_base >>= 3; + reg_2968_base >>= 3; + reg_296c = 0x00000000; + } + else if (window->dst_h >= window->src_h) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_296c = 0x00000000; + if (window->interlaced_y) { + reg_2964_base >>= 3; + } + else { + reg_296c ++; + reg_2964_base >>= 2; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 3; + } + else if (window->dst_h >= window->src_h / 2) { + master_height = (window->src_h * 0x00200000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_296c = 0x00000101; + if (window->interlaced_y) { + reg_2964_base >>= 2; + } + else { + reg_296c ++; + reg_2964_base >>= 1; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 2; + } + else { + master_height = (window->src_h * 0x00100000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_2964_base >>= 1; + reg_2968_base >>= 2; + reg_296c = 0x00000102; + } + + /* FIXME These registers change depending on scaled / unscaled output + We really need to work out what they should be */ + if (window->src_h == window->dst_h){ + reg_2934 = 0x00020000; + reg_293c = 0x00100000; + reg_2944 = 0x00040000; + reg_294c = 0x000b0000; + } + else { + reg_2934 = 0x00000FF0; + reg_293c = 0x00000FF0; + reg_2944 = 0x00000FF0; + reg_294c = 0x00000FF0; + } + + /* The first line to be displayed */ + reg_2950 = 0x00010000 + src_y_major_y; + if (window->interlaced_y) reg_2950 += 0x00010000; + reg_2954 = reg_2950 + 1; + + reg_2958 = 0x00010000 + (src_y_major_y >> 1); + if (window->interlaced_uv) reg_2958 += 0x00010000; + reg_295c = reg_2958 + 1; + + if (itv->yuv_info.decode_height == 480) + reg_289c = 0x011e0017; + else + reg_289c = 0x01500017; + + if (window->dst_y < 0) + reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1); + else + reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1); + + /* How much of the source to decode. + Take into account the source offset */ + reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) | + ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15); + + /* Calculate correct value for register 2964 */ + if (window->src_h == window->dst_h) + reg_2964 = 1; + else { + reg_2964 = 2 + ((window->dst_h << 1) / window->src_h); + reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); + } + reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); + reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); + + /* Okay, we've wasted time working out the correct value, + but if we use it, it fouls the the window alignment. + Fudge it to what we want... */ + reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); + reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); + + /* Deviate further from what it should be. I find the flicker headache + inducing so try to reduce it slightly. Leave 2968 as-is otherwise + colours foul. */ + if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h)) + reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2); + + if (!window->interlaced_y) reg_2964 -= 0x00010001; + if (!window->interlaced_uv) reg_2968 -= 0x00010001; + + reg_2964 += ((reg_2964_base << 16) | reg_2964_base); + reg_2968 += ((reg_2968_base << 16) | reg_2968_base); + + /* Select the vertical filter */ + if (window->src_h == window->dst_h) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + write_reg(reg_2934, 0x02934); + write_reg(reg_293c, 0x0293c); + IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c); + write_reg(reg_2944, 0x02944); + write_reg(reg_294c, 0x0294c); + IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c); + + /* Ensure 2970 is 0 (does it ever change ?) */ +/* write_reg(0,0x02970); */ +/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */ + + write_reg(reg_2930, 0x02938); + write_reg(reg_2930, 0x02930); + IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930); + + write_reg(reg_2928, 0x02928); + write_reg(reg_2928+0x514, 0x0292C); + IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514); + + write_reg(reg_2920, 0x02920); + write_reg(reg_2920+0x514, 0x02924); + IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920); + + write_reg (reg_2918,0x02918); + write_reg (reg_291c,0x0291C); + IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c); + + write_reg(reg_296c, 0x0296c); + IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c); + + write_reg(reg_2940, 0x02948); + write_reg(reg_2940, 0x02940); + IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940); + + write_reg(reg_2950, 0x02950); + write_reg(reg_2954, 0x02954); + IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954); + + write_reg(reg_2958, 0x02958); + write_reg(reg_295c, 0x0295C); + IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c); + + write_reg(reg_2960, 0x02960); + IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960); + + write_reg(reg_2964, 0x02964); + write_reg(reg_2968, 0x02968); + IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968); + + write_reg( reg_289c,0x0289c); + IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c); + + /* Only update filter 1 if we really need to */ + if (v_filter_1 != itv->yuv_info.v_filter_1) { + ivtv_yuv_filter (itv,-1,v_filter_1,-1); + itv->yuv_info.v_filter_1 = v_filter_1; + } + + /* Only update filter 2 if we really need to */ + if (v_filter_2 != itv->yuv_info.v_filter_2) { + ivtv_yuv_filter (itv,-1,-1,v_filter_2); + itv->yuv_info.v_filter_2 = v_filter_2; + } + + itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced; +} + +/* Modify the supplied coordinate information to fit the visible osd area */ +static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window) +{ + int osd_crop, lace_threshold; + u32 osd_scale; + u32 yuv_update = 0; + + lace_threshold = itv->yuv_info.lace_threshold; + if (lace_threshold < 0) + lace_threshold = itv->yuv_info.decode_height - 1; + + /* Work out the lace settings */ + switch (itv->yuv_info.lace_mode) { + case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ + itv->yuv_info.frame_interlaced = 0; + if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + break; + + case IVTV_YUV_MODE_AUTO: + if (window->tru_h <= lace_threshold || window->tru_h > 576 || window->tru_w > 720){ + itv->yuv_info.frame_interlaced = 0; + if ((window->tru_h < 512) || + (window->tru_h > 576 && window->tru_h < 1021) || + (window->tru_w > 720 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + } + else { + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + } + break; + + case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ + default: + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + break; + } + + /* Sorry, but no negative coords for src */ + if (window->src_x < 0) window->src_x = 0; + if (window->src_y < 0) window->src_y = 0; + + /* Can only reduce width down to 1/4 original size */ + if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) { + window->src_x += osd_crop / 2; + window->src_w = (window->src_w - osd_crop) & ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + + /* Can only reduce height down to 1/4 original size */ + if (window->src_h / window->dst_h >= 2) { + /* Overflow may be because we're running progressive, so force mode switch */ + window->interlaced_y = 1; + /* Make sure we're still within limits for interlace */ + if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) { + /* If we reach here we'll have to force the height. */ + window->src_y += osd_crop / 2; + window->src_h = (window->src_h - osd_crop) & ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + } + + /* If there's nothing to safe to display, we may as well stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Ensure video remains inside OSD area */ + osd_scale = (window->src_h << 16) / window->dst_h; + + if ((osd_crop = window->pan_y - window->dst_y) > 0) { + /* Falls off the upper edge - crop */ + window->src_y += (osd_scale * osd_crop) >> 16; + window->src_h -= (osd_scale * osd_crop) >> 16; + window->dst_h -= osd_crop; + window->dst_y = 0; + } + else { + window->dst_y -= window->pan_y; + } + + if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) { + /* Falls off the lower edge - crop */ + window->dst_h -= osd_crop; + window->src_h -= (osd_scale * osd_crop) >> 16; + } + + osd_scale = (window->src_w << 16) / window->dst_w; + + if ((osd_crop = window->pan_x - window->dst_x) > 0) { + /* Fall off the left edge - crop */ + window->src_x += (osd_scale * osd_crop) >> 16; + window->src_w -= (osd_scale * osd_crop) >> 16; + window->dst_w -= osd_crop; + window->dst_x = 0; + } + else { + window->dst_x -= window->pan_x; + } + + if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) { + /* Falls off the right edge - crop */ + window->dst_w -= osd_crop; + window->src_w -= (osd_scale * osd_crop) >> 16; + } + + /* The OSD can be moved. Track to it */ + window->dst_x += itv->yuv_info.osd_x_offset; + window->dst_y += itv->yuv_info.osd_y_offset; + + /* Width & height for both src & dst must be even. + Same for coordinates. */ + window->dst_w &= ~1; + window->dst_x &= ~1; + + window->src_w += window->src_x & 1; + window->src_x &= ~1; + + window->src_w &= ~1; + window->dst_w &= ~1; + + window->dst_h &= ~1; + window->dst_y &= ~1; + + window->src_h += window->src_y & 1; + window->src_y &= ~1; + + window->src_h &= ~1; + window->dst_h &= ~1; + + /* Due to rounding, we may have reduced the output size to <1/4 of the source + Check again, but this time just resize. Don't change source coordinates */ + if (window->dst_w < window->src_w / 4) { + window->src_w &= ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + if (window->dst_h < window->src_h / 4) { + window->src_h &= ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + + /* Check again. If there's nothing to safe to display, stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Both x offset & width are linked, so they have to be done together */ + if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) || + (itv->yuv_info.old_frame_info.src_w != window->src_w) || + (itv->yuv_info.old_frame_info.dst_x != window->dst_x) || + (itv->yuv_info.old_frame_info.src_x != window->src_x) || + (itv->yuv_info.old_frame_info.pan_x != window->pan_x) || + (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) { + yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; + } + + if ((itv->yuv_info.old_frame_info.src_h != window->src_h) || + (itv->yuv_info.old_frame_info.dst_h != window->dst_h) || + (itv->yuv_info.old_frame_info.dst_y != window->dst_y) || + (itv->yuv_info.old_frame_info.src_y != window->src_y) || + (itv->yuv_info.old_frame_info.pan_y != window->pan_y) || + (itv->yuv_info.old_frame_info.vis_h != window->vis_h) || + (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) || + (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) { + yuv_update |= IVTV_YUV_UPDATE_VERTICAL; + } + + return yuv_update; +} + +/* Update the scaling register to the requested value */ +void ivtv_yuv_work_handler (struct ivtv *itv) +{ + struct yuv_frame_info window; + u32 yuv_update; + + int frame = itv->yuv_info.update_frame; + +/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */ + memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window)); + + /* Update the osd pan info */ + window.pan_x = itv->yuv_info.osd_x_pan; + window.pan_y = itv->yuv_info.osd_y_pan; + window.vis_w = itv->yuv_info.osd_vis_w; + window.vis_h = itv->yuv_info.osd_vis_h; + + /* Calculate the display window coordinates. Exit if nothing left */ + if (!(yuv_update = ivtv_yuv_window_setup (itv, &window))) + return; + + /* Update horizontal settings */ + if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) + ivtv_yuv_handle_horizontal(itv, &window); + + if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) + ivtv_yuv_handle_vertical(itv, &window); + + memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info)); +} + +static void ivtv_yuv_init (struct ivtv *itv) +{ + IVTV_DEBUG_YUV("ivtv_yuv_init\n"); + + /* Take a snapshot of the current register settings */ + itv->yuv_info.reg_2834 = read_reg(0x02834); + itv->yuv_info.reg_2838 = read_reg(0x02838); + itv->yuv_info.reg_283c = read_reg(0x0283c); + itv->yuv_info.reg_2840 = read_reg(0x02840); + itv->yuv_info.reg_2844 = read_reg(0x02844); + itv->yuv_info.reg_2848 = read_reg(0x02848); + itv->yuv_info.reg_2854 = read_reg(0x02854); + itv->yuv_info.reg_285c = read_reg(0x0285c); + itv->yuv_info.reg_2864 = read_reg(0x02864); + itv->yuv_info.reg_2870 = read_reg(0x02870); + itv->yuv_info.reg_2874 = read_reg(0x02874); + itv->yuv_info.reg_2898 = read_reg(0x02898); + itv->yuv_info.reg_2890 = read_reg(0x02890); + + itv->yuv_info.reg_289c = read_reg(0x0289c); + itv->yuv_info.reg_2918 = read_reg(0x02918); + itv->yuv_info.reg_291c = read_reg(0x0291c); + itv->yuv_info.reg_2920 = read_reg(0x02920); + itv->yuv_info.reg_2924 = read_reg(0x02924); + itv->yuv_info.reg_2928 = read_reg(0x02928); + itv->yuv_info.reg_292c = read_reg(0x0292c); + itv->yuv_info.reg_2930 = read_reg(0x02930); + itv->yuv_info.reg_2934 = read_reg(0x02934); + itv->yuv_info.reg_2938 = read_reg(0x02938); + itv->yuv_info.reg_293c = read_reg(0x0293c); + itv->yuv_info.reg_2940 = read_reg(0x02940); + itv->yuv_info.reg_2944 = read_reg(0x02944); + itv->yuv_info.reg_2948 = read_reg(0x02948); + itv->yuv_info.reg_294c = read_reg(0x0294c); + itv->yuv_info.reg_2950 = read_reg(0x02950); + itv->yuv_info.reg_2954 = read_reg(0x02954); + itv->yuv_info.reg_2958 = read_reg(0x02958); + itv->yuv_info.reg_295c = read_reg(0x0295c); + itv->yuv_info.reg_2960 = read_reg(0x02960); + itv->yuv_info.reg_2964 = read_reg(0x02964); + itv->yuv_info.reg_2968 = read_reg(0x02968); + itv->yuv_info.reg_296c = read_reg(0x0296c); + itv->yuv_info.reg_2970 = read_reg(0x02970); + + itv->yuv_info.v_filter_1 = -1; + itv->yuv_info.v_filter_2 = -1; + itv->yuv_info.h_filter = -1; + + /* Set some valid size info */ + itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF; + itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF; + + /* Bit 2 of reg 2878 indicates current decoder output format + 0 : NTSC 1 : PAL */ + if (read_reg(0x2878) & 4) + itv->yuv_info.decode_height = 576; + else + itv->yuv_info.decode_height = 480; + + /* If no visible size set, assume full size */ + if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset; + if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset; + + /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ + itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL); + if (itv->yuv_info.blanking_ptr) { + itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE); + } + else { + itv->yuv_info.blanking_dmaptr = 0; + IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n"); + } + + IVTV_DEBUG_WARN("Enable video output\n"); + write_reg_sync(0x00108080, 0x2898); + + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + + set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); + atomic_set(&itv->yuv_info.next_dma_frame,0); +} + +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + DEFINE_WAIT(wait); + int rc = 0; + int got_sig = 0; + int frame, next_fill_frame, last_fill_frame; + + IVTV_DEBUG_INFO("yuv_prep_frame\n"); + + if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv); + + frame = atomic_read(&itv->yuv_info.next_fill_frame); + next_fill_frame = (frame + 1) & 0x3; + last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3; + + if (next_fill_frame != last_fill_frame && last_fill_frame != frame) { + /* Buffers are full - Overwrite the last frame */ + next_fill_frame = frame; + frame = (frame - 1) & 3; + } + + /* Take a snapshot of the yuv coordinate information */ + itv->yuv_info.new_frame_info[frame].src_x = args->src.left; + itv->yuv_info.new_frame_info[frame].src_y = args->src.top; + itv->yuv_info.new_frame_info[frame].src_w = args->src.width; + itv->yuv_info.new_frame_info[frame].src_h = args->src.height; + itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top; + itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width; + itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height; + itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].tru_w = args->src_width; + itv->yuv_info.new_frame_info[frame].tru_h = args->src_height; + + /* Are we going to offset the Y plane */ + if (args->src.height + args->src.top < 512-16) + itv->yuv_info.new_frame_info[frame].offset_y = 1; + else + itv->yuv_info.new_frame_info[frame].offset_y = 0; + + /* Snapshot the osd pan info */ + itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan; + itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan; + itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w; + itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h; + + itv->yuv_info.new_frame_info[frame].update = 0; + itv->yuv_info.new_frame_info[frame].interlaced_y = 0; + itv->yuv_info.new_frame_info[frame].interlaced_uv = 0; + + if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], + sizeof (itv->yuv_info.new_frame_info[frame]))) { + memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args)); + itv->yuv_info.new_frame_info[frame].update = 1; +/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + } + + /* DMA the frame */ + mutex_lock(&itv->udma.lock); + + if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) { + mutex_unlock(&itv->udma.lock); + return rc; + } + + ivtv_udma_prepare(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + /* if no UDMA is pending and no UDMA is in progress, then the DMA + is finished */ + while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + /* don't interrupt if the DMA is in progress but break off + a still pending DMA. */ + got_sig = signal_pending(current); + if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + + /* Unmap Last DMA Xfer */ + ivtv_udma_unmap(itv); + + if (got_sig) { + IVTV_DEBUG_INFO("User stopped YUV UDMA\n"); + mutex_unlock(&itv->udma.lock); + return -EINTR; + } + + atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame); + + mutex_unlock(&itv->udma.lock); + return rc; +} + +void ivtv_yuv_close(struct ivtv *itv) +{ + int h_filter, v_filter_1, v_filter_2; + + IVTV_DEBUG_YUV("ivtv_yuv_close\n"); + ivtv_waitq(&itv->vsync_waitq); + + atomic_set(&itv->yuv_info.next_dma_frame, -1); + atomic_set(&itv->yuv_info.next_fill_frame, 0); + + /* Reset registers we have changed so mpeg playback works */ + + /* If we fully restore this register, the display may remain active. + Restore, but set one bit to blank the video. Firmware will always + clear this bit when needed, so not a problem. */ + write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898); + + write_reg(itv->yuv_info.reg_2834, 0x02834); + write_reg(itv->yuv_info.reg_2838, 0x02838); + write_reg(itv->yuv_info.reg_283c, 0x0283c); + write_reg(itv->yuv_info.reg_2840, 0x02840); + write_reg(itv->yuv_info.reg_2844, 0x02844); + write_reg(itv->yuv_info.reg_2848, 0x02848); + write_reg(itv->yuv_info.reg_2854, 0x02854); + write_reg(itv->yuv_info.reg_285c, 0x0285c); + write_reg(itv->yuv_info.reg_2864, 0x02864); + write_reg(itv->yuv_info.reg_2870, 0x02870); + write_reg(itv->yuv_info.reg_2874, 0x02874); + write_reg(itv->yuv_info.reg_2890, 0x02890); + write_reg(itv->yuv_info.reg_289c, 0x0289c); + + write_reg(itv->yuv_info.reg_2918, 0x02918); + write_reg(itv->yuv_info.reg_291c, 0x0291c); + write_reg(itv->yuv_info.reg_2920, 0x02920); + write_reg(itv->yuv_info.reg_2924, 0x02924); + write_reg(itv->yuv_info.reg_2928, 0x02928); + write_reg(itv->yuv_info.reg_292c, 0x0292c); + write_reg(itv->yuv_info.reg_2930, 0x02930); + write_reg(itv->yuv_info.reg_2934, 0x02934); + write_reg(itv->yuv_info.reg_2938, 0x02938); + write_reg(itv->yuv_info.reg_293c, 0x0293c); + write_reg(itv->yuv_info.reg_2940, 0x02940); + write_reg(itv->yuv_info.reg_2944, 0x02944); + write_reg(itv->yuv_info.reg_2948, 0x02948); + write_reg(itv->yuv_info.reg_294c, 0x0294c); + write_reg(itv->yuv_info.reg_2950, 0x02950); + write_reg(itv->yuv_info.reg_2954, 0x02954); + write_reg(itv->yuv_info.reg_2958, 0x02958); + write_reg(itv->yuv_info.reg_295c, 0x0295c); + write_reg(itv->yuv_info.reg_2960, 0x02960); + write_reg(itv->yuv_info.reg_2964, 0x02964); + write_reg(itv->yuv_info.reg_2968, 0x02968); + write_reg(itv->yuv_info.reg_296c, 0x0296c); + write_reg(itv->yuv_info.reg_2970, 0x02970); + + /* Prepare to restore filters */ + + /* First the horizontal filter */ + if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0. */ + if (h_filter < 1) h_filter = 1; + } + + /* Now the vertical filter */ + if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + /* Now restore the filters */ + ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2); + + /* and clear a few registers */ + write_reg(0, 0x02814); + write_reg(0, 0x0282c); + write_reg(0, 0x02904); + write_reg(0, 0x02910); + + /* Release the blanking buffer */ + if (itv->yuv_info.blanking_ptr) { + kfree (itv->yuv_info.blanking_ptr); + itv->yuv_info.blanking_ptr = NULL; + pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); + } + + /* Invalidate the old dimension information */ + itv->yuv_info.old_frame_info.src_w = 0; + itv->yuv_info.old_frame_info.src_h = 0; + itv->yuv_info.old_frame_info_args.src_w = 0; + itv->yuv_info.old_frame_info_args.src_h = 0; + + /* All done. */ + clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); +} + diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h new file mode 100644 index 0000000..88972d3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.h @@ -0,0 +1,24 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong <ian@iarmst.demon.co.uk> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_yuv_filter_check(struct ivtv *itv); +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); +void ivtv_yuv_close(struct ivtv *itv); +void ivtv_yuv_work_handler (struct ivtv *itv); diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index 98681da..664aba8 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -925,13 +925,13 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, if (p->palette != VIDEO_PALETTE_YUV422 && p->palette != VIDEO_PALETTE_YUYV) return -EINVAL; mutex_lock(&meye.lock); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, p->brightness >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, p->hue >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, p->colour >> 10); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, p->contrast >> 10); meye.picture = *p; mutex_unlock(&meye.lock); @@ -1043,11 +1043,11 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, meye.params.quality != jp->quality) mchip_hic_stop(); /* need restart */ meye.params = *jp; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, meye.params.sharpness); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, meye.params.agc); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, meye.params.picture); mutex_unlock(&meye.lock); break; @@ -1287,38 +1287,38 @@ static int meye_do_ioctl(struct inode *inode, struct file *file, mutex_lock(&meye.lock); switch (c->id) { case V4L2_CID_BRIGHTNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERABRIGHTNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, c->value); meye.picture.brightness = c->value << 10; break; case V4L2_CID_HUE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAHUE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAHUE, c->value); meye.picture.hue = c->value << 10; break; case V4L2_CID_CONTRAST: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACONTRAST, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACONTRAST, c->value); meye.picture.contrast = c->value << 10; break; case V4L2_CID_SATURATION: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERACOLOR, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERACOLOR, c->value); meye.picture.colour = c->value << 10; break; case V4L2_CID_AGC: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAAGC, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAAGC, c->value); meye.params.agc = c->value; break; case V4L2_CID_SHARPNESS: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERASHARPNESS, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERASHARPNESS, c->value); meye.params.sharpness = c->value; break; case V4L2_CID_PICTURE: - sonypi_camera_command( - SONYPI_COMMAND_SETCAMERAPICTURE, c->value); + sony_pic_camera_command( + SONY_PIC_COMMAND_SETCAMERAPICTURE, c->value); meye.params.picture = c->value; break; case V4L2_CID_JPEGQUAL: @@ -1848,7 +1848,7 @@ static int __devinit meye_probe(struct pci_dev *pcidev, memcpy(meye.video_dev, &meye_template, sizeof(meye_template)); meye.video_dev->dev = &meye.mchip_dev->dev; - if ((ret = sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 1))) { + if ((ret = sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 1))) { printk(KERN_ERR "meye: unable to power on the camera\n"); printk(KERN_ERR "meye: did you enable the camera in " "sonypi using the module options ?\n"); @@ -1928,13 +1928,13 @@ static int __devinit meye_probe(struct pci_dev *pcidev, meye.params.picture = 0; meye.params.framerate = 0; - sonypi_camera_command(SONYPI_COMMAND_SETCAMERABRIGHTNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAHUE, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACOLOR, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERACONTRAST, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERASHARPNESS, 32); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAPICTURE, 0); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERAAGC, 48); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERABRIGHTNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAHUE, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACOLOR, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERACONTRAST, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERASHARPNESS, 32); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAPICTURE, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERAAGC, 48); printk(KERN_INFO "meye: Motion Eye Camera Driver v%s.\n", MEYE_DRIVER_VERSION); @@ -1953,7 +1953,7 @@ outremap: outregions: pci_disable_device(meye.mchip_dev); outenabledev: - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); outsonypienable: kfifo_free(meye.doneq); outkfifoalloc2: @@ -1986,7 +1986,7 @@ static void __devexit meye_remove(struct pci_dev *pcidev) pci_disable_device(meye.mchip_dev); - sonypi_camera_command(SONYPI_COMMAND_SETCAMERA, 0); + sony_pic_camera_command(SONY_PIC_COMMAND_SETCAMERA, 0); kfifo_free(meye.doneq); kfifo_free(meye.grabq); diff --git a/drivers/media/video/meye.h b/drivers/media/video/meye.h index ea107cb..323d007 100644 --- a/drivers/media/video/meye.h +++ b/drivers/media/video/meye.h @@ -255,7 +255,7 @@ /****************************************************************************/ /* Sony Programmable I/O Controller for accessing the camera commands */ -#include <linux/sonypi.h> +#include <linux/sony-laptop.h> /* private API definitions */ #include <linux/meye.h> diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 2fb9fe6..3bb7d66 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -773,6 +773,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2); + default: /* unknown */ return -EINVAL; @@ -780,18 +783,16 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int msp_suspend(struct device * dev, pm_message_t state) +static int msp_suspend(struct i2c_client *client, pm_message_t state) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); v4l_dbg(1, msp_debug, client, "suspend\n"); msp_reset(client); return 0; } -static int msp_resume(struct device * dev) +static int msp_resume(struct i2c_client *client) { - struct i2c_client *client = container_of(dev, struct i2c_client, dev); v4l_dbg(1, msp_debug, client, "resume\n"); msp_wake_thread(client); @@ -825,7 +826,7 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) if (msp_reset(client) == -1) { v4l_dbg(1, msp_debug, client, "msp3400 not found\n"); kfree(client); - return -1; + return 0; } state = kmalloc(sizeof(*state), GFP_KERNEL); @@ -859,7 +860,7 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) v4l_dbg(1, msp_debug, client, "not an msp3400 (cannot read chip version)\n"); kfree(state); kfree(client); - return -1; + return 0; } msp_set_audio(client); @@ -874,6 +875,8 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d", msp_family, msp_product, msp_revision, msp_hard, msp_rom); + /* Rev B=2, C=3, D=4, G=7 */ + state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@'; /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5; @@ -996,11 +999,11 @@ static struct i2c_driver i2c_driver = { .id = I2C_DRIVERID_MSP3400, .attach_adapter = msp_probe, .detach_client = msp_detach, + .suspend = msp_suspend, + .resume = msp_resume, .command = msp_command, .driver = { .name = "msp3400", - .suspend = msp_suspend, - .resume = msp_resume, }, }; diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 7531efa..ab69a29 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -50,6 +50,7 @@ extern int msp_stereo_thresh; struct msp_state { int rev1, rev2; + int ident; u8 has_nicam; u8 has_radio; u8 has_headphones; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 5ed0adc..03bc369 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -5,6 +5,8 @@ * by Jonathan Corbet with substantial inspiration from Mark * McClelland's ovcamchip code. * + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * * This file may be distributed under the terms of the GNU General * Public License, version 2. */ @@ -15,6 +17,7 @@ #include <linux/delay.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <linux/i2c.h> @@ -162,6 +165,10 @@ MODULE_LICENSE("GPL"); #define REG_GFIX 0x69 /* Fix gain control */ +#define REG_REG76 0x76 /* OV's name */ +#define R76_BLKPCOR 0x80 /* Black pixel correction enable */ +#define R76_WHTPCOR 0x40 /* White pixel correction enable */ + #define REG_RGB444 0x8c /* RGB 444 control */ #define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ #define R444_RGBX 0x01 /* Empty nibble at end */ @@ -255,7 +262,7 @@ static struct regval_list ov7670_default_regs[] = { /* Almost all of these are magic "reserved" values. */ { REG_COM5, 0x61 }, { REG_COM6, 0x4b }, - { 0x16, 0x02 }, { REG_MVFP, 0x07|MVFP_MIRROR }, + { 0x16, 0x02 }, { REG_MVFP, 0x07 }, { 0x21, 0x02 }, { 0x22, 0x91 }, { 0x29, 0x07 }, { 0x33, 0x0b }, { 0x35, 0x0b }, { 0x37, 0x1d }, @@ -380,6 +387,13 @@ static struct regval_list ov7670_fmt_rgb444[] = { { 0xff, 0xff }, }; +static struct regval_list ov7670_fmt_raw[] = { + { REG_COM7, COM7_BAYER }, + { REG_COM13, 0x08 }, /* No gamma, magic rsvd bit */ + { REG_COM16, 0x3d }, /* Edge enhancement, denoise */ + { REG_REG76, 0xe1 }, /* Pix correction, magic rsvd */ + { 0xff, 0xff }, +}; @@ -483,32 +497,39 @@ static struct ov7670_format_struct { __u32 pixelformat; struct regval_list *regs; int cmatrix[CMATRIX_LEN]; + int bpp; /* Bytes per pixel */ } ov7670_formats[] = { { .desc = "YUYV 4:2:2", .pixelformat = V4L2_PIX_FMT_YUYV, .regs = ov7670_fmt_yuv422, .cmatrix = { 128, -128, 0, -34, -94, 128 }, + .bpp = 2, }, { .desc = "RGB 444", .pixelformat = V4L2_PIX_FMT_RGB444, .regs = ov7670_fmt_rgb444, .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 2, }, { .desc = "RGB 565", .pixelformat = V4L2_PIX_FMT_RGB565, .regs = ov7670_fmt_rgb565, .cmatrix = { 179, -179, 0, -61, -176, 228 }, + .bpp = 2, + }, + { + .desc = "Raw RGB Bayer", + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .regs = ov7670_fmt_raw, + .cmatrix = { 0, 0, 0, 0, 0, 0 }, + .bpp = 1 }, }; -#define N_OV7670_FMTS (sizeof(ov7670_formats)/sizeof(ov7670_formats[0])) +#define N_OV7670_FMTS ARRAY_SIZE(ov7670_formats) -/* - * All formats we support are 2 bytes/pixel. - */ -#define BYTES_PER_PIXEL 2 /* * Then there is the issue of window sizes. Try to capture the info here. @@ -685,7 +706,7 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, */ pix->width = wsize->width; pix->height = wsize->height; - pix->bytesperline = pix->width*BYTES_PER_PIXEL; + pix->bytesperline = pix->width*ov7670_formats[index].bpp; pix->sizeimage = pix->height*pix->bytesperline; return 0; } @@ -1270,9 +1291,8 @@ static int ov7670_command(struct i2c_client *client, unsigned int cmd, void *arg) { switch (cmd) { - case VIDIOC_INT_G_CHIP_IDENT: - * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670; - return 0; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); case VIDIOC_INT_RESET: ov7670_reset(client); diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c index 86d2884..1455a8f 100644 --- a/drivers/media/video/planb.c +++ b/drivers/media/video/planb.c @@ -2160,7 +2160,7 @@ static int find_planb(void) if (!machine_is(powermac)) return 0; - planb_devices = find_devices("planb"); + planb_devices = of_find_node_by_name(NULL, "planb"); if (planb_devices == 0) { planb_num=0; printk(KERN_WARNING "PlanB: no device found!\n"); @@ -2175,12 +2175,14 @@ static int find_planb(void) if (planb_devices->n_addrs != 1) { printk (KERN_WARNING "PlanB: expecting 1 address for planb " "(got %d)", planb_devices->n_addrs); + of_node_put(planb_devices); return 0; } if (planb_devices->n_intrs == 0) { printk(KERN_WARNING "PlanB: no intrs for device %s\n", planb_devices->full_name); + of_node_put(planb_devices); return 0; } else { irq = planb_devices->intrs[0].line; @@ -2202,12 +2204,13 @@ static int find_planb(void) confreg = planb_devices->addrs[0].space & 0xff; old_base = planb_devices->addrs[0].address; new_base = 0xf1000000; + of_node_put(planb_devices); DEBUG("PlanB: Found on bus %d, dev %d, func %d, " "membase 0x%x (base reg. 0x%x)\n", bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg); - pdev = pci_find_slot (bus, dev_fn); + pdev = pci_get_bus_and_slot(bus, dev_fn); if (!pdev) { printk(KERN_ERR "planb: cannot find slot\n"); goto err_out; @@ -2237,6 +2240,7 @@ static int find_planb(void) pb->planb_base = planb_regs; pb->planb_base_phys = (struct planb_registers *)new_base; pb->irq = irq; + pb->dev = pdev; return planb_num; @@ -2244,6 +2248,7 @@ err_out_disable: pci_disable_device(pdev); err_out: /* FIXME handle error */ /* comment moved from pci_find_slot, above */ + pci_dev_put(pdev); return 0; } @@ -2271,6 +2276,8 @@ static void release_planb(void) printk(KERN_INFO "PlanB: unregistering with v4l\n"); video_unregister_device(&pb->video_dev); + pci_dev_put(pb->dev); + /* note that iounmap() does nothing on the PPC right now */ iounmap ((void *)pb->planb_base); } diff --git a/drivers/media/video/planb.h b/drivers/media/video/planb.h index 9282321..e21b573 100644 --- a/drivers/media/video/planb.h +++ b/drivers/media/video/planb.h @@ -177,6 +177,7 @@ struct planb { struct mutex lock; unsigned int irq; /* interrupt number */ volatile unsigned int intr_mask; + struct pci_dev *dev; /* Our PCI device */ int overlay; /* overlay running? */ struct planb_window win; diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 9846c46..379645e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -31,7 +31,6 @@ struct pvr2_msp3400_handler { struct pvr2_hdw *hdw; struct pvr2_i2c_client *client; struct pvr2_i2c_handler i2c_handler; - struct pvr2_audio_stat astat; unsigned long stale_mask; }; @@ -44,13 +43,6 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - if (hdw->input_val == PVR2_CVAL_INPUT_TV) { - struct v4l2_tuner vt; - memset(&vt,0,sizeof(vt)); - vt.audmode = hdw->audiomode_val; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt); - } - route.input = MSP_INPUT_DEFAULT; route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); switch (hdw->input_val) { @@ -78,8 +70,7 @@ static void set_stereo(struct pvr2_msp3400_handler *ctxt) static int check_stereo(struct pvr2_msp3400_handler *ctxt) { struct pvr2_hdw *hdw = ctxt->hdw; - return (hdw->input_dirty || - hdw->audiomode_dirty); + return hdw->input_dirty; } @@ -99,8 +90,7 @@ static int msp3400_check(struct pvr2_msp3400_handler *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (msp3400_ops[idx].check(ctxt)) { @@ -116,8 +106,7 @@ static void msp3400_update(struct pvr2_msp3400_handler *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -126,27 +115,9 @@ static void msp3400_update(struct pvr2_msp3400_handler *ctxt) } -/* This reads back the current signal type */ -static int get_audio_status(struct pvr2_msp3400_handler *ctxt) -{ - struct v4l2_tuner vt; - int stat; - - memset(&vt,0,sizeof(vt)); - stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (stat < 0) return stat; - - ctxt->hdw->flag_stereo = (vt.audmode & V4L2_TUNER_MODE_STEREO) != 0; - ctxt->hdw->flag_bilingual = - (vt.audmode & V4L2_TUNER_MODE_LANG2) != 0; - return 0; -} - - static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) { ctxt->client->handler = NULL; - ctxt->hdw->audio_stat = NULL; kfree(ctxt); } @@ -158,7 +129,7 @@ static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt, } -const static struct pvr2_i2c_handler_functions msp3400_funcs = { +static const struct pvr2_i2c_handler_functions msp3400_funcs = { .detach = (void (*)(void *))pvr2_msp3400_detach, .check = (int (*)(void *))msp3400_check, .update = (void (*)(void *))msp3400_update, @@ -169,24 +140,17 @@ const static struct pvr2_i2c_handler_functions msp3400_funcs = { int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) { struct pvr2_msp3400_handler *ctxt; - if (hdw->audio_stat) return 0; if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->i2c_handler.func_data = ctxt; ctxt->i2c_handler.func_table = &msp3400_funcs; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->astat.ctxt = ctxt; - ctxt->astat.status = (int (*)(void *))get_audio_status; - ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach; - ctxt->stale_mask = (1 << (sizeof(msp3400_ops)/ - sizeof(msp3400_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(msp3400_ops)) - 1; cp->handler = &ctxt->i2c_handler; - hdw->audio_stat = &ctxt->astat; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up", cp->client->addr); return !0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c index cf12974..6bbed88 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -83,9 +83,8 @@ struct pvr2_context *pvr2_context_create( void (*setup_func)(struct pvr2_context *)) { struct pvr2_context *mp = NULL; - mp = kmalloc(sizeof(*mp),GFP_KERNEL); + mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; - memset(mp,0,sizeof(*mp)); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); mp->setup_func = setup_func; mutex_init(&mp->mutex); diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index c77de85..f569b00 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -26,6 +26,27 @@ #include <linux/mutex.h> +static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) +{ + if (cptr->info->check_value) { + if (!cptr->info->check_value(cptr,val)) return -ERANGE; + } else { + int lim; + lim = cptr->info->def.type_int.min_value; + if (cptr->info->get_min_value) { + cptr->info->get_min_value(cptr,&lim); + } + if (val < lim) return -ERANGE; + lim = cptr->info->def.type_int.max_value; + if (cptr->info->get_max_value) { + cptr->info->get_max_value(cptr,&lim); + } + if (val > lim) return -ERANGE; + } + return 0; +} + + /* Set the given control. */ int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val) { @@ -43,17 +64,8 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) if (cptr->info->type == pvr2_ctl_bitmask) { mask &= cptr->info->def.type_bitmask.valid_bits; } else if (cptr->info->type == pvr2_ctl_int) { - int lim; - lim = cptr->info->def.type_int.min_value; - if (cptr->info->get_min_value) { - cptr->info->get_min_value(cptr,&lim); - } - if (val < lim) break; - lim = cptr->info->def.type_int.max_value; - if (cptr->info->get_max_value) { - cptr->info->get_max_value(cptr,&lim); - } - if (val > lim) break; + ret = pvr2_ctrl_range_check(cptr,val); + if (ret < 0) break; } else if (cptr->info->type == pvr2_ctl_enum) { if (val >= cptr->info->def.type_enum.count) { break; @@ -498,16 +510,13 @@ int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, LOCK_TAKE(cptr->hdw->big_lock); do { if (cptr->info->type == pvr2_ctl_int) { ret = parse_token(ptr,len,valptr,NULL,0); - if ((ret >= 0) && - ((*valptr < cptr->info->def.type_int.min_value) || - (*valptr > cptr->info->def.type_int.max_value))) { - ret = -ERANGE; + if (ret >= 0) { + ret = pvr2_ctrl_range_check(cptr,*valptr); } if (maskptr) *maskptr = ~0; } else if (cptr->info->type == pvr2_ctl_bool) { - ret = parse_token( - ptr,len,valptr,boolNames, - sizeof(boolNames)/sizeof(boolNames[0])); + ret = parse_token(ptr,len,valptr,boolNames, + ARRAY_SIZE(boolNames)); if (ret == 1) { *valptr = *valptr ? !0 : 0; } else if (ret == 0) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 848fb233..e8a9252 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -63,6 +63,7 @@ static void set_input(struct pvr2_v4l_cx2584x *ctxt) vid_input = CX25840_COMPOSITE7; aud_input = CX25840_AUDIO8; break; + case PVR2_CVAL_INPUT_RADIO: // Treat same as composite case PVR2_CVAL_INPUT_COMPOSITE: vid_input = CX25840_COMPOSITE3; aud_input = CX25840_AUDIO_SERIAL; @@ -71,7 +72,6 @@ static void set_input(struct pvr2_v4l_cx2584x *ctxt) vid_input = CX25840_SVIDEO1; aud_input = CX25840_AUDIO_SERIAL; break; - case PVR2_CVAL_INPUT_RADIO: default: // Just set it to be composite input for now... vid_input = CX25840_COMPOSITE3; @@ -150,8 +150,7 @@ static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (decoder_ops[idx].check(ctxt)) { @@ -167,8 +166,7 @@ static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -199,18 +197,6 @@ static int decoder_detect(struct pvr2_i2c_client *cp) } -static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) -{ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (ret < 0) return -EINVAL; - return vt.signal ? 1 : 0; -} - - static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, char *buf,unsigned int cnt) { @@ -226,7 +212,7 @@ static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) } -const static struct pvr2_i2c_handler_functions hfuncs = { +static const struct pvr2_i2c_handler_functions hfuncs = { .detach = (void (*)(void *))decoder_detach, .check = (int (*)(void *))decoder_check, .update = (void (*)(void *))decoder_update, @@ -243,21 +229,18 @@ int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, if (cp->handler) return 0; if (!decoder_detect(cp)) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->ctrl.ctxt = ctxt; ctxt->ctrl.detach = (void (*)(void *))decoder_detach; ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ - sizeof(decoder_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; { diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index f985f00..e9da9bb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -152,7 +152,7 @@ static unsigned long debugifc_find_mask(const char *buf,unsigned int count) { struct debugifc_mask_item *mip; unsigned int idx; - for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { + for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { mip = mask_items + idx; if (debugifc_match_keyword(buf,count,mip->name)) { return mip->msk; @@ -169,7 +169,7 @@ static int debugifc_print_mask(char *buf,unsigned int sz, unsigned int idx; int bcnt = 0; int ccnt; - for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) { + for (idx = 0; idx < ARRAY_SIZE(mask_items); idx++) { mip = mask_items + idx; if (!(mip->msk & msk)) continue; ccnt = scnprintf(buf,sz,"%s%c%s", diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c index 6cff8e7..45cbca0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c +++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c @@ -102,9 +102,8 @@ static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw) } msg[1].len = pcnt; msg[1].buf = eeprom+tcnt; - if ((ret = i2c_transfer( - &hdw->i2c_adap, - msg,sizeof(msg)/sizeof(msg[0]))) != 2) { + if ((ret = i2c_transfer(&hdw->i2c_adap, + msg,ARRAY_SIZE(msg))) != 2) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "eeprom fetch set offs err=%d",ret); kfree(eeprom); diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index c94f97b..5669c8c 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -26,6 +26,7 @@ #include "pvrusb2-encoder.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" @@ -34,34 +35,41 @@ #define IVTV_MBOX_DRIVER_DONE 0x00000002 #define IVTV_MBOX_DRIVER_BUSY 0x00000001 +#define MBOX_BASE 0x44 + static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, + unsigned int offs, const u32 *data, unsigned int dlen) { - unsigned int idx; + unsigned int idx,addr; + unsigned int bAddr; int ret; - unsigned int offs = 0; unsigned int chunkCnt; /* Format: First byte must be 0x01. Remaining 32 bit words are - spread out into chunks of 7 bytes each, little-endian ordered, - offset at zero within each 2 blank bytes following and a - single byte that is 0x44 plus the offset of the word. Repeat - request for additional words, with offset adjusted - accordingly. + spread out into chunks of 7 bytes each, with the first 4 bytes + being the data word (little endian), and the next 3 bytes + being the address where that data word is to be written (big + endian). Repeat request for additional words, with offset + adjusted accordingly. */ while (dlen) { chunkCnt = 8; if (chunkCnt > dlen) chunkCnt = dlen; memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); - hdw->cmd_buffer[0] = 0x01; + bAddr = 0; + hdw->cmd_buffer[bAddr++] = FX2CMD_MEM_WRITE_DWORD; for (idx = 0; idx < chunkCnt; idx++) { - hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs; - PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7), - data[idx]); + addr = idx + offs; + hdw->cmd_buffer[bAddr+6] = (addr & 0xffu); + hdw->cmd_buffer[bAddr+5] = ((addr>>8) & 0xffu); + hdw->cmd_buffer[bAddr+4] = ((addr>>16) & 0xffu); + PVR2_DECOMPOSE_LE(hdw->cmd_buffer, bAddr,data[idx]); + bAddr += 7; } ret = pvr2_send_request(hdw, hdw->cmd_buffer,1+(chunkCnt*7), @@ -76,33 +84,42 @@ static int pvr2_encoder_write_words(struct pvr2_hdw *hdw, } -static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl, +static int pvr2_encoder_read_words(struct pvr2_hdw *hdw, + unsigned int offs, u32 *data, unsigned int dlen) { unsigned int idx; int ret; - unsigned int offs = 0; unsigned int chunkCnt; /* Format: First byte must be 0x02 (status check) or 0x28 (read back block of 32 bit words). Next 6 bytes must be zero, - followed by a single byte of 0x44+offset for portion to be - read. Returned data is packed set of 32 bits words that were - read. + followed by a single byte of MBOX_BASE+offset for portion to + be read. Returned data is packed set of 32 bits words that + were read. */ while (dlen) { chunkCnt = 16; if (chunkCnt > dlen) chunkCnt = dlen; - memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer)); - hdw->cmd_buffer[0] = statusFl ? 0x02 : 0x28; - hdw->cmd_buffer[7] = 0x44 + offs; + if (chunkCnt < 16) chunkCnt = 1; + hdw->cmd_buffer[0] = + ((chunkCnt == 1) ? + FX2CMD_MEM_READ_DWORD : FX2CMD_MEM_READ_64BYTES); + hdw->cmd_buffer[1] = 0; + hdw->cmd_buffer[2] = 0; + hdw->cmd_buffer[3] = 0; + hdw->cmd_buffer[4] = 0; + hdw->cmd_buffer[5] = ((offs>>16) & 0xffu); + hdw->cmd_buffer[6] = ((offs>>8) & 0xffu); + hdw->cmd_buffer[7] = (offs & 0xffu); ret = pvr2_send_request(hdw, hdw->cmd_buffer,8, - hdw->cmd_buffer,chunkCnt * 4); + hdw->cmd_buffer, + (chunkCnt == 1 ? 4 : 16 * 4)); if (ret) return ret; for (idx = 0; idx < chunkCnt; idx++) { @@ -129,6 +146,8 @@ static int pvr2_encoder_cmd(void *ctxt, u32 *argp) { unsigned int poll_count; + unsigned int try_count = 0; + int retry_flag; int ret = 0; unsigned int idx; /* These sizes look to be limited by the FX2 firmware implementation */ @@ -140,14 +159,15 @@ static int pvr2_encoder_cmd(void *ctxt, /* The encoder seems to speak entirely using blocks 32 bit words. - In ivtv driver terms, this is a mailbox which we populate with - data and watch what the hardware does with it. The first word - is a set of flags used to control the transaction, the second - word is the command to execute, the third byte is zero (ivtv - driver suggests that this is some kind of return value), and - the fourth byte is a specified timeout (windows driver always - uses 0x00060000 except for one case when it is zero). All - successive words are the argument words for the command. + In ivtv driver terms, this is a mailbox at MBOX_BASE which we + populate with data and watch what the hardware does with it. + The first word is a set of flags used to control the + transaction, the second word is the command to execute, the + third byte is zero (ivtv driver suggests that this is some + kind of return value), and the fourth byte is a specified + timeout (windows driver always uses 0x00060000 except for one + case when it is zero). All successive words are the argument + words for the command. First, write out the entire set of words, with the first word being zero. @@ -156,44 +176,42 @@ static int pvr2_encoder_cmd(void *ctxt, IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which probably means "go"). - Next, read back 16 words as status. Check the first word, + Next, read back the return count words. Check the first word, which should have IVTV_MBOX_FIRMWARE_DONE set. If however that bit is not set, then the command isn't done so repeat the - read. - - Next, read back 32 words and compare with the original - arugments. Hopefully they will match. + read until it is set. Finally, write out just the first word again, but set it to 0x0 this time (which probably means "idle"). */ - if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) { + if (arg_cnt_send > (ARRAY_SIZE(wrData) - 4)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many input arguments" - " (was given %u limit %u)", - arg_cnt_send, - (unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4); + " (was given %u limit %lu)", + arg_cnt_send, (long unsigned) ARRAY_SIZE(wrData) - 4); return -EINVAL; } - if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) { + if (arg_cnt_recv > (ARRAY_SIZE(rdData) - 4)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many return arguments" - " (was given %u limit %u)", - arg_cnt_recv, - (unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4); + " (was given %u limit %lu)", + arg_cnt_recv, (long unsigned) ARRAY_SIZE(rdData) - 4); return -EINVAL; } LOCK_TAKE(hdw->ctl_lock); do { + retry_flag = 0; + try_count++; + ret = 0; wrData[0] = 0; wrData[1] = cmd; wrData[2] = 0; @@ -201,59 +219,74 @@ static int pvr2_encoder_cmd(void *ctxt, for (idx = 0; idx < arg_cnt_send; idx++) { wrData[idx+4] = argp[idx]; } - for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) { + for (; idx < ARRAY_SIZE(wrData) - 4; idx++) { wrData[idx+4] = 0; } - ret = pvr2_encoder_write_words(hdw,wrData,idx); + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,idx); if (ret) break; wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY; - ret = pvr2_encoder_write_words(hdw,wrData,1); + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); if (ret) break; poll_count = 0; while (1) { - if (poll_count < 10000000) poll_count++; - ret = pvr2_encoder_read_words(hdw,!0,rdData,1); - if (ret) break; + poll_count++; + ret = pvr2_encoder_read_words(hdw,MBOX_BASE,rdData, + arg_cnt_recv+4); + if (ret) { + break; + } if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) { break; } - if (poll_count == 100) { + if (rdData[0] && (poll_count < 1000)) continue; + if (!rdData[0]) { + retry_flag = !0; pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "***WARNING*** device's encoder" - " appears to be stuck" - " (status=0%08x)",rdData[0]); + "Encoder timed out waiting for us" + "; arranging to retry"); + } else { pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "Encoder command: 0x%02x",cmd); - for (idx = 4; idx < arg_cnt_send; idx++) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "Encoder arg%d: 0x%08x", - idx-3,wrData[idx]); - } + "***WARNING*** device's encoder" + " appears to be stuck" + " (status=0x%08x)",rdData[0]); + } + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Encoder command: 0x%02x",cmd); + for (idx = 4; idx < arg_cnt_send; idx++) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, - "Giving up waiting." - " It is likely that" - " this is a bad idea..."); - ret = -EBUSY; - break; + "Encoder arg%d: 0x%08x", + idx-3,wrData[idx]); } + ret = -EBUSY; + break; + } + if (retry_flag) { + if (try_count < 20) continue; + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Too many retries..."); + ret = -EBUSY; + } + if (ret) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "Giving up on command." + " It is likely that" + " this is a bad idea..."); + break; } - if (ret) break; wrData[0] = 0x7; - ret = pvr2_encoder_read_words( - hdw,0,rdData, - sizeof(rdData)/sizeof(rdData[0])); - if (ret) break; for (idx = 0; idx < arg_cnt_recv; idx++) { argp[idx] = rdData[idx+4]; } wrData[0] = 0x0; - ret = pvr2_encoder_write_words(hdw,wrData,1); + ret = pvr2_encoder_write_words(hdw,MBOX_BASE,wrData,1); if (ret) break; } while(0); LOCK_GIVE(hdw->ctl_lock); @@ -269,13 +302,13 @@ static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, unsigned int idx; u32 data[12]; - if (args > sizeof(data)/sizeof(data[0])) { + if (args > ARRAY_SIZE(data)) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Failed to write cx23416 command" " - too many arguments" - " (was given %u limit %u)", - args,(unsigned int)(sizeof(data)/sizeof(data[0]))); + " (was given %u limit %lu)", + args, (long unsigned) ARRAY_SIZE(data)); return -EINVAL; } @@ -288,6 +321,73 @@ static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd, return pvr2_encoder_cmd(hdw,cmd,args,0,data); } + +/* This implements some extra setup for the encoder that seems to be + specific to the PVR USB2 hardware. */ +static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) +{ + int ret = 0; + int encMisc3Arg = 0; + +#if 0 + /* This inexplicable bit happens in the Hauppage windows + driver (for both 24xxx and 29xxx devices). However I + currently see no difference in behavior with or without + this stuff. Leave this here as a note of its existence, + but don't use it. */ + LOCK_TAKE(hdw->ctl_lock); do { + u32 dat[1]; + dat[0] = 0x80000640; + pvr2_encoder_write_words(hdw,0x01fe,dat,1); + pvr2_encoder_write_words(hdw,0x023e,dat,1); + } while(0); LOCK_GIVE(hdw->ctl_lock); +#endif + + /* Mike Isely <isely@pobox.com> 26-Jan-2006 The windows driver + sends the following list of ENC_MISC commands (for both + 24xxx and 29xxx devices). Meanings are not entirely clear, + however without the ENC_MISC(3,1) command then we risk + random perpetual video corruption whenever the video input + breaks up for a moment (like when switching channels). */ + + +#if 0 + /* This ENC_MISC(5,0) command seems to hurt 29xxx sync + performance on channel changes, but is not a problem on + 24xxx devices. */ + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); +#endif + + /* This ENC_MISC(3,encMisc3Arg) command is critical - without + it there will eventually be video corruption. Also, the + 29xxx case is strange - the Windows driver is passing 1 + regardless of device type but if we have 1 for 29xxx device + the video turns sluggish. */ + switch (hdw->hdw_type) { + case PVR2_HDW_TYPE_24XXX: encMisc3Arg = 1; break; + case PVR2_HDW_TYPE_29XXX: encMisc3Arg = 0; break; + default: break; + } + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3, + encMisc3Arg,0,0); + + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); + +#if 0 + /* This ENC_MISC(4,1) command is poisonous, so it is commented + out. But I'm leaving it here anyway to document its + existence in the Windows driver. The effect of this + command is that apps displaying the stream become sluggish + with stuttering video. */ + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); +#endif + + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); + ret |= pvr2_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); + + return ret; +} + int pvr2_encoder_configure(struct pvr2_hdw *hdw) { int ret; @@ -302,6 +402,8 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) ret = 0; + ret |= pvr2_encoder_prep_config(hdw); + if (!ret) ret = pvr2_encoder_vcmd( hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, 0xf0, 0xf0); @@ -360,15 +462,22 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481); pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - if (hdw->config == pvr2_config_vbi) { + pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, + hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); + + switch (hdw->config) { + case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 0x01,0x14); - } else if (hdw->config == pvr2_config_mpeg) { + break; + case pvr2_config_mpeg: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 0,0x13); - } else { + break; + default: /* Unhandled cases for now */ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2, 0,0x13); + break; } if (!status) { hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); @@ -383,15 +492,19 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) /* mask all interrupts */ pvr2_write_register(hdw, 0x0048, 0xffffffff); - if (hdw->config == pvr2_config_vbi) { + switch (hdw->config) { + case pvr2_config_vbi: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 0x01,0x01,0x14); - } else if (hdw->config == pvr2_config_mpeg) { + break; + case pvr2_config_mpeg: status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 0x01,0,0x13); - } else { + break; + default: /* Unhandled cases for now */ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3, 0x01,0,0x13); + break; } /* change some GPIO data */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h new file mode 100644 index 0000000..ffbc6d0 --- /dev/null +++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -0,0 +1,62 @@ +/* + * + * $Id$ + * + * Copyright (C) 2007 Michael Krufky <mkrufky@linuxtv.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _PVRUSB2_FX2_CMD_H_ +#define _PVRUSB2_FX2_CMD_H_ + +#define FX2CMD_MEM_WRITE_DWORD 0x01 +#define FX2CMD_MEM_READ_DWORD 0x02 + +#define FX2CMD_MEM_READ_64BYTES 0x28 + +#define FX2CMD_REG_WRITE 0x04 +#define FX2CMD_REG_READ 0x05 +#define FX2CMD_MEMSEL 0x06 + +#define FX2CMD_I2C_WRITE 0x08 +#define FX2CMD_I2C_READ 0x09 + +#define FX2CMD_GET_USB_SPEED 0x0b + +#define FX2CMD_STREAMING_ON 0x36 +#define FX2CMD_STREAMING_OFF 0x37 + +#define FX2CMD_FWPOST1 0x52 + +#define FX2CMD_POWER_OFF 0xdc +#define FX2CMD_POWER_ON 0xde + +#define FX2CMD_DEEP_RESET 0xdd + +#define FX2CMD_GET_EEPROM_ADDR 0xeb +#define FX2CMD_GET_IR_CODE 0xec + +#endif /* _PVRUSB2_FX2_CMD_H_ */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 75 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 34b08fb..ce66ab8 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -60,6 +60,7 @@ struct pvr2_decoder; typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); +typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *); typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val); typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val, @@ -83,6 +84,7 @@ struct pvr2_ctl_info { pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */ pvr2_ctlf_get_value get_max_value; /* Get maximum allowed value */ pvr2_ctlf_set_value set_value; /* Set its value */ + pvr2_ctlf_check_value check_value; /* Check that value is valid */ pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */ pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */ pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */ @@ -135,17 +137,10 @@ struct pvr2_ctrl { }; -struct pvr2_audio_stat { - void *ctxt; - void (*detach)(void *); - int (*status)(void *); -}; - struct pvr2_decoder_ctrl { void *ctxt; void (*detach)(void *); void (*enable)(void *,int); - int (*tuned)(void *); void (*force_reset)(void *); }; @@ -212,7 +207,6 @@ struct pvr2_hdw { /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; unsigned int freqProgSlot; - unsigned int freqSlot; /* Stuff for handling low level control interaction with device */ struct mutex ctl_lock_mutex; @@ -258,9 +252,17 @@ struct pvr2_hdw { /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; - unsigned int freqVal; + unsigned int freqValTelevision; /* Current freq for tv mode */ + unsigned int freqValRadio; /* Current freq for radio mode */ + unsigned int freqSlotTelevision; /* Current slot for tv mode */ + unsigned int freqSlotRadio; /* Current slot for radio mode */ + unsigned int freqSelector; /* 0=radio 1=television */ int freqDirty; + /* Current tuner info - this information is polled from the I2C bus */ + struct v4l2_tuner tuner_signal_info; + int tuner_signal_stale; + /* Video standard handling */ v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from @@ -281,20 +283,19 @@ struct pvr2_hdw { int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ - /* Minor number used by v4l logic (yes, this is a hack, as there should - be no v4l junk here). Probably a better way to do this. */ - int v4l_minor_number; + char bus_info[32]; /* Bus location info */ + + /* Minor numbers used by v4l logic (yes, this is a hack, as there + should be no v4l junk here). Probably a better way to do this. */ + int v4l_minor_number_video; + int v4l_minor_number_vbi; + int v4l_minor_number_radio; /* Location of eeprom or a negative number if none */ int eeprom_addr; enum pvr2_config config; - /* Information about what audio signal we're hearing */ - int flag_stereo; - int flag_bilingual; - struct pvr2_audio_stat *audio_stat; - /* Control state needed for cx2341x module */ struct cx2341x_mpeg_params enc_cur_state; struct cx2341x_mpeg_params enc_ctl_state; @@ -327,6 +328,9 @@ struct pvr2_hdw { unsigned int control_cnt; }; +/* This function gets the current frequency */ +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); + #endif /* __PVRUSB2_HDW_INTERNAL_H */ /* diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d200496..acf651e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -36,6 +36,10 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-encoder.h" #include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" + +#define TV_MIN_FREQ 55250000L +#define TV_MAX_FREQ 850000000L struct usb_device_id pvr2_device_table[] = { [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) }, @@ -71,12 +75,10 @@ static const char *pvr2_client_29xxx[] = { static struct pvr2_string_table pvr2_client_lists[] = { [PVR2_HDW_TYPE_29XXX] = { - pvr2_client_29xxx, - sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]), + pvr2_client_29xxx, ARRAY_SIZE(pvr2_client_29xxx) }, [PVR2_HDW_TYPE_24XXX] = { - pvr2_client_24xxx, - sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]), + pvr2_client_24xxx, ARRAY_SIZE(pvr2_client_24xxx) }, }; @@ -160,9 +162,6 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { .strid = "video_gop_closure", .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, },{ - .strid = "video_pulldown", - .id = V4L2_CID_MPEG_VIDEO_PULLDOWN, - },{ .strid = "video_bitrate_mode", .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, },{ @@ -212,7 +211,7 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, } }; -#define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0])) +#define MPEGDEF_COUNT ARRAY_SIZE(mpeg_ids) static const char *control_values_srate[] = { @@ -255,10 +254,10 @@ static const char *control_values_subsystem[] = { [PVR2_SUBSYS_B_ENC_RUN] = "enc_run", }; +static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw); @@ -272,8 +271,6 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, unsigned int timeout,int probe_fl, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); -static int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res); -static int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res); static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) { @@ -289,8 +286,21 @@ static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp) static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v) { struct pvr2_hdw *hdw = cptr->hdw; - if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) { - hdw->freqTable[hdw->freqProgSlot-1] = v; + unsigned int slotId = hdw->freqProgSlot; + if ((slotId > 0) && (slotId <= FREQTABLE_SIZE)) { + hdw->freqTable[slotId-1] = v; + /* Handle side effects correctly - if we're tuned to this + slot, then forgot the slot id relation since the stored + frequency has been changed. */ + if (hdw->freqSelector) { + if (hdw->freqSlotRadio == slotId) { + hdw->freqSlotRadio = 0; + } + } else { + if (hdw->freqSlotTelevision == slotId) { + hdw->freqSlotTelevision = 0; + } + } } return 0; } @@ -312,28 +322,32 @@ static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v) static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->freqSlot; + struct pvr2_hdw *hdw = cptr->hdw; + *vp = hdw->freqSelector ? hdw->freqSlotRadio : hdw->freqSlotTelevision; return 0; } -static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v) +static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int slotId) { unsigned freq = 0; struct pvr2_hdw *hdw = cptr->hdw; - hdw->freqSlot = v; - if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) { - freq = hdw->freqTable[hdw->freqSlot-1]; - } - if (freq && (freq != hdw->freqVal)) { - hdw->freqVal = freq; - hdw->freqDirty = !0; + if ((slotId < 0) || (slotId > FREQTABLE_SIZE)) return 0; + if (slotId > 0) { + freq = hdw->freqTable[slotId-1]; + if (!freq) return 0; + pvr2_hdw_set_cur_freq(hdw,freq); + } + if (hdw->freqSelector) { + hdw->freqSlotRadio = slotId; + } else { + hdw->freqSlotTelevision = slotId; } return 0; } static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = cptr->hdw->freqVal; + *vp = pvr2_hdw_get_cur_freq(cptr->hdw); return 0; } @@ -349,10 +363,7 @@ static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v) { - struct pvr2_hdw *hdw = cptr->hdw; - hdw->freqVal = v; - hdw->freqDirty = !0; - hdw->freqSlot = 0; + pvr2_hdw_set_cur_freq(cptr->hdw,v); return 0; } @@ -378,6 +389,89 @@ static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) +{ + *vp = cptr->hdw->input_val; + return 0; +} + +static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) +{ + struct pvr2_hdw *hdw = cptr->hdw; + + if (hdw->input_val != v) { + hdw->input_val = v; + hdw->input_dirty = !0; + } + + /* Handle side effects - if we switch to a mode that needs the RF + tuner, then select the right frequency choice as well and mark + it dirty. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + return 0; +} + +static int ctrl_isdirty_input(struct pvr2_ctrl *cptr) +{ + return cptr->hdw->input_dirty != 0; +} + +static void ctrl_cleardirty_input(struct pvr2_ctrl *cptr) +{ + cptr->hdw->input_dirty = 0; +} + + +static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) +{ + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangehigh; + if (!fv) { + /* Safety fallback */ + *vp = TV_MAX_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } + *vp = fv; + return 0; +} + +static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) +{ + unsigned long fv; + struct pvr2_hdw *hdw = cptr->hdw; + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + fv = hdw->tuner_signal_info.rangelow; + if (!fv) { + /* Safety fallback */ + *vp = TV_MIN_FREQ; + return 0; + } + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } + *vp = fv; + return 0; +} + static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr) { return cptr->hdw->enc_stale != 0; @@ -534,8 +628,32 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) { - *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) & - PVR2_SIGNAL_OK) ? 1 : 0); + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_i2c_core_status_poll(hdw); + *vp = hdw->tuner_signal_info.signal; + return 0; +} + +static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) +{ + int val = 0; + unsigned int subchan; + struct pvr2_hdw *hdw = cptr->hdw; + pvr2_i2c_core_status_poll(hdw); + subchan = hdw->tuner_signal_info.rxsubchans; + if (subchan & V4L2_TUNER_SUB_MONO) { + val |= (1 << V4L2_TUNER_MODE_MONO); + } + if (subchan & V4L2_TUNER_SUB_STEREO) { + val |= (1 << V4L2_TUNER_MODE_STEREO); + } + if (subchan & V4L2_TUNER_SUB_LANG1) { + val |= (1 << V4L2_TUNER_MODE_LANG1); + } + if (subchan & V4L2_TUNER_SUB_LANG2) { + val |= (1 << V4L2_TUNER_MODE_LANG2); + } + *vp = val; return 0; } @@ -604,7 +722,7 @@ static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) #define DEFENUM(tab) \ .type = pvr2_ctl_enum, \ - .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \ + .def.type_enum.count = ARRAY_SIZE(tab), \ .def.type_enum.value_names = tab #define DEFBOOL \ @@ -641,15 +759,11 @@ VCREATE_FUNCS(balance) VCREATE_FUNCS(bass) VCREATE_FUNCS(treble) VCREATE_FUNCS(mute) -VCREATE_FUNCS(input) VCREATE_FUNCS(audiomode) VCREATE_FUNCS(res_hor) VCREATE_FUNCS(res_ver) VCREATE_FUNCS(srate) -#define MIN_FREQ 55250000L -#define MAX_FREQ 850000000L - /* Table definition of all controls which can be manipulated */ static const struct pvr2_ctl_info control_defs[] = { { @@ -684,7 +798,7 @@ static const struct pvr2_ctl_info control_defs[] = { .v4l_id = V4L2_CID_AUDIO_VOLUME, .desc = "Volume", .name = "volume", - .default_value = 65535, + .default_value = 62000, DEFREF(volume), DEFINT(0,65535), },{ @@ -758,12 +872,16 @@ static const struct pvr2_ctl_info control_defs[] = { .desc = "Tuner Frequency (Hz)", .name = "frequency", .internal_id = PVR2_CID_FREQUENCY, - .default_value = 175250000L, + .default_value = 0, .set_value = ctrl_freq_set, .get_value = ctrl_freq_get, .is_dirty = ctrl_freq_is_dirty, .clear_dirty = ctrl_freq_clear_dirty, - DEFINT(MIN_FREQ,MAX_FREQ), + DEFINT(0,0), + /* Hook in check for input value (tv/radio) and adjust + max/min values accordingly */ + .get_max_value = ctrl_freq_max_get, + .get_min_value = ctrl_freq_min_get, },{ .desc = "Channel", .name = "channel", @@ -775,7 +893,11 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "freq_table_value", .set_value = ctrl_channelfreq_set, .get_value = ctrl_channelfreq_get, - DEFINT(MIN_FREQ,MAX_FREQ), + DEFINT(0,0), + /* Hook in check for input value (tv/radio) and adjust + max/min values accordingly */ + .get_max_value = ctrl_freq_max_get, + .get_min_value = ctrl_freq_min_get, },{ .desc = "Channel Program ID", .name = "freq_table_channel", @@ -796,7 +918,20 @@ static const struct pvr2_ctl_info control_defs[] = { .desc = "Signal Present", .name = "signal_present", .get_value = ctrl_signal_get, - DEFBOOL, + DEFINT(0,65535), + },{ + .desc = "Audio Modes Present", + .name = "audio_modes_present", + .get_value = ctrl_audio_modes_present_get, + /* For this type we "borrow" the V4L2_TUNER_MODE enum from + v4l. Nothing outside of this module cares about this, + but I reuse it in order to also reuse the + control_values_audiomode string table. */ + DEFMASK(((1 << V4L2_TUNER_MODE_MONO)| + (1 << V4L2_TUNER_MODE_STEREO)| + (1 << V4L2_TUNER_MODE_LANG1)| + (1 << V4L2_TUNER_MODE_LANG2)), + control_values_audiomode), },{ .desc = "Video Standards Available Mask", .name = "video_standard_mask_available", @@ -846,7 +981,7 @@ static const struct pvr2_ctl_info control_defs[] = { } }; -#define CTRLDEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0])) +#define CTRLDEF_COUNT ARRAY_SIZE(control_defs) const char *pvr2_config_get_name(enum pvr2_config cfg) @@ -855,7 +990,8 @@ const char *pvr2_config_get_name(enum pvr2_config cfg) case pvr2_config_empty: return "empty"; case pvr2_config_mpeg: return "mpeg"; case pvr2_config_vbi: return "vbi"; - case pvr2_config_radio: return "radio"; + case pvr2_config_pcm: return "pcm"; + case pvr2_config_rawvideo: return "raw video"; } return "<unknown>"; } @@ -872,6 +1008,47 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw) return hdw->serial_number; } + +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw) +{ + return hdw->bus_info; +} + + +unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw) +{ + return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio; +} + +/* Set the currently tuned frequency and account for all possible + driver-core side effects of this action. */ +void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val) +{ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + if (hdw->freqSelector) { + /* Swing over to radio frequency selection */ + hdw->freqSelector = 0; + hdw->freqDirty = !0; + } + if (hdw->freqValRadio != val) { + hdw->freqValRadio = val; + hdw->freqSlotRadio = 0; + hdw->freqDirty = !0; + } + } else { + if (!(hdw->freqSelector)) { + /* Swing over to television frequency selection */ + hdw->freqSelector = 1; + hdw->freqDirty = !0; + } + if (hdw->freqValTelevision != val) { + hdw->freqValTelevision = val; + hdw->freqSlotTelevision = 0; + hdw->freqDirty = !0; + } + } +} + int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw) { return hdw->unit_number; @@ -960,12 +1137,10 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) }; static const struct pvr2_string_table fw_file_defs[] = { [PVR2_HDW_TYPE_29XXX] = { - fw_files_29xxx, - sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]), + fw_files_29xxx, ARRAY_SIZE(fw_files_29xxx) }, [PVR2_HDW_TYPE_24XXX] = { - fw_files_24xxx, - sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]), + fw_files_24xxx, ARRAY_SIZE(fw_files_24xxx) }, }; hdw->fw1_state = FW1_STATE_FAILED; // default result @@ -1041,7 +1216,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) { const struct firmware *fw_entry = NULL; void *fw_ptr; - unsigned int pipe, fw_len, fw_done; + unsigned int pipe, fw_len, fw_done, bcnt, icnt; int actual_length; int ret = 0; int fwidx; @@ -1052,8 +1227,7 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) trace_firmware("pvr2_upload_firmware2"); ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder", - sizeof(fw_files)/sizeof(fw_files[0]), - fw_files); + ARRAY_SIZE(fw_files), fw_files); if (ret < 0) return ret; fwidx = ret; ret = 0; @@ -1079,8 +1253,13 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/ - ret |= pvr2_write_u8(hdw, 0x52, 0); - ret |= pvr2_write_u16(hdw, 0x0600, 0); + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_FWPOST1; + ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); + hdw->cmd_buffer[0] = FX2CMD_MEMSEL; + hdw->cmd_buffer[1] = 0; + ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); + } while (0); LOCK_GIVE(hdw->ctl_lock); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -1093,11 +1272,11 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) fw_len = fw_entry->size; - if (fw_len % FIRMWARE_CHUNK_SIZE) { + if (fw_len % sizeof(u32)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "size of %s firmware" - " must be a multiple of 8192B", - fw_files[fwidx]); + " must be a multiple of %zu bytes", + fw_files[fwidx],sizeof(u32)); release_firmware(fw_entry); return -1; } @@ -1112,18 +1291,21 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT); - for (fw_done = 0 ; (fw_done < fw_len) && !ret ; - fw_done += FIRMWARE_CHUNK_SIZE ) { - int i; - memcpy(fw_ptr, fw_entry->data + fw_done, FIRMWARE_CHUNK_SIZE); - /* Usbsnoop log shows that we must swap bytes... */ - for (i = 0; i < FIRMWARE_CHUNK_SIZE/4 ; i++) - ((u32 *)fw_ptr)[i] = ___swab32(((u32 *)fw_ptr)[i]); - - ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr, - FIRMWARE_CHUNK_SIZE, + fw_done = 0; + for (fw_done = 0; fw_done < fw_len;) { + bcnt = fw_len - fw_done; + if (bcnt > FIRMWARE_CHUNK_SIZE) bcnt = FIRMWARE_CHUNK_SIZE; + memcpy(fw_ptr, fw_entry->data + fw_done, bcnt); + /* Usbsnoop log shows that we must swap bytes... */ + for (icnt = 0; icnt < bcnt/4 ; icnt++) + ((u32 *)fw_ptr)[icnt] = + ___swab32(((u32 *)fw_ptr)[icnt]); + + ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,bcnt, &actual_length, HZ); - ret |= (actual_length != FIRMWARE_CHUNK_SIZE); + ret |= (actual_length != bcnt); + if (ret) break; + fw_done += bcnt; } trace_firmware("upload of %s : %i / %i ", @@ -1142,7 +1324,11 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/ - ret |= pvr2_write_u16(hdw, 0x0600, 0); + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = FX2CMD_MEMSEL; + hdw->cmd_buffer[1] = 0; + ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0); + } while (0); LOCK_GIVE(hdw->ctl_lock); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -1479,7 +1665,7 @@ static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw) firmware needs be loaded. */ int result; LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = 0xeb; + hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; result = pvr2_send_request_ex(hdw,HZ*1,!0, hdw->cmd_buffer,1, hdw->cmd_buffer,1); @@ -1611,6 +1797,16 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->info->set_value(cptr,~0,cptr->info->default_value); } + /* Set up special default values for the television and radio + frequencies here. It's not really important what these defaults + are, but I set them to something usable in the Chicago area just + to make driver testing a little easier. */ + + /* US Broadcast channel 7 (175.25 MHz) */ + hdw->freqValTelevision = 175250000L; + /* 104.3 MHz, a usable FM station for my area */ + hdw->freqValRadio = 104300000L; + // Do not use pvr2_reset_ctl_endpoints() here. It is not // thread-safe against the normal pvr2_send_request() mechanism. // (We should make it thread safe). @@ -1750,26 +1946,24 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, struct pvr2_ctl_info *ciptr; hdw_type = devid - pvr2_device_table; - if (hdw_type >= - sizeof(pvr2_device_names)/sizeof(pvr2_device_names[0])) { + if (hdw_type >= ARRAY_SIZE(pvr2_device_names)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Bogus device type of %u reported",hdw_type); return NULL; } - hdw = kmalloc(sizeof(*hdw),GFP_KERNEL); + hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,pvr2_device_names[hdw_type]); if (!hdw) goto fail; - memset(hdw,0,sizeof(*hdw)); + hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; - hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, + hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, GFP_KERNEL); if (!hdw->controls) goto fail; - memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * hdw->control_cnt); hdw->hdw_type = hdw_type; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; @@ -1783,11 +1977,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = control_defs+idx; } /* Define and configure additional controls from cx2341x module. */ - hdw->mpeg_ctrl_info = kmalloc( + hdw->mpeg_ctrl_info = kzalloc( sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); if (!hdw->mpeg_ctrl_info) goto fail; - memset(hdw->mpeg_ctrl_info,0, - sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT); for (idx = 0; idx < MPEGDEF_COUNT; idx++) { cptr = hdw->controls + idx + CTRLDEF_COUNT; ciptr = &(hdw->mpeg_ctrl_info[idx].info); @@ -1872,7 +2064,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->eeprom_addr = -1; hdw->unit_number = -1; - hdw->v4l_minor_number = -1; + hdw->v4l_minor_number_video = -1; + hdw->v4l_minor_number_vbi = -1; + hdw->v4l_minor_number_radio = -1; hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); if (!hdw->ctl_write_buffer) goto fail; hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL); @@ -1918,6 +2112,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->usb_intf = intf; hdw->usb_dev = interface_to_usbdev(intf); + scnprintf(hdw->bus_info,sizeof(hdw->bus_info), + "usb %s address %d", + hdw->usb_dev->dev.bus_id, + hdw->usb_dev->devnum); + ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber; usb_set_interface(hdw->usb_dev,ifnum,0); @@ -1929,10 +2128,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, if (hdw) { usb_free_urb(hdw->ctl_read_urb); usb_free_urb(hdw->ctl_write_urb); - if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer); - if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer); - if (hdw->controls) kfree(hdw->controls); - if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info); + kfree(hdw->ctl_read_buffer); + kfree(hdw->ctl_write_buffer); + kfree(hdw->controls); + kfree(hdw->mpeg_ctrl_info); kfree(hdw); } return NULL; @@ -1982,9 +2181,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_stream_destroy(hdw->vid_stream); hdw->vid_stream = NULL; } - if (hdw->audio_stat) { - hdw->audio_stat->detach(hdw->audio_stat->ctxt); - } if (hdw->decoder_ctrl) { hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); } @@ -1997,10 +2193,10 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) unit_pointers[hdw->unit_number] = NULL; } } while (0); up(&pvr2_unit_sem); - if (hdw->controls) kfree(hdw->controls); - if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info); - if (hdw->std_defs) kfree(hdw->std_defs); - if (hdw->std_enum_names) kfree(hdw->std_enum_names); + kfree(hdw->controls); + kfree(hdw->mpeg_ctrl_info); + kfree(hdw->std_defs); + kfree(hdw->std_enum_names); kfree(hdw); } @@ -2210,10 +2406,9 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) cptr = hdw->controls + idx; if (cptr->info->is_dirty == 0) continue; if (!cptr->info->is_dirty(cptr)) continue; - if (!commit_flag) { - commit_flag = !0; - } + commit_flag = !0; + if (!(pvrusb2_debug & PVR2_TRACE_CTL)) continue; bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ", cptr->info->name); value = 0; @@ -2263,6 +2458,13 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw) stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG); } + if (hdw->input_dirty) { + /* pk: If input changes to or from radio, then the encoder + needs to be restarted (for ENC_MUTE_VIDEO to work) */ + stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN); + } + + if (hdw->srate_dirty) { /* Write new sample rate into control structure since * the master copy is stale. We must track srate @@ -2343,39 +2545,11 @@ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) } -/* Return bit mask indicating signal status */ -static unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw) -{ - unsigned int msk = 0; - switch (hdw->input_val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_RADIO: - if (hdw->decoder_ctrl && - hdw->decoder_ctrl->tuned(hdw->decoder_ctrl->ctxt)) { - msk |= PVR2_SIGNAL_OK; - if (hdw->audio_stat && - hdw->audio_stat->status(hdw->audio_stat->ctxt)) { - if (hdw->flag_stereo) { - msk |= PVR2_SIGNAL_STEREO; - } - if (hdw->flag_bilingual) { - msk |= PVR2_SIGNAL_SAP; - } - } - } - break; - default: - msk |= PVR2_SIGNAL_OK | PVR2_SIGNAL_STEREO; - } - return msk; -} - - int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) { int result; LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = 0x0b; + hdw->cmd_buffer[0] = FX2CMD_GET_USB_SPEED; result = pvr2_send_request(hdw, hdw->cmd_buffer,1, hdw->cmd_buffer,1); @@ -2386,14 +2560,25 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) } -/* Return bit mask indicating signal status */ -unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw) +/* Execute poll of tuner status */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) +{ + LOCK_TAKE(hdw->big_lock); do { + pvr2_i2c_core_status_poll(hdw); + } while (0); LOCK_GIVE(hdw->big_lock); +} + + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { - unsigned int msk = 0; LOCK_TAKE(hdw->big_lock); do { - msk = pvr2_hdw_get_signal_status_internal(hdw); + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } + memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); } while (0); LOCK_GIVE(hdw->big_lock); - return msk; + return 0; } @@ -2442,14 +2627,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag) pvr2_trace(PVR2_TRACE_FIRMWARE, "Preparing to suck out CPU firmware"); hdw->fw_size = 0x2000; - hdw->fw_buffer = kmalloc(hdw->fw_size,GFP_KERNEL); + hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); if (!hdw->fw_buffer) { hdw->fw_size = 0; break; } - memset(hdw->fw_buffer,0,hdw->fw_size); - /* We have to hold the CPU during firmware upload. */ pvr2_hdw_cpureset_assert(hdw,1); @@ -2513,16 +2696,28 @@ int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs, } -int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw) +int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw, + enum pvr2_v4l_type index) { - return hdw->v4l_minor_number; + switch (index) { + case pvr2_v4l_type_video: return hdw->v4l_minor_number_video; + case pvr2_v4l_type_vbi: return hdw->v4l_minor_number_vbi; + case pvr2_v4l_type_radio: return hdw->v4l_minor_number_radio; + default: return -1; + } } -/* Store the v4l minor device number */ -void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v) +/* Store a v4l minor device number */ +void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw, + enum pvr2_v4l_type index,int v) { - hdw->v4l_minor_number = v; + switch (index) { + case pvr2_v4l_type_video: hdw->v4l_minor_number_video = v; + case pvr2_v4l_type_vbi: hdw->v4l_minor_number_vbi = v; + case pvr2_v4l_type_radio: hdw->v4l_minor_number_radio = v; + default: break; + } } @@ -2804,7 +2999,7 @@ int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data) LOCK_TAKE(hdw->ctl_lock); - hdw->cmd_buffer[0] = 0x04; /* write register prefix */ + hdw->cmd_buffer[0] = FX2CMD_REG_WRITE; /* write register prefix */ PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data); hdw->cmd_buffer[5] = 0; hdw->cmd_buffer[6] = (reg >> 8) & 0xff; @@ -2825,7 +3020,7 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) LOCK_TAKE(hdw->ctl_lock); - hdw->cmd_buffer[0] = 0x05; /* read register prefix */ + hdw->cmd_buffer[0] = FX2CMD_REG_READ; /* read register prefix */ hdw->cmd_buffer[1] = 0; hdw->cmd_buffer[2] = 0; hdw->cmd_buffer[3] = 0; @@ -2843,39 +3038,6 @@ static int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data) } -static int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res) -{ - int ret; - - LOCK_TAKE(hdw->ctl_lock); - - hdw->cmd_buffer[0] = (data >> 8) & 0xff; - hdw->cmd_buffer[1] = data & 0xff; - - ret = pvr2_send_request(hdw, hdw->cmd_buffer, 2, hdw->cmd_buffer, res); - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - - -static int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res) -{ - int ret; - - LOCK_TAKE(hdw->ctl_lock); - - hdw->cmd_buffer[0] = data; - - ret = pvr2_send_request(hdw, hdw->cmd_buffer, 1, hdw->cmd_buffer, res); - - LOCK_GIVE(hdw->ctl_lock); - - return ret; -} - - static void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw) { if (!hdw->flag_ok) return; @@ -2949,7 +3111,7 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) LOCK_TAKE(hdw->ctl_lock); do { pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset"); hdw->flag_ok = !0; - hdw->cmd_buffer[0] = 0xdd; + hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; @@ -2961,7 +3123,7 @@ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) int status; LOCK_TAKE(hdw->ctl_lock); do { pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); - hdw->cmd_buffer[0] = 0xde; + hdw->cmd_buffer[0] = FX2CMD_POWER_ON; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; @@ -2994,7 +3156,8 @@ static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { int status; LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = (runFl ? 0x36 : 0x37); + hdw->cmd_buffer[0] = + (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); if (!status) { @@ -3093,7 +3256,7 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { int result; LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = 0xeb; + hdw->cmd_buffer[0] = FX2CMD_GET_EEPROM_ADDR; result = pvr2_send_request(hdw, hdw->cmd_buffer,1, hdw->cmd_buffer,1); @@ -3105,8 +3268,8 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - u32 chip_id,unsigned long reg_id, - int setFl,u32 *val_ptr) + u32 match_type, u32 match_chip, u64 reg_id, + int setFl,u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG struct list_head *item; @@ -3115,16 +3278,23 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, int stat = 0; int okFl = 0; - req.i2c_id = chip_id; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + + req.match_type = match_type; + req.match_chip = match_chip; req.reg = reg_id; if (setFl) req.val = *val_ptr; mutex_lock(&hdw->i2c_list_lock); do { list_for_each(item,&hdw->i2c_clients) { cp = list_entry(item,struct pvr2_i2c_client,list); - if (cp->client->driver->id != chip_id) continue; + if (!v4l2_chip_match_i2c_client( + cp->client, + req.match_type, req.match_chip)) { + continue; + } stat = pvr2_i2c_client_cmd( - cp,(setFl ? VIDIOC_INT_S_REGISTER : - VIDIOC_INT_G_REGISTER),&req); + cp,(setFl ? VIDIOC_DBG_S_REGISTER : + VIDIOC_DBG_G_REGISTER),&req); if (!setFl) *val_ptr = req.val; okFl = !0; break; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 29979bb..4dba8d0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -44,12 +44,6 @@ #define PVR2_CVAL_INPUT_COMPOSITE 2 #define PVR2_CVAL_INPUT_RADIO 3 -/* Values that pvr2_hdw_get_signal_status() returns */ -#define PVR2_SIGNAL_OK 0x0001 -#define PVR2_SIGNAL_STEREO 0x0002 -#define PVR2_SIGNAL_SAP 0x0004 - - /* Subsystem definitions - these are various pieces that can be independently stopped / started. Usually you don't want to mess with this directly (let the driver handle things itself), but it is useful @@ -72,10 +66,17 @@ PVR2_SUBSYS_RUN_ALL ) enum pvr2_config { - pvr2_config_empty, - pvr2_config_mpeg, - pvr2_config_vbi, - pvr2_config_radio, + pvr2_config_empty, /* No configuration */ + pvr2_config_mpeg, /* Encoded / compressed video */ + pvr2_config_vbi, /* Standard vbi info */ + pvr2_config_pcm, /* Audio raw pcm stream */ + pvr2_config_rawvideo, /* Video raw frames */ +}; + +enum pvr2_v4l_type { + pvr2_v4l_type_video, + pvr2_v4l_type_vbi, + pvr2_v4l_type_radio, }; const char *pvr2_config_get_name(enum pvr2_config); @@ -123,6 +124,9 @@ struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *); /* Retrieve serial number of device */ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *); +/* Retrieve bus location info of device */ +const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *); + /* Called when hardware has been unplugged */ void pvr2_hdw_disconnect(struct pvr2_hdw *); @@ -148,8 +152,11 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); -/* Return PVR2_SIGNAL_XXXX bit mask indicating signal status */ -unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *); +/* Mark tuner status stale so that it will be re-fetched */ +void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *); + +/* Return information about the tuner */ +int pvr2_hdw_get_tuner_status(struct pvr2_hdw *,struct v4l2_tuner *); /* Query device and see if it thinks it is on a high-speed USB link */ int pvr2_hdw_is_hsm(struct pvr2_hdw *); @@ -205,20 +212,22 @@ int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *); int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs, char *buf,unsigned int cnt); -/* Retrieve previously stored v4l minor device number */ -int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *); +/* Retrieve a previously stored v4l minor device number */ +int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *,enum pvr2_v4l_type index); -/* Store the v4l minor device number */ -void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,int); +/* Store a v4l minor device number */ +void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, + enum pvr2_v4l_type index,int); /* Direct read/write access to chip's registers: - chip_id - unique id of chip (e.g. I2C_DRIVERD_xxxx) + match_type - how to interpret match_chip (e.g. driver ID, i2c address) + match_chip - chip match value (e.g. I2C_DRIVERD_xxxx) reg_id - register number to access setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ int pvr2_hdw_register_access(struct pvr2_hdw *, - u32 chip_id,unsigned long reg_id, - int setFl,u32 *val_ptr); + u32 match_type, u32 match_chip,u64 reg_id, + int setFl,u64 *val_ptr); /* The following entry points are all lower level things you normally don't want to worry about. */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 0512166..4977376 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -33,15 +33,17 @@ #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) #define OP_STANDARD 0 -#define OP_BCSH 1 -#define OP_VOLUME 2 -#define OP_FREQ 3 -#define OP_AUDIORATE 4 -#define OP_SIZE 5 -#define OP_LOG 6 +#define OP_AUDIOMODE 1 +#define OP_BCSH 2 +#define OP_VOLUME 3 +#define OP_FREQ 4 +#define OP_AUDIORATE 5 +#define OP_SIZE 6 +#define OP_LOG 7 static const struct pvr2_i2c_op * const ops[] = { [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, + [OP_AUDIOMODE] = &pvr2_i2c_op_v4l2_audiomode, [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, @@ -54,11 +56,13 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) int id; id = cp->client->driver->id; cp->ctl_mask = ((1 << OP_STANDARD) | + (1 << OP_AUDIOMODE) | (1 << OP_BCSH) | (1 << OP_VOLUME) | (1 << OP_FREQ) | (1 << OP_SIZE) | (1 << OP_LOG)); + cp->status_poll = pvr2_v4l2_cmd_status_poll; if (id == I2C_DRIVERID_MSP3400) { if (pvr2_i2c_msp3400_setup(hdw,cp)) { diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 05ea17a..c650e02 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -24,22 +24,26 @@ #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" #include <linux/videodev2.h> - +#include <media/v4l2-common.h> static void set_standard(struct pvr2_hdw *hdw) { - v4l2_std_id vs; - vs = hdw->std_mask_cur; - pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_standard(0x%llx)",(long long unsigned)vs); - - pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_standard"); + + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + pvr2_i2c_core_cmd(hdw,AUDC_SET_RADIO,NULL); + } else { + v4l2_std_id vs; + vs = hdw->std_mask_cur; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); + } + hdw->tuner_signal_stale = !0; } static int check_standard(struct pvr2_hdw *hdw) { - return hdw->std_dirty != 0; + return (hdw->input_dirty != 0) || (hdw->std_dirty != 0); } @@ -136,16 +140,53 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { }; +static void set_audiomode(struct pvr2_hdw *hdw) +{ + struct v4l2_tuner vt; + memset(&vt,0,sizeof(vt)); + vt.audmode = hdw->audiomode_val; + pvr2_i2c_core_cmd(hdw,VIDIOC_S_TUNER,&vt); +} + + +static int check_audiomode(struct pvr2_hdw *hdw) +{ + return (hdw->input_dirty || + hdw->audiomode_dirty); +} + + +const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode = { + .check = check_audiomode, + .update = set_audiomode, + .name = "v4l2_audiomode", +}; + + static void set_frequency(struct pvr2_hdw *hdw) { unsigned long fv; struct v4l2_frequency freq; - fv = hdw->freqVal; + fv = pvr2_hdw_get_cur_freq(hdw); pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); + if (hdw->tuner_signal_stale) { + pvr2_i2c_core_status_poll(hdw); + } memset(&freq,0,sizeof(freq)); - freq.frequency = fv / 62500; + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + // ((fv * 1000) / 62500) + freq.frequency = (fv * 2) / 125; + } else { + freq.frequency = fv / 62500; + } + /* tuner-core currently doesn't seem to care about this, but + let's set it anyway for completeness. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + freq.type = V4L2_TUNER_RADIO; + } else { + freq.type = V4L2_TUNER_ANALOG_TV; + } freq.tuner = 0; - freq.type = V4L2_TUNER_ANALOG_TV; pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); } @@ -221,6 +262,12 @@ void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) } +void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) +{ + pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&cp->hdw->tuner_signal_info); +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index ecabddb..c838df6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -26,13 +26,16 @@ #include "pvrusb2-i2c-core.h" extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; +extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); +void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *); #endif /* __PVRUSB2_CMD_V4L2_H */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 62a7cfc..58fc3c7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -22,6 +22,7 @@ #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" +#include "pvrusb2-fx2-cmd.h" #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -66,7 +67,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); /* Set up command buffer for an I2C write */ - hdw->cmd_buffer[0] = 0x08; /* write prefix */ + hdw->cmd_buffer[0] = FX2CMD_I2C_WRITE; /* write prefix */ hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */ hdw->cmd_buffer[2] = length; /* length of what follows */ if (length) memcpy(hdw->cmd_buffer + 3, data, length); @@ -128,7 +129,7 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer)); /* Set up command buffer for an I2C write followed by a read */ - hdw->cmd_buffer[0] = 0x09; /* read prefix */ + hdw->cmd_buffer[0] = FX2CMD_I2C_READ; /* read prefix */ hdw->cmd_buffer[1] = dlen; /* arg length */ hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one more byte (status). */ @@ -221,7 +222,7 @@ static int i2c_24xxx_ir(struct pvr2_hdw *hdw, /* Issue a command to the FX2 to read the IR receiver. */ LOCK_TAKE(hdw->ctl_lock); do { - hdw->cmd_buffer[0] = 0xec; + hdw->cmd_buffer[0] = FX2CMD_GET_IR_CODE; stat = pvr2_send_request(hdw, hdw->cmd_buffer,1, hdw->cmd_buffer,4); @@ -590,6 +591,33 @@ static int handler_check(struct pvr2_i2c_client *cp) #define BUFSIZE 500 + +void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw) +{ + struct list_head *item; + struct pvr2_i2c_client *cp; + mutex_lock(&hdw->i2c_list_lock); do { + struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + memset(vtp,0,sizeof(*vtp)); + list_for_each(item,&hdw->i2c_clients) { + cp = list_entry(item,struct pvr2_i2c_client,list); + if (!cp->detected_flag) continue; + if (!cp->status_poll) continue; + cp->status_poll(cp); + } + hdw->tuner_signal_stale = 0; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll" + " type=%u strength=%u audio=0x%x cap=0x%x" + " low=%u hi=%u", + vtp->type, + vtp->signal,vtp->rxsubchans,vtp->capability, + vtp->rangelow,vtp->rangehigh); + } while (0); mutex_unlock(&hdw->i2c_list_lock); +} + + +/* Issue various I2C operations to bring chip-level drivers into sync with + state stored in this driver. */ void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) { unsigned long msk; @@ -870,12 +898,12 @@ static int pvr2_i2c_attach_inform(struct i2c_client *client) struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); struct pvr2_i2c_client *cp; int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); - cp = kmalloc(sizeof(*cp),GFP_KERNEL); + cp = kzalloc(sizeof(*cp),GFP_KERNEL); trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", client->name, client->addr,cp); if (!cp) return -ENOMEM; - memset(cp,0,sizeof(*cp)); + cp->hdw = hdw; INIT_LIST_HEAD(&cp->list); cp->client = client; mutex_lock(&hdw->i2c_list_lock); do { @@ -948,8 +976,7 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) printk("%s: i2c scan beginning\n",hdw->name); for (i = 0; i < 128; i++) { msg[0].addr = i; - rc = i2c_transfer(&hdw->i2c_adap,msg, - sizeof(msg)/sizeof(msg[0])); + rc = i2c_transfer(&hdw->i2c_adap,msg, ARRAY_SIZE(msg)); if (rc != 1) continue; printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i); } diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index 6d7e252..bd0807b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -35,10 +35,12 @@ struct pvr2_i2c_client { struct i2c_client *client; struct pvr2_i2c_handler *handler; struct list_head list; + struct pvr2_hdw *hdw; int detected_flag; int recv_enable; unsigned long pend_mask; unsigned long ctl_mask; + void (*status_poll)(struct pvr2_i2c_client *); }; struct pvr2_i2c_handler { @@ -67,6 +69,7 @@ int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); int pvr2_i2c_core_check_stale(struct pvr2_hdw *); void pvr2_i2c_core_sync(struct pvr2_hdw *); +void pvr2_i2c_core_status_poll(struct pvr2_hdw *); unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); #define PVR2_I2C_DETAIL_DEBUG 0x0001 #define PVR2_I2C_DETAIL_HANDLER 0x0002 diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c index 57fb320..ce3c898 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-io.c +++ b/drivers/media/video/pvrusb2/pvrusb2-io.c @@ -474,9 +474,8 @@ static void buffer_complete(struct urb *urb) struct pvr2_stream *pvr2_stream_create(void) { struct pvr2_stream *sp; - sp = kmalloc(sizeof(*sp),GFP_KERNEL); + sp = kzalloc(sizeof(*sp),GFP_KERNEL); if (!sp) return sp; - memset(sp,0,sizeof(*sp)); pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp); pvr2_stream_init(sp); return sp; diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c index b71f9a9..f782418 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-ioread.c +++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c @@ -87,10 +87,9 @@ static void pvr2_ioread_done(struct pvr2_ioread *cp) struct pvr2_ioread *pvr2_ioread_create(void) { struct pvr2_ioread *cp; - cp = kmalloc(sizeof(*cp),GFP_KERNEL); + cp = kzalloc(sizeof(*cp),GFP_KERNEL); if (!cp) return NULL; pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp); - memset(cp,0,sizeof(*cp)); if (pvr2_ioread_init(cp) < 0) { kfree(cp); return NULL; diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c index f95c598..81de26b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -78,14 +78,14 @@ struct std_name { #define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) /* Mapping of standard bits to color system */ -const static struct std_name std_groups[] = { +static const struct std_name std_groups[] = { {"PAL",CSTD_PAL}, {"NTSC",CSTD_NTSC}, {"SECAM",CSTD_SECAM}, }; /* Mapping of standard bits to modulation system */ -const static struct std_name std_items[] = { +static const struct std_name std_items[] = { {"B",TSTD_B}, {"B1",TSTD_B1}, {"D",TSTD_D}, @@ -141,10 +141,8 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, cnt = 0; while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++; if (cnt >= bufSize) return 0; // No more characters - sp = find_std_name( - std_groups, - sizeof(std_groups)/sizeof(std_groups[0]), - bufPtr,cnt); + sp = find_std_name(std_groups, ARRAY_SIZE(std_groups), + bufPtr,cnt); if (!sp) return 0; // Illegal color system name cnt++; bufPtr += cnt; @@ -163,8 +161,7 @@ int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr, if (ch == '/') break; cnt++; } - sp = find_std_name(std_items, - sizeof(std_items)/sizeof(std_items[0]), + sp = find_std_name(std_items, ARRAY_SIZE(std_items), bufPtr,cnt); if (!sp) return 0; // Illegal modulation system ID t = sp->id & cmsk; @@ -189,14 +186,10 @@ unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize, unsigned int c1,c2; cfl = 0; c1 = 0; - for (idx1 = 0; - idx1 < sizeof(std_groups)/sizeof(std_groups[0]); - idx1++) { + for (idx1 = 0; idx1 < ARRAY_SIZE(std_groups); idx1++) { gp = std_groups + idx1; gfl = 0; - for (idx2 = 0; - idx2 < sizeof(std_items)/sizeof(std_items[0]); - idx2++) { + for (idx2 = 0; idx2 < ARRAY_SIZE(std_items); idx2++) { ip = std_items + idx2; if (!(gp->id & ip->id & id)) continue; if (!gfl) { @@ -279,7 +272,7 @@ static struct v4l2_standard generic_standards[] = { } }; -#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0])) +#define generic_standards_cnt ARRAY_SIZE(generic_standards) static struct v4l2_standard *match_std(v4l2_std_id id) { @@ -348,7 +341,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, fmsk |= idmsk; } - for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) { + for (idx2 = 0; idx2 < ARRAY_SIZE(std_mixes); idx2++) { if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; } @@ -366,16 +359,15 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, std_cnt); if (!std_cnt) return NULL; // paranoia - stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt, + stddefs = kzalloc(sizeof(struct v4l2_standard) * std_cnt, GFP_KERNEL); - memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt); for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx; idx = 0; /* Enumerate potential special cases */ - for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) && - (idx < std_cnt)); idx2++) { + for (idx2 = 0; (idx2 < ARRAY_SIZE(std_mixes)) && (idx < std_cnt); + idx2++) { if (!(id & std_mixes[idx2])) continue; if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index c294f46..a741c556 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -40,9 +40,13 @@ struct pvr2_sysfs { struct pvr2_sysfs_ctl_item *item_first; struct pvr2_sysfs_ctl_item *item_last; struct class_device_attribute attr_v4l_minor_number; + struct class_device_attribute attr_v4l_radio_minor_number; struct class_device_attribute attr_unit_number; + struct class_device_attribute attr_bus_info; int v4l_minor_number_created_ok; + int v4l_radio_minor_number_created_ok; int unit_number_created_ok; + int bus_info_created_ok; }; #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC @@ -491,7 +495,7 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) unsigned int cnt,acnt; int ret; - if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) { + if ((ctl_id < 0) || (ctl_id >= ARRAY_SIZE(funcs))) { return; } @@ -499,9 +503,8 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id) cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id); if (!cptr) return; - cip = kmalloc(sizeof(*cip),GFP_KERNEL); + cip = kzalloc(sizeof(*cip),GFP_KERNEL); if (!cip) return; - memset(cip,0,sizeof(*cip)); pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip); cip->cptr = cptr; @@ -611,9 +614,8 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp) struct pvr2_sysfs_debugifc *dip; int ret; - dip = kmalloc(sizeof(*dip),GFP_KERNEL); + dip = kzalloc(sizeof(*dip),GFP_KERNEL); if (!dip) return; - memset(dip,0,sizeof(*dip)); dip->attr_debugcmd.attr.owner = THIS_MODULE; dip->attr_debugcmd.attr.name = "debugcmd"; dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP; @@ -705,10 +707,18 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp) pvr2_sysfs_tear_down_debugifc(sfp); #endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */ pvr2_sysfs_tear_down_controls(sfp); + if (sfp->bus_info_created_ok) { + class_device_remove_file(sfp->class_dev, + &sfp->attr_bus_info); + } if (sfp->v4l_minor_number_created_ok) { class_device_remove_file(sfp->class_dev, &sfp->attr_v4l_minor_number); } + if (sfp->v4l_radio_minor_number_created_ok) { + class_device_remove_file(sfp->class_dev, + &sfp->attr_v4l_radio_minor_number); + } if (sfp->unit_number_created_ok) { class_device_remove_file(sfp->class_dev, &sfp->attr_unit_number); @@ -726,7 +736,30 @@ static ssize_t v4l_minor_number_show(struct class_device *class_dev,char *buf) sfp = (struct pvr2_sysfs *)class_dev->class_data; if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", - pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw)); + pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, + pvr2_v4l_type_video)); +} + + +static ssize_t bus_info_show(struct class_device *class_dev,char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->class_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%s\n", + pvr2_hdw_get_bus_info(sfp->channel.hdw)); +} + + +static ssize_t v4l_radio_minor_number_show(struct class_device *class_dev, + char *buf) +{ + struct pvr2_sysfs *sfp; + sfp = (struct pvr2_sysfs *)class_dev->class_data; + if (!sfp) return -EINVAL; + return scnprintf(buf,PAGE_SIZE,"%d\n", + pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, + pvr2_v4l_type_radio)); } @@ -749,9 +782,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp, usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw); if (!usb_dev) return; - class_dev = kmalloc(sizeof(*class_dev),GFP_KERNEL); + class_dev = kzalloc(sizeof(*class_dev),GFP_KERNEL); if (!class_dev) return; - memset(class_dev,0,sizeof(*class_dev)); pvr2_sysfs_trace("Creating class_dev id=%p",class_dev); @@ -793,6 +825,20 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->v4l_minor_number_created_ok = !0; } + sfp->attr_v4l_radio_minor_number.attr.owner = THIS_MODULE; + sfp->attr_v4l_radio_minor_number.attr.name = "v4l_radio_minor_number"; + sfp->attr_v4l_radio_minor_number.attr.mode = S_IRUGO; + sfp->attr_v4l_radio_minor_number.show = v4l_radio_minor_number_show; + sfp->attr_v4l_radio_minor_number.store = NULL; + ret = class_device_create_file(sfp->class_dev, + &sfp->attr_v4l_radio_minor_number); + if (ret < 0) { + printk(KERN_WARNING "%s: class_device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->v4l_radio_minor_number_created_ok = !0; + } + sfp->attr_unit_number.attr.owner = THIS_MODULE; sfp->attr_unit_number.attr.name = "unit_number"; sfp->attr_unit_number.attr.mode = S_IRUGO; @@ -806,6 +852,20 @@ static void class_dev_create(struct pvr2_sysfs *sfp, sfp->unit_number_created_ok = !0; } + sfp->attr_bus_info.attr.owner = THIS_MODULE; + sfp->attr_bus_info.attr.name = "bus_info_str"; + sfp->attr_bus_info.attr.mode = S_IRUGO; + sfp->attr_bus_info.show = bus_info_show; + sfp->attr_bus_info.store = NULL; + ret = class_device_create_file(sfp->class_dev, + &sfp->attr_bus_info); + if (ret < 0) { + printk(KERN_WARNING "%s: class_device_create_file error: %d\n", + __FUNCTION__, ret); + } else { + sfp->bus_info_created_ok = !0; + } + pvr2_sysfs_add_controls(sfp); #ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC pvr2_sysfs_add_debugifc(sfp); @@ -829,9 +889,8 @@ struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp, struct pvr2_sysfs_class *class_ptr) { struct pvr2_sysfs *sfp; - sfp = kmalloc(sizeof(*sfp),GFP_KERNEL); + sfp = kzalloc(sizeof(*sfp),GFP_KERNEL); if (!sfp) return sfp; - memset(sfp,0,sizeof(*sfp)); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp); pvr2_channel_init(&sfp->channel,mp); sfp->channel.check_func = pvr2_sysfs_internal_check; @@ -852,9 +911,8 @@ static int pvr2_sysfs_hotplug(struct class_device *cd,char **envp, struct pvr2_sysfs_class *pvr2_sysfs_class_create(void) { struct pvr2_sysfs_class *clp; - clp = kmalloc(sizeof(*clp),GFP_KERNEL); + clp = kzalloc(sizeof(*clp),GFP_KERNEL); if (!clp) return clp; - memset(clp,0,sizeof(*clp)); pvr2_sysfs_trace("Creating pvr2_sysfs_class id=%p",clp); clp->class.name = "pvrusb2"; clp->class.class_release = pvr2_sysfs_class_release; diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c index af9f246..05e65ce 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.c @@ -80,7 +80,7 @@ static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *bu } -const static struct pvr2_i2c_handler_functions tuner_funcs = { +static const struct pvr2_i2c_handler_functions tuner_funcs = { .detach = (void (*)(void *))pvr2_tuner_detach, .check = (int (*)(void *))tuner_check, .update = (void (*)(void *))tuner_update, @@ -93,9 +93,8 @@ int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) struct pvr2_tuner_handler *ctxt; if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->i2c_handler.func_data = ctxt; ctxt->i2c_handler.func_table = &tuner_funcs; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 6cf17080..4563b3d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -40,7 +40,10 @@ struct pvr2_v4l2_dev { struct video_device devbase; /* MUST be first! */ struct pvr2_v4l2 *v4lp; struct pvr2_context_stream *stream; - enum pvr2_config config; + /* Information about this device: */ + enum pvr2_config config; /* Expected stream format */ + int v4l_type; /* V4L defined type for this device node */ + enum pvr2_v4l_type minor_type; /* pvr2-understood minor device type */ }; struct pvr2_v4l2_fh { @@ -54,6 +57,7 @@ struct pvr2_v4l2_fh { struct pvr2_v4l2_fh *vprev; wait_queue_head_t wait_data; int fw_mode_flag; + int prev_input_val; }; struct pvr2_v4l2 { @@ -63,13 +67,22 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; - /* streams */ - struct pvr2_v4l2_dev *vdev; + /* streams - Note that these must be separately, individually, + * allocated pointers. This is because the v4l core is going to + * manage their deletion - separately, individually... */ + struct pvr2_v4l2_dev *dev_video; + struct pvr2_v4l2_dev *dev_radio; }; static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; module_param_array(video_nr, int, NULL, 0444); -MODULE_PARM_DESC(video_nr, "Offset for device's minor"); +MODULE_PARM_DESC(video_nr, "Offset for device's video dev minor"); +static int radio_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Offset for device's radio dev minor"); +static int vbi_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1}; +module_param_array(vbi_nr, int, NULL, 0444); +MODULE_PARM_DESC(vbi_nr, "Offset for device's vbi dev minor"); static struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", @@ -77,30 +90,11 @@ static struct v4l2_capability pvr_capability ={ .bus_info = "usb", .version = KERNEL_VERSION(0,8,0), .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE | - V4L2_CAP_TUNER | V4L2_CAP_AUDIO | + V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), .reserved = {0,0,0,0} }; -static struct v4l2_tuner pvr_v4l2_tuners[]= { - { - .index = 0, - .name = "TV Tuner", - .type = V4L2_TUNER_ANALOG_TV, - .capability = (V4L2_TUNER_CAP_NORM | - V4L2_TUNER_CAP_STEREO | - V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2), - .rangelow = 0, - .rangehigh = 0, - .rxsubchans = V4L2_TUNER_SUB_STEREO, - .audmode = V4L2_TUNER_MODE_STEREO, - .signal = 0, - .afc = 0, - .reserved = {0,0,0,0} - } -}; - static struct v4l2_fmtdesc pvr_fmtdesc [] = { { .index = 0, @@ -154,6 +148,18 @@ static struct v4l2_format pvr_format [] = { } }; + +static const char *get_v4l_name(int v4l_type) +{ + switch (v4l_type) { + case VFL_TYPE_GRABBER: return "video"; + case VFL_TYPE_RADIO: return "radio"; + case VFL_TYPE_VBI: return "vbi"; + default: return "?"; + } +} + + /* * pvr_ioctl() * @@ -197,6 +203,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_capability *cap = arg; memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); ret = 0; break; @@ -315,13 +323,39 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_ENUMAUDIO: { + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + struct v4l2_audio *vin = arg; ret = -EINVAL; + if (vin->index > 0) break; + strncpy(vin->name, "PVRUSB2 Audio",14); + vin->capability = V4L2_AUDCAP_STEREO; + ret = 0; + break; break; } case VIDIOC_G_AUDIO: { - ret = -EINVAL; + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + struct v4l2_audio *vin = arg; + memset(vin,0,sizeof(*vin)); + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio",14); + vin->capability = V4L2_AUDCAP_STEREO; + ret = 0; break; } @@ -333,34 +367,11 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - unsigned int status_mask; - int val; - if (vt->index !=0) break; - status_mask = pvr2_hdw_get_signal_status(hdw); + if (vt->index != 0) break; /* Only answer for the 1st tuner */ - memcpy(vt, &pvr_v4l2_tuners[vt->index], - sizeof(struct v4l2_tuner)); - - vt->signal = 0; - if (status_mask & PVR2_SIGNAL_OK) { - if (status_mask & PVR2_SIGNAL_STEREO) { - vt->rxsubchans = V4L2_TUNER_SUB_STEREO; - } else { - vt->rxsubchans = V4L2_TUNER_SUB_MONO; - } - if (status_mask & PVR2_SIGNAL_SAP) { - vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2); - } - vt->signal = 65535; - } - - val = 0; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), - &val); - vt->audmode = val; + pvr2_hdw_execute_tuner_poll(hdw); + ret = pvr2_hdw_get_tuner_status(hdw,vt); break; } @@ -374,14 +385,40 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), vt->audmode); + break; } case VIDIOC_S_FREQUENCY: { const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; + unsigned long fv; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + ret = pvr2_hdw_get_tuner_status(hdw,&vt); + if (ret != 0) break; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp,&cur_input); + if (ret != 0) break; + if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(ctrlp, + PVR2_CVAL_INPUT_RADIO); + } + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(ctrlp, + PVR2_CVAL_INPUT_TV); + } + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) { + fv = (fv * 125) / 2; + } else { + fv = fv * 62500; + } ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), - vf->frequency * 62500); + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); break; } @@ -389,10 +426,27 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; int val = 0; + int cur_input; + struct v4l2_tuner vt; + ret = pvr2_hdw_get_tuner_status(hdw,&vt); + if (ret != 0) break; ret = pvr2_ctrl_get_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), &val); - val /= 62500; + if (ret != 0) break; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), + &cur_input); + if (cur_input == PVR2_CVAL_INPUT_RADIO) { + vf->type = V4L2_TUNER_RADIO; + } else { + vf->type = V4L2_TUNER_ANALOG_TV; + } + if (vt.capability & V4L2_TUNER_CAP_LOW) { + val = (val * 2) / 125; + } else { + val /= 62500; + } vf->frequency = val; break; } @@ -449,7 +503,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, ret = 0; switch(vf->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - int lmin,lmax; + int lmin,lmax,ldef; struct pvr2_ctrl *hcp,*vcp; int h = vf->fmt.pix.height; int w = vf->fmt.pix.width; @@ -458,14 +512,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, lmin = pvr2_ctrl_get_min(hcp); lmax = pvr2_ctrl_get_max(hcp); - if (w < lmin) { + ldef = pvr2_ctrl_get_def(hcp); + if (w == -1) { + w = ldef; + } else if (w < lmin) { w = lmin; } else if (w > lmax) { w = lmax; } lmin = pvr2_ctrl_get_min(vcp); lmax = pvr2_ctrl_get_max(vcp); - if (h < lmin) { + ldef = pvr2_ctrl_get_def(vcp); + if (h == -1) { + h = ldef; + } else if (h < lmin) { h = lmin; } else if (h > lmax) { h = lmax; @@ -494,6 +554,13 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_STREAMON: { + if (!fh->dev_info->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + ret = -EPERM; + break; + } ret = pvr2_hdw_set_stream_type(hdw,dev_info->config); if (ret < 0) return ret; ret = pvr2_hdw_set_streaming(hdw,!0); @@ -502,6 +569,13 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_STREAMOFF: { + if (!fh->dev_info->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + ret = -EPERM; + break; + } ret = pvr2_hdw_set_streaming(hdw,0); break; } @@ -599,6 +673,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_ext_control *ctrl; unsigned int idx; int val; + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; ret = pvr2_ctrl_get_value( @@ -621,6 +696,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, (struct v4l2_ext_controls *)arg; struct v4l2_ext_control *ctrl; unsigned int idx; + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; ret = pvr2_ctrl_set_value( @@ -643,6 +719,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, unsigned int idx; /* For the moment just validate that the requested control actually exists. */ + ret = 0; for (idx = 0; idx < ctls->count; idx++) { ctrl = ctls->controls + idx; pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); @@ -662,16 +739,16 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, break; } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: { - u32 val; + u64 val; struct v4l2_register *req = (struct v4l2_register *)arg; - if (cmd == VIDIOC_INT_S_REGISTER) val = req->val; + if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; ret = pvr2_hdw_register_access( - hdw,req->i2c_id,req->reg, - cmd == VIDIOC_INT_S_REGISTER,&val); - if (cmd == VIDIOC_INT_G_REGISTER) req->val = val; + hdw,req->match_type,req->match_chip,req->reg, + cmd == VIDIOC_DBG_S_REGISTER,&val); + if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; break; } #endif @@ -707,8 +784,12 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { - printk(KERN_INFO "pvrusb2: unregistering device video%d [%s]\n", - dip->devbase.minor,pvr2_config_get_name(dip->config)); + int minor_id = dip->devbase.minor; + struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; + enum pvr2_config cfg = dip->config; + int v4l_type = dip->v4l_type; + + pvr2_hdw_v4l_store_minor_number(hdw,dip->minor_type,-1); /* Paranoia */ dip->v4lp = NULL; @@ -717,13 +798,24 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) /* Actual deallocation happens later when all internal references are gone. */ video_unregister_device(&dip->devbase); + + printk(KERN_INFO "pvrusb2: unregistered device %s%u [%s]\n", + get_v4l_name(v4l_type),minor_id & 0x1f, + pvr2_config_get_name(cfg)); + } static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { - pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,-1); - pvr2_v4l2_dev_destroy(vp->vdev); + if (vp->dev_video) { + pvr2_v4l2_dev_destroy(vp->dev_video); + vp->dev_video = NULL; + } + if (vp->dev_radio) { + pvr2_v4l2_dev_destroy(vp->dev_radio); + vp->dev_radio = NULL; + } pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -766,23 +858,37 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) struct pvr2_v4l2_fh *fhp = file->private_data; struct pvr2_v4l2 *vp = fhp->vhead; struct pvr2_context *mp = fhp->vhead->channel.mc_head; + struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); if (fhp->rhp) { struct pvr2_stream *sp; - struct pvr2_hdw *hdw; - hdw = fhp->channel.mc_head->hdw; pvr2_hdw_set_streaming(hdw,0); sp = pvr2_ioread_get_stream(fhp->rhp); if (sp) pvr2_stream_set_callback(sp,NULL,NULL); pvr2_ioread_destroy(fhp->rhp); fhp->rhp = NULL; } + v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; pvr2_context_enter(mp); do { + /* Restore the previous input selection, if it makes sense + to do so. */ + if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { + struct pvr2_ctrl *cp; + int pval; + cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + pvr2_ctrl_get_value(cp,&pval); + /* Only restore if we're still selecting the radio */ + if (pval == PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(cp,fhp->prev_input_val); + pvr2_hdw_commit_ctl(hdw); + } + } + if (fhp->vnext) { fhp->vnext->vprev = fhp->vprev; } else { @@ -828,11 +934,10 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) return -EIO; } - fhp = kmalloc(sizeof(*fhp),GFP_KERNEL); + fhp = kzalloc(sizeof(*fhp),GFP_KERNEL); if (!fhp) { return -ENOMEM; } - memset(fhp,0,sizeof(*fhp)); init_waitqueue_head(&fhp->wait_data); fhp->dev_info = dip; @@ -840,6 +945,7 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) pvr2_context_enter(vp->channel.mc_head); do { pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + fhp->vnext = NULL; fhp->vprev = vp->vlast; if (vp->vlast) { @@ -849,6 +955,18 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) } vp->vlast = fhp; fhp->vhead = vp; + + /* Opening the /dev/radioX device implies a mode switch. + So execute that here. Note that you can get the + IDENTICAL effect merely by opening the normal video + device and setting the input appropriately. */ + if (dip->v4l_type == VFL_TYPE_RADIO) { + struct pvr2_ctrl *cp; + cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + pvr2_ctrl_get_value(cp,&fhp->prev_input_val); + pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); + pvr2_hdw_commit_ctl(hdw); + } } while (0); pvr2_context_exit(vp->channel.mc_head); fhp->file = file; @@ -873,6 +991,12 @@ static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh) struct pvr2_hdw *hdw; if (fh->rhp) return 0; + if (!fh->dev_info->stream) { + /* No stream defined for this node. This means that we're + not currently allowed to stream from this node. */ + return -EPERM; + } + /* First read() attempt. Try to claim the stream and start it... */ if ((ret = pvr2_channel_claim_stream(&fh->channel, @@ -1012,25 +1136,37 @@ static struct video_device vdev_template = { static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *vp, - enum pvr2_config cfg) + int v4l_type) { int mindevnum; int unit_number; - int v4l_type; + int *nr_ptr = NULL; dip->v4lp = vp; - dip->config = cfg; - switch (cfg) { - case pvr2_config_mpeg: - v4l_type = VFL_TYPE_GRABBER; + dip->v4l_type = v4l_type; + switch (v4l_type) { + case VFL_TYPE_GRABBER: dip->stream = &vp->channel.mc_head->video_stream; + dip->config = pvr2_config_mpeg; + dip->minor_type = pvr2_v4l_type_video; + nr_ptr = video_nr; + if (!dip->stream) { + err("Failed to set up pvrusb2 v4l video dev" + " due to missing stream instance"); + return; + } break; - case pvr2_config_vbi: - v4l_type = VFL_TYPE_VBI; + case VFL_TYPE_VBI: + dip->config = pvr2_config_vbi; + dip->minor_type = pvr2_v4l_type_vbi; + nr_ptr = vbi_nr; break; - case pvr2_config_radio: - v4l_type = VFL_TYPE_RADIO; + case VFL_TYPE_RADIO: + dip->stream = &vp->channel.mc_head->video_stream; + dip->config = pvr2_config_mpeg; + dip->minor_type = pvr2_v4l_type_radio; + nr_ptr = radio_nr; break; default: /* Bail out (this should be impossible) */ @@ -1039,30 +1175,27 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, return; } - if (!dip->stream) { - err("Failed to set up pvrusb2 v4l dev" - " due to missing stream instance"); - return; - } - memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); dip->devbase.release = pvr2_video_device_release; mindevnum = -1; unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); - if ((unit_number >= 0) && (unit_number < PVR_NUM)) { - mindevnum = video_nr[unit_number]; + if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { + mindevnum = nr_ptr[unit_number]; } - if ((video_register_device(&dip->devbase, v4l_type, mindevnum) < 0) && - (video_register_device(&dip->devbase, v4l_type, -1) < 0)) { - err("Failed to register pvrusb2 v4l video device"); - } else { - printk(KERN_INFO "pvrusb2: registered device video%d [%s]\n", - dip->devbase.minor,pvr2_config_get_name(dip->config)); + if ((video_register_device(&dip->devbase, + dip->v4l_type, mindevnum) < 0) && + (video_register_device(&dip->devbase, + dip->v4l_type, -1) < 0)) { + err("Failed to register pvrusb2 v4l device"); } + printk(KERN_INFO "pvrusb2: registered device %s%u [%s]\n", + get_v4l_name(dip->v4l_type),dip->devbase.minor & 0x1f, + pvr2_config_get_name(dip->config)); + pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, - dip->devbase.minor); + dip->minor_type,dip->devbase.minor); } @@ -1070,22 +1203,24 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) { struct pvr2_v4l2 *vp; - vp = kmalloc(sizeof(*vp),GFP_KERNEL); + vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; - memset(vp,0,sizeof(*vp)); - vp->vdev = kmalloc(sizeof(*vp->vdev),GFP_KERNEL); - if (!vp->vdev) { + vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); + vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + if (!(vp->dev_video && vp->dev_radio)) { + kfree(vp->dev_video); + kfree(vp->dev_radio); kfree(vp); return NULL; } - memset(vp->vdev,0,sizeof(*vp->vdev)); pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); vp->channel.check_func = pvr2_v4l2_internal_check; /* register streams */ - pvr2_v4l2_dev_init(vp->vdev,vp,pvr2_config_mpeg); + pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); + pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); return vp; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 05f2cdd..61efa6f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -66,7 +66,9 @@ static void set_input(struct pvr2_v4l_decoder *ctxt) route.input = SAA7115_SVIDEO2; break; case PVR2_CVAL_INPUT_RADIO: - // ????? No idea yet what to do here + // In radio mode, we mute the video, but point at one + // spot just to stay consistent + route.input = SAA7115_COMPOSITE5; default: return; } @@ -137,8 +139,7 @@ static int decoder_check(struct pvr2_v4l_decoder *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (decoder_ops[idx].check(ctxt)) { @@ -154,8 +155,7 @@ static void decoder_update(struct pvr2_v4l_decoder *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -183,25 +183,13 @@ static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) } -static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt) -{ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); - if (ret < 0) return -EINVAL; - return vt.signal ? 1 : 0; -} - - static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt) { return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l"); } -const static struct pvr2_i2c_handler_functions hfuncs = { +static const struct pvr2_i2c_handler_functions hfuncs = { .detach = (void (*)(void *))decoder_detach, .check = (int (*)(void *))decoder_check, .update = (void (*)(void *))decoder_update, @@ -218,20 +206,17 @@ int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, if (cp->handler) return 0; if (!decoder_detect(cp)) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->ctrl.ctxt = ctxt; ctxt->ctrl.detach = (void (*)(void *))decoder_detach; ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ - sizeof(decoder_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; hdw->decoder_ctrl = &ctxt->ctrl; cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 2413e51..66b4d36 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -50,15 +50,21 @@ static void set_input(struct pvr2_v4l_wm8775 *ctxt) { struct v4l2_routing route; struct pvr2_hdw *hdw = ctxt->hdw; - int msk = 0; memset(&route,0,sizeof(route)); - pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", - hdw->input_val,msk); + switch(hdw->input_val) { + case PVR2_CVAL_INPUT_RADIO: + route.input = 1; + break; + default: + /* All other cases just use the second input */ + route.input = 2; + break; + } + pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d route=0x%x)", + hdw->input_val,route.input); - // Always point to input #1 no matter what - route.input = 2; pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); } @@ -99,8 +105,7 @@ static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { msk = 1 << idx; if (ctxt->stale_mask & msk) continue; if (wm8775_ops[idx].check(ctxt)) { @@ -116,8 +121,7 @@ static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) unsigned long msk; unsigned int idx; - for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); - idx++) { + for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { msk = 1 << idx; if (!(ctxt->stale_mask & msk)) continue; ctxt->stale_mask &= ~msk; @@ -126,7 +130,7 @@ static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) } -const static struct pvr2_i2c_handler_functions hfuncs = { +static const struct pvr2_i2c_handler_functions hfuncs = { .detach = (void (*)(void *))wm8775_detach, .check = (int (*)(void *))wm8775_check, .update = (void (*)(void *))wm8775_update, @@ -140,16 +144,14 @@ int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) if (cp->handler) return 0; - ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); if (!ctxt) return 0; - memset(ctxt,0,sizeof(*ctxt)); ctxt->handler.func_data = ctxt; ctxt->handler.func_table = &hfuncs; ctxt->client = cp; ctxt->hdw = hdw; - ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ - sizeof(wm8775_ops[0]))) - 1; + ctxt->stale_mask = (1 << ARRAY_SIZE(wm8775_ops)) - 1; cp->handler = &ctxt->handler; pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", cp->client->addr); diff --git a/drivers/media/video/pwc/Makefile b/drivers/media/video/pwc/Makefile index 9db2260..f5c8ec2 100644 --- a/drivers/media/video/pwc/Makefile +++ b/drivers/media/video/pwc/Makefile @@ -2,11 +2,3 @@ pwc-objs := pwc-if.o pwc-misc.o pwc-ctrl.o pwc-v4l.o pwc-uncompress.o pwc-objs += pwc-dec1.o pwc-dec23.o pwc-kiara.o pwc-timon.o obj-$(CONFIG_USB_PWC) += pwc.o - -ifeq ($(CONFIG_USB_PWC_DEBUG),y) -EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=1 -else -EXTRA_CFLAGS += -DCONFIG_PWC_DEBUG=0 -endif - - diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 0bd1155..338ced7 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -140,6 +140,8 @@ static const char *size2name[PSZ_MAX] = An alternate value of 0 means this mode is not available at all. */ +#define PWC_FPS_MAX_NALA 8 + struct Nala_table_entry { char alternate; /* USB alternate setting */ int compressed; /* Compressed yes/no */ @@ -147,7 +149,9 @@ struct Nala_table_entry { unsigned char mode[3]; /* precomputed mode table */ }; -static struct Nala_table_entry Nala_table[PSZ_MAX][8] = +static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; + +static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = { #include "pwc-nala.h" }; @@ -423,6 +427,59 @@ int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frame return 0; } +static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_NALA; i++) { + if (Nala_table[size][i].alternate) { + if (index--==0) return Nala_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { + if (Kiara_table[size][i][3].alternate) { + if (index--==0) return Kiara_fps_vector[i]; + } + } + return 0; +} + +static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int i; + + for (i=0; i < PWC_FPS_MAX_TIMON; i++) { + if (Timon_table[size][i][3].alternate) { + if (index--==0) return Timon_fps_vector[i]; + } + } + return 0; +} + +unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) +{ + unsigned int ret; + + if (DEVICE_USE_CODEC1(pdev->type)) { + ret = pwc_get_fps_Nala(pdev, index, size); + + } else if (DEVICE_USE_CODEC3(pdev->type)) { + ret = pwc_get_fps_Kiara(pdev, index, size); + + } else { + ret = pwc_get_fps_Timon(pdev, index, size); + } + + return ret; +} + #define BLACK_Y 0 #define BLACK_U 128 #define BLACK_V 128 @@ -1343,7 +1400,7 @@ int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); if (ret < 0) break; - ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); + ret = pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); if (ret < 0) break; } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 9825fd3..085332a 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -95,8 +95,8 @@ static const struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ - { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ - { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ + { USB_DEVICE(0x046D, 0x08B6) }, /* Cisco VT Camera */ + { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech ViewPort AV 100 */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ @@ -128,7 +128,7 @@ static int default_size = PSZ_QCIF; static int default_fps = 10; static int default_fbufs = 3; /* Default number of frame buffers */ int pwc_mbufs = 2; /* Default number of mmap() buffers */ -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG int pwc_trace = PWC_DEBUG_LEVEL; #endif static int power_save = 0; @@ -1051,7 +1051,7 @@ static void pwc_remove_sysfs_files(struct video_device *vdev) video_device_remove_file(vdev, &class_device_attr_button); } -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG static const char *pwc_sensor_type_to_string(unsigned int sensor_type) { switch(sensor_type) { @@ -1493,7 +1493,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id case 0x0329: PWC_INFO("Philips SPC 900NC USB webcam detected.\n"); name = "Philips SPC 900NC webcam"; - type_id = 720; + type_id = 740; break; default: return -ENODEV; @@ -1547,8 +1547,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id features |= FEATURE_MOTOR_PANTILT; break; case 0x08b6: + PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n"); + name = "Cisco VT Camera"; + type_id = 740; /* CCD sensor */ + break; case 0x08b7: - case 0x08b8: + PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n"); + name = "Logitech ViewPort AV 100"; + type_id = 740; /* CCD sensor */ + break; + case 0x08b8: /* Where this released? */ PWC_INFO("Logitech QuickCam detected (reserved ID).\n"); name = "Logitech QuickCam (res.)"; type_id = 730; /* Assuming CMOS */ @@ -1835,7 +1843,7 @@ module_param(size, charp, 0444); module_param(fps, int, 0444); module_param(fbufs, int, 0444); module_param(mbufs, int, 0444); -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG module_param_named(trace, pwc_trace, int, 0644); #endif module_param(power_save, int, 0444); @@ -1908,7 +1916,7 @@ static int __init usb_pwc_init(void) default_fbufs = fbufs; PWC_DEBUG_MODULE("Number of frame buffers set to %d.\n", default_fbufs); } -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG if (pwc_trace >= 0) { PWC_DEBUG_MODULE("Trace options: 0x%04x\n", pwc_trace); } diff --git a/drivers/media/video/pwc/pwc-ioctl.h b/drivers/media/video/pwc/pwc-ioctl.h index 784bc72..cec6602 100644 --- a/drivers/media/video/pwc/pwc-ioctl.h +++ b/drivers/media/video/pwc/pwc-ioctl.h @@ -2,7 +2,7 @@ #define PWC_IOCTL_H /* (C) 2001-2004 Nemosoft Unv. - (C) 2004 Luc Saillard (luc@saillard.org) + (C) 2004-2006 Luc Saillard (luc@saillard.org) NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx driver and thus may have bugs that are not present in the original version. @@ -25,7 +25,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* This is pwc-ioctl.h belonging to PWC 8.12.1 +/* This is pwc-ioctl.h belonging to PWC 10.0.10 It contains structures and defines to communicate from user space directly to the driver. */ @@ -51,6 +51,9 @@ ... the function */ +#include <linux/types.h> +#include <linux/version.h> + /* Enumeration of image sizes */ #define PSZ_SQCIF 0x00 @@ -65,6 +68,8 @@ /* The frame rate is encoded in the video_window.flags parameter using the upper 16 bits, since some flags are defined nowadays. The following defines provide a mask and shift to filter out this value. + This value can also be passing using the private flag when using v4l2 and + VIDIOC_S_FMT ioctl. In 'Snapshot' mode the camera freezes its automatic exposure and colour balance controls. @@ -73,6 +78,8 @@ #define PWC_FPS_MASK 0x00FF0000 #define PWC_FPS_FRMASK 0x003F0000 #define PWC_FPS_SNAPSHOT 0x00400000 +#define PWC_QLT_MASK 0x03000000 +#define PWC_QLT_SHIFT 24 /* structure for transferring x & y coordinates */ @@ -289,4 +296,29 @@ struct pwc_table_init_buffer { }; #define VIDIOCPWCGVIDTABLE _IOR('v', 216, struct pwc_table_init_buffer) +/* + * This is private command used when communicating with v4l2. + * In the future all private ioctl will be remove/replace to + * use interface offer by v4l2. + */ + +#define V4L2_CID_PRIVATE_SAVE_USER (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_RESTORE_USER (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_RESTORE_FACTORY (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_COLOUR_MODE (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AUTOCONTOUR (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_PRIVATE_CONTOUR (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_PRIVATE_BACKLIGHT (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_PRIVATE_FLICKERLESS (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_PRIVATE_NOISE_REDUCTION (V4L2_CID_PRIVATE_BASE + 8) + +struct pwc_raw_frame { + __le16 type; /* type of the webcam */ + __le16 vbandlength; /* Size of 4lines compressed (used by the decompressor) */ + __u8 cmd[4]; /* the four byte of the command (in case of nala, + only the first 3 bytes is filled) */ + __u8 rawframe[0]; /* frame_size = H/4*vbandlength */ +} __attribute__ ((packed)); + + #endif diff --git a/drivers/media/video/pwc/pwc-kiara.c b/drivers/media/video/pwc/pwc-kiara.c index fec39cc..f4ae83c 100644 --- a/drivers/media/video/pwc/pwc-kiara.c +++ b/drivers/media/video/pwc/pwc-kiara.c @@ -42,6 +42,8 @@ #include "pwc-kiara.h" #include "pwc-uncompress.h" +const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA] = { 5, 10, 15, 20, 25, 30 }; + const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4] = { /* SQCIF */ diff --git a/drivers/media/video/pwc/pwc-kiara.h b/drivers/media/video/pwc/pwc-kiara.h index 0bdb225..047dad8 100644 --- a/drivers/media/video/pwc/pwc-kiara.h +++ b/drivers/media/video/pwc/pwc-kiara.h @@ -29,6 +29,8 @@ #include <media/pwc-ioctl.h> +#define PWC_FPS_MAX_KIARA 6 + struct Kiara_table_entry { char alternate; /* USB alternate interface */ @@ -37,8 +39,9 @@ struct Kiara_table_entry unsigned char mode[12]; /* precomputed mode settings for cam */ }; -extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][6][4]; +extern const struct Kiara_table_entry Kiara_table[PSZ_MAX][PWC_FPS_MAX_KIARA][4]; extern const unsigned int KiaraRomTable[8][2][16][8]; +extern const unsigned int Kiara_fps_vector[PWC_FPS_MAX_KIARA]; #endif diff --git a/drivers/media/video/pwc/pwc-timon.c b/drivers/media/video/pwc/pwc-timon.c index be65bdc..c56c174 100644 --- a/drivers/media/video/pwc/pwc-timon.c +++ b/drivers/media/video/pwc/pwc-timon.c @@ -40,7 +40,9 @@ #include "pwc-timon.h" -const struct Timon_table_entry Timon_table[PSZ_MAX][6][4] = +const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON] = { 5, 10, 15, 20, 25, 30 }; + +const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4] = { /* SQCIF */ { diff --git a/drivers/media/video/pwc/pwc-timon.h b/drivers/media/video/pwc/pwc-timon.h index eef9e2c..a6e22224 100644 --- a/drivers/media/video/pwc/pwc-timon.h +++ b/drivers/media/video/pwc/pwc-timon.h @@ -44,6 +44,8 @@ #include <media/pwc-ioctl.h> +#define PWC_FPS_MAX_TIMON 6 + struct Timon_table_entry { char alternate; /* USB alternate interface */ @@ -52,9 +54,9 @@ struct Timon_table_entry unsigned char mode[13]; /* precomputed mode settings for cam */ }; -extern const struct Timon_table_entry Timon_table[PSZ_MAX][6][4]; +extern const struct Timon_table_entry Timon_table[PSZ_MAX][PWC_FPS_MAX_TIMON][4]; extern const unsigned int TimonRomTable [16][2][16][8]; - +extern const unsigned int Timon_fps_vector[PWC_FPS_MAX_TIMON]; #endif diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index b7eb3ce..32fbe1a 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -350,7 +350,7 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, if (pdev == NULL) return -EFAULT; -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) v4l_printk_ioctl(cmd); #endif @@ -1168,7 +1168,7 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, buf->sequence = 0; buf->memory = V4L2_MEMORY_MMAP; buf->m.offset = pdev->fill_image * pdev->len_per_image; - buf->length = buf->bytesused; + buf->length = pdev->len_per_image; pwc_next_image(pdev); PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index); @@ -1193,6 +1193,64 @@ int pwc_video_do_ioctl(struct inode *inode, struct file *file, return 0; } + case VIDIOC_ENUM_FRAMESIZES: + { + struct v4l2_frmsizeenum *fsize = arg; + unsigned int i = 0, index = fsize->index; + + if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) { + for (i = 0; i < PSZ_MAX; i++) { + if (pdev->image_mask & (1UL << i)) { + if (!index--) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pwc_image_sizes[i].x; + fsize->discrete.height = pwc_image_sizes[i].y; + return 0; + } + } + } + } else if (fsize->index == 0 && + ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) || + (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) { + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = pdev->abs_max.x; + fsize->discrete.height = pdev->abs_max.y; + return 0; + } + return -EINVAL; + } + + case VIDIOC_ENUM_FRAMEINTERVALS: + { + struct v4l2_frmivalenum *fival = arg; + int size = -1; + unsigned int i; + + for (i = 0; i < PSZ_MAX; i++) { + if (pwc_image_sizes[i].x == fival->width && + pwc_image_sizes[i].y == fival->height) { + size = i; + break; + } + } + + /* TODO: Support raw format */ + if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) { + return -EINVAL; + } + + i = pwc_get_fps(pdev, fival->index, size); + if (!i) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1; + fival->discrete.denominator = i; + + return 0; + } + default: return pwc_ioctl(pdev, cmd, arg); } /* ..switch */ diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index 7e9c423..acbb931 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -39,17 +39,12 @@ #include "pwc-uncompress.h" #include <media/pwc-ioctl.h> -/* Turn some debugging options on/off */ -#ifndef CONFIG_PWC_DEBUG -#define CONFIG_PWC_DEBUG 1 -#endif - /* Version block */ #define PWC_MAJOR 10 #define PWC_MINOR 0 #define PWC_EXTRAMINOR 12 #define PWC_VERSION_CODE KERNEL_VERSION(PWC_MAJOR,PWC_MINOR,PWC_EXTRAMINOR) -#define PWC_VERSION "10.0.12" +#define PWC_VERSION "10.0.13" #define PWC_NAME "pwc" #define PFX PWC_NAME ": " @@ -76,7 +71,7 @@ #define PWC_DEBUG_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG #define PWC_DEBUG_LEVEL (PWC_DEBUG_LEVEL_MODULE) @@ -90,7 +85,7 @@ #define PWC_INFO(fmt, args...) printk(KERN_INFO PFX fmt, ##args) #define PWC_TRACE(fmt, args...) PWC_DEBUG(TRACE, fmt, ##args) -#else /* if ! CONFIG_PWC_DEBUG */ +#else /* if ! CONFIG_USB_PWC_DEBUG */ #define PWC_ERROR(fmt, args...) printk(KERN_ERR PFX fmt, ##args) #define PWC_WARNING(fmt, args...) printk(KERN_WARNING PFX fmt, ##args) @@ -270,7 +265,7 @@ extern "C" { #endif /* Global variables */ -#if CONFIG_PWC_DEBUG +#ifdef CONFIG_USB_PWC_DEBUG extern int pwc_trace; #endif extern int pwc_mbufs; @@ -292,6 +287,7 @@ void pwc_construct(struct pwc_device *pdev); /** Functions in pwc-ctrl.c */ /* Request a certain video mode. Returns < 0 if not possible */ extern int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot); +extern unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size); /* Calculate the number of bytes per image (not frame) */ extern int pwc_mpt_reset(struct pwc_device *pdev, int flags); extern int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt); diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c index 76f5f5d..e20aa36 100644 --- a/drivers/media/video/saa5246a.c +++ b/drivers/media/video/saa5246a.c @@ -111,7 +111,7 @@ static int saa5246a_attach(struct i2c_adapter *adap, int addr, int kind) for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0])); - t->is_searching[pgbuf] = FALSE; + t->is_searching[pgbuf] = false; } vd->priv=t; @@ -198,7 +198,7 @@ static int i2c_senddata(struct saa5246a_device *t, ...) /* Get count number of bytes from I²C-device at address adr, store them in buf. * Start & stop handshaking is done by this routine, ack will be sent after the - * last byte to inhibit further sending of data. If uaccess is TRUE, data is + * last byte to inhibit further sending of data. If uaccess is 'true', data is * written to user-space with put_user. Returns -1 if I²C-device didn't send * acknowledge, 0 otherwise */ @@ -338,7 +338,7 @@ static int saa5246a_request_page(struct saa5246a_device *t, return -EIO; } - t->is_searching[req->pgbuf] = TRUE; + t->is_searching[req->pgbuf] = true; return 0; } @@ -452,7 +452,7 @@ static inline int saa5246a_get_status(struct saa5246a_device *t, } } if (!info->hamming && !info->notfound) - t->is_searching[dau_no] = FALSE; + t->is_searching[dau_no] = false; return 0; } @@ -564,7 +564,7 @@ static inline int saa5246a_stop_dau(struct saa5246a_device *t, { return -EIO; } - t->is_searching[dau_no] = FALSE; + t->is_searching[dau_no] = false; return 0; } diff --git a/drivers/media/video/saa5246a.h b/drivers/media/video/saa5246a.h index 7b91112..64394c0 100644 --- a/drivers/media/video/saa5246a.h +++ b/drivers/media/video/saa5246a.h @@ -41,23 +41,18 @@ #define POS_HEADER_START 7 #define POS_HEADER_END 31 -/* Returns TRUE if the part of the videotext page described with req contains +/* Returns 'true' if the part of the videotext page described with req contains (at least parts of) the time field */ #define REQ_CONTAINS_TIME(p_req) \ ((p_req)->start <= POS_TIME_END && \ (p_req)->end >= POS_TIME_START) -/* Returns TRUE if the part of the videotext page described with req contains +/* Returns 'true' if the part of the videotext page described with req contains (at least parts of) the page header */ #define REQ_CONTAINS_HEADER(p_req) \ ((p_req)->start <= POS_HEADER_END && \ (p_req)->end >= POS_HEADER_START) -#ifndef FALSE -#define FALSE 0 -#define TRUE 1 -#endif - /*****************************************************************************/ /* Mode register numbers of the SAA5246A */ /*****************************************************************************/ diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 3e84737..f2a2f34 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c @@ -124,11 +124,6 @@ struct saa5249_device /* General defines and debugging support */ -#ifndef FALSE -#define FALSE 0 -#define TRUE 1 -#endif - #define RESCHED do { cond_resched(); } while(0) static struct video_device saa_template; /* Declared near bottom */ @@ -183,9 +178,9 @@ static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); t->vdau[pgbuf].expire = 0; - t->vdau[pgbuf].clrfound = TRUE; - t->vdau[pgbuf].stopped = TRUE; - t->is_searching[pgbuf] = FALSE; + t->vdau[pgbuf].clrfound = true; + t->vdau[pgbuf].stopped = true; + t->is_searching[pgbuf] = false; } vd->priv=t; @@ -298,7 +293,7 @@ static int i2c_senddata(struct saa5249_device *t, ...) /* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop * handshaking is done by this routine, ack will be sent after the last byte to inhibit further - * sending of data. If uaccess is TRUE, data is written to user-space with put_user. + * sending of data. If uaccess is 'true', data is written to user-space with put_user. * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise */ @@ -317,7 +312,7 @@ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) static int do_saa5249_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - static int virtual_mode = FALSE; + static int virtual_mode = false; struct video_device *vd = video_devdata(file); struct saa5249_device *t=vd->priv; @@ -340,7 +335,7 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) return -EINVAL; memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); - t->vdau[req->pgbuf].clrfound = TRUE; + t->vdau[req->pgbuf].clrfound = true; return 0; } @@ -350,7 +345,7 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) return -EINVAL; - t->vdau[req->pgbuf].clrfound = TRUE; + t->vdau[req->pgbuf].clrfound = true; return 0; } @@ -376,9 +371,9 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); - t->vdau[req->pgbuf].stopped = FALSE; - t->vdau[req->pgbuf].clrfound = TRUE; - t->is_searching[req->pgbuf] = TRUE; + t->vdau[req->pgbuf].stopped = false; + t->vdau[req->pgbuf].clrfound = true; + t->is_searching[req->pgbuf] = true; return 0; } @@ -430,7 +425,7 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) return -EIO; } - t->vdau[req->pgbuf].clrfound = FALSE; + t->vdau[req->pgbuf].clrfound = false; memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); } else @@ -474,7 +469,7 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, return -EFAULT; if (!info.hamming && !info.notfound) { - t->is_searching[req->pgbuf] = FALSE; + t->is_searching[req->pgbuf] = false; } return 0; } @@ -530,8 +525,8 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) return -EINVAL; - t->vdau[req->pgbuf].stopped = TRUE; - t->is_searching[req->pgbuf] = FALSE; + t->vdau[req->pgbuf].stopped = true; + t->is_searching[req->pgbuf] = false; return 0; } @@ -660,11 +655,11 @@ static int saa5249_open(struct inode *inode, struct file *file) memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); t->vdau[pgbuf].expire = 0; - t->vdau[pgbuf].clrfound = TRUE; - t->vdau[pgbuf].stopped = TRUE; - t->is_searching[pgbuf] = FALSE; + t->vdau[pgbuf].clrfound = true; + t->vdau[pgbuf].stopped = true; + t->is_searching[pgbuf] = false; } - t->virtual_mode=FALSE; + t->virtual_mode = false; return 0; fail: diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c2374ed..2d18f00 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -45,6 +45,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/saa7115.h> #include <asm/div64.h> @@ -71,6 +72,7 @@ I2C_CLIENT_INSMOD; struct saa711x_state { v4l2_std_id std; int input; + int output; int enable; int radio; int bright; @@ -79,7 +81,7 @@ struct saa711x_state { int sat; int width; int height; - enum v4l2_chip_ident ident; + u32 ident; u32 audclk_freq; u32 crystal_freq; u8 ucgc; @@ -960,7 +962,7 @@ static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std) reg |= 0x10; } else if (std == V4L2_STD_NTSC_M_JP) { reg |= 0x40; - } else if (std == V4L2_STD_SECAM) { + } else if (std & V4L2_STD_SECAM) { reg |= 0x50; } saa711x_write(client, R_0E_CHROMA_CNTL_1, reg); @@ -1231,7 +1233,6 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct saa711x_state *state = i2c_get_clientdata(client); - int *iarg = arg; /* ioctls to allow direct access to the saa7115 registers for testing */ switch (cmd) { @@ -1301,7 +1302,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar struct v4l2_routing *route = arg; route->input = state->input; - route->output = 0; + route->output = state->output; break; } @@ -1309,7 +1310,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar { struct v4l2_routing *route = arg; - v4l_dbg(1, debug, client, "decoder set input %d\n", route->input); + v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); /* saa7113 does not have these inputs */ if (state->ident == V4L2_IDENT_SAA7113 && (route->input == SAA7115_COMPOSITE4 || @@ -1318,10 +1319,12 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar } if (route->input > SAA7115_SVIDEO3) return -EINVAL; - if (state->input == route->input) + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) break; - v4l_dbg(1, debug, client, "now setting %s input\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite"); + v4l_dbg(1, debug, client, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); state->input = route->input; /* select mode */ @@ -1333,6 +1336,14 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar saa711x_write(client, R_09_LUMA_CNTL, (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); + + state->output = route->output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); + } break; } @@ -1377,6 +1388,9 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar { struct v4l2_sliced_vbi_data *data = arg; + /* Note: the internal field ID is inverted for NTSC, + so data->field 0 maps to the saa7115 even field, + whereas for PAL it maps to the saa7115 odd field. */ switch (data->id) { case V4L2_SLICED_WSS_625: if (saa711x_read(client, 0x6b) & 0xc0) @@ -1387,17 +1401,17 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar case V4L2_SLICED_CAPTION_525: if (data->field == 0) { /* CC */ - if (saa711x_read(client, 0x66) & 0xc0) + if (saa711x_read(client, 0x66) & 0x30) return -EIO; - data->data[0] = saa711x_read(client, 0x67); - data->data[1] = saa711x_read(client, 0x68); + data->data[0] = saa711x_read(client, 0x69); + data->data[1] = saa711x_read(client, 0x6a); return 0; } /* XDS */ - if (saa711x_read(client, 0x66) & 0x30) + if (saa711x_read(client, 0x66) & 0xc0) return -EIO; - data->data[0] = saa711x_read(client, 0x69); - data->data[1] = saa711x_read(client, 0x6a); + data->data[0] = saa711x_read(client, 0x67); + data->data[1] = saa711x_read(client, 0x68); return 0; default: return -EINVAL; @@ -1406,32 +1420,25 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_SAA711X) - return -EINVAL; - reg->val = saa711x_read(client, reg->reg & 0xff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (reg->i2c_id != I2C_DRIVERID_SAA711X) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = saa711x_read(client, reg->reg & 0xff); + else + saa711x_write(client, reg->reg & 0xff, reg->val & 0xff); break; } #endif - case VIDIOC_INT_G_CHIP_IDENT: - *iarg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; @@ -1479,6 +1486,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) if (memcmp(name, "1f711", 5)) { v4l_dbg(1, debug, client, "chip found @ 0x%x (ID %s) does not match a known saa711x chip.\n", address << 1, name); + kfree(client); return 0; } @@ -1492,6 +1500,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) return -ENOMEM; } state->input = -1; + state->output = SAA7115_IPORT_ON; state->enable = 1; state->radio = 0; state->bright = 128; @@ -1550,7 +1559,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) static int saa711x_probe(struct i2c_adapter *adapter) { - if (adapter->class & I2C_CLASS_TV_ANALOG) + if (adapter->class & I2C_CLASS_TV_ANALOG || adapter->class & I2C_CLASS_TV_DIGITAL) return i2c_probe(adapter, &addr_data, &saa711x_attach); return 0; } diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index ad401bd..9f98693 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -54,6 +54,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/saa7127.h> static int debug = 0; @@ -234,7 +235,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { struct saa7127_state { v4l2_std_id std; - enum v4l2_chip_ident ident; + u32 ident; enum saa7127_input_type input_type; enum saa7127_output_type output_type; int video_enable; @@ -550,12 +551,12 @@ static int saa7127_command(struct i2c_client *client, struct v4l2_routing *route = arg; switch (cmd) { - case VIDIOC_S_STD: + case VIDIOC_INT_S_STD_OUTPUT: if (state->std == *(v4l2_std_id *)arg) break; return saa7127_set_std(client, *(v4l2_std_id *)arg); - case VIDIOC_G_STD: + case VIDIOC_INT_G_STD_OUTPUT: *(v4l2_std_id *)arg = state->std; break; @@ -614,25 +615,19 @@ static int saa7127_command(struct i2c_client *client, break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_SAA7127) - return -EINVAL; - reg->val = saa7127_read(client, reg->reg & 0xff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (reg->i2c_id != I2C_DRIVERID_SAA7127) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = saa7127_read(client, reg->reg & 0xff); + else + saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); break; } #endif @@ -656,9 +651,8 @@ static int saa7127_command(struct i2c_client *client, break; } - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 59da79c..309dca3 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -46,6 +46,7 @@ config VIDEO_SAA7134_DVB select DVB_NXT200X if !DVB_FE_CUSTOMISE select DVB_TDA10086 if !DVB_FE_CUSTOMISE select DVB_TDA826X if !DVB_FE_CUSTOMISE + select DVB_TDA827X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index 89a1565..c85c8a8 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/drivers/media/video/saa7134/Makefile @@ -14,7 +14,3 @@ obj-$(CONFIG_VIDEO_SAA7134_DVB) += saa7134-dvb.o EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends - -extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1 - -EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index ae984bb..4ea479b 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1543,12 +1543,12 @@ struct saa7134_board saa7134_boards[] = { },{ .name = name_comp1, .vmux = 0, - .amux = LINE2, + .amux = LINE1, .gpio = 0x02, },{ .name = name_svideo, .vmux = 6, - .amux = LINE2, + .amux = LINE1, .gpio = 0x02, }}, .radio = { @@ -1778,17 +1778,19 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_FLYDVBTDUO] = { /* LifeView FlyDVB-T DUO */ /* "Nico Sabbi <nsabbi@tiscali.it> Hartmut Hackmann hartmut.hackmann@t-online.de*/ - .name = "LifeView FlyDVB-T DUO", + .name = "LifeView FlyDVB-T DUO / MSI TV@nywhere Duo", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .gpiomask = 0x00200000, .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 1, .amux = TV, + .gpio = 0x200000, /* GPIO21=High for TV input */ .tv = 1, },{ .name = name_comp1, /* Composite signal on S-Video input */ @@ -1803,6 +1805,11 @@ struct saa7134_board saa7134_boards[] = { .vmux = 8, .amux = LINE2, }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x000000, /* GPIO21=Low for FM radio antenna */ + }, }, [SAA7134_BOARD_PHILIPS_TOUGH] = { .name = "Philips TOUGH DVB-T reference design", @@ -2469,6 +2476,11 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, .gpio = 0x0200000, },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ .name = name_svideo, .vmux = 8, .amux = LINE2, @@ -2541,8 +2553,9 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 0, .mpeg = SAA7134_MPEG_DVB, - .gpiomask = 1 << 21, + .gpiomask = 0x0200000, .inputs = {{ .name = name_tv, .vmux = 1, @@ -2619,7 +2632,7 @@ struct saa7134_board saa7134_boards[] = { }}, .radio = { .name = name_radio, - .amux = LINE1, + .amux = TV, .gpio = 0x0200000, }, }, @@ -3038,6 +3051,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .tuner_config = 1, .mpeg = SAA7134_MPEG_DVB, .gpiomask = 0x000200000, .inputs = {{ @@ -3183,6 +3197,216 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }}, }, + [SAA7134_BOARD_ENCORE_ENLTV] = { + /* Steven Walter <stevenrwalter@gmail.com> + Juan Pablo Sormani <sorman@gmail.com> */ + .name = "Encore ENLTV", + .audio_clock = 0x00200000, + .tuner_type = TUNER_TNF_5335MF, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, +/* .gpio = 0x00300001,*/ + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM] = { + /* Juan Pablo Sormani <sorman@gmail.com> */ + .name = "Encore ENLTV-FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_ATSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_CINERGY_HT_PCI] = { + .name = "Terratec Cinergy HT PCI", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE1, + }}, + }, + [SAA7134_BOARD_PHILIPS_TIGER_S] = { + .name = "Philips Tiger - S Reference design", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, + [SAA7134_BOARD_AVERMEDIA_M102] = { + .name = "Avermedia M102", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .gpiomask = 1<<21, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 0, + .amux = LINE2, + },{ + .name = name_svideo, + .vmux = 6, + .amux = LINE2, + }}, + }, + [SAA7134_BOARD_ASUS_P7131_4871] = { + .name = "ASUS P7131 4871", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .mpeg = SAA7134_MPEG_DVB, + .gpiomask = 0x0200000, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0200000, + }}, + }, + [SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA] = { + .name = "ASUSTeK P7131 Hybrid", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x0000000, + },{ + .name = name_comp1, + .vmux = 3, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_comp2, + .vmux = 0, + .amux = LINE2, + .gpio = 0x0200000, + },{ + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + .gpio = 0x0200000, + }}, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -3808,7 +4032,7 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4876, - .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -3822,6 +4046,60 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x1172, .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA, },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = PCI_VENDOR_ID_PHILIPS, + .subdevice = 0x2342, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x2341, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x3016, + .subdevice = 0x2344, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x230f, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x153b, + .subdevice = 0x1175, + .driver_data = SAA7134_BOARD_CINERGY_HT_PCI, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf31e, + .driver_data = SAA7134_BOARD_AVERMEDIA_M102, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x4E42, /* MSI */ + .subdevice = 0x0306, /* TV@nywhere DUO */ + .driver_data = SAA7134_BOARD_FLYDVBTDUO, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4871, + .driver_data = SAA7134_BOARD_ASUS_P7131_4871, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x4857, + .driver_data = SAA7134_BOARD_ASUSTeK_P7131_DUAL, + },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -3835,7 +4113,6 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0, .driver_data = SAA7134_BOARD_NOAUTO, },{ - /* --- default catch --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -3926,9 +4203,13 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_TERMINATOR: case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -3964,8 +4245,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - saa_writeb(SAA7134_GPIO_GPMODE3, 0x08); - saa_writeb(SAA7134_GPIO_GPSTATUS3, 0x00); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08000000, 0x08000000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08000000, 0x00000000); break; case SAA7134_BOARD_AVERMEDIA_CARDBUS: /* power-up tuner chip */ @@ -3998,6 +4279,11 @@ int saa7134_board_init1(struct saa7134_dev *dev) "%s: Dual decoder functionality is disabled for now, use the other chip.\n", dev->name,card(dev).name,dev->name,dev->name); break; + case SAA7134_BOARD_AVERMEDIA_M102: + /* enable tuner */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); + break; } return 0; } @@ -4007,6 +4293,9 @@ int saa7134_board_init2(struct saa7134_dev *dev) { unsigned char buf; int board; + struct tuner_setup tun_setup; + tun_setup.config = 0; + tun_setup.tuner_callback = saa7134_tuner_callback; switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: @@ -4023,8 +4312,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->tuner_type = saa7134_boards[dev->board].tuner_type; if (TUNER_ABSENT != dev->tuner_type) { - struct tuner_setup tun_setup; - tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; @@ -4034,7 +4321,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case SAA7134_BOARD_MD7134: { - struct tuner_setup tun_setup; u8 subaddr; u8 data[3]; int ret, tuner_t; @@ -4106,7 +4392,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) * the channel decoder. We have to make it transparent to find it */ { - struct tuner_setup tun_setup; u8 data[] = { 0x07, 0x02}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); @@ -4119,16 +4404,38 @@ int saa7134_board_init2(struct saa7134_dev *dev) } break; case SAA7134_BOARD_PHILIPS_TIGER: + case SAA7134_BOARD_PHILIPS_TIGER_S: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + if(dev->autodetected && (dev->eedata[0x49] == 0x50)) { + dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; + printk(KERN_INFO "%s: Reconfigured board as %s\n", + dev->name, saa7134_boards[dev->board].name); + } + if(dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) { + tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; + tun_setup.type = TUNER_PHILIPS_TDA8290; + tun_setup.addr = 0x4b; + tun_setup.config = 2; + + saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); + data[2] = 0x68; + } + i2c_transfer(&dev->i2c_adap, &msg, 1); + } + break; case SAA7134_BOARD_PINNACLE_PCTV_310i: case SAA7134_BOARD_TEVION_DVBT_220RF: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_MEDION_MD8800_QUADRO: case SAA7134_BOARD_HAUPPAUGE_HVR1110: /* this is a hybrid board, initialize to analog mode * and configure firmware eeprom address */ { - u8 data[] = { 0x3c, 0x33, 0x68}; + u8 data[] = { 0x3c, 0x33, 0x60}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } @@ -4142,17 +4449,18 @@ int saa7134_board_init2(struct saa7134_dev *dev) break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - /* make the tda10046 find its eeprom */ + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x62}; + u8 data[] = { 0x3c, 0x33, 0x6a}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - /* make the tda10046 find its eeprom */ + case SAA7134_BOARD_CINERGY_HT_PCI: + /* initialize analog mode */ { - u8 data[] = { 0x3c, 0x33, 0x60}; + u8 data[] = { 0x3c, 0x33, 0x68}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); } diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index ed038ff..25f8470 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -117,6 +117,64 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) dev->name, mode, (~mode) & status, mode & status, msg); } +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) +{ + u32 index, bitval; + + index = 1 << bit_no; + switch (value) { + case 0: /* static value */ + case 1: dprintk("setting GPIO%d to static %d\n", bit_no, value); + /* turn sync mode off if necessary */ + if (index & 0x00c00000) + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x00); + if (value) + bitval = index; + else + bitval = 0; + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, index); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, index, bitval); + break; + case 3: /* tristate */ + dprintk("setting GPIO%d to tristate\n", bit_no); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); + break; + } +} + +int saa7134_tuner_callback(void *ptr, int command, int arg) +{ + u8 sync_control; + struct saa7134_dev *dev = ptr; + + switch (dev->tuner_type) { + case TUNER_PHILIPS_TDA8290: + switch (command) { + case 0: /* switch LNA gain through GPIO 22*/ + saa7134_set_gpio(dev, 22, arg) ; + break; + case 1: /* vsync output at GPIO22. 50 / 60Hz */ + dprintk("setting GPIO22 to vsync %d\n", arg); + saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); + saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); + if (arg == 1) + sync_control = 11; + else + sync_control = 17; + saa_writeb(SAA7134_VGATE_START, sync_control); + saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); + saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); + break; + default: + return -EINVAL; + } + break; + default: + return -ENODEV; + } + return 0; +} + /* ------------------------------------------------------------------ */ @@ -124,55 +182,28 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) /* delayed request_module */ #if defined(CONFIG_MODULES) && defined(MODULE) -static int need_empress; -static int need_dvb; -static int need_alsa; -static int need_oss; -static int pending_call(struct notifier_block *self, unsigned long state, - void *module) -{ - if (module != THIS_MODULE || state != MODULE_STATE_LIVE) - return NOTIFY_DONE; - if (need_empress) +static void request_module_async(struct work_struct *work){ + struct saa7134_dev* dev = container_of(work, struct saa7134_dev, request_module_wk); + if (card_is_empress(dev)) request_module("saa7134-empress"); - if (need_dvb) + if (card_is_dvb(dev)) request_module("saa7134-dvb"); - if (need_alsa) + if (alsa) request_module("saa7134-alsa"); - if (need_oss) + if (oss) request_module("saa7134-oss"); - return NOTIFY_DONE; } -static int pending_registered; -static struct notifier_block pending_notifier = { - .notifier_call = pending_call, -}; - -static void request_module_depend(char *name, int *flag) +static void request_submodules(struct saa7134_dev *dev) { - int err; - switch (THIS_MODULE->state) { - case MODULE_STATE_COMING: - if (!pending_registered) { - err = register_module_notifier(&pending_notifier); - pending_registered = 1; - } - *flag = 1; - break; - case MODULE_STATE_LIVE: - request_module(name); - break; - default: - /* nothing */; - break; - } + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); } #else -#define request_module_depend(name,flag) +#define request_submodules(dev) #endif /* CONFIG_MODULES */ /* ------------------------------------------------------------------ */ @@ -703,7 +734,6 @@ static int saa7134_hwfini(struct saa7134_dev *dev) saa7134_ts_fini(dev); saa7134_input_fini(dev); saa7134_vbi_fini(dev); - saa7134_video_fini(dev); saa7134_tvaudio_fini(dev); return 0; } @@ -944,18 +974,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, request_module("tuner"); if (card_is_empress(dev)) { request_module("saa6752hs"); - request_module_depend("saa7134-empress",&need_empress); } - if (card_is_dvb(dev)) - request_module_depend("saa7134-dvb",&need_dvb); - - - if (alsa) - request_module_depend("saa7134-alsa",&need_alsa); - - if (oss) - request_module_depend("saa7134-oss",&need_oss); + request_submodules(dev); v4l2_prio_init(&dev->prio); @@ -1013,6 +1034,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, saa7134_dmasound_init(dev); } + if (TUNER_ABSENT != dev->tuner_type) + saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + return 0; fail4: @@ -1152,10 +1176,6 @@ static int saa7134_init(void) static void saa7134_fini(void) { -#if defined(CONFIG_MODULES) && defined(MODULE) - if (pending_registered) - unregister_module_notifier(&pending_notifier); -#endif /* CONFIG_MODULES */ pci_unregister_driver(&saa7134_pci_driver); } @@ -1164,6 +1184,7 @@ module_exit(saa7134_fini); /* ----------------------------------------------------------- */ +EXPORT_SYMBOL(saa7134_set_gpio); EXPORT_SYMBOL(saa7134_i2c_call_clients); EXPORT_SYMBOL(saa7134_devlist); EXPORT_SYMBOL(saa7134_boards); diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index c33f6a6..65aec88 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -41,7 +41,9 @@ #include "tda10086.h" #include "tda826x.h" +#include "tda827x.h" #include "isl6421.h" + MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); MODULE_LICENSE("GPL"); @@ -54,7 +56,21 @@ static int use_frontend = 0; module_param(use_frontend, int, 0644); MODULE_PARM_DESC(use_frontend,"for cards with multiple frontends (0: terrestrial, 1: satellite)"); -/* ------------------------------------------------------------------ */ +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off module debugging (default:off)."); + +#define dprintk(fmt, arg...) do { if (debug) \ + printk(KERN_DEBUG "%s/dvb: " fmt, dev->name , ## arg); } while(0) + +/* Print a warning */ +#define wprintk(fmt, arg...) \ + printk(KERN_WARNING "%s/dvb: " fmt, dev->name, ## arg) + +/* ------------------------------------------------------------------ + * mt352 based DVB-T cards + */ + static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) { u32 ok; @@ -75,8 +91,7 @@ static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on) saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 28)); udelay(10); ok = saa_readl(SAA7134_GPIO_GPSTATUS0) & (1 << 27); - printk("%s: %s %s\n", dev->name, __FUNCTION__, - ok ? "on" : "off"); + dprintk("%s %s\n", __FUNCTION__, ok ? "on" : "off"); if (!ok) saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, (1 << 26)); @@ -96,7 +111,7 @@ static int mt352_pinnacle_init(struct dvb_frontend* fe) static u8 irq_cfg [] = { INTERRUPT_EN_0, 0x00, 0x00, 0x00, 0x00 }; struct saa7134_dev *dev= fe->dvb->priv; - printk("%s: %s called\n",dev->name,__FUNCTION__); + dprintk("%s called\n", __FUNCTION__); mt352_write(fe, clock_config, sizeof(clock_config)); udelay(200); @@ -185,10 +200,26 @@ static struct mt352_config avermedia_777 = { .demod_init = mt352_aver777_init, }; -/* ------------------------------------------------------------------ */ -static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +/* ================================================================== + * tda1004x based DVB-T cards, helper functions + */ + +static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, + const struct firmware **fw, char *name) +{ + struct saa7134_dev *dev = fe->dvb->priv; + return request_firmware(fw, name, &dev->pci->dev); +} + +/* ------------------------------------------------------------------ + * these tuners are tu1216, td1316(a) + */ + +static int philips_tda6651_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; @@ -263,15 +294,20 @@ static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + wprintk("could not write to tuner at addr: 0x%02x\n", + addr << 1); return -EIO; + } msleep(1); return 0; } -static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) +static int philips_tu1216_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; @@ -287,46 +323,17 @@ static int philips_tda6651_pll_init(u8 addr, struct dvb_frontend *fe) /* ------------------------------------------------------------------ */ -static int philips_tu1216_tuner_60_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x60, fe); -} - -static int philips_tu1216_tuner_60_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x60, fe, params); -} - -static int philips_tda1004x_request_firmware(struct dvb_frontend *fe, - const struct firmware **fw, char *name) -{ - struct saa7134_dev *dev = fe->dvb->priv; - return request_firmware(fw, name, &dev->pci->dev); -} - static struct tda1004x_config philips_tu1216_60_config = { - .demod_address = 0x8, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int philips_tu1216_tuner_61_init(struct dvb_frontend *fe) -{ - return philips_tda6651_pll_init(0x61, fe); -} - -static int philips_tu1216_tuner_61_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - return philips_tda6651_pll_set(0x61, fe, params); -} - static struct tda1004x_config philips_tu1216_61_config = { .demod_address = 0x8, @@ -335,7 +342,8 @@ static struct tda1004x_config philips_tu1216_61_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, - .request_firmware = philips_tda1004x_request_firmware, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -343,24 +351,42 @@ static struct tda1004x_config philips_tu1216_61_config = { static int philips_td1316_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; static u8 msg[] = { 0x0b, 0xf5, 0x86, 0xab }; - struct i2c_msg init_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; + struct i2c_msg init_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; /* setup PLL configuration */ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); if (i2c_transfer(&dev->i2c_adap, &init_msg, 1) != 1) return -EIO; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); return 0; } static int philips_td1316_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { - return philips_tda6651_pll_set(0x61, fe, params); + return philips_tda6651_pll_set(fe, params); } +static int philips_td1316_tuner_sleep(struct dvb_frontend *fe) +{ + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; + static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; + struct i2c_msg analog_msg = {.addr = addr,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + /* switch the tuner to analog mode */ + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + if (i2c_transfer(&dev->i2c_adap, &analog_msg, 1) != 1) + return -EIO; + return 0; +} + +/* ------------------------------------------------------------------ */ + static int philips_europa_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; @@ -380,18 +406,14 @@ static int philips_europa_tuner_init(struct dvb_frontend *fe) static int philips_europa_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - /* this message actually turns the tuner back to analog mode */ - static u8 msg[] = { 0x0b, 0xdc, 0x86, 0xa4 }; - struct i2c_msg analog_msg = {.addr = 0x61,.flags = 0,.buf = msg,.len = sizeof(msg) }; - i2c_transfer(&dev->i2c_adap, &analog_msg, 1); - msleep(1); + static u8 msg[] = { 0x00, 0x14 }; + struct i2c_msg analog_msg = {.addr = 0x43,.flags = 0,.buf = msg,.len = sizeof(msg) }; + + if (philips_td1316_tuner_sleep(fe)) + return -EIO; /* switch the board to analog mode */ - analog_msg.addr = 0x43; - analog_msg.len = 0x02; - msg[0] = 0x00; - msg[1] = 0x14; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); i2c_transfer(&dev->i2c_adap, &analog_msg, 1); @@ -416,7 +438,8 @@ static struct tda1004x_config philips_europa_config = { .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_IFO_AUTO_POS, .if_freq = TDA10046_FREQ_052, - .request_firmware = NULL, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; /* ------------------------------------------------------------------ */ @@ -424,9 +447,11 @@ static struct tda1004x_config philips_europa_config = { static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message is to set up ATC and ALC */ static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0xa0 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -440,9 +465,11 @@ static int philips_fmd1216_tuner_init(struct dvb_frontend *fe) static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; /* this message actually turns the tuner back to analog mode */ - static u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; + u8 fmd1216_init[] = { 0x0b, 0xdc, 0x9c, 0x60 }; + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = fmd1216_init,.len = sizeof(fmd1216_init) }; if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); @@ -460,8 +487,10 @@ static int philips_fmd1216_tuner_sleep(struct dvb_frontend *fe) static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->tuner_address; u8 tuner_buf[4]; - struct i2c_msg tuner_msg = {.addr = 0x61,.flags = 0,.buf = tuner_buf,.len = + struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) }; int tuner_frequency = 0; int divider = 0; @@ -536,8 +565,11 @@ static int philips_fmd1216_tuner_set_params(struct dvb_frontend *fe, struct dvb_ if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) + if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) { + wprintk("could not write to tuner at addr: 0x%02x\n", + addr << 1); return -EIO; + } return 0; } @@ -548,582 +580,365 @@ static struct tda1004x_config medion_cardbus = { .xtal_freq = TDA10046_XTAL_16M, .agc_config = TDA10046_AGC_IFO_AUTO_NEG, .if_freq = TDA10046_FREQ_3613, - .request_firmware = NULL, -}; - -/* ------------------------------------------------------------------ */ - -struct tda827x_data { - u32 lomax; - u8 spd; - u8 bs; - u8 bp; - u8 cp; - u8 gc3; - u8 div1p5; -}; - -static struct tda827x_data tda827x_dvbt[] = { - { .lomax = 62000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 66000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 1}, - { .lomax = 76000000, .spd = 3, .bs = 1, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 84000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 3, .div1p5 = 0}, - { .lomax = 93000000, .spd = 3, .bs = 2, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 98000000, .spd = 3, .bs = 3, .bp = 0, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 109000000, .spd = 3, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 123000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 133000000, .spd = 2, .bs = 3, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 151000000, .spd = 2, .bs = 1, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 154000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 181000000, .spd = 2, .bs = 2, .bp = 1, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 185000000, .spd = 2, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 217000000, .spd = 2, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 244000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 265000000, .spd = 1, .bs = 3, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 302000000, .spd = 1, .bs = 1, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 324000000, .spd = 1, .bs = 2, .bp = 2, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 370000000, .spd = 1, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 454000000, .spd = 1, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 493000000, .spd = 0, .bs = 2, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 530000000, .spd = 0, .bs = 3, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 1}, - { .lomax = 554000000, .spd = 0, .bs = 1, .bp = 3, .cp = 0, .gc3 = 1, .div1p5 = 0}, - { .lomax = 604000000, .spd = 0, .bs = 1, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 696000000, .spd = 0, .bs = 2, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 740000000, .spd = 0, .bs = 2, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 820000000, .spd = 0, .bs = 3, .bp = 4, .cp = 0, .gc3 = 0, .div1p5 = 0}, - { .lomax = 865000000, .spd = 0, .bs = 3, .bp = 4, .cp = 1, .gc3 = 0, .div1p5 = 0}, - { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0} + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; -static int philips_tda827x_tuner_init(struct dvb_frontend *fe) -{ - return 0; -} +/* ------------------------------------------------------------------ + * tda 1004x based cards with philips silicon tuner + */ -static int philips_tda827x_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static void philips_tda827x_lna_gain(struct dvb_frontend *fe, int high) { struct saa7134_dev *dev = fe->dvb->priv; - u8 tuner_buf[14]; - - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf, - .len = sizeof(tuner_buf) }; - int i, tuner_freq, if_freq; - u32 N; - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - if_freq = 4000000; - break; - case BANDWIDTH_7_MHZ: - if_freq = 4500000; - break; - default: /* 8 MHz or Auto */ - if_freq = 5000000; - break; - } - tuner_freq = params->frequency + if_freq; - - i = 0; - while (tda827x_dvbt[i].lomax < tuner_freq) { - if(tda827x_dvbt[i + 1].lomax == 0) - break; - i++; + struct tda1004x_state *state = fe->demodulator_priv; + u8 addr = state->config->i2c_gate; + u8 config = state->config->tuner_config; + u8 GP00_CF[] = {0x20, 0x01}; + u8 GP00_LEV[] = {0x22, 0x00}; + + struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = GP00_CF, .len = 2}; + if (config) { + if (high) { + dprintk("setting LNA to high gain\n"); + } else { + dprintk("setting LNA to low gain\n"); + } } - - N = ((tuner_freq + 125000) / 250000) << (tda827x_dvbt[i].spd + 2); - tuner_buf[0] = 0; - tuner_buf[1] = (N>>8) | 0x40; - tuner_buf[2] = N & 0xff; - tuner_buf[3] = 0; - tuner_buf[4] = 0x52; - tuner_buf[5] = (tda827x_dvbt[i].spd << 6) + (tda827x_dvbt[i].div1p5 << 5) + - (tda827x_dvbt[i].bs << 3) + tda827x_dvbt[i].bp; - tuner_buf[6] = (tda827x_dvbt[i].gc3 << 4) + 0x8f; - tuner_buf[7] = 0xbf; - tuner_buf[8] = 0x2a; - tuner_buf[9] = 0x05; - tuner_buf[10] = 0xff; - tuner_buf[11] = 0x00; - tuner_buf[12] = 0x00; - tuner_buf[13] = 0x40; - - tuner_msg.len = 14; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &tuner_msg, 1) != 1) - return -EIO; - - msleep(500); - /* correct CP value */ - tuner_buf[0] = 0x30; - tuner_buf[1] = 0x50 + tda827x_dvbt[i].cp; - tuner_msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - - return 0; -} - -static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda827x_sleep[] = { 0x30, 0xd0}; - struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tda827x_sleep, - .len = sizeof(tda827x_sleep) }; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - return 0; -} - -static struct tda1004x_config tda827x_lifeview_config = { - .demod_address = 0x08, - .invert = 1, - .invert_oclk = 0, - .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, - .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, -}; - -/* ------------------------------------------------------------------ */ - -struct tda827xa_data { - u32 lomax; - u8 svco; - u8 spd; - u8 scr; - u8 sbs; - u8 gc3; -}; - -static struct tda827xa_data tda827xa_dvbt[] = { - { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1}, - { .lomax = 113750000, .svco = 3, .spd = 3, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 134500000, .svco = 0, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 154000000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 162500000, .svco = 1, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 183000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 1, .gc3 = 1}, - { .lomax = 195000000, .svco = 2, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 227500000, .svco = 3, .spd = 2, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 269000000, .svco = 0, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 290000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 2, .gc3 = 1}, - { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 390000000, .svco = 2, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 455000000, .svco = 3, .spd = 1, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 520000000, .svco = 0, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 538000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1}, - { .lomax = 550000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 3, .gc3 = 1}, - { .lomax = 620000000, .svco = 1, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 650000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 700000000, .svco = 2, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 780000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 820000000, .svco = 3, .spd = 0, .scr = 0, .sbs = 4, .gc3 = 0}, - { .lomax = 870000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 0}, - { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 0}, - { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}}; - - -static int philips_tda827xa_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - struct saa7134_dev *dev = fe->dvb->priv; - u8 tuner_buf[14]; - unsigned char reg2[2]; - - struct i2c_msg msg = {.addr = addr,.flags = 0,.buf = tuner_buf}; - int i, tuner_freq, if_freq; - u32 N; - - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_6_MHZ: - if_freq = 4000000; + switch (config) { + case 0: /* no LNA */ break; - case BANDWIDTH_7_MHZ: - if_freq = 4500000; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync off */ + saa7134_set_gpio(dev, 22, 0); + GP00_LEV[1] = high ? 0 : 1; + if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) { + wprintk("could not access tda8290 at addr: 0x%02x\n", + addr << 1); + return; + } + msg.buf = GP00_LEV; + if (config == 2) + GP00_LEV[1] = high ? 1 : 0; + i2c_transfer(&dev->i2c_adap, &msg, 1); break; - default: /* 8 MHz or Auto */ - if_freq = 5000000; + case 3: /* switch with GPIO of saa713x */ + saa7134_set_gpio(dev, 22, high); break; } - tuner_freq = params->frequency + if_freq; - - i = 0; - while (tda827xa_dvbt[i].lomax < tuner_freq) { - if(tda827xa_dvbt[i + 1].lomax == 0) - break; - i++; - } - - N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd; - tuner_buf[0] = 0; // subaddress - tuner_buf[1] = N >> 8; - tuner_buf[2] = N & 0xff; - tuner_buf[3] = 0; - tuner_buf[4] = 0x16; - tuner_buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) + - tda827xa_dvbt[i].sbs; - tuner_buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4); - tuner_buf[7] = 0x0c; - tuner_buf[8] = 0x06; - tuner_buf[9] = 0x24; - tuner_buf[10] = 0xff; - tuner_buf[11] = 0x60; - tuner_buf[12] = 0x00; - tuner_buf[13] = 0x39; // lpsel - msg.len = 14; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - - msg.buf= reg2; - msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - reg2[0] = 0xa0; - reg2[1] = 0x40; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - msleep(2); - /* correct CP value */ - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_dvbt[i].scr; - msg.len = 2; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &msg, 1); - - return 0; - } -static int philips_tda827xa_tuner_sleep(u8 addr, struct dvb_frontend *fe) +static int tda8290_i2c_gate_ctrl( struct dvb_frontend* fe, int enable) { - struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda827xa_sleep[] = { 0x30, 0x90}; - struct i2c_msg tuner_msg = {.addr = addr,.flags = 0,.buf = tda827xa_sleep, - .len = sizeof(tda827xa_sleep) }; - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 1); - i2c_transfer(&dev->i2c_adap, &tuner_msg, 1); - if (fe->ops.i2c_gate_ctrl) - fe->ops.i2c_gate_ctrl(fe, 0); - return 0; -} - -/* ------------------------------------------------------------------ */ + struct tda1004x_state *state = fe->demodulator_priv; -static int tda8290_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) -{ - struct saa7134_dev *dev = fe->dvb->priv; + u8 addr = state->config->i2c_gate; static u8 tda8290_close[] = { 0x21, 0xc0}; static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; + struct i2c_msg tda8290_msg = {.addr = addr,.flags = 0, .len = 2}; if (enable) { tda8290_msg.buf = tda8290_close; } else { tda8290_msg.buf = tda8290_open; } - if (i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1) != 1) + if (i2c_transfer(state->i2c, &tda8290_msg, 1) != 1) { + struct saa7134_dev *dev = fe->dvb->priv; + wprintk("could not access tda8290 I2C gate\n"); return -EIO; + } msleep(20); return 0; } /* ------------------------------------------------------------------ */ -static int philips_tiger_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int philips_tda827x_tuner_init(struct dvb_frontend *fe) { - int ret; + struct saa7134_dev *dev = fe->dvb->priv; + struct tda1004x_state *state = fe->demodulator_priv; - ret = philips_tda827xa_pll_set(0x61, fe, params); - if (ret != 0) - return ret; + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + case 2: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + } return 0; } -static int philips_tiger_tuner_init(struct dvb_frontend *fe) +static int philips_tda827x_tuner_sleep(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; + struct tda1004x_state *state = fe->demodulator_priv; - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; + switch (state->config->antenna_switch) { + case 0: break; + case 1: dprintk("setting GPIO21 to 1 (Radio antenna?)\n"); + saa7134_set_gpio(dev, 21, 1); + break; + case 2: dprintk("setting GPIO21 to 0 (TV antenna?)\n"); + saa7134_set_gpio(dev, 21, 0); + break; + } return 0; } -static int philips_tiger_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; +static struct tda827x_config tda827x_cfg = { + .lna_gain = philips_tda827x_lna_gain, + .init = philips_tda827x_tuner_init, + .sleep = philips_tda827x_tuner_sleep +}; - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; +static void configure_tda827x_fe(struct saa7134_dev *dev, struct tda1004x_config *tda_conf) +{ + dev->dvb.frontend = dvb_attach(tda10046_attach, tda_conf, &dev->i2c_adap); + if (dev->dvb.frontend) { + if (tda_conf->i2c_gate) + dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; + if (dvb_attach(tda827x_attach, dev->dvb.frontend, tda_conf->tuner_address, + &dev->i2c_adap,&tda827x_cfg) == NULL) { + wprintk("no tda827x tuner found at addr: %02x\n", + tda_conf->tuner_address); + } + } } -static struct tda1004x_config philips_tiger_config = { +/* ------------------------------------------------------------------ */ +static struct tda1004x_config tda827x_lifeview_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ -static int cinergy_ht_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x62}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - return 0; -} - -static int cinergy_ht_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x60}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} +static struct tda1004x_config philips_tiger_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config cinergy_ht_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP01, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config cinergy_ht_pci_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware +}; -static struct tda1004x_config pinnacle_pctv_310i_config = { +static struct tda1004x_config philips_tiger_s_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 1, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config pinnacle_pctv_310i_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 1, + .request_firmware = philips_tda1004x_request_firmware +}; static struct tda1004x_config hauppauge_hvr_1110_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - static struct tda1004x_config asus_p7131_dual_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = philips_tda1004x_request_firmware, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 0, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware }; -static int asus_p7131_dual_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x6a}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1) - return -EIO; - /* make sure the DVB-T antenna input is set */ - saa_setl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -static int asus_p7131_dual_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); - /* reset antenna inputs for analog usage */ - saa_clearl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0200000); - return 0; -} - -/* ------------------------------------------------------------------ */ - -static int lifeview_trio_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int lifeview_trio_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep(0x60, fe); - return 0; -} - static struct tda1004x_config lifeview_trio_config = { .demod_address = 0x09, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP00, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int ads_duo_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - - ret = philips_tda827xa_pll_set(0x61, fe, params); - return ret; -} - -static int ads_duo_tuner_init(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the channel decoder */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x60); - return 0; -} - -static int ads_duo_tuner_sleep(struct dvb_frontend *fe) -{ - struct saa7134_dev *dev = fe->dvb->priv; - /* route TDA8275a AGC input to the analog IF chip*/ - saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x20); - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} - -static struct tda1004x_config ads_tech_duo_config = { +static struct tda1004x_config tevion_dvbt220rf_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP00, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x60, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ - -static int tevion_dvb220rf_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) -{ - int ret; - ret = philips_tda827xa_pll_set(0x60, fe, params); - return ret; -} - -static int tevion_dvb220rf_tuner_sleep(struct dvb_frontend *fe) -{ - philips_tda827xa_tuner_sleep( 0x61, fe); - return 0; -} +static struct tda1004x_config md8800_dvbt_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x60, + .tuner_config = 0, + .request_firmware = philips_tda1004x_request_firmware +}; -static struct tda1004x_config tevion_dvbt220rf_config = { +static struct tda1004x_config asus_p7131_4871_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP01_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware }; -/* ------------------------------------------------------------------ */ +static struct tda1004x_config asus_p7131_hybrid_lna_config = { + .demod_address = 0x08, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .tuner_config = 2, + .antenna_switch= 2, + .request_firmware = philips_tda1004x_request_firmware +}; +/* ------------------------------------------------------------------ + * special case: this card uses saa713x GPIO22 for the mode switch + */ -static int md8800_dvbt_analog_mode(struct dvb_frontend *fe) +static int ads_duo_tuner_init(struct dvb_frontend *fe) { struct saa7134_dev *dev = fe->dvb->priv; - static u8 data[] = { 0x3c, 0x33, 0x68}; - struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; - - i2c_transfer(&dev->i2c_adap, &msg, 1); - philips_tda827xa_tuner_sleep( 0x61, fe); + philips_tda827x_tuner_init(fe); + /* route TDA8275a AGC input to the channel decoder */ + saa7134_set_gpio(dev, 22, 1); return 0; } -static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +static int ads_duo_tuner_sleep(struct dvb_frontend *fe) { - int ret; struct saa7134_dev *dev = fe->dvb->priv; - static u8 tda8290_close[] = { 0x21, 0xc0}; - static u8 tda8290_open[] = { 0x21, 0x80}; - struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2}; - /* close tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_close; - ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - if (ret != 1) - return -EIO; - msleep(20); - ret = philips_tda827xa_pll_set(0x60, fe, params); - if (ret != 0) - return ret; - /* open tda8290 i2c bridge */ - tda8290_msg.buf = tda8290_open; - i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1); - return ret; + /* route TDA8275a AGC input to the analog IF chip*/ + saa7134_set_gpio(dev, 22, 0); + philips_tda827x_tuner_sleep(fe); + return 0; } -static struct tda1004x_config md8800_dvbt_config = { +static struct tda827x_config ads_duo_cfg = { + .lna_gain = philips_tda827x_lna_gain, + .init = ads_duo_tuner_init, + .sleep = ads_duo_tuner_sleep +}; + +static struct tda1004x_config ads_tech_duo_config = { .demod_address = 0x08, .invert = 1, .invert_oclk = 0, .xtal_freq = TDA10046_XTAL_16M, - .agc_config = TDA10046_AGC_TDA827X_GP11, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP00_I, .if_freq = TDA10046_FREQ_045, - .request_firmware = NULL, + .tuner_address = 0x61, + .request_firmware = philips_tda1004x_request_firmware }; +/* ================================================================== + * tda10086 based DVB-S cards, helper functions + */ + static struct tda10086_config flydvbs = { .demod_address = 0x0e, .invert = 0, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * nxt200x based ATSC cards, helper functions + */ static struct nxt200x_config avertvhda180 = { .demod_address = 0x0a, @@ -1143,10 +958,13 @@ static struct nxt200x_config kworldatsc110 = { .set_pll_input = nxt200x_set_pll_input, }; -/* ------------------------------------------------------------------ */ +/* ================================================================== + * Core code + */ static int dvb_init(struct saa7134_dev *dev) { + int ret; /* init struct videobuf_dvb */ dev->ts.nr_bufs = 32; dev->ts.nr_packets = 32*4; @@ -1160,7 +978,7 @@ static int dvb_init(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL: - printk("%s: pinnacle 300i dvb setup\n",dev->name); + dprintk("pinnacle 300i dvb setup\n"); dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i, &dev->i2c_adap); if (dev->dvb.frontend) { @@ -1169,7 +987,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_AVERMEDIA_777: case SAA7134_BOARD_AVERMEDIA_A16AR: - printk("%s: avertv 777 dvb setup\n",dev->name); + dprintk("avertv 777 dvb setup\n"); dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777, &dev->i2c_adap); if (dev->dvb.frontend) { @@ -1191,42 +1009,15 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tu1216_60_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; } break; case SAA7134_BOARD_FLYDVBTDUO: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } - break; case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config); break; case SAA7134_BOARD_PHILIPS_EUROPA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_europa_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->original_demod_sleep = dev->dvb.frontend->ops.sleep; - dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep; - dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_td1316_tuner_set_params; - } - break; case SAA7134_BOARD_VIDEOMATE_DVBT_300: dev->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, @@ -1244,125 +1035,61 @@ static int dvb_init(struct saa7134_dev *dev) &philips_tu1216_61_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params; + dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_init; + dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda6651_pll_set; } break; case SAA7134_BOARD_PHILIPS_TIGER: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &philips_tiger_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &philips_tiger_config); break; case SAA7134_BOARD_PINNACLE_PCTV_310i: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &pinnacle_pctv_310i_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &pinnacle_pctv_310i_config); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &hauppauge_hvr_1110_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &hauppauge_hvr_1110_config); break; case SAA7134_BOARD_ASUSTeK_P7131_DUAL: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &asus_p7131_dual_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = asus_p7131_dual_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = asus_p7131_dual_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - } + configure_tda827x_fe(dev, &asus_p7131_dual_config); break; case SAA7134_BOARD_FLYDVBT_LR301: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tda827x_lifeview_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tda827x_tuner_set_params; - } + configure_tda827x_fe(dev, &tda827x_lifeview_config); break; case SAA7134_BOARD_FLYDVB_TRIO: if(! use_frontend) { //terrestrial - dev->dvb.frontend = dvb_attach(tda10046_attach, - &lifeview_trio_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = - lifeview_trio_tuner_set_params; - } + configure_tda827x_fe(dev, &lifeview_trio_config); } else { //satellite dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs, &dev->i2c_adap); if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x63, &dev->i2c_adap, 0) == NULL) { - printk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No tda826x found!\n", __FUNCTION__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - printk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: Lifeview Trio, No ISL6421 found!\n", __FUNCTION__); } } } break; case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331: + case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: dev->dvb.frontend = dvb_attach(tda10046_attach, &ads_tech_duo_config, &dev->i2c_adap); if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; + if (dvb_attach(tda827x_attach,dev->dvb.frontend, + ads_tech_duo_config.tuner_address, + &dev->i2c_adap,&ads_duo_cfg) == NULL) { + wprintk("no tda827x tuner found at addr: %02x\n", + ads_tech_duo_config.tuner_address); + } } break; case SAA7134_BOARD_TEVION_DVBT_220RF: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &tevion_dvbt220rf_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params; - } - break; - case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &ads_tech_duo_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params; - } + configure_tda827x_fe(dev, &tevion_dvbt220rf_config); break; case SAA7134_BOARD_MEDION_MD8800_QUADRO: - dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode; - dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set; - } + configure_tda827x_fe(dev, &md8800_dvbt_config); break; case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180: dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180, @@ -1386,11 +1113,11 @@ static int dvb_init(struct saa7134_dev *dev) if (dev->dvb.frontend) { if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60, &dev->i2c_adap, 0) == NULL) { - printk("%s: No tda826x found!\n", __FUNCTION__); + wprintk("%s: No tda826x found!\n", __FUNCTION__); } if (dvb_attach(isl6421_attach, dev->dvb.frontend, &dev->i2c_adap, 0x08, 0, 0) == NULL) { - printk("%s: No ISL6421 found!\n", __FUNCTION__); + wprintk("%s: No ISL6421 found!\n", __FUNCTION__); } } break; @@ -1415,29 +1142,45 @@ static int dvb_init(struct saa7134_dev *dev) } break; case SAA7134_BOARD_CINERGY_HT_PCMCIA: - dev->dvb.frontend = dvb_attach(tda10046_attach, - &cinergy_ht_config, - &dev->i2c_adap); - if (dev->dvb.frontend) { - dev->dvb.frontend->ops.i2c_gate_ctrl = tda8290_i2c_gate_ctrl; - dev->dvb.frontend->ops.tuner_ops.init = cinergy_ht_tuner_init; - dev->dvb.frontend->ops.tuner_ops.sleep = cinergy_ht_tuner_sleep; - dev->dvb.frontend->ops.tuner_ops.set_params = philips_tiger_tuner_set_params; - - } + configure_tda827x_fe(dev, &cinergy_ht_config); + break; + case SAA7134_BOARD_CINERGY_HT_PCI: + configure_tda827x_fe(dev, &cinergy_ht_pci_config); + break; + case SAA7134_BOARD_PHILIPS_TIGER_S: + configure_tda827x_fe(dev, &philips_tiger_s_config); + break; + case SAA7134_BOARD_ASUS_P7131_4871: + configure_tda827x_fe(dev, &asus_p7131_4871_config); + break; + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + configure_tda827x_fe(dev, &asus_p7131_hybrid_lna_config); break; default: - printk("%s: Huh? unknown DVB card?\n",dev->name); + wprintk("Huh? unknown DVB card?\n"); break; } if (NULL == dev->dvb.frontend) { - printk("%s: frontend initialization failed\n",dev->name); + printk(KERN_ERR "%s/dvb: frontend initialization failed\n", dev->name); return -1; } /* register everything else */ - return videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + ret = videobuf_dvb_register(&dev->dvb, THIS_MODULE, dev, &dev->pci->dev); + + /* this sequence is necessary to make the tda1004x load its firmware + * and to enter analog mode of hybrid boards + */ + if (!ret) { + if (dev->dvb.frontend->ops.init) + dev->dvb.frontend->ops.init(dev->dvb.frontend); + if (dev->dvb.frontend->ops.sleep) + dev->dvb.frontend->ops.sleep(dev->dvb.frontend); + if (dev->dvb.frontend->ops.tuner_ops.sleep) + dev->dvb.frontend->ops.tuner_ops.sleep(dev->dvb.frontend); + } + return ret; } static int dvb_fini(struct saa7134_dev *dev) diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 6f9fe86..1cb8c70 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -120,9 +120,9 @@ static inline int i2c_is_error(enum i2c_status status) case ARB_LOST: case SEQ_ERR: case ST_ERR: - return TRUE; + return true; default: - return FALSE; + return false; } } @@ -131,9 +131,9 @@ static inline int i2c_is_idle(enum i2c_status status) switch (status) { case IDLE: case DONE_STOP: - return TRUE; + return true; default: - return FALSE; + return false; } } @@ -141,9 +141,9 @@ static inline int i2c_is_busy(enum i2c_status status) { switch (status) { case BUSY: - return TRUE; + return true; default: - return FALSE; + return false; } } @@ -159,8 +159,8 @@ static int i2c_is_busy_wait(struct saa7134_dev *dev) saa_wait(I2C_WAIT_DELAY); } if (I2C_WAIT_RETRY == count) - return FALSE; - return TRUE; + return false; + return true; } static int i2c_reset(struct saa7134_dev *dev) @@ -171,7 +171,7 @@ static int i2c_reset(struct saa7134_dev *dev) d2printk(KERN_DEBUG "%s: i2c reset\n",dev->name); status = i2c_get_status(dev); if (!i2c_is_error(status)) - return TRUE; + return true; i2c_set_status(dev,status); for (count = 0; count < I2C_WAIT_RETRY; count++) { @@ -181,13 +181,13 @@ static int i2c_reset(struct saa7134_dev *dev) udelay(I2C_WAIT_DELAY); } if (I2C_WAIT_RETRY == count) - return FALSE; + return false; if (!i2c_is_idle(status)) - return FALSE; + return false; i2c_set_attr(dev,NOP); - return TRUE; + return true; } static inline int i2c_send_byte(struct saa7134_dev *dev, @@ -370,6 +370,8 @@ static int attach_inform(struct i2c_client *client) tun_setup.type = tuner; tun_setup.addr = saa7134_boards[dev->board].tuner_addr; + tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.tuner_callback = saa7134_tuner_callback; if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) { @@ -445,7 +447,7 @@ static void do_i2c_scan(char *name, struct i2c_client *c) unsigned char buf; int i,rc; - for (i = 0; i < 128; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_devs); i++) { c->addr = i; rc = i2c_master_recv(c,&buf,0); if (rc < 0) diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index e425268..c0de37e 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -40,16 +40,24 @@ static int pinnacle_remote = 0; module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); +static int ir_rc5_remote_gap = 885; +module_param(ir_rc5_remote_gap, int, 0644); +static int ir_rc5_key_timeout = 115; +module_param(ir_rc5_key_timeout, int, 0644); + #define dprintk(fmt, arg...) if (ir_debug) \ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg) #define i2cdprintk(fmt, arg...) if (ir_debug) \ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg) +/** rc5 functions */ +static int saa7134_rc5_irq(struct saa7134_dev *dev); + /* -------------------- GPIO generic keycode builder -------------------- */ static int build_key(struct saa7134_dev *dev) { - struct saa7134_ir *ir = dev->remote; + struct card_ir *ir = dev->remote; u32 gpio, data; /* rising SAA7134_GPIO_GPRESCAN reads the status */ @@ -134,16 +142,19 @@ static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) void saa7134_input_irq(struct saa7134_dev *dev) { - struct saa7134_ir *ir = dev->remote; + struct card_ir *ir = dev->remote; - if (!ir->polling) + if (!ir->polling && !ir->rc5_gpio) { build_key(dev); + } else if (ir->rc5_gpio) { + saa7134_rc5_irq(dev); + } } static void saa7134_input_timer(unsigned long data) { struct saa7134_dev *dev = (struct saa7134_dev*)data; - struct saa7134_ir *ir = dev->remote; + struct card_ir *ir = dev->remote; unsigned long timeout; build_key(dev); @@ -151,7 +162,7 @@ static void saa7134_input_timer(unsigned long data) mod_timer(&ir->timer, timeout); } -static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir) +static void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir) { if (ir->polling) { init_timer(&ir->timer); @@ -159,6 +170,19 @@ static void saa7134_ir_start(struct saa7134_dev *dev, struct saa7134_ir *ir) ir->timer.data = (unsigned long)dev; ir->timer.expires = jiffies + HZ; add_timer(&ir->timer); + } else if (ir->rc5_gpio) { + /* set timer_end for code completion */ + init_timer(&ir->timer_end); + ir->timer_end.function = ir_rc5_timer_end; + ir->timer_end.data = (unsigned long)ir; + init_timer(&ir->timer_keyup); + ir->timer_keyup.function = ir_rc5_timer_keyup; + ir->timer_keyup.data = (unsigned long)ir; + ir->shift_by = 2; + ir->start = 0x2; + ir->addr = 0x17; + ir->rc5_key_timeout = ir_rc5_key_timeout; + ir->rc5_remote_gap = ir_rc5_remote_gap; } } @@ -170,13 +194,14 @@ static void saa7134_ir_stop(struct saa7134_dev *dev) int saa7134_input_init1(struct saa7134_dev *dev) { - struct saa7134_ir *ir; + struct card_ir *ir; struct input_dev *input_dev; IR_KEYTAB_TYPE *ir_codes = NULL; u32 mask_keycode = 0; u32 mask_keydown = 0; u32 mask_keyup = 0; int polling = 0; + int rc5_gpio = 0; int ir_type = IR_TYPE_OTHER; int err; @@ -295,6 +320,19 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x0001F00; mask_keydown = 0x0040000; break; + case SAA7134_BOARD_ASUSTeK_P7131_DUAL: + case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: + ir_codes = ir_codes_asus_pc39; + mask_keydown = 0x0040000; + rc5_gpio = 1; + break; + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: + ir_codes = ir_codes_encore_enltv; + mask_keycode = 0x00007f; + mask_keyup = 0x040000; + polling = 50; // ms + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -316,6 +354,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) ir->mask_keydown = mask_keydown; ir->mask_keyup = mask_keyup; ir->polling = polling; + ir->rc5_gpio = rc5_gpio; /* init input device */ snprintf(ir->name, sizeof(ir->name), "saa7134 IR (%s)", @@ -402,6 +441,49 @@ void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) } } + +static int saa7134_rc5_irq(struct saa7134_dev *dev) +{ + struct card_ir *ir = dev->remote; + struct timeval tv; + u32 gap; + unsigned long current_jiffies, timeout; + + /* get time of bit */ + current_jiffies = jiffies; + do_gettimeofday(&tv); + + /* avoid overflow with gap >1s */ + if (tv.tv_sec - ir->base_time.tv_sec > 1) { + gap = 200000; + } else { + gap = 1000000 * (tv.tv_sec - ir->base_time.tv_sec) + + tv.tv_usec - ir->base_time.tv_usec; + } + + /* active code => add bit */ + if (ir->active) { + /* only if in the code (otherwise spurious IRQ or timer + late) */ + if (ir->last_bit < 28) { + ir->last_bit = (gap - ir_rc5_remote_gap / 2) / + ir_rc5_remote_gap; + ir->code |= 1 << ir->last_bit; + } + /* starting new code */ + } else { + ir->active = 1; + ir->code = 0; + ir->base_time = tv; + ir->last_bit = 0; + + timeout = current_jiffies + (500 + 30 * HZ) / 1000; + mod_timer(&ir->timer_end, timeout); + } + + return 1; +} + /* ---------------------------------------------------------------------- * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index f2cb630..9985ded 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -26,6 +26,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/sort.h> #include "saa7134-reg.h" #include "saa7134.h" @@ -516,14 +517,12 @@ static int res_get(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int return 1; } -static -int res_check(struct saa7134_fh *fh, unsigned int bit) +static int res_check(struct saa7134_fh *fh, unsigned int bit) { return (fh->resources & bit); } -static -int res_locked(struct saa7134_dev *dev, unsigned int bit) +static int res_locked(struct saa7134_dev *dev, unsigned int bit) { return (dev->resources & bit); } @@ -603,7 +602,14 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm) saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); - saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + /* only tell the tuner if this is a tv input */ + if (card_in(dev,dev->ctl_input).tv) { + if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290) + && ((card(dev).tuner_config == 1) + || (card(dev).tuner_config == 2))) + saa7134_set_gpio(dev, 22, 5); + saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id); + } } static void video_mux(struct saa7134_dev *dev, int input) @@ -732,25 +738,6 @@ struct cliplist { __u8 disable; }; -static void sort_cliplist(struct cliplist *cl, int entries) -{ - struct cliplist swap; - int i,j,n; - - for (i = entries-2; i >= 0; i--) { - for (n = 0, j = 0; j <= i; j++) { - if (cl[j].position > cl[j+1].position) { - swap = cl[j]; - cl[j] = cl[j+1]; - cl[j+1] = swap; - n++; - } - } - if (0 == n) - break; - } -} - static void set_cliplist(struct saa7134_dev *dev, int reg, struct cliplist *cl, int entries, char *name) { @@ -784,15 +771,27 @@ static int clip_range(int val) return val; } +/* Sort into smallest position first order */ +static int cliplist_cmp(const void *a, const void *b) +{ + const struct cliplist *cla = a; + const struct cliplist *clb = b; + if (cla->position < clb->position) + return -1; + if (cla->position > clb->position) + return 1; + return 0; +} + static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, int nclips, int interlace) { struct cliplist col[16], row[16]; - int cols, rows, i; + int cols = 0, rows = 0, i; int div = interlace ? 2 : 1; - memset(col,0,sizeof(col)); cols = 0; - memset(row,0,sizeof(row)); rows = 0; + memset(col, 0, sizeof(col)); + memset(row, 0, sizeof(row)); for (i = 0; i < nclips && i < 8; i++) { col[cols].position = clip_range(clips[i].c.left); col[cols].enable = (1 << i); @@ -808,8 +807,8 @@ static int setup_clipping(struct saa7134_dev *dev, struct v4l2_clip *clips, row[rows].disable = (1 << i); rows++; } - sort_cliplist(col,cols); - sort_cliplist(row,rows); + sort(col, cols, sizeof col[0], cliplist_cmp, NULL); + sort(row, rows, sizeof row[0], cliplist_cmp, NULL); set_cliplist(dev,0x380,col,cols,"cols"); set_cliplist(dev,0x384,row,rows,"rows"); return 0; @@ -1261,19 +1260,14 @@ static struct videobuf_queue* saa7134_queue(struct saa7134_fh *fh) static int saa7134_resource(struct saa7134_fh *fh) { - int res = 0; + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return RESOURCE_VIDEO; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - res = RESOURCE_VIDEO; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - res = RESOURCE_VBI; - break; - default: - BUG(); - } - return res; + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return RESOURCE_VBI; + + BUG(); + return 0; } static int video_open(struct inode *inode, struct file *file) @@ -1461,8 +1455,7 @@ static int video_release(struct inode *inode, struct file *file) return 0; } -static int -video_mmap(struct file *file, struct vm_area_struct * vma) +static int video_mmap(struct file *file, struct vm_area_struct * vma) { struct saa7134_fh *fh = file->private_data; @@ -2461,12 +2454,6 @@ int saa7134_video_init2(struct saa7134_dev *dev) return 0; } -int saa7134_video_fini(struct saa7134_dev *dev) -{ - /* nothing */ - return 0; -} - void saa7134_irq_video_intl(struct saa7134_dev *dev) { static const char *st[] = { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 88cd1297..62224cc 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -41,14 +41,10 @@ #include <sound/driver.h> #include <sound/core.h> #include <sound/pcm.h> +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) #include <media/video-buf-dvb.h> - -#ifndef TRUE -# define TRUE (1==1) -#endif -#ifndef FALSE -# define FALSE (1==0) #endif + #define UNSET (-1U) /* ----------------------------------------------------------- */ @@ -232,6 +228,13 @@ struct saa7134_format { #define SAA7134_BOARD_VIDEOMATE_DVBT_200A 103 #define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 #define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 +#define SAA7134_BOARD_ENCORE_ENLTV 106 +#define SAA7134_BOARD_ENCORE_ENLTV_FM 107 +#define SAA7134_BOARD_CINERGY_HT_PCI 108 +#define SAA7134_BOARD_PHILIPS_TIGER_S 109 +#define SAA7134_BOARD_AVERMEDIA_M102 110 +#define SAA7134_BOARD_ASUS_P7131_4871 111 +#define SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA 112 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 @@ -281,6 +284,7 @@ struct saa7134_board { unsigned char radio_addr; unsigned int tda9887_conf; + unsigned int tuner_config; /* peripheral I/O */ enum saa7134_video_out video_out; @@ -411,20 +415,6 @@ struct saa7134_dmasound { struct snd_pcm_substream *substream; }; -/* IR input */ -struct saa7134_ir { - struct input_dev *dev; - struct ir_input_state ir; - char name[32]; - char phys[32]; - u32 mask_keycode; - u32 mask_keydown; - u32 mask_keyup; - int polling; - u32 last_gpio; - struct timer_list timer; -}; - /* ts/mpeg status */ struct saa7134_ts { /* TS capture */ @@ -450,6 +440,8 @@ struct saa7134_dev { #ifdef VIDIOC_G_PRIORITY struct v4l2_prio_state prio; #endif + /* workstruct for loading modules */ + struct work_struct request_module_wk; /* insmod option/autodetected */ int autodetected; @@ -463,7 +455,7 @@ struct saa7134_dev { /* infrared remote */ int has_remote; - struct saa7134_ir *remote; + struct card_ir *remote; /* pci i/o */ char name[32]; @@ -543,9 +535,11 @@ struct saa7134_dev { struct work_struct empress_workqueue; int empress_started; +#if defined(CONFIG_VIDEO_BUF_DVB) || defined(CONFIG_VIDEO_BUF_DVB_MODULE) /* SAA7134_MPEG_DVB only */ struct videobuf_dvb dvb; int (*original_demod_sleep)(struct dvb_frontend* fe); +#endif }; /* ----------------------------------------------------------- */ @@ -575,6 +569,8 @@ extern struct list_head saa7134_devlist; extern int saa7134_no_overlay; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); +void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); +int saa7134_tuner_callback(void *ptr, int command, int arg); #define SAA7134_PGTABLE_SIZE 4096 @@ -633,7 +629,6 @@ int saa7134_common_ioctl(struct saa7134_dev *dev, int saa7134_video_init1(struct saa7134_dev *dev); int saa7134_video_init2(struct saa7134_dev *dev); -int saa7134_video_fini(struct saa7134_dev *dev); void saa7134_irq_video_intl(struct saa7134_dev *dev); void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status); @@ -698,6 +693,7 @@ void saa7134_input_fini(struct saa7134_dev *dev); void saa7134_input_irq(struct saa7134_dev *dev); void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir); + /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 038448f..93fb04e 100644 --- a/drivers/media/video/se401.c +++ b/drivers/media/video/se401.c @@ -450,6 +450,13 @@ static int se401_start_stream(struct usb_se401 *se401) } for (i=0; i<SE401_NUMSBUF; i++) { se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->sbuf[i].data) { + for(i = i - 1; i >= 0; i--) { + kfree(se401->sbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; + } } se401->bayeroffset=0; @@ -458,13 +465,26 @@ static int se401_start_stream(struct usb_se401 *se401) se401->scratch_overflow=0; for (i=0; i<SE401_NUMSCRATCH; i++) { se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->scratch[i].data) { + for(i = i - 1; i >= 0; i--) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + goto nomem_sbuf; + } se401->scratch[i].state=BUFFER_UNUSED; } for (i=0; i<SE401_NUMSBUF; i++) { urb=usb_alloc_urb(0, GFP_KERNEL); - if(!urb) - return -ENOMEM; + if(!urb) { + for(i = i - 1; i >= 0; i--) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + } + goto nomem_scratch; + } usb_fill_bulk_urb(urb, se401->dev, usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), @@ -482,6 +502,18 @@ static int se401_start_stream(struct usb_se401 *se401) se401->framecount=0; return 0; + + nomem_scratch: + for (i=0; i<SE401_NUMSCRATCH; i++) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + nomem_sbuf: + for (i=0; i<SE401_NUMSBUF; i++) { + kfree(se401->sbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; } static int se401_stop_stream(struct usb_se401 *se401) diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index cf552e6..19204f5 100644 --- a/drivers/media/video/sn9c102/Kconfig +++ b/drivers/media/video/sn9c102/Kconfig @@ -1,9 +1,9 @@ config USB_SN9C102 - tristate "USB SN9C10x PC Camera Controller support" - depends on USB && VIDEO_V4L1 + tristate "USB SN9C1xx PC Camera Controller support" + depends on USB && VIDEO_V4L2 ---help--- Say Y here if you want support for cameras based on SONiX SN9C101, - SN9C102 or SN9C103 PC Camera Controllers. + SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. See <file:Documentation/video4linux/sn9c102.txt> for more info. diff --git a/drivers/media/video/sn9c102/Makefile b/drivers/media/video/sn9c102/Makefile index 536ad30..a56d16f 100644 --- a/drivers/media/video/sn9c102/Makefile +++ b/drivers/media/video/sn9c102/Makefile @@ -1,7 +1,14 @@ -sn9c102-objs := sn9c102_core.o sn9c102_hv7131d.o sn9c102_mi0343.o \ - sn9c102_ov7630.o sn9c102_pas106b.o sn9c102_pas202bca.o \ - sn9c102_pas202bcb.o sn9c102_tas5110c1b.o \ - sn9c102_tas5130d1b.o +sn9c102-objs := sn9c102_core.o \ + sn9c102_hv7131d.o \ + sn9c102_hv7131r.o \ + sn9c102_mi0343.o \ + sn9c102_mi0360.o \ + sn9c102_ov7630.o \ + sn9c102_ov7660.o \ + sn9c102_pas106b.o \ + sn9c102_pas202bcb.o \ + sn9c102_tas5110c1b.o \ + sn9c102_tas5110d.o \ + sn9c102_tas5130d1b.o obj-$(CONFIG_USB_SN9C102) += sn9c102.o - diff --git a/drivers/media/video/sn9c102/sn9c102.h b/drivers/media/video/sn9c102/sn9c102.h index 2c6ff39..680e746 100644 --- a/drivers/media/video/sn9c102/sn9c102.h +++ b/drivers/media/video/sn9c102/sn9c102.h @@ -1,5 +1,5 @@ /*************************************************************************** - * V4L2 driver for SN9C10x PC Camera Controllers * + * V4L2 driver for SN9C1xx PC Camera Controllers * * * * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * * * @@ -37,33 +37,10 @@ #include <linux/string.h> #include <linux/stddef.h> +#include "sn9c102_config.h" #include "sn9c102_sensor.h" +#include "sn9c102_devtable.h" -/*****************************************************************************/ - -#define SN9C102_DEBUG -#define SN9C102_DEBUG_LEVEL 2 -#define SN9C102_MAX_DEVICES 64 -#define SN9C102_PRESERVE_IMGSCALE 0 -#define SN9C102_FORCE_MUNMAP 0 -#define SN9C102_MAX_FRAMES 32 -#define SN9C102_URBS 2 -#define SN9C102_ISO_PACKETS 7 -#define SN9C102_ALTERNATE_SETTING 8 -#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) -#define SN9C102_CTRL_TIMEOUT 300 -#define SN9C102_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -enum sn9c102_bridge { - BRIDGE_SN9C101 = 0x01, - BRIDGE_SN9C102 = 0x02, - BRIDGE_SN9C103 = 0x04, -}; - -SN9C102_ID_TABLE -SN9C102_SENSOR_TABLE enum sn9c102_frame_state { F_UNUSED, @@ -99,13 +76,16 @@ enum sn9c102_stream_state { STREAM_ON, }; -typedef char sn9c103_sof_header_t[18]; -typedef char sn9c102_sof_header_t[12]; -typedef char sn9c102_eof_header_t[4]; +typedef char sn9c102_sof_header_t[62]; + +struct sn9c102_sof_t { + sn9c102_sof_header_t header; + u16 bytesread; +}; struct sn9c102_sysfs_attr { - u8 reg, i2c_reg; - sn9c103_sof_header_t frame_header; + u16 reg, i2c_reg; + sn9c102_sof_header_t frame_header; }; struct sn9c102_module_param { @@ -137,8 +117,8 @@ struct sn9c102_device { struct v4l2_jpegcompression compression; struct sn9c102_sysfs_attr sysfs; - sn9c103_sof_header_t sof_header; - u16 reg[63]; + struct sn9c102_sof_t sof; + u16 reg[384]; struct sn9c102_module_param module_param; @@ -155,10 +135,7 @@ struct sn9c102_device { struct sn9c102_device* sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id) { - if (usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id)) - return cam; - - return NULL; + return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; } @@ -169,6 +146,19 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, memcpy(&cam->sensor, sensor, sizeof(struct sn9c102_sensor)); } + +enum sn9c102_bridge +sn9c102_get_bridge(struct sn9c102_device* cam) +{ + return cam->bridge; +} + + +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam) +{ + return &cam->sensor; +} + /*****************************************************************************/ #undef DBG @@ -197,8 +187,8 @@ do { \ if ((level) == 1 || (level) == 2) \ pr_info("sn9c102: " fmt "\n", ## args); \ else if ((level) == 3) \ - pr_debug("sn9c102: [%s:%d] " fmt "\n", __FUNCTION__, \ - __LINE__ , ## args); \ + pr_debug("sn9c102: [%s:%d] " fmt "\n", \ + __FUNCTION__, __LINE__ , ## args); \ } \ } while (0) #else @@ -209,8 +199,8 @@ do { \ #undef PDBG #define PDBG(fmt, args...) \ -dev_info(&cam->usbdev->dev, "[%s:%d] " fmt "\n", \ - __FUNCTION__, __LINE__ , ## args) +dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", __FILE__, __FUNCTION__, \ + __LINE__ , ## args) #undef PDBGG #define PDBGG(fmt, args...) do {;} while(0) /* placeholder */ diff --git a/drivers/media/video/sn9c102/sn9c102_config.h b/drivers/media/video/sn9c102/sn9c102_config.h new file mode 100644 index 0000000..0f4e037 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_config.h @@ -0,0 +1,86 @@ +/*************************************************************************** + * Global parameters for the V4L2 driver for SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_CONFIG_H_ +#define _SN9C102_CONFIG_H_ + +#include <linux/types.h> +#include <linux/jiffies.h> + +#define SN9C102_DEBUG +#define SN9C102_DEBUG_LEVEL 2 +#define SN9C102_MAX_DEVICES 64 +#define SN9C102_PRESERVE_IMGSCALE 0 +#define SN9C102_FORCE_MUNMAP 0 +#define SN9C102_MAX_FRAMES 32 +#define SN9C102_URBS 2 +#define SN9C102_ISO_PACKETS 7 +#define SN9C102_ALTERNATE_SETTING 8 +#define SN9C102_URB_TIMEOUT msecs_to_jiffies(2 * SN9C102_ISO_PACKETS) +#define SN9C102_CTRL_TIMEOUT 300 +#define SN9C102_FRAME_TIMEOUT 0 + +/*****************************************************************************/ + +static const u8 SN9C102_Y_QTABLE0[64] = { + 8, 5, 5, 8, 12, 20, 25, 30, + 6, 6, 7, 9, 13, 29, 30, 27, + 7, 6, 8, 12, 20, 28, 34, 28, + 7, 8, 11, 14, 25, 43, 40, 31, + 9, 11, 18, 28, 34, 54, 51, 38, + 12, 17, 27, 32, 40, 52, 56, 46, + 24, 32, 39, 43, 51, 60, 60, 50, + 36, 46, 47, 49, 56, 50, 51, 49 +}; + +static const u8 SN9C102_UV_QTABLE0[64] = { + 8, 9, 12, 23, 49, 49, 49, 49, + 9, 10, 13, 33, 49, 49, 49, 49, + 12, 13, 28, 49, 49, 49, 49, 49, + 23, 33, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49, + 49, 49, 49, 49, 49, 49, 49, 49 +}; + +static const u8 SN9C102_Y_QTABLE1[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +static const u8 SN9C102_UV_QTABLE1[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +#endif /* _SN9C102_CONFIG_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index 04d4c8f..89f8335 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -1,7 +1,7 @@ /*************************************************************************** - * V4L2 driver for SN9C10x PC Camera Controllers * + * V4L2 driver for SN9C1xx PC Camera Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -43,12 +43,13 @@ /*****************************************************************************/ -#define SN9C102_MODULE_NAME "V4L2 driver for SN9C10x PC Camera Controllers" -#define SN9C102_MODULE_AUTHOR "(C) 2004-2006 Luca Risolia" +#define SN9C102_MODULE_NAME "V4L2 driver for SN9C1xx PC Camera Controllers" +#define SN9C102_MODULE_ALIAS "sn9c1xx" +#define SN9C102_MODULE_AUTHOR "(C) 2004-2007 Luca Risolia" #define SN9C102_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define SN9C102_MODULE_LICENSE "GPL" -#define SN9C102_MODULE_VERSION "1:1.27" -#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 27) +#define SN9C102_MODULE_VERSION "1:1.39" +#define SN9C102_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 39) /*****************************************************************************/ @@ -56,6 +57,7 @@ MODULE_DEVICE_TABLE(usb, sn9c102_id_table); MODULE_AUTHOR(SN9C102_MODULE_AUTHOR " " SN9C102_AUTHOR_EMAIL); MODULE_DESCRIPTION(SN9C102_MODULE_NAME); +MODULE_ALIAS(SN9C102_MODULE_ALIAS); MODULE_VERSION(SN9C102_MODULE_VERSION); MODULE_LICENSE(SN9C102_MODULE_LICENSE); @@ -91,7 +93,8 @@ static unsigned int frame_timeout[] = {[0 ... SN9C102_MAX_DEVICES-1] = SN9C102_FRAME_TIMEOUT}; module_param_array(frame_timeout, uint, NULL, 0644); MODULE_PARM_DESC(frame_timeout, - "\n<n[,...]> Timeout for a video frame in seconds." + "\n<0|n[,...]> Timeout for a video frame in seconds before" + "\nreturning an I/O error; 0 for infinity." "\nThis parameter is specific for each detected camera." "\nDefault value is "__MODULE_STRING(SN9C102_FRAME_TIMEOUT)"." "\n"); @@ -105,51 +108,35 @@ MODULE_PARM_DESC(debug, "\n1 = critical errors" "\n2 = significant informations" "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." + "\nLevel 3 is useful for testing only." "\nDefault value is "__MODULE_STRING(SN9C102_DEBUG_LEVEL)"." "\n"); #endif /*****************************************************************************/ -static sn9c102_sof_header_t sn9c102_sof_header[] = { - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x00}, - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x01}, -}; - -static sn9c103_sof_header_t sn9c103_sof_header[] = { - {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96, 0x20}, -}; - -static sn9c102_eof_header_t sn9c102_eof_header[] = { - {0x00, 0x00, 0x00, 0x00}, - {0x40, 0x00, 0x00, 0x00}, - {0x80, 0x00, 0x00, 0x00}, - {0xc0, 0x00, 0x00, 0x00}, -}; - -/*****************************************************************************/ - static u32 sn9c102_request_buffers(struct sn9c102_device* cam, u32 count, enum sn9c102_io_method io) { struct v4l2_pix_format* p = &(cam->sensor.pix_format); struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; + size_t imagesize = cam->module_param.force_munmap || io == IO_READ ? + (p->width * p->height * p->priv) / 8 : + (r->width * r->height * p->priv) / 8; void* buff = NULL; u32 i; if (count > SN9C102_MAX_FRAMES) count = SN9C102_MAX_FRAMES; + if (cam->bridge == BRIDGE_SN9C105 || cam->bridge == BRIDGE_SN9C120) + imagesize += 589 + 2; /* length of JPEG header + EOI marker */ + cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -222,27 +209,40 @@ static void sn9c102_queue_unusedframes(struct sn9c102_device* cam) } /*****************************************************************************/ - -int sn9c102_write_regs(struct sn9c102_device* cam, u8* buff, u16 index) +/* + * Write a sequence of count value/register pairs. Returns -1 after the + * first failed write, or 0 for no errors. + */ +int sn9c102_write_regs(struct sn9c102_device* cam, const u8 valreg[][2], + int count) { struct usb_device* udev = cam->usbdev; + u8* value = cam->control_buffer; /* Needed for DMA'able memory */ int i, res; - if (index + sizeof(buff) >= ARRAY_SIZE(cam->reg)) - return -1; + for (i = 0; i < count; i++) { + u8 index = valreg[i][1]; + + /* + * index is a u8, so it must be <256 and can't be out of range. + * If we put in a check anyway, gcc annoys us with a warning + * that our check is useless. People get all uppity when they + * see warnings in the kernel compile. + */ + + *value = valreg[i][0]; + res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x08, 0x41, index, 0, + value, 1, SN9C102_CTRL_TIMEOUT); + if (res < 0) { + DBG(3, "Failed to write a register (value 0x%02X, " + "index 0x%02X, error %d)", *value, index, res); + return -1; + } - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, - index, 0, buff, sizeof(buff), - SN9C102_CTRL_TIMEOUT*sizeof(buff)); - if (res < 0) { - DBG(3, "Failed to write registers (index 0x%02X, error %d)", - index, res); - return -1; + cam->reg[index] = *value; } - for (i = 0; i < sizeof(buff); i++) - cam->reg[index+i] = buff[i]; - return 0; } @@ -322,9 +322,21 @@ static int sn9c102_i2c_detect_read_error(struct sn9c102_device* cam, struct sn9c102_sensor* sensor) { - int r; + int r , err = 0; + r = sn9c102_read_reg(cam, 0x08); - return (r < 0 || (r >= 0 && !(r & 0x08))) ? -EIO : 0; + if (r < 0) + err += r; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { + if (!(r & 0x08)) + err += -1; + } else { + if (r & 0x08) + err += -1; + } + + return err ? -EIO : 0; } @@ -415,7 +427,7 @@ sn9c102_i2c_try_raw_write(struct sn9c102_device* cam, data[4] = data3; data[5] = data4; data[6] = data5; - data[7] = 0x14; + data[7] = 0x17; res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x08, 0x41, 0x08, 0, data, 8, SN9C102_CTRL_TIMEOUT); if (res < 0) @@ -467,34 +479,63 @@ int sn9c102_i2c_write(struct sn9c102_device* cam, u8 address, u8 value) /*****************************************************************************/ -static void* -sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +static size_t sn9c102_sof_length(struct sn9c102_device* cam) { - size_t soflen = 0, i; - u8 j, n = 0; - switch (cam->bridge) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: - soflen = sizeof(sn9c102_sof_header_t); - n = sizeof(sn9c102_sof_header) / soflen; - break; + return 12; case BRIDGE_SN9C103: - soflen = sizeof(sn9c103_sof_header_t); - n = sizeof(sn9c103_sof_header) / soflen; - } - - for (i = 0; (len >= soflen) && (i <= len - soflen); i++) - for (j = 0; j < n; j++) - /* The invariable part of the header is 6 bytes long */ - if ((cam->bridge != BRIDGE_SN9C103 && - !memcmp(mem + i, sn9c102_sof_header[j], 6)) || - (cam->bridge == BRIDGE_SN9C103 && - !memcmp(mem + i, sn9c103_sof_header[j], 6))) { - memcpy(cam->sof_header, mem + i, soflen); - /* Skip the header */ - return mem + i + soflen; + return 18; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + return 62; + } + + return 0; +} + + +static void* +sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) +{ + static const char marker[6] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; + const char *m = mem; + size_t soflen = 0, i, j; + + soflen = sn9c102_sof_length(cam); + + for (i = 0; i < len; i++) { + size_t b; + + /* Read the variable part of the header */ + if (unlikely(cam->sof.bytesread >= sizeof(marker))) { + cam->sof.header[cam->sof.bytesread] = *(m+i); + if (++cam->sof.bytesread == soflen) { + cam->sof.bytesread = 0; + return mem + i; } + continue; + } + + /* Search for the SOF marker (fixed part) in the header */ + for (j = 0, b=cam->sof.bytesread; j+b < sizeof(marker); j++) { + if (unlikely(i+j) == len) + return NULL; + if (*(m+i+j) == marker[cam->sof.bytesread]) { + cam->sof.header[cam->sof.bytesread] = *(m+i+j); + if (++cam->sof.bytesread == sizeof(marker)) { + PDBGG("Bytes to analyze: %zd. SOF " + "starts at byte #%zd", len, i); + i += j+1; + break; + } + } else { + cam->sof.bytesread = 0; + break; + } + } + } return NULL; } @@ -503,21 +544,129 @@ sn9c102_find_sof_header(struct sn9c102_device* cam, void* mem, size_t len) static void* sn9c102_find_eof_header(struct sn9c102_device* cam, void* mem, size_t len) { - size_t eoflen = sizeof(sn9c102_eof_header_t), i; - unsigned j, n = sizeof(sn9c102_eof_header) / eoflen; + static const u8 eof_header[4][4] = { + {0x00, 0x00, 0x00, 0x00}, + {0x40, 0x00, 0x00, 0x00}, + {0x80, 0x00, 0x00, 0x00}, + {0xc0, 0x00, 0x00, 0x00}, + }; + size_t i, j; - if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) - return NULL; /* EOF header does not exist in compressed data */ + /* The EOF header does not exist in compressed data */ + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + return NULL; - for (i = 0; (len >= eoflen) && (i <= len - eoflen); i++) - for (j = 0; j < n; j++) - if (!memcmp(mem + i, sn9c102_eof_header[j], eoflen)) + /* + The EOF header might cross the packet boundary, but this is not a + problem, since the end of a frame is determined by checking its size + in the first place. + */ + for (i = 0; (len >= 4) && (i <= len - 4); i++) + for (j = 0; j < ARRAY_SIZE(eof_header); j++) + if (!memcmp(mem + i, eof_header[j], 4)) return mem + i; return NULL; } +static void +sn9c102_write_jpegheader(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static const u8 jpeg_header[589] = { + 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x84, 0x00, 0x06, 0x04, 0x05, + 0x06, 0x05, 0x04, 0x06, 0x06, 0x05, 0x06, 0x07, 0x07, 0x06, + 0x08, 0x0a, 0x10, 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, + 0x0f, 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, 0x16, + 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, 0x23, 0x1c, 0x16, + 0x16, 0x20, 0x2c, 0x20, 0x23, 0x26, 0x27, 0x29, 0x2a, 0x29, + 0x19, 0x1f, 0x2d, 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, + 0x28, 0x01, 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, + 0x0a, 0x13, 0x28, 0x1a, 0x16, 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, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 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, 0xc0, 0x00, 0x11, + 0x08, 0x01, 0xe0, 0x02, 0x80, 0x03, 0x01, 0x21, 0x00, 0x02, + 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, + 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 + }; + u8 *pos = f->bufmem; + + memcpy(pos, jpeg_header, sizeof(jpeg_header)); + *(pos + 6) = 0x00; + *(pos + 7 + 64) = 0x01; + if (cam->compression.quality == 0) { + memcpy(pos + 7, SN9C102_Y_QTABLE0, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE0, 64); + } else if (cam->compression.quality == 1) { + memcpy(pos + 7, SN9C102_Y_QTABLE1, 64); + memcpy(pos + 8 + 64, SN9C102_UV_QTABLE1, 64); + } + *(pos + 564) = cam->sensor.pix_format.width & 0xFF; + *(pos + 563) = (cam->sensor.pix_format.width >> 8) & 0xFF; + *(pos + 562) = cam->sensor.pix_format.height & 0xFF; + *(pos + 561) = (cam->sensor.pix_format.height >> 8) & 0xFF; + *(pos + 567) = 0x21; + + f->buf.bytesused += sizeof(jpeg_header); +} + + +static void +sn9c102_write_eoimarker(struct sn9c102_device* cam, struct sn9c102_frame_t* f) +{ + static const u8 eoi_marker[2] = {0xff, 0xd9}; + + memcpy(f->bufmem + f->buf.bytesused, eoi_marker, sizeof(eoi_marker)); + f->buf.bytesused += sizeof(eoi_marker); +} + + static void sn9c102_urb_complete(struct urb *urb) { struct sn9c102_device* cam = urb->context; @@ -535,7 +684,8 @@ static void sn9c102_urb_complete(struct urb *urb) cam->stream = STREAM_OFF; if ((*f)) (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); + cam->sof.bytesread = 0; + DBG(3, "Stream interrupted by application"); wake_up(&cam->wait_stream); } @@ -557,10 +707,9 @@ static void sn9c102_urb_complete(struct urb *urb) imagesize = (cam->sensor.pix_format.width * cam->sensor.pix_format.height * cam->sensor.pix_format.priv) / 8; - - soflen = (cam->bridge) == BRIDGE_SN9C103 ? - sizeof(sn9c103_sof_header_t) : - sizeof(sn9c102_sof_header_t); + if (cam->sensor.pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + imagesize += 589; /* length of jpeg header */ + soflen = sn9c102_sof_length(cam); for (i = 0; i < urb->number_of_packets; i++) { unsigned int img, len, status; @@ -573,6 +722,7 @@ static void sn9c102_urb_complete(struct urb *urb) if (status) { DBG(3, "Error in isochronous frame"); (*f)->state = F_ERROR; + cam->sof.bytesread = 0; continue; } @@ -589,13 +739,13 @@ end_of_frame: if (eof) img = (eof > pos) ? eof - pos - 1 : 0; - if ((*f)->buf.bytesused+img > imagesize) { + if ((*f)->buf.bytesused + img > imagesize) { u32 b; b = (*f)->buf.bytesused + img - imagesize; img = imagesize - (*f)->buf.bytesused; - DBG(3, "Expected EOF not found: " - "video frame cut"); + PDBGG("Expected EOF not found: video " + "frame cut"); if (eof) DBG(3, "Exceeded limit: +%u " "bytes", (unsigned)(b)); @@ -610,12 +760,16 @@ end_of_frame: (*f)->buf.bytesused += img; if ((*f)->buf.bytesused == imagesize || - (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X && eof)) { + ((cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) && eof)) { u32 b; + b = (*f)->buf.bytesused; (*f)->state = F_DONE; (*f)->buf.sequence= ++cam->frame_count; + spin_lock(&cam->queue_lock); list_move_tail(&(*f)->frame, &cam->outqueue); @@ -627,8 +781,10 @@ end_of_frame: else (*f) = NULL; spin_unlock(&cam->queue_lock); + memcpy(cam->sysfs.frame_header, - cam->sof_header, soflen); + cam->sof.header, soflen); + DBG(3, "Video frame captured: %lu " "bytes", (unsigned long)(b)); @@ -661,6 +817,9 @@ start_of_frame: (*f)->buf.bytesused = 0; len -= (sof - pos); pos = sof; + if (cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) + sn9c102_write_jpegheader(cam, (*f)); DBG(3, "SOF detected: new video frame"); if (len) goto redo; @@ -671,8 +830,16 @@ start_of_frame: goto end_of_frame; /* (1) */ else { if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_SN9C10X) { - eof = sof - soflen; + V4L2_PIX_FMT_SN9C10X || + cam->sensor.pix_format.pixelformat == + V4L2_PIX_FMT_JPEG) { + if (sof - pos >= soflen) { + eof = sof - soflen; + } else { /* remove header */ + eof = pos; + (*f)->buf.bytesused -= + (soflen - (sof - pos)); + } goto end_of_frame; } else { DBG(3, "SOF before expected EOF after " @@ -701,13 +868,11 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) { struct usb_device *udev = cam->usbdev; struct urb* urb; - const unsigned int sn9c102_wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1023}; - const unsigned int sn9c103_wMaxPacketSize[] = {0, 128, 256, 384, 512, - 680, 800, 900, 1003}; - const unsigned int psz = (cam->bridge == BRIDGE_SN9C103) ? - sn9c103_wMaxPacketSize[SN9C102_ALTERNATE_SETTING] : - sn9c102_wMaxPacketSize[SN9C102_ALTERNATE_SETTING]; + struct usb_host_interface* altsetting = usb_altnum_to_altsetting( + usb_ifnum_to_if(udev, 0), + SN9C102_ALTERNATE_SETTING); + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); s8 i, j; int err = 0; @@ -761,6 +926,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) } cam->frame_current = NULL; + cam->sof.bytesread = 0; for (i = 0; i < SN9C102_URBS; i++) { err = usb_submit_urb(cam->urb[i], GFP_KERNEL); @@ -775,7 +941,7 @@ static int sn9c102_start_transfer(struct sn9c102_device* cam) return 0; free_urbs: - for (i = 0; i < SN9C102_URBS; i++) + for (i = 0; (i < SN9C102_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]); free_buffers: @@ -834,29 +1000,29 @@ static int sn9c102_stream_interrupt(struct sn9c102_device* cam) /*****************************************************************************/ #ifdef CONFIG_VIDEO_ADV_DEBUG -static u8 sn9c102_strtou8(const char* buff, size_t len, ssize_t* count) +static u16 sn9c102_strtou16(const char* buff, size_t len, ssize_t* count) { - char str[5]; + char str[7]; char* endp; unsigned long val; - if (len < 4) { + if (len < 6) { strncpy(str, buff, len); - str[len+1] = '\0'; + str[len] = '\0'; } else { - strncpy(str, buff, 4); - str[4] = '\0'; + strncpy(str, buff, 6); + str[6] = '\0'; } val = simple_strtoul(str, &endp, 0); *count = 0; - if (val <= 0xff) + if (val <= 0xffff) *count = (ssize_t)(endp - str); if ((*count) && (len == *count+1) && (buff[*count] == '\n')) *count += 1; - return (u8)val; + return (u16)val; } /* @@ -873,7 +1039,8 @@ static ssize_t sn9c102_show_reg(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -891,27 +1058,28 @@ static ssize_t sn9c102_store_reg(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 index; + u16 index; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - index = sn9c102_strtou8(buf, len, &count); - if (index > 0x1f || !count) { + index = sn9c102_strtou16(buf, len, &count); + if (index >= ARRAY_SIZE(cam->reg) || !count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; } cam->sysfs.reg = index; - DBG(2, "Moved SN9C10X register index to 0x%02X", cam->sysfs.reg); + DBG(2, "Moved SN9C1XX register index to 0x%02X", cam->sysfs.reg); DBG(3, "Written bytes: %zd", count); mutex_unlock(&sn9c102_sysfs_lock); @@ -929,7 +1097,8 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -942,7 +1111,7 @@ static ssize_t sn9c102_show_val(struct class_device* cd, char* buf) count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -954,20 +1123,21 @@ static ssize_t sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 value; + u16 value; ssize_t count; int err; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -979,7 +1149,7 @@ sn9c102_store_val(struct class_device* cd, const char* buf, size_t len) return -EIO; } - DBG(2, "Written SN9C10X reg. 0x%02X, val. 0x%02X", + DBG(2, "Written SN9C1XX reg. 0x%02X, val. 0x%02X", cam->sysfs.reg, value); DBG(3, "Written bytes: %zd", count); @@ -997,7 +1167,8 @@ static ssize_t sn9c102_show_i2c_reg(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1017,19 +1188,20 @@ static ssize_t sn9c102_store_i2c_reg(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 index; + u16 index; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; } - index = sn9c102_strtou8(buf, len, &count); + index = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -1055,7 +1227,8 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1073,7 +1246,7 @@ static ssize_t sn9c102_show_i2c_val(struct class_device* cd, char* buf) count = sprintf(buf, "%d\n", val); - DBG(3, "Read bytes: %zd", count); + DBG(3, "Read bytes: %zd, value: %d", count, val); mutex_unlock(&sn9c102_sysfs_lock); @@ -1085,14 +1258,15 @@ static ssize_t sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) { struct sn9c102_device* cam; - u8 value; + u16 value; ssize_t count; int err; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1103,7 +1277,7 @@ sn9c102_store_i2c_val(struct class_device* cd, const char* buf, size_t len) return -ENOSYS; } - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) { mutex_unlock(&sn9c102_sysfs_lock); return -EINVAL; @@ -1131,13 +1305,14 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) struct sn9c102_device* cam; enum sn9c102_bridge bridge; ssize_t res = 0; - u8 value; + u16 value; ssize_t count; if (mutex_lock_interruptible(&sn9c102_sysfs_lock)) return -ERESTARTSYS; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) { mutex_unlock(&sn9c102_sysfs_lock); return -ENODEV; @@ -1147,7 +1322,7 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) mutex_unlock(&sn9c102_sysfs_lock); - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count) return -EINVAL; @@ -1160,9 +1335,11 @@ sn9c102_store_green(struct class_device* cd, const char* buf, size_t len) res = sn9c102_store_val(cd, buf, len); break; case BRIDGE_SN9C103: + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: if (value > 0x7f) return -EINVAL; - if ((res = sn9c102_store_reg(cd, "0x04", 4)) >= 0) + if ((res = sn9c102_store_reg(cd, "0x07", 4)) >= 0) res = sn9c102_store_val(cd, buf, len); break; } @@ -1175,10 +1352,10 @@ static ssize_t sn9c102_store_blue(struct class_device* cd, const char* buf, size_t len) { ssize_t res = 0; - u8 value; + u16 value; ssize_t count; - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count || value > 0x7f) return -EINVAL; @@ -1193,10 +1370,10 @@ static ssize_t sn9c102_store_red(struct class_device* cd, const char* buf, size_t len) { ssize_t res = 0; - u8 value; + u16 value; ssize_t count; - value = sn9c102_strtou8(buf, len, &count); + value = sn9c102_strtou16(buf, len, &count); if (!count || value > 0x7f) return -EINVAL; @@ -1212,7 +1389,8 @@ static ssize_t sn9c102_show_frame_header(struct class_device* cd, char* buf) struct sn9c102_device* cam; ssize_t count; - cam = video_get_drvdata(to_video_device(cd)); + cam = video_get_drvdata(container_of(cd, struct video_device, + class_dev)); if (!cam) return -ENODEV; @@ -1242,51 +1420,57 @@ static CLASS_DEVICE_ATTR(frame_header, S_IRUGO, static int sn9c102_create_sysfs(struct sn9c102_device* cam) { - struct video_device *v4ldev = cam->v4ldev; - int rc; + struct class_device *classdev = &(cam->v4ldev->class_dev); + int err = 0; - rc = video_device_create_file(v4ldev, &class_device_attr_reg); - if (rc) goto err; - rc = video_device_create_file(v4ldev, &class_device_attr_val); - if (rc) goto err_reg; - rc = video_device_create_file(v4ldev, &class_device_attr_frame_header); - if (rc) goto err_val; + if ((err = class_device_create_file(classdev, &class_device_attr_reg))) + goto err_out; + if ((err = class_device_create_file(classdev, &class_device_attr_val))) + goto err_reg; + if ((err = class_device_create_file(classdev, + &class_device_attr_frame_header))) + goto err_val; if (cam->sensor.sysfs_ops) { - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_reg); - if (rc) goto err_frhead; - rc = video_device_create_file(v4ldev, &class_device_attr_i2c_val); - if (rc) goto err_i2c_reg; + if ((err = class_device_create_file(classdev, + &class_device_attr_i2c_reg))) + goto err_frame_header; + if ((err = class_device_create_file(classdev, + &class_device_attr_i2c_val))) + goto err_i2c_reg; } if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) { - rc = video_device_create_file(v4ldev, &class_device_attr_green); - if (rc) goto err_i2c_val; - } else if (cam->bridge == BRIDGE_SN9C103) { - rc = video_device_create_file(v4ldev, &class_device_attr_blue); - if (rc) goto err_i2c_val; - rc = video_device_create_file(v4ldev, &class_device_attr_red); - if (rc) goto err_blue; + if ((err = class_device_create_file(classdev, + &class_device_attr_green))) + goto err_i2c_val; + } else { + if ((err = class_device_create_file(classdev, + &class_device_attr_blue))) + goto err_i2c_val; + if ((err = class_device_create_file(classdev, + &class_device_attr_red))) + goto err_blue; } return 0; err_blue: - video_device_remove_file(v4ldev, &class_device_attr_blue); + class_device_remove_file(classdev, &class_device_attr_blue); err_i2c_val: if (cam->sensor.sysfs_ops) - video_device_remove_file(v4ldev, &class_device_attr_i2c_val); + class_device_remove_file(classdev, &class_device_attr_i2c_val); err_i2c_reg: if (cam->sensor.sysfs_ops) - video_device_remove_file(v4ldev, &class_device_attr_i2c_reg); -err_frhead: - video_device_remove_file(v4ldev, &class_device_attr_frame_header); + class_device_remove_file(classdev, &class_device_attr_i2c_reg); +err_frame_header: + class_device_remove_file(classdev, &class_device_attr_frame_header); err_val: - video_device_remove_file(v4ldev, &class_device_attr_val); + class_device_remove_file(classdev, &class_device_attr_val); err_reg: - video_device_remove_file(v4ldev, &class_device_attr_reg); -err: - return rc; + class_device_remove_file(classdev, &class_device_attr_reg); +err_out: + return err; } #endif /* CONFIG_VIDEO_ADV_DEBUG */ @@ -1297,10 +1481,36 @@ sn9c102_set_pix_format(struct sn9c102_device* cam, struct v4l2_pix_format* pix) { int err = 0; - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, 0x18); - else - err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, 0x18); + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + } + } else { + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0x7f, + 0x18); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x80, + 0x18); + break; + } + } return err ? -EIO : 0; } @@ -1310,12 +1520,46 @@ static int sn9c102_set_compression(struct sn9c102_device* cam, struct v4l2_jpegcompression* compression) { - int err = 0; + int i, err = 0; - if (compression->quality == 0) - err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, 0x17); - else if (compression->quality == 1) - err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, 0x17); + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (compression->quality == 0) + err += sn9c102_write_reg(cam, cam->reg[0x17] | 0x01, + 0x17); + else if (compression->quality == 1) + err += sn9c102_write_reg(cam, cam->reg[0x17] & 0xfe, + 0x17); + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (compression->quality == 0) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] & 0xbf, + 0x18); + } else if (compression->quality == 1) { + for (i = 0; i <= 63; i++) { + err += sn9c102_write_reg(cam, + SN9C102_Y_QTABLE1[i], + 0x100 + i); + err += sn9c102_write_reg(cam, + SN9C102_UV_QTABLE1[i], + 0x140 + i); + } + err += sn9c102_write_reg(cam, cam->reg[0x18] | 0x40, + 0x18); + } + break; + } return err ? -EIO : 0; } @@ -1399,7 +1643,20 @@ static int sn9c102_init(struct sn9c102_device* cam) } if (!(cam->state & DEV_INITIALIZED)) - cam->compression.quality = cam->reg[0x17] & 0x01 ? 0 : 1; + if (cam->bridge == BRIDGE_SN9C101 || + cam->bridge == BRIDGE_SN9C102 || + cam->bridge == BRIDGE_SN9C103) { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) + s->pix_format.pixelformat= V4L2_PIX_FMT_SBGGR8; + cam->compression.quality = cam->reg[0x17] & 0x01 ? + 0 : 1; + } else { + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + s->pix_format.pixelformat = V4L2_PIX_FMT_JPEG; + cam->compression.quality = cam->reg[0x18] & 0x40 ? + 0 : 1; + err += sn9c102_set_compression(cam, &cam->compression); + } else err += sn9c102_set_compression(cam, &cam->compression); err += sn9c102_set_pix_format(cam, &s->pix_format); @@ -1408,7 +1665,8 @@ static int sn9c102_init(struct sn9c102_device* cam) if (err) return err; - if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X) + if (s->pix_format.pixelformat == V4L2_PIX_FMT_SN9C10X || + s->pix_format.pixelformat == V4L2_PIX_FMT_JPEG) DBG(3, "Compressed video format is active, quality %d", cam->compression.quality); else @@ -1490,6 +1748,7 @@ static int sn9c102_open(struct inode* inode, struct file* filp) if (cam->users) { DBG(2, "Device /dev/video%d is busy...", cam->v4ldev->minor); + DBG(3, "Simultaneous opens are not supported"); if ((filp->f_flags & O_NONBLOCK) || (filp->f_flags & O_NDELAY)) { err = -EWOULDBLOCK; @@ -1599,7 +1858,7 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) DBG(3, "Close and open the device again to choose " "the read method"); mutex_unlock(&cam->fileop_mutex); - return -EINVAL; + return -EBUSY; } if (cam->io == IO_NONE) { @@ -1628,22 +1887,39 @@ sn9c102_read(struct file* filp, char __user * buf, size_t count, loff_t* f_pos) mutex_unlock(&cam->fileop_mutex); return -EAGAIN; } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) { + mutex_unlock(&cam->fileop_mutex); + return err; + } + } else { + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) { + mutex_unlock(&cam->fileop_mutex); + return timeout; + } else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + mutex_unlock(&cam->fileop_mutex); + return -EIO; + } } if (cam->state & DEV_DISCONNECTED) { mutex_unlock(&cam->fileop_mutex); return -ENODEV; } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { + if (cam->state & DEV_MISCONFIGURED) { mutex_unlock(&cam->fileop_mutex); return -EIO; } @@ -1778,7 +2054,12 @@ static int sn9c102_mmap(struct file* filp, struct vm_area_struct *vma) return -EIO; } - if (cam->io != IO_MMAP || !(vma->vm_flags & VM_WRITE) || + if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { + mutex_unlock(&cam->fileop_mutex); + return -EACCES; + } + + if (cam->io != IO_MMAP || size != PAGE_ALIGN(cam->frame[0].buf.length)) { mutex_unlock(&cam->fileop_mutex); return -EINVAL; @@ -1940,6 +2221,9 @@ exit: if (copy_to_user(arg, &ctrl, sizeof(ctrl))) return -EFAULT; + PDBGG("VIDIOC_G_CTRL: id %lu, value %lu", + (unsigned long)ctrl.id, (unsigned long)ctrl.value); + return err; } @@ -2041,7 +2325,7 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_CROP failed. " "Unmap the buffers first."); - return -EINVAL; + return -EBUSY; } /* Preserve R,G or B origin */ @@ -2127,6 +2411,45 @@ sn9c102_vidioc_s_crop(struct sn9c102_device* cam, void __user * arg) static int +sn9c102_vidioc_enum_framesizes(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0) + return -EINVAL; + + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (frmsize.pixel_format != V4L2_PIX_FMT_SN9C10X && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG && + frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) + return -EINVAL; + } + + frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; + frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; + frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; + frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; + frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + +static int sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) { struct v4l2_fmtdesc fmtd; @@ -2134,12 +2457,26 @@ sn9c102_vidioc_enum_fmt(struct sn9c102_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "bayer rgb"); fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; } else if (fmtd.index == 1) { - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + strcpy(fmtd.description, "compressed"); + fmtd.pixelformat = V4L2_PIX_FMT_SN9C10X; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + strcpy(fmtd.description, "JPEG"); + fmtd.pixelformat = V4L2_PIX_FMT_JPEG; + break; + } fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; } else return -EINVAL; @@ -2166,7 +2503,10 @@ sn9c102_vidioc_g_fmt(struct sn9c102_device* cam, void __user * arg) if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_SN9C10X) + pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pfmt->bytesperline = (pfmt->pixelformat == V4L2_PIX_FMT_SN9C10X || + pfmt->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pfmt->width * pfmt->priv) / 8; pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); pfmt->field = V4L2_FIELD_NONE; @@ -2237,12 +2577,26 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, pix->width = rect.width / scale; pix->height = rect.height / scale; - if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; + switch (cam->bridge) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + case BRIDGE_SN9C103: + if (pix->pixelformat != V4L2_PIX_FMT_SN9C10X && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat != V4L2_PIX_FMT_JPEG && + pix->pixelformat != V4L2_PIX_FMT_SBGGR8) + pix->pixelformat = pfmt->pixelformat; + break; + } pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = pfmt->colorspace; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_JPEG) ? + V4L2_COLORSPACE_JPEG : V4L2_COLORSPACE_SRGB; + pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_SN9C10X || + pix->pixelformat == V4L2_PIX_FMT_JPEG) ? 0 : (pix->width * pix->priv) / 8; pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); pix->field = V4L2_FIELD_NONE; @@ -2258,7 +2612,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_S_FMT failed. Unmap the " "buffers first."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2315,8 +2669,7 @@ sn9c102_vidioc_try_s_fmt(struct sn9c102_device* cam, unsigned int cmd, static int sn9c102_vidioc_g_jpegcomp(struct sn9c102_device* cam, void __user * arg) { - if (copy_to_user(arg, &cam->compression, - sizeof(cam->compression))) + if (copy_to_user(arg, &cam->compression, sizeof(cam->compression))) return -EFAULT; return 0; @@ -2374,14 +2727,14 @@ sn9c102_vidioc_reqbufs(struct sn9c102_device* cam, void __user * arg) if (cam->io == IO_READ) { DBG(3, "Close and open the device again to choose the mmap " "I/O method"); - return -EINVAL; + return -EBUSY; } for (i = 0; i < cam->nbuffers; i++) if (cam->frame[i].vma_use_count) { DBG(3, "VIDIOC_REQBUFS failed. Previous buffers are " "still mapped."); - return -EINVAL; + return -EBUSY; } if (cam->stream == STREAM_ON) @@ -2471,6 +2824,7 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, struct sn9c102_frame_t *f; unsigned long lock_flags; long timeout; + int err = 0; if (copy_from_user(&b, arg, sizeof(b))) return -EFAULT; @@ -2483,18 +2837,33 @@ sn9c102_vidioc_dqbuf(struct sn9c102_device* cam, struct file* filp, return -EINVAL; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; + if (!cam->module_param.frame_timeout) { + err = wait_event_interruptible + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED) ); + if (err) + return err; + } else { + timeout = wait_event_interruptible_timeout + ( cam->wait_frame, + (!list_empty(&cam->outqueue)) || + (cam->state & DEV_DISCONNECTED) || + (cam->state & DEV_MISCONFIGURED), + cam->module_param.frame_timeout * + 1000 * msecs_to_jiffies(1) ); + if (timeout < 0) + return timeout; + else if (timeout == 0 && + !(cam->state & DEV_DISCONNECTED)) { + DBG(1, "Video frame timeout elapsed"); + return -EIO; + } + } if (cam->state & DEV_DISCONNECTED) return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) + if (cam->state & DEV_MISCONFIGURED) return -EIO; } @@ -2529,9 +2898,6 @@ sn9c102_vidioc_streamon(struct sn9c102_device* cam, void __user * arg) if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) return -EINVAL; - if (list_empty(&cam->inqueue)) - return -EINVAL; - cam->stream = STREAM_ON; DBG(3, "Stream on"); @@ -2612,6 +2978,70 @@ sn9c102_vidioc_s_parm(struct sn9c102_device* cam, void __user * arg) } +static int +sn9c102_vidioc_enumaudio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + strcpy(audio.name, "Microphone"); + audio.capability = 0; + audio.mode = 0; + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_g_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + memset(&audio, 0, sizeof(audio)); + strcpy(audio.name, "Microphone"); + + if (copy_to_user(arg, &audio, sizeof(audio))) + return -EFAULT; + + return 0; +} + + +static int +sn9c102_vidioc_s_audio(struct sn9c102_device* cam, void __user * arg) +{ + struct v4l2_audio audio; + + if (cam->bridge == BRIDGE_SN9C101 || cam->bridge == BRIDGE_SN9C102) + return -EINVAL; + + if (copy_from_user(&audio, arg, sizeof(audio))) + return -EFAULT; + + if (audio.index != 0) + return -EINVAL; + + return 0; +} + + static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, unsigned int cmd, void __user * arg) { @@ -2649,6 +3079,9 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_CROP: return sn9c102_vidioc_s_crop(cam, arg); + case VIDIOC_ENUM_FRAMESIZES: + return sn9c102_vidioc_enum_framesizes(cam, arg); + case VIDIOC_ENUM_FMT: return sn9c102_vidioc_enum_fmt(cam, arg); @@ -2689,11 +3122,21 @@ static int sn9c102_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_PARM: return sn9c102_vidioc_s_parm(cam, arg); + case VIDIOC_ENUMAUDIO: + return sn9c102_vidioc_enumaudio(cam, arg); + + case VIDIOC_G_AUDIO: + return sn9c102_vidioc_g_audio(cam, arg); + + case VIDIOC_S_AUDIO: + return sn9c102_vidioc_s_audio(cam, arg); + case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -2741,6 +3184,7 @@ static const struct file_operations sn9c102_fops = { .open = sn9c102_open, .release = sn9c102_release, .ioctl = sn9c102_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = sn9c102_read, .poll = sn9c102_poll, .mmap = sn9c102_mmap, @@ -2765,7 +3209,7 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) cam->usbdev = udev; if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); + DBG(1, "kzalloc() failed"); err = -ENOMEM; goto fail; } @@ -2779,24 +3223,31 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); r = sn9c102_read_reg(cam, 0x00); - if (r < 0 || r != 0x10) { - DBG(1, "Sorry, this is not a SN9C10x based camera " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + if (r < 0 || (r != 0x10 && r != 0x11 && r != 0x12)) { + DBG(1, "Sorry, this is not a SN9C1xx-based camera " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); err = -ENODEV; goto fail; } - cam->bridge = (id->idProduct & 0xffc0) == 0x6080 ? - BRIDGE_SN9C103 : BRIDGE_SN9C102; + cam->bridge = id->driver_info; switch (cam->bridge) { case BRIDGE_SN9C101: case BRIDGE_SN9C102: DBG(2, "SN9C10[12] PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; case BRIDGE_SN9C103: DBG(2, "SN9C103 PC Camera Controller detected " - "(vid/pid 0x%04X/0x%04X)", id->idVendor, id->idProduct); + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C105: + DBG(2, "SN9C105 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + break; + case BRIDGE_SN9C120: + DBG(2, "SN9C120 PC Camera Controller detected " + "(vid:pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); break; } @@ -2816,12 +3267,18 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) goto fail; } + if (!(cam->bridge & cam->sensor.supported_bridge)) { + DBG(1, "Bridge not supported"); + err = -ENODEV; + goto fail; + } + if (sn9c102_init(cam)) { DBG(1, "Initialization failed. I will retry on open()."); cam->state |= DEV_MISCONFIGURED; } - strcpy(cam->v4ldev->name, "SN9C10x PC Camera"); + strcpy(cam->v4ldev->name, "SN9C1xx PC Camera"); cam->v4ldev->owner = THIS_MODULE; cam->v4ldev->type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->v4ldev->hardware = 0; @@ -2838,7 +3295,10 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) DBG(1, "V4L2 device registration failed"); if (err == -ENFILE && video_nr[dev_nr] == -1) DBG(1, "Free /dev/videoX node not found"); - goto fail2; + video_nr[dev_nr] = -1; + dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; + mutex_unlock(&cam->dev_mutex); + goto fail; } DBG(2, "V4L2 device registered as /dev/video%d", cam->v4ldev->minor); @@ -2850,9 +3310,16 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) #ifdef CONFIG_VIDEO_ADV_DEBUG err = sn9c102_create_sysfs(cam); - if (err) - goto fail3; - DBG(2, "Optional device control through 'sysfs' interface ready"); + if (!err) + DBG(2, "Optional device control through 'sysfs' " + "interface ready"); + else + DBG(2, "Failed to create optional 'sysfs' interface for " + "device controlling. Error #%d", err); +#else + DBG(2, "Optional device control through 'sysfs' interface disabled"); + DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " + "configuration option to enable it."); #endif usb_set_intfdata(intf, cam); @@ -2861,14 +3328,6 @@ sn9c102_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) return 0; -#ifdef CONFIG_VIDEO_ADV_DEBUG -fail3: - video_unregister_device(cam->v4ldev); -#endif -fail2: - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < SN9C102_MAX_DEVICES-1) ? dev_nr+1 : 0; - mutex_unlock(&cam->dev_mutex); fail: if (cam) { kfree(cam->control_buffer); diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h new file mode 100644 index 0000000..f49bd8c --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -0,0 +1,154 @@ +/*************************************************************************** + * Table of device identifiers of the SN9C1xx PC Camera Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#ifndef _SN9C102_DEVTABLE_H_ +#define _SN9C102_DEVTABLE_H_ + +#include <linux/usb.h> + +struct sn9c102_device; + +/* + Each SN9C1xx camera has proper PID/VID identifiers. + SN9C103, SN9C105, SN9C120 support multiple interfaces, but we only have to + handle the video class interface. +*/ +#define SN9C102_USB_DEVICE(vend, prod, bridge) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = (vend), \ + .idProduct = (prod), \ + .bInterfaceClass = 0xff, \ + .driver_info = (bridge) + +static const struct usb_device_id sn9c102_id_table[] = { + /* SN9C101 and SN9C102 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, + { SN9C102_USB_DEVICE(0x0c45, 0x603f, BRIDGE_SN9C102), }, + /* SN9C103 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6080, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6082, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6083, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6088, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608a, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608b, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608c, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608e, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x608f, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a0, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a2, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a3, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60a8, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60aa, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ab, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ac, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ae, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60af, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b0, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b2, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b3, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60b8, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ba, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60bb, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60bc, BRIDGE_SN9C103), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60be, BRIDGE_SN9C103), }, + /* SN9C105 */ + { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c2, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60c8, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60cc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ea, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ec, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60ef, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fa, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fb, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fc, BRIDGE_SN9C105), }, + { SN9C102_USB_DEVICE(0x0c45, 0x60fe, BRIDGE_SN9C105), }, + /* SN9C120 */ + { SN9C102_USB_DEVICE(0x0c45, 0x6102, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6108, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x610f, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6130, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x6138, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), }, + { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), }, + { } +}; + +/* + Probing functions: on success, you must attach the sensor to the camera + by calling sn9c102_attach_sensor(). + To enable the I2C communication, you might need to perform a really basic + initialization of the SN9C1XX chip. + Functions must return 0 on success, the appropriate error otherwise. +*/ +extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); +extern int sn9c102_probe_hv7131r(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); +extern int sn9c102_probe_mi0360(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); +extern int sn9c102_probe_ov7660(struct sn9c102_device* cam); +extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); +extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5110d(struct sn9c102_device* cam); +extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); + +/* + Add the above entries to this table. Be sure to add the entry in the right + place, since, on failure, the next probing routine is called according to + the order of the list below, from top to bottom. +*/ +static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { + &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ + &sn9c102_probe_hv7131r, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ + &sn9c102_probe_mi0360, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ + &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7630, /* strong detection based on SENSOR ids */ + &sn9c102_probe_ov7660, /* strong detection based on SENSOR ids */ + &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5110d, /* detection based on USB pid/vid */ + &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ + NULL, +}; + +#endif /* _SN9C102_DEVTABLE_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131d.c b/drivers/media/video/sn9c102/sn9c102_hv7131d.c index c4117bf..28a861a 100644 --- a/drivers/media/video/sn9c102/sn9c102_hv7131d.c +++ b/drivers/media/video/sn9c102/sn9c102_hv7131d.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for HV7131D image sensor connected to the SN9C10x PC Camera * + * Plug-in for HV7131D image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,19 +22,13 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor hv7131d; - - static int hv7131d_init(struct sn9c102_device* cam) { - int err = 0; + int err; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0e, 0x18); - err += sn9c102_write_reg(cam, 0xf2, 0x19); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x60, 0x17}, + {0x0e, 0x18}, {0xf2, 0x19}); err += sn9c102_i2c_write(cam, 0x01, 0x04); err += sn9c102_i2c_write(cam, 0x02, 0x00); @@ -124,7 +118,7 @@ static int hv7131d_set_ctrl(struct sn9c102_device* cam, static int hv7131d_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &hv7131d; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 2, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; @@ -153,6 +147,7 @@ static int hv7131d_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor hv7131d = { .name = "HV7131D", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -249,11 +244,10 @@ static struct sn9c102_sensor hv7131d = { int sn9c102_probe_hv7131d(struct sn9c102_device* cam) { - int r0 = 0, r1 = 0, err = 0; + int r0 = 0, r1 = 0, err; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); if (err) return -EIO; @@ -262,7 +256,7 @@ int sn9c102_probe_hv7131d(struct sn9c102_device* cam) if (r0 < 0 || r1 < 0) return -EIO; - if (r0 != 0x00 && r1 != 0x04) + if (r0 != 0x00 || r1 != 0x04) return -ENODEV; sn9c102_attach_sensor(cam, &hv7131d); diff --git a/drivers/media/video/sn9c102/sn9c102_hv7131r.c b/drivers/media/video/sn9c102/sn9c102_hv7131r.c new file mode 100644 index 0000000..5a495ba --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_hv7131r.c @@ -0,0 +1,366 @@ +/*************************************************************************** + * Plug-in for HV7131R image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int hv7131r_init(struct sn9c102_device* cam) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x03, 0x10}, {0x00, 0x14}, + {0x60, 0x17}, {0x0a, 0x18}, + {0xf0, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + err = sn9c102_write_const_regs(cam, {0x44, 0x01}, {0x40, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x44, 0x05}, {0x3e, 0x06}, + {0x1a, 0x07}, {0x03, 0x10}, + {0x08, 0x14}, {0xa3, 0x17}, + {0x4b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + break; + default: + break; + } + + err += sn9c102_i2c_write(cam, 0x20, 0x00); + err += sn9c102_i2c_write(cam, 0x21, 0xd6); + err += sn9c102_i2c_write(cam, 0x25, 0x06); + + return err; +} + + +static int hv7131r_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x30)) < 0) + return -EIO; + return 0; + case V4L2_CID_RED_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x31)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLUE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x33)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x32)) < 0) + return -EIO; + ctrl->value = ctrl->value & 0x3f; + return 0; + case V4L2_CID_BLACK_LEVEL: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x01)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x08) ? 1 : 0; + return 0; + default: + return -EINVAL; + } +} + + +static int hv7131r_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x30, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_write(cam, 0x31, ctrl->value); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_write(cam, 0x33, ctrl->value); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_write(cam, 0x32, ctrl->value); + break; + case V4L2_CID_BLACK_LEVEL: + { + int r = sn9c102_i2c_read(cam, 0x01); + if (r < 0) + return -EIO; + err += sn9c102_i2c_write(cam, 0x01, + (ctrl->value<<3) | (r&0xf7)); + } + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int hv7131r_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int hv7131r_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C103: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa0, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } else { + err += sn9c102_write_reg(cam, 0x30, 0x19); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + case BRIDGE_SN9C105: + case BRIDGE_SN9C120: + if (pix->pixelformat == V4L2_PIX_FMT_SBGGR8) { + err += sn9c102_write_reg(cam, 0xa5, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x24); + } else { + err += sn9c102_write_reg(cam, 0xa3, 0x17); + err += sn9c102_i2c_write(cam, 0x01, 0x04); + } + break; + default: + break; + } + + return err; +} + + +static struct sn9c102_sensor hv7131r = { + .name = "HV7131R", + .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C103 | BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x11, + .init = &hv7131r_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x40, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x08, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x1a, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x3f, + .step = 0x01, + .default_value = 0x2f, + .flags = 0, + }, + { + .id = V4L2_CID_BLACK_LEVEL, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto black level compensation", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + }, + .get_ctrl = &hv7131r_get_ctrl, + .set_ctrl = &hv7131r_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &hv7131r_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &hv7131r_set_pix_format +}; + + +int sn9c102_probe_hv7131r(struct sn9c102_device* cam) +{ + int devid, err; + + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, {0x44, 0x02}, + {0x34, 0x01}, {0x20, 0x17}, + {0x34, 0x01}, {0x46, 0x01}); + + if (err) + return -EIO; + + devid = sn9c102_i2c_try_read(cam, &hv7131r, 0x00); + if (devid < 0) + return -EIO; + + if (devid != 0x02) + return -ENODEV; + + sn9c102_attach_sensor(cam, &hv7131r); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_mi0343.c b/drivers/media/video/sn9c102/sn9c102_mi0343.c index 4169ea4..9200845 100644 --- a/drivers/media/video/sn9c102/sn9c102_mi0343.c +++ b/drivers/media/video/sn9c102/sn9c102_mi0343.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for MI-0343 image sensor connected to the SN9C10x PC Camera * + * Plug-in for MI-0343 image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,36 +22,30 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor mi0343; -static u8 mi0343_i2c_data[5+1]; - - static int mi0343_init(struct sn9c102_device* cam) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x0a, 0x14); - err += sn9c102_write_reg(cam, 0x40, 0x01); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x07, 0x18); - err += sn9c102_write_reg(cam, 0xa0, 0x19); - - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x01, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x0d, 0x00, 0x00, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x03, 0x01, 0xe1, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x04, 0x02, 0x81, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x05, 0x00, 0x17, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x06, 0x00, 0x11, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, mi0343.i2c_slave_id, - 0x62, 0x04, 0x9a, 0, 0); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); return err; } @@ -60,43 +54,46 @@ static int mi0343_init(struct sn9c102_device* cam) static int mi0343_get_ctrl(struct sn9c102_device* cam, struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + switch (ctrl->id) { case V4L2_CID_EXPOSURE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x09, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[2]; + ctrl->value = data[2]; return 0; case V4L2_CID_GAIN: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x35, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_HFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x20 ? 1 : 0; + ctrl->value = data[3] & 0x20 ? 1 : 0; return 0; case V4L2_CID_VFLIP: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x20, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) return -EIO; - ctrl->value = mi0343_i2c_data[3] & 0x80 ? 1 : 0; + ctrl->value = data[3] & 0x80 ? 1 : 0; return 0; case V4L2_CID_RED_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2d, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) return -EIO; break; case V4L2_CID_BLUE_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2c, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) return -EIO; break; case SN9C102_V4L2_CID_GREEN_BALANCE: - if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, - 0x2e, 2+1, mi0343_i2c_data) < 0) + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) return -EIO; break; default: @@ -108,7 +105,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam, case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: case SN9C102_V4L2_CID_GREEN_BALANCE: - ctrl->value = mi0343_i2c_data[3] | (mi0343_i2c_data[2] << 8); + ctrl->value = data[3] | (data[2] << 8); if (ctrl->value >= 0x10 && ctrl->value <= 0x3f) ctrl->value -= 0x10; else if (ctrl->value >= 0x60 && ctrl->value <= 0x7f) @@ -124,6 +121,7 @@ static int mi0343_get_ctrl(struct sn9c102_device* cam, static int mi0343_set_ctrl(struct sn9c102_device* cam, const struct v4l2_control* ctrl) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); u16 reg = 0; int err = 0; @@ -143,50 +141,42 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam, switch (ctrl->id) { case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x09, ctrl->value, 0x00, 0, 0); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x35, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_HFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x40:0x00, ctrl->value ? 0x20:0x00, 0, 0); break; case V4L2_CID_VFLIP: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x20, ctrl->value ? 0x80:0x00, ctrl->value ? 0x80:0x00, 0, 0); break; case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2d, reg >> 8, reg & 0xff, 0, 0); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2c, reg >> 8, reg & 0xff, 0, 0); break; case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2b, reg >> 8, reg & 0xff, 0, 0); - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x2e, reg >> 8, reg & 0xff, 0, 0); break; @@ -201,7 +191,7 @@ static int mi0343_set_ctrl(struct sn9c102_device* cam, static int mi0343_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &mi0343; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 2; @@ -216,16 +206,15 @@ static int mi0343_set_crop(struct sn9c102_device* cam, static int mi0343_set_pix_format(struct sn9c102_device* cam, const struct v4l2_pix_format* pix) { + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x03, 0, 0); err += sn9c102_write_reg(cam, 0x20, 0x19); } else { - err += sn9c102_i2c_try_raw_write(cam, &mi0343, 4, - mi0343.i2c_slave_id, + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0a, 0x00, 0x05, 0, 0); err += sn9c102_write_reg(cam, 0xa0, 0x19); } @@ -237,6 +226,7 @@ static int mi0343_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor mi0343 = { .name = "MI-0343", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x5d, @@ -342,19 +332,20 @@ static struct sn9c102_sensor mi0343 = { int sn9c102_probe_mi0343(struct sn9c102_device* cam) { + u8 data[5+1]; int err = 0; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + if (err) return -EIO; if (sn9c102_i2c_try_raw_read(cam, &mi0343, mi0343.i2c_slave_id, 0x00, - 2, mi0343_i2c_data) < 0) + 2, data) < 0) return -EIO; - if (mi0343_i2c_data[4] != 0x32 && mi0343_i2c_data[3] != 0xe3) + if (data[4] != 0x32 || data[3] != 0xe3) return -ENODEV; sn9c102_attach_sensor(cam, &mi0343); diff --git a/drivers/media/video/sn9c102/sn9c102_mi0360.c b/drivers/media/video/sn9c102/sn9c102_mi0360.c new file mode 100644 index 0000000..64698ac --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_mi0360.c @@ -0,0 +1,338 @@ +/*************************************************************************** + * Plug-in for MI-0360 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int mi0360_init(struct sn9c102_device* cam) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x40, 0x01}, + {0x20, 0x17}, {0x07, 0x18}, + {0xa0, 0x19}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x01, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x0d, + 0x00, 0x00, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x03, + 0x01, 0xe1, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x04, + 0x02, 0x81, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x05, + 0x00, 0x17, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x06, + 0x00, 0x11, 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, 0x62, + 0x04, 0x9a, 0, 0); + + return err; +} + + +static int mi0360_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + u8 data[5+1]; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x09, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[2]; + return 0; + case V4L2_CID_GAIN: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x35, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_RED_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2c, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_BLUE_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2d, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case SN9C102_V4L2_CID_GREEN_BALANCE: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x2e, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3]; + return 0; + case V4L2_CID_HFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x20 ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + if (sn9c102_i2c_try_raw_read(cam, s, s->i2c_slave_id, 0x20, + 2+1, data) < 0) + return -EIO; + ctrl->value = data[3] & 0x80 ? 1 : 0; + return 0; + default: + return -EINVAL; + } + + return 0; +} + + +static int mi0360_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x09, ctrl->value, 0x00, + 0, 0); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x35, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2c, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2d, 0x03, ctrl->value, + 0, 0); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2b, 0x03, ctrl->value, + 0, 0); + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x2e, 0x03, ctrl->value, + 0, 0); + break; + case V4L2_CID_HFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x40:0x00, + ctrl->value ? 0x20:0x00, + 0, 0); + break; + case V4L2_CID_VFLIP: + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x20, ctrl->value ? 0x80:0x00, + ctrl->value ? 0x80:0x00, + 0, 0); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int mi0360_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 0, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int mi0360_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x02, 0, 0); + err += sn9c102_write_reg(cam, 0x20, 0x19); + } else { + err += sn9c102_i2c_try_raw_write(cam, s, 4, s->i2c_slave_id, + 0x0a, 0x00, 0x05, 0, 0); + err += sn9c102_write_reg(cam, 0x60, 0x19); + } + + return err; +} + + +static struct sn9c102_sensor mi0360 = { + .name = "MI-0360", + .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C103, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x5d, + .init = &mi0360_init, + .qctrl = { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x0f, + .step = 0x01, + .default_value = 0x05, + .flags = 0, + }, + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0f, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x32, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x25, + .flags = 0, + }, + }, + .get_ctrl = &mi0360_get_ctrl, + .set_ctrl = &mi0360_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &mi0360_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &mi0360_set_pix_format +}; + + +int sn9c102_probe_mi0360(struct sn9c102_device* cam) +{ + u8 data[5+1]; + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + if (err) + return -EIO; + + if (sn9c102_i2c_try_raw_read(cam, &mi0360, mi0360.i2c_slave_id, 0x00, + 2+1, data) < 0) + return -EIO; + + if (data[2] != 0x82 || data[3] != 0x43) + return -ENODEV; + + sn9c102_attach_sensor(cam, &mi0360); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_ov7630.c b/drivers/media/video/sn9c102/sn9c102_ov7630.c index 3da0420..31b6080 100644 --- a/drivers/media/video/sn9c102/sn9c102_ov7630.c +++ b/drivers/media/video/sn9c102/sn9c102_ov7630.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for OV7630 image sensor connected to the SN9C10x PC Camera * + * Plug-in for OV7630 image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2005-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,86 +22,180 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor ov7630; - - static int ov7630_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x0f, 0x18); - err += sn9c102_write_reg(cam, 0x50, 0x19); - - err += sn9c102_i2c_write(cam, 0x12, 0x80); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - err += sn9c102_i2c_write(cam, 0x15, 0x34); - err += sn9c102_i2c_write(cam, 0x16, 0x03); - err += sn9c102_i2c_write(cam, 0x17, 0x1c); - err += sn9c102_i2c_write(cam, 0x18, 0xbd); - err += sn9c102_i2c_write(cam, 0x19, 0x06); - err += sn9c102_i2c_write(cam, 0x1a, 0xf6); - err += sn9c102_i2c_write(cam, 0x1b, 0x04); - err += sn9c102_i2c_write(cam, 0x20, 0xf6); - err += sn9c102_i2c_write(cam, 0x23, 0xee); - err += sn9c102_i2c_write(cam, 0x26, 0xa0); - err += sn9c102_i2c_write(cam, 0x27, 0x9a); - err += sn9c102_i2c_write(cam, 0x28, 0xa0); - err += sn9c102_i2c_write(cam, 0x29, 0x30); - err += sn9c102_i2c_write(cam, 0x2a, 0xa0); - err += sn9c102_i2c_write(cam, 0x2b, 0x1f); - err += sn9c102_i2c_write(cam, 0x2f, 0x3d); - err += sn9c102_i2c_write(cam, 0x30, 0x24); - err += sn9c102_i2c_write(cam, 0x32, 0x86); - err += sn9c102_i2c_write(cam, 0x60, 0xa9); - err += sn9c102_i2c_write(cam, 0x61, 0x42); - err += sn9c102_i2c_write(cam, 0x65, 0x00); - err += sn9c102_i2c_write(cam, 0x69, 0x38); - err += sn9c102_i2c_write(cam, 0x6f, 0x88); - err += sn9c102_i2c_write(cam, 0x70, 0x0b); - err += sn9c102_i2c_write(cam, 0x71, 0x00); - err += sn9c102_i2c_write(cam, 0x74, 0x21); - err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + err += sn9c102_i2c_write(cam, 0x15, 0x35); + err += sn9c102_i2c_write(cam, 0x16, 0x03); + err += sn9c102_i2c_write(cam, 0x17, 0x1c); + err += sn9c102_i2c_write(cam, 0x18, 0xbd); + err += sn9c102_i2c_write(cam, 0x19, 0x06); + err += sn9c102_i2c_write(cam, 0x1a, 0xf6); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x20, 0x05}, + {0x20, 0x06}, {0x20, 0x07}, + {0x03, 0x10}, {0x0a, 0x14}, + {0x60, 0x17}, {0x0f, 0x18}, + {0x50, 0x19}, {0x1d, 0x1a}, + {0x10, 0x1b}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + + err += sn9c102_i2c_write(cam, 0x12, 0x8d); + err += sn9c102_i2c_write(cam, 0x12, 0x0d); + err += sn9c102_i2c_write(cam, 0x15, 0x34); + err += sn9c102_i2c_write(cam, 0x11, 0x01); + err += sn9c102_i2c_write(cam, 0x1b, 0x04); + err += sn9c102_i2c_write(cam, 0x20, 0x44); + err += sn9c102_i2c_write(cam, 0x23, 0xee); + err += sn9c102_i2c_write(cam, 0x26, 0xa0); + err += sn9c102_i2c_write(cam, 0x27, 0x9a); + err += sn9c102_i2c_write(cam, 0x28, 0x20); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2f, 0x3d); + err += sn9c102_i2c_write(cam, 0x30, 0x24); + err += sn9c102_i2c_write(cam, 0x32, 0x86); + err += sn9c102_i2c_write(cam, 0x60, 0xa9); + err += sn9c102_i2c_write(cam, 0x61, 0x42); + err += sn9c102_i2c_write(cam, 0x65, 0x00); + err += sn9c102_i2c_write(cam, 0x69, 0x38); + err += sn9c102_i2c_write(cam, 0x6f, 0x88); + err += sn9c102_i2c_write(cam, 0x70, 0x0b); + err += sn9c102_i2c_write(cam, 0x71, 0x00); + err += sn9c102_i2c_write(cam, 0x74, 0x21); + err += sn9c102_i2c_write(cam, 0x7d, 0xf7); + break; + default: + break; + } return err; } -static int ov7630_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) +static int ov7630_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) { int err = 0; switch (ctrl->id) { case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value >> 2); - err += sn9c102_i2c_write(cam, 0x76, ctrl->value & 0x03); + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; break; case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x02, ctrl->value); + ctrl->value = sn9c102_pread_reg(cam, 0x07); break; case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x01, ctrl->value); + ctrl->value = sn9c102_pread_reg(cam, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x05); break; case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x3f; break; - case V4L2_CID_CONTRAST: - err += ctrl->value ? sn9c102_i2c_write(cam, 0x05, - (ctrl->value-1) | 0x20) - : sn9c102_i2c_write(cam, 0x05, 0x00); + case V4L2_CID_DO_WHITE_BALANCE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0c)) < 0) + return -EIO; + ctrl->value &= 0x3f; break; - case V4L2_CID_BRIGHTNESS: - err += sn9c102_i2c_write(cam, 0x06, ctrl->value); + case V4L2_CID_WHITENESS: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x0d)) < 0) + return -EIO; + ctrl->value &= 0x3f; break; - case V4L2_CID_SATURATION: - err += sn9c102_i2c_write(cam, 0x03, ctrl->value << 4); + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + case V4L2_CID_VFLIP: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x75)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x80) ? 1 : 0; + break; + case SN9C102_V4L2_CID_GAMMA: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x14)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; break; - case V4L2_CID_HUE: - err += ctrl->value ? sn9c102_i2c_write(cam, 0x04, - (ctrl->value-1) | 0x20) - : sn9c102_i2c_write(cam, 0x04, 0x00); + case SN9C102_V4L2_CID_BAND_FILTER: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x2d)) < 0) + return -EIO; + ctrl->value = (ctrl->value & 0x02) ? 1 : 0; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7630_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); break; case V4L2_CID_DO_WHITE_BALANCE: err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); @@ -109,23 +203,15 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, case V4L2_CID_WHITENESS: err += sn9c102_i2c_write(cam, 0x0d, ctrl->value); break; - case V4L2_CID_AUTO_WHITE_BALANCE: - err += sn9c102_i2c_write(cam, 0x12, (ctrl->value << 2) | 0x78); - break; case V4L2_CID_AUTOGAIN: - err += sn9c102_i2c_write(cam, 0x13, ctrl->value); + err += sn9c102_i2c_write(cam, 0x13, ctrl->value | + (ctrl->value << 1)); break; case V4L2_CID_VFLIP: err += sn9c102_i2c_write(cam, 0x75, 0x0e | (ctrl->value << 7)); break; - case V4L2_CID_BLACK_LEVEL: - err += sn9c102_i2c_write(cam, 0x25, ctrl->value); - break; - case SN9C102_V4L2_CID_BRIGHT_LEVEL: - err += sn9c102_i2c_write(cam, 0x24, ctrl->value); - break; case SN9C102_V4L2_CID_GAMMA: - err += sn9c102_i2c_write(cam, 0x14, (ctrl->value << 2) | 0x80); + err += sn9c102_i2c_write(cam, 0x14, ctrl->value << 2); break; case SN9C102_V4L2_CID_BAND_FILTER: err += sn9c102_i2c_write(cam, 0x2d, ctrl->value << 2); @@ -141,10 +227,12 @@ static int ov7630_set_ctrl(struct sn9c102_device* cam, static int ov7630_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &ov7630; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - u8 v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); return err; @@ -168,7 +256,8 @@ static int ov7630_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor ov7630 = { .name = "OV7630", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .sysfs_ops = SN9C102_I2C_WRITE, + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, .i2c_slave_id = 0x21, @@ -185,73 +274,23 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "hue", - .minimum = 0x00, - .maximum = 0x1f+1, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "saturation", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x08, - .flags = 0, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "contrast", - .minimum = 0x00, - .maximum = 0x1f+1, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "exposure", - .minimum = 0x000, - .maximum = 0x3ff, - .step = 0x001, - .default_value = 0x83<<2, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x3a, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", .minimum = 0x00, .maximum = 0xff, .step = 0x01, - .default_value = 0x77, + .default_value = 0x60, .flags = 0, }, { - .id = V4L2_CID_BRIGHTNESS, + .id = V4L2_CID_WHITENESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", + .name = "white balance background: red", .minimum = 0x00, - .maximum = 0xff, + .maximum = 0x3f, .step = 0x01, - .default_value = 0xa0, + .default_value = 0x20, .flags = 0, }, { @@ -265,31 +304,31 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, { - .id = V4L2_CID_WHITENESS, + .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "white balance background: red", + .name = "red balance", .minimum = 0x00, - .maximum = 0x3f, + .maximum = 0x7f, .step = 0x01, .default_value = 0x20, .flags = 0, }, { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", .minimum = 0x00, - .maximum = 0x01, + .maximum = 0x7f, .step = 0x01, - .default_value = 0x01, + .default_value = 0x20, .flags = 0, }, { .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain & exposure mode", + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", .minimum = 0x00, - .maximum = 0x03, + .maximum = 0x01, .step = 0x01, .default_value = 0x00, .flags = 0, @@ -305,23 +344,13 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "black pixel ratio", - .minimum = 0x01, - .maximum = 0x9a, - .step = 0x01, - .default_value = 0x8a, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_BRIGHT_LEVEL, + .id = SN9C102_V4L2_CID_GREEN_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "bright pixel ratio", - .minimum = 0x01, - .maximum = 0x9a, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, .step = 0x01, - .default_value = 0x10, + .default_value = 0x20, .flags = 0, }, { @@ -345,6 +374,7 @@ static struct sn9c102_sensor ov7630 = { .flags = 0, }, }, + .get_ctrl = &ov7630_get_ctrl, .set_ctrl = &ov7630_set_ctrl, .cropcap = { .bounds = { @@ -364,7 +394,7 @@ static struct sn9c102_sensor ov7630 = { .pix_format = { .width = 640, .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, + .pixelformat = V4L2_PIX_FMT_SN9C10X, .priv = 8, }, .set_pix_format = &ov7630_set_pix_format @@ -373,28 +403,35 @@ static struct sn9c102_sensor ov7630 = { int sn9c102_probe_ov7630(struct sn9c102_device* cam) { - const struct usb_device_id ov7630_id_table[] = { - { USB_DEVICE(0x0c45, 0x602c), }, - { USB_DEVICE(0x0c45, 0x602d), }, - { USB_DEVICE(0x0c45, 0x608f), }, - { USB_DEVICE(0x0c45, 0x60b0), }, - { } - }; - int err = 0; + int pid, ver, err = 0; - if (!sn9c102_match_id(cam, ov7630_id_table)) - return -ENODEV; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, + {0x00, 0x01}, {0x28, 0x17}); - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) - return -EIO; + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, + {0x42, 0x01}, {0x28, 0x17}, + {0x44, 0x02}); + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + if (err || pid < 0) { /* try a different initialization */ + err = sn9c102_write_reg(cam, 0x01, 0x01); + err += sn9c102_write_reg(cam, 0x00, 0x01); + } + break; + default: + break; + } - err += sn9c102_i2c_try_write(cam, &ov7630, 0x0b, 0); - if (err) + pid = sn9c102_i2c_try_read(cam, &ov7630, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7630, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x31) return -ENODEV; - sn9c102_attach_sensor(cam, &ov7630); return 0; diff --git a/drivers/media/video/sn9c102/sn9c102_ov7660.c b/drivers/media/video/sn9c102/sn9c102_ov7660.c new file mode 100644 index 0000000..c898e948 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_ov7660.c @@ -0,0 +1,514 @@ +/*************************************************************************** + * Plug-in for OV7660 image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int ov7660_init(struct sn9c102_device* cam) +{ + int err = 0; + + err = sn9c102_write_const_regs(cam, {0x40, 0x02}, {0x00, 0x03}, + {0x1a, 0x04}, {0x03, 0x10}, + {0x08, 0x14}, {0x20, 0x17}, + {0x8b, 0x18}, {0x00, 0x19}, + {0x1d, 0x1a}, {0x10, 0x1b}, + {0x02, 0x1c}, {0x03, 0x1d}, + {0x0f, 0x1e}, {0x0c, 0x1f}, + {0x00, 0x20}, {0x29, 0x21}, + {0x40, 0x22}, {0x54, 0x23}, + {0x66, 0x24}, {0x76, 0x25}, + {0x85, 0x26}, {0x94, 0x27}, + {0xa1, 0x28}, {0xae, 0x29}, + {0xbb, 0x2a}, {0xc7, 0x2b}, + {0xd3, 0x2c}, {0xde, 0x2d}, + {0xea, 0x2e}, {0xf4, 0x2f}, + {0xff, 0x30}, {0x00, 0x3F}, + {0xC7, 0x40}, {0x01, 0x41}, + {0x44, 0x42}, {0x00, 0x43}, + {0x44, 0x44}, {0x00, 0x45}, + {0x44, 0x46}, {0x00, 0x47}, + {0xC7, 0x48}, {0x01, 0x49}, + {0xC7, 0x4A}, {0x01, 0x4B}, + {0xC7, 0x4C}, {0x01, 0x4D}, + {0x44, 0x4E}, {0x00, 0x4F}, + {0x44, 0x50}, {0x00, 0x51}, + {0x44, 0x52}, {0x00, 0x53}, + {0xC7, 0x54}, {0x01, 0x55}, + {0xC7, 0x56}, {0x01, 0x57}, + {0xC7, 0x58}, {0x01, 0x59}, + {0x44, 0x5A}, {0x00, 0x5B}, + {0x44, 0x5C}, {0x00, 0x5D}, + {0x44, 0x5E}, {0x00, 0x5F}, + {0xC7, 0x60}, {0x01, 0x61}, + {0xC7, 0x62}, {0x01, 0x63}, + {0xC7, 0x64}, {0x01, 0x65}, + {0x44, 0x66}, {0x00, 0x67}, + {0x44, 0x68}, {0x00, 0x69}, + {0x44, 0x6A}, {0x00, 0x6B}, + {0xC7, 0x6C}, {0x01, 0x6D}, + {0xC7, 0x6E}, {0x01, 0x6F}, + {0xC7, 0x70}, {0x01, 0x71}, + {0x44, 0x72}, {0x00, 0x73}, + {0x44, 0x74}, {0x00, 0x75}, + {0x44, 0x76}, {0x00, 0x77}, + {0xC7, 0x78}, {0x01, 0x79}, + {0xC7, 0x7A}, {0x01, 0x7B}, + {0xC7, 0x7C}, {0x01, 0x7D}, + {0x44, 0x7E}, {0x00, 0x7F}, + {0x14, 0x84}, {0x00, 0x85}, + {0x27, 0x86}, {0x00, 0x87}, + {0x07, 0x88}, {0x00, 0x89}, + {0xEC, 0x8A}, {0x0f, 0x8B}, + {0xD8, 0x8C}, {0x0f, 0x8D}, + {0x3D, 0x8E}, {0x00, 0x8F}, + {0x3D, 0x90}, {0x00, 0x91}, + {0xCD, 0x92}, {0x0f, 0x93}, + {0xf7, 0x94}, {0x0f, 0x95}, + {0x0C, 0x96}, {0x00, 0x97}, + {0x00, 0x98}, {0x66, 0x99}, + {0x05, 0x9A}, {0x00, 0x9B}, + {0x04, 0x9C}, {0x00, 0x9D}, + {0x08, 0x9E}, {0x00, 0x9F}, + {0x2D, 0xC0}, {0x2D, 0xC1}, + {0x3A, 0xC2}, {0x05, 0xC3}, + {0x04, 0xC4}, {0x3F, 0xC5}, + {0x00, 0xC6}, {0x00, 0xC7}, + {0x50, 0xC8}, {0x3C, 0xC9}, + {0x28, 0xCA}, {0xD8, 0xCB}, + {0x14, 0xCC}, {0xEC, 0xCD}, + {0x32, 0xCE}, {0xDD, 0xCF}, + {0x32, 0xD0}, {0xDD, 0xD1}, + {0x6A, 0xD2}, {0x50, 0xD3}, + {0x00, 0xD4}, {0x00, 0xD5}, + {0x00, 0xD6}); + + err += sn9c102_i2c_write(cam, 0x12, 0x80); + err += sn9c102_i2c_write(cam, 0x11, 0x09); + err += sn9c102_i2c_write(cam, 0x00, 0x0A); + err += sn9c102_i2c_write(cam, 0x01, 0x78); + err += sn9c102_i2c_write(cam, 0x02, 0x90); + err += sn9c102_i2c_write(cam, 0x03, 0x00); + err += sn9c102_i2c_write(cam, 0x04, 0x00); + err += sn9c102_i2c_write(cam, 0x05, 0x08); + err += sn9c102_i2c_write(cam, 0x06, 0x0B); + err += sn9c102_i2c_write(cam, 0x07, 0x00); + err += sn9c102_i2c_write(cam, 0x08, 0x1C); + err += sn9c102_i2c_write(cam, 0x09, 0x01); + err += sn9c102_i2c_write(cam, 0x0A, 0x76); + err += sn9c102_i2c_write(cam, 0x0B, 0x60); + err += sn9c102_i2c_write(cam, 0x0C, 0x00); + err += sn9c102_i2c_write(cam, 0x0D, 0x08); + err += sn9c102_i2c_write(cam, 0x0E, 0x04); + err += sn9c102_i2c_write(cam, 0x0F, 0x6F); + err += sn9c102_i2c_write(cam, 0x10, 0x20); + err += sn9c102_i2c_write(cam, 0x11, 0x03); + err += sn9c102_i2c_write(cam, 0x12, 0x05); + err += sn9c102_i2c_write(cam, 0x13, 0xF8); + err += sn9c102_i2c_write(cam, 0x14, 0x2C); + err += sn9c102_i2c_write(cam, 0x15, 0x00); + err += sn9c102_i2c_write(cam, 0x16, 0x02); + err += sn9c102_i2c_write(cam, 0x17, 0x10); + err += sn9c102_i2c_write(cam, 0x18, 0x60); + err += sn9c102_i2c_write(cam, 0x19, 0x02); + err += sn9c102_i2c_write(cam, 0x1A, 0x7B); + err += sn9c102_i2c_write(cam, 0x1B, 0x02); + err += sn9c102_i2c_write(cam, 0x1C, 0x7F); + err += sn9c102_i2c_write(cam, 0x1D, 0xA2); + err += sn9c102_i2c_write(cam, 0x1E, 0x01); + err += sn9c102_i2c_write(cam, 0x1F, 0x0E); + err += sn9c102_i2c_write(cam, 0x20, 0x05); + err += sn9c102_i2c_write(cam, 0x21, 0x05); + err += sn9c102_i2c_write(cam, 0x22, 0x05); + err += sn9c102_i2c_write(cam, 0x23, 0x05); + err += sn9c102_i2c_write(cam, 0x24, 0x68); + err += sn9c102_i2c_write(cam, 0x25, 0x58); + err += sn9c102_i2c_write(cam, 0x26, 0xD4); + err += sn9c102_i2c_write(cam, 0x27, 0x80); + err += sn9c102_i2c_write(cam, 0x28, 0x80); + err += sn9c102_i2c_write(cam, 0x29, 0x30); + err += sn9c102_i2c_write(cam, 0x2A, 0x00); + err += sn9c102_i2c_write(cam, 0x2B, 0x00); + err += sn9c102_i2c_write(cam, 0x2C, 0x80); + err += sn9c102_i2c_write(cam, 0x2D, 0x00); + err += sn9c102_i2c_write(cam, 0x2E, 0x00); + err += sn9c102_i2c_write(cam, 0x2F, 0x0E); + err += sn9c102_i2c_write(cam, 0x30, 0x08); + err += sn9c102_i2c_write(cam, 0x31, 0x30); + err += sn9c102_i2c_write(cam, 0x32, 0xB4); + err += sn9c102_i2c_write(cam, 0x33, 0x00); + err += sn9c102_i2c_write(cam, 0x34, 0x07); + err += sn9c102_i2c_write(cam, 0x35, 0x84); + err += sn9c102_i2c_write(cam, 0x36, 0x00); + err += sn9c102_i2c_write(cam, 0x37, 0x0C); + err += sn9c102_i2c_write(cam, 0x38, 0x02); + err += sn9c102_i2c_write(cam, 0x39, 0x43); + err += sn9c102_i2c_write(cam, 0x3A, 0x00); + err += sn9c102_i2c_write(cam, 0x3B, 0x02); + err += sn9c102_i2c_write(cam, 0x3C, 0x6C); + err += sn9c102_i2c_write(cam, 0x3D, 0x99); + err += sn9c102_i2c_write(cam, 0x3E, 0x0E); + err += sn9c102_i2c_write(cam, 0x3F, 0x41); + err += sn9c102_i2c_write(cam, 0x40, 0xC1); + err += sn9c102_i2c_write(cam, 0x41, 0x22); + err += sn9c102_i2c_write(cam, 0x42, 0x08); + err += sn9c102_i2c_write(cam, 0x43, 0xF0); + err += sn9c102_i2c_write(cam, 0x44, 0x10); + err += sn9c102_i2c_write(cam, 0x45, 0x78); + err += sn9c102_i2c_write(cam, 0x46, 0xA8); + err += sn9c102_i2c_write(cam, 0x47, 0x60); + err += sn9c102_i2c_write(cam, 0x48, 0x80); + err += sn9c102_i2c_write(cam, 0x49, 0x00); + err += sn9c102_i2c_write(cam, 0x4A, 0x00); + err += sn9c102_i2c_write(cam, 0x4B, 0x00); + err += sn9c102_i2c_write(cam, 0x4C, 0x00); + err += sn9c102_i2c_write(cam, 0x4D, 0x00); + err += sn9c102_i2c_write(cam, 0x4E, 0x00); + err += sn9c102_i2c_write(cam, 0x4F, 0x46); + err += sn9c102_i2c_write(cam, 0x50, 0x36); + err += sn9c102_i2c_write(cam, 0x51, 0x0F); + err += sn9c102_i2c_write(cam, 0x52, 0x17); + err += sn9c102_i2c_write(cam, 0x53, 0x7F); + err += sn9c102_i2c_write(cam, 0x54, 0x96); + err += sn9c102_i2c_write(cam, 0x55, 0x40); + err += sn9c102_i2c_write(cam, 0x56, 0x40); + err += sn9c102_i2c_write(cam, 0x57, 0x40); + err += sn9c102_i2c_write(cam, 0x58, 0x0F); + err += sn9c102_i2c_write(cam, 0x59, 0xBA); + err += sn9c102_i2c_write(cam, 0x5A, 0x9A); + err += sn9c102_i2c_write(cam, 0x5B, 0x22); + err += sn9c102_i2c_write(cam, 0x5C, 0xB9); + err += sn9c102_i2c_write(cam, 0x5D, 0x9B); + err += sn9c102_i2c_write(cam, 0x5E, 0x10); + err += sn9c102_i2c_write(cam, 0x5F, 0xF0); + err += sn9c102_i2c_write(cam, 0x60, 0x05); + err += sn9c102_i2c_write(cam, 0x61, 0x60); + err += sn9c102_i2c_write(cam, 0x62, 0x00); + err += sn9c102_i2c_write(cam, 0x63, 0x00); + err += sn9c102_i2c_write(cam, 0x64, 0x50); + err += sn9c102_i2c_write(cam, 0x65, 0x30); + err += sn9c102_i2c_write(cam, 0x66, 0x00); + err += sn9c102_i2c_write(cam, 0x67, 0x80); + err += sn9c102_i2c_write(cam, 0x68, 0x7A); + err += sn9c102_i2c_write(cam, 0x69, 0x90); + err += sn9c102_i2c_write(cam, 0x6A, 0x80); + err += sn9c102_i2c_write(cam, 0x6B, 0x0A); + err += sn9c102_i2c_write(cam, 0x6C, 0x30); + err += sn9c102_i2c_write(cam, 0x6D, 0x48); + err += sn9c102_i2c_write(cam, 0x6E, 0x80); + err += sn9c102_i2c_write(cam, 0x6F, 0x74); + err += sn9c102_i2c_write(cam, 0x70, 0x64); + err += sn9c102_i2c_write(cam, 0x71, 0x60); + err += sn9c102_i2c_write(cam, 0x72, 0x5C); + err += sn9c102_i2c_write(cam, 0x73, 0x58); + err += sn9c102_i2c_write(cam, 0x74, 0x54); + err += sn9c102_i2c_write(cam, 0x75, 0x4C); + err += sn9c102_i2c_write(cam, 0x76, 0x40); + err += sn9c102_i2c_write(cam, 0x77, 0x38); + err += sn9c102_i2c_write(cam, 0x78, 0x34); + err += sn9c102_i2c_write(cam, 0x79, 0x30); + err += sn9c102_i2c_write(cam, 0x7A, 0x2F); + err += sn9c102_i2c_write(cam, 0x7B, 0x2B); + err += sn9c102_i2c_write(cam, 0x7C, 0x03); + err += sn9c102_i2c_write(cam, 0x7D, 0x07); + err += sn9c102_i2c_write(cam, 0x7E, 0x17); + err += sn9c102_i2c_write(cam, 0x7F, 0x34); + err += sn9c102_i2c_write(cam, 0x80, 0x41); + err += sn9c102_i2c_write(cam, 0x81, 0x4D); + err += sn9c102_i2c_write(cam, 0x82, 0x58); + err += sn9c102_i2c_write(cam, 0x83, 0x63); + err += sn9c102_i2c_write(cam, 0x84, 0x6E); + err += sn9c102_i2c_write(cam, 0x85, 0x77); + err += sn9c102_i2c_write(cam, 0x86, 0x87); + err += sn9c102_i2c_write(cam, 0x87, 0x95); + err += sn9c102_i2c_write(cam, 0x88, 0xAF); + err += sn9c102_i2c_write(cam, 0x89, 0xC7); + err += sn9c102_i2c_write(cam, 0x8A, 0xDF); + err += sn9c102_i2c_write(cam, 0x8B, 0x99); + err += sn9c102_i2c_write(cam, 0x8C, 0x99); + err += sn9c102_i2c_write(cam, 0x8D, 0xCF); + err += sn9c102_i2c_write(cam, 0x8E, 0x20); + err += sn9c102_i2c_write(cam, 0x8F, 0x26); + err += sn9c102_i2c_write(cam, 0x90, 0x10); + err += sn9c102_i2c_write(cam, 0x91, 0x0C); + err += sn9c102_i2c_write(cam, 0x92, 0x25); + err += sn9c102_i2c_write(cam, 0x93, 0x00); + err += sn9c102_i2c_write(cam, 0x94, 0x50); + err += sn9c102_i2c_write(cam, 0x95, 0x50); + err += sn9c102_i2c_write(cam, 0x96, 0x00); + err += sn9c102_i2c_write(cam, 0x97, 0x01); + err += sn9c102_i2c_write(cam, 0x98, 0x10); + err += sn9c102_i2c_write(cam, 0x99, 0x40); + err += sn9c102_i2c_write(cam, 0x9A, 0x40); + err += sn9c102_i2c_write(cam, 0x9B, 0x20); + err += sn9c102_i2c_write(cam, 0x9C, 0x00); + err += sn9c102_i2c_write(cam, 0x9D, 0x99); + err += sn9c102_i2c_write(cam, 0x9E, 0x7F); + err += sn9c102_i2c_write(cam, 0x9F, 0x00); + err += sn9c102_i2c_write(cam, 0xA0, 0x00); + err += sn9c102_i2c_write(cam, 0xA1, 0x00); + + return err; +} + + +static int ov7660_get_ctrl(struct sn9c102_device* cam, + struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x10)) < 0) + return -EIO; + break; + case V4L2_CID_DO_WHITE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x02); + ctrl->value = (ctrl->value & 0x04) ? 1 : 0; + break; + case V4L2_CID_RED_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x05); + ctrl->value &= 0x7f; + break; + case V4L2_CID_BLUE_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x06); + ctrl->value &= 0x7f; + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + ctrl->value = sn9c102_pread_reg(cam, 0x07); + ctrl->value &= 0x7f; + break; + case V4L2_CID_GAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x00)) < 0) + return -EIO; + ctrl->value &= 0x7f; + break; + case V4L2_CID_AUTOGAIN: + if ((ctrl->value = sn9c102_i2c_read(cam, 0x13)) < 0) + return -EIO; + ctrl->value &= 0x01; + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_ctrl(struct sn9c102_device* cam, + const struct v4l2_control* ctrl) +{ + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_EXPOSURE: + err += sn9c102_i2c_write(cam, 0x10, ctrl->value); + break; + case V4L2_CID_DO_WHITE_BALANCE: + err += sn9c102_write_reg(cam, 0x43 | (ctrl->value << 2), 0x02); + break; + case V4L2_CID_RED_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x05); + break; + case V4L2_CID_BLUE_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x06); + break; + case SN9C102_V4L2_CID_GREEN_BALANCE: + err += sn9c102_write_reg(cam, ctrl->value, 0x07); + break; + case V4L2_CID_GAIN: + err += sn9c102_i2c_write(cam, 0x00, ctrl->value); + break; + case V4L2_CID_AUTOGAIN: + err += sn9c102_i2c_write(cam, 0x13, 0xf0 | ctrl->value | + (ctrl->value << 1)); + break; + default: + return -EINVAL; + } + + return err ? -EIO : 0; +} + + +static int ov7660_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 1, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 1; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + return err; +} + + +static int ov7660_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int r0, err = 0; + + r0 = sn9c102_pread_reg(cam, 0x01); + + if (pix->pixelformat == V4L2_PIX_FMT_JPEG) { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x00); + } else { + err += sn9c102_write_reg(cam, r0 | 0x40, 0x01); + err += sn9c102_write_reg(cam, 0xa2, 0x17); + err += sn9c102_i2c_write(cam, 0x11, 0x0d); + } + + return err; +} + + +static struct sn9c102_sensor ov7660 = { + .name = "OV7660", + .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C105 | BRIDGE_SN9C120, + .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x21, + .init = &ov7660_init, + .qctrl = { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "global gain", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x0a, + .flags = 0, + }, + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = 0x50, + .flags = 0, + }, + { + .id = V4L2_CID_DO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "night mode", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x1f, + .flags = 0, + }, + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x1e, + .flags = 0, + }, + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto adjust", + .minimum = 0x00, + .maximum = 0x01, + .step = 0x01, + .default_value = 0x00, + .flags = 0, + }, + { + .id = SN9C102_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7f, + .step = 0x01, + .default_value = 0x20, + .flags = 0, + }, + }, + .get_ctrl = &ov7660_get_ctrl, + .set_ctrl = &ov7660_set_ctrl, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 640, + .height = 480, + }, + }, + .set_crop = &ov7660_set_crop, + .pix_format = { + .width = 640, + .height = 480, + .pixelformat = V4L2_PIX_FMT_JPEG, + .priv = 8, + }, + .set_pix_format = &ov7660_set_pix_format +}; + + +int sn9c102_probe_ov7660(struct sn9c102_device* cam) +{ + int pid, ver, err; + + err = sn9c102_write_const_regs(cam, {0x01, 0xf1}, {0x00, 0xf1}, + {0x01, 0x01}, {0x00, 0x01}, + {0x28, 0x17}); + + pid = sn9c102_i2c_try_read(cam, &ov7660, 0x0a); + ver = sn9c102_i2c_try_read(cam, &ov7660, 0x0b); + if (err || pid < 0 || ver < 0) + return -EIO; + if (pid != 0x76 || ver != 0x60) + return -ENODEV; + sn9c102_attach_sensor(cam, &ov7660); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_pas106b.c b/drivers/media/video/sn9c102/sn9c102_pas106b.c index 9915944..6715196 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas106b.c +++ b/drivers/media/video/sn9c102/sn9c102_pas106b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PAS106B image sensor connected to the SN9C10x PC Camera * + * Plug-in for PAS106B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -23,19 +23,13 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas106b; - - static int pas106b_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x20, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, {0x00, 0x11}, + {0x00, 0x14}, {0x20, 0x17}, + {0x20, 0x19}, {0x09, 0x18}); err += sn9c102_i2c_write(cam, 0x02, 0x0c); err += sn9c102_i2c_write(cam, 0x05, 0x5a); @@ -143,7 +137,7 @@ static int pas106b_set_ctrl(struct sn9c102_device* cam, static int pas106b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &pas106b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; @@ -172,6 +166,7 @@ static int pas106b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor pas106b = { .name = "PAS106B", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -278,16 +273,17 @@ static struct sn9c102_sensor pas106b = { int sn9c102_probe_pas106b(struct sn9c102_device* cam) { - int r0 = 0, r1 = 0, err = 0; + int r0 = 0, r1 = 0, err; unsigned int pid = 0; /* Minimal initialization to enable the I2C communication NOTE: do NOT change the values! */ - err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x00, 0x01); /* sensor power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* sensor power down */ + {0x00, 0x01}, /* sensor power on */ + {0x28, 0x17});/* sensor clock 24 MHz */ if (err) return -EIO; diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bca.c b/drivers/media/video/sn9c102/sn9c102_pas202bca.c deleted file mode 100644 index c8f1ae2..0000000 --- a/drivers/media/video/sn9c102/sn9c102_pas202bca.c +++ /dev/null @@ -1,238 +0,0 @@ -/*************************************************************************** - * Plug-in for PAS202BCA image sensor connected to the SN9C10x PC Camera * - * Controllers * - * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the Free Software * - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - ***************************************************************************/ - -#include <linux/delay.h> -#include "sn9c102_sensor.h" - - -static struct sn9c102_sensor pas202bca; - - -static int pas202bca_init(struct sn9c102_device* cam) -{ - int err = 0; - - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); - - err += sn9c102_i2c_write(cam, 0x02, 0x14); - err += sn9c102_i2c_write(cam, 0x03, 0x40); - err += sn9c102_i2c_write(cam, 0x0d, 0x2c); - err += sn9c102_i2c_write(cam, 0x0e, 0x01); - err += sn9c102_i2c_write(cam, 0x0f, 0xa9); - err += sn9c102_i2c_write(cam, 0x10, 0x08); - err += sn9c102_i2c_write(cam, 0x13, 0x63); - err += sn9c102_i2c_write(cam, 0x15, 0x70); - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - msleep(400); - - return err; -} - - -static int pas202bca_set_pix_format(struct sn9c102_device* cam, - const struct v4l2_pix_format* pix) -{ - int err = 0; - - if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x24, 0x17); - else - err += sn9c102_write_reg(cam, 0x20, 0x17); - - return err; -} - - -static int pas202bca_set_ctrl(struct sn9c102_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_EXPOSURE: - err += sn9c102_i2c_write(cam, 0x04, ctrl->value >> 6); - err += sn9c102_i2c_write(cam, 0x05, ctrl->value & 0x3f); - break; - case V4L2_CID_RED_BALANCE: - err += sn9c102_i2c_write(cam, 0x09, ctrl->value); - break; - case V4L2_CID_BLUE_BALANCE: - err += sn9c102_i2c_write(cam, 0x07, ctrl->value); - break; - case V4L2_CID_GAIN: - err += sn9c102_i2c_write(cam, 0x10, ctrl->value); - break; - case SN9C102_V4L2_CID_GREEN_BALANCE: - err += sn9c102_i2c_write(cam, 0x08, ctrl->value); - break; - case SN9C102_V4L2_CID_DAC_MAGNITUDE: - err += sn9c102_i2c_write(cam, 0x0c, ctrl->value); - break; - default: - return -EINVAL; - } - err += sn9c102_i2c_write(cam, 0x11, 0x01); - - return err ? -EIO : 0; -} - - -static int pas202bca_set_crop(struct sn9c102_device* cam, - const struct v4l2_rect* rect) -{ - struct sn9c102_sensor* s = &pas202bca; - int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3, - v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; - - err += sn9c102_write_reg(cam, h_start, 0x12); - err += sn9c102_write_reg(cam, v_start, 0x13); - - return err; -} - - -static struct sn9c102_sensor pas202bca = { - .name = "PAS202BCA", - .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", - .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, - .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, - .interface = SN9C102_I2C_2WIRES, - .i2c_slave_id = 0x40, - .init = &pas202bca_init, - .qctrl = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x01e5, - .maximum = 0x3fff, - .step = 0x0001, - .default_value = 0x01e5, - .flags = 0, - }, - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0x1f, - .step = 0x01, - .default_value = 0x0c, - .flags = 0, - }, - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x01, - .flags = 0, - }, - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x05, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_GREEN_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "green balance", - .minimum = 0x00, - .maximum = 0x0f, - .step = 0x01, - .default_value = 0x00, - .flags = 0, - }, - { - .id = SN9C102_V4L2_CID_DAC_MAGNITUDE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "DAC magnitude", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = 0x04, - .flags = 0, - }, - }, - .set_ctrl = &pas202bca_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .set_crop = &pas202bca_set_crop, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, - .set_pix_format = &pas202bca_set_pix_format -}; - - -int sn9c102_probe_pas202bca(struct sn9c102_device* cam) -{ - const struct usb_device_id pas202bca_id_table[] = { - { USB_DEVICE(0x0c45, 0x60af), }, - { } - }; - int err = 0; - - if (!sn9c102_match_id(cam,pas202bca_id_table)) - return -ENODEV; - - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x40, 0x01); - err += sn9c102_write_reg(cam, 0x28, 0x17); - if (err) - return -EIO; - - if (sn9c102_i2c_try_write(cam, &pas202bca, 0x10, 0)) /* try to write */ - return -ENODEV; - - sn9c102_attach_sensor(cam, &pas202bca); - - return 0; -} diff --git a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c index e3c1178..c1b8d6b 100644 --- a/drivers/media/video/sn9c102/sn9c102_pas202bcb.c +++ b/drivers/media/video/sn9c102/sn9c102_pas202bcb.c @@ -1,13 +1,13 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the SN9C10x PC Camera * + * Plug-in for PAS202BCB image sensor connected to the SN9C1xx PC Camera * * Controllers * * * * Copyright (C) 2004 by Carlos Eduardo Medaglia Dyonisio * * <medaglia@undl.org.br> * * http://cadu.homelinux.com:8080/ * * * - * DAC Magnitude, exposure and green gain controls added by * - * Luca Risolia <luca.risolia@studio.unibo.it> * + * Support for SN9C103, DAC Magnitude, exposure and green gain controls * + * added by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -28,19 +28,40 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor pas202bcb; - - static int pas202bcb_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x30, 0x19); - err += sn9c102_write_reg(cam, 0x09, 0x18); + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, {0x00, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x20, 0x17}, {0x30, 0x19}, + {0x09, 0x18}); + break; + case BRIDGE_SN9C103: + err = sn9c102_write_const_regs(cam, {0x00, 0x02}, + {0x00, 0x03}, {0x1a, 0x04}, + {0x20, 0x05}, {0x20, 0x06}, + {0x20, 0x07}, {0x00, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x20, 0x17}, {0x30, 0x19}, + {0x09, 0x18}, {0x02, 0x1c}, + {0x03, 0x1d}, {0x0f, 0x1e}, + {0x0c, 0x1f}, {0x00, 0x20}, + {0x10, 0x21}, {0x20, 0x22}, + {0x30, 0x23}, {0x40, 0x24}, + {0x50, 0x25}, {0x60, 0x26}, + {0x70, 0x27}, {0x80, 0x28}, + {0x90, 0x29}, {0xa0, 0x2a}, + {0xb0, 0x2b}, {0xc0, 0x2c}, + {0xd0, 0x2d}, {0xe0, 0x2e}, + {0xf0, 0x2f}, {0xff, 0x30}); + break; + default: + break; + } err += sn9c102_i2c_write(cam, 0x02, 0x14); err += sn9c102_i2c_write(cam, 0x03, 0x40); @@ -107,7 +128,7 @@ static int pas202bcb_set_pix_format(struct sn9c102_device* cam, int err = 0; if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) - err += sn9c102_write_reg(cam, 0x24, 0x17); + err += sn9c102_write_reg(cam, 0x28, 0x17); else err += sn9c102_write_reg(cam, 0x20, 0x17); @@ -152,11 +173,23 @@ static int pas202bcb_set_ctrl(struct sn9c102_device* cam, static int pas202bcb_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &pas202bcb; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; - u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4, + u8 h_start = 0, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 3; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 4; + break; + case BRIDGE_SN9C103: + h_start = (u8)(rect->left - s->cropcap.bounds.left) + 3; + break; + default: + break; + } + err += sn9c102_write_reg(cam, h_start, 0x12); err += sn9c102_write_reg(cam, v_start, 0x13); @@ -166,8 +199,8 @@ static int pas202bcb_set_crop(struct sn9c102_device* cam, static struct sn9c102_sensor pas202bcb = { .name = "PAS202BCB", - .maintainer = "Carlos Eduardo Medaglia Dyonisio " - "<medaglia@undl.org.br>", + .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102 | BRIDGE_SN9C103, .sysfs_ops = SN9C102_I2C_READ | SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_400KHZ | SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_2WIRES, @@ -191,7 +224,7 @@ static struct sn9c102_sensor pas202bcb = { .minimum = 0x00, .maximum = 0x1f, .step = 0x01, - .default_value = 0x0c, + .default_value = 0x0b, .flags = 0, }, { @@ -201,7 +234,7 @@ static struct sn9c102_sensor pas202bcb = { .minimum = 0x00, .maximum = 0x0f, .step = 0x01, - .default_value = 0x01, + .default_value = 0x00, .flags = 0, }, { @@ -271,16 +304,27 @@ int sn9c102_probe_pas202bcb(struct sn9c102_device* cam) * Minimal initialization to enable the I2C communication * NOTE: do NOT change the values! */ - err += sn9c102_write_reg(cam, 0x01, 0x01); /* sensor power down */ - err += sn9c102_write_reg(cam, 0x40, 0x01); /* sensor power on */ - err += sn9c102_write_reg(cam, 0x28, 0x17); /* sensor clock at 24 MHz */ - if (err) - return -EIO; + switch (sn9c102_get_bridge(cam)) { + case BRIDGE_SN9C101: + case BRIDGE_SN9C102: + err = sn9c102_write_const_regs(cam, + {0x01, 0x01}, /* power down */ + {0x40, 0x01}, /* power on */ + {0x28, 0x17});/* clock 24 MHz */ + break; + case BRIDGE_SN9C103: /* do _not_ change anything! */ + err = sn9c102_write_const_regs(cam, {0x09, 0x01}, + {0x44, 0x01}, {0x44, 0x02}, + {0x29, 0x17}); + break; + default: + break; + } r0 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x00); r1 = sn9c102_i2c_try_read(cam, &pas202bcb, 0x01); - if (r0 < 0 || r1 < 0) + if (err || r0 < 0 || r1 < 0) return -EIO; pid = (r0 << 4) | ((r1 & 0xf0) >> 4); diff --git a/drivers/media/video/sn9c102/sn9c102_sensor.h b/drivers/media/video/sn9c102/sn9c102_sensor.h index 2a874ee6..1bbf64c 100644 --- a/drivers/media/video/sn9c102/sn9c102_sensor.h +++ b/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -1,7 +1,7 @@ /*************************************************************************** - * API for image sensors connected to the SN9C10x PC Camera Controllers * + * API for image sensors connected to the SN9C1xx PC Camera Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -36,14 +36,13 @@ struct sn9c102_sensor; /* OVERVIEW. This is a small interface that allows you to add support for any CCD/CMOS - image sensors connected to the SN9C10X bridges. The entire API is documented + image sensors connected to the SN9C1XX bridges. The entire API is documented below. In the most general case, to support a sensor there are three steps you have to follow: 1) define the main "sn9c102_sensor" structure by setting the basic fields; 2) write a probing function to be called by the core module when the USB camera is recognized, then add both the USB ids and the name of that - function to the two corresponding tables SENSOR_TABLE and ID_TABLE (see - below); + function to the two corresponding tables in sn9c102_devtable.h; 3) implement the methods that you want/need (and fill the rest of the main structure accordingly). "sn9c102_pas106b.c" is an example of all this stuff. Remember that you do @@ -54,42 +53,21 @@ struct sn9c102_sensor; /*****************************************************************************/ -/* - Probing functions: on success, you must attach the sensor to the camera - by calling sn9c102_attach_sensor() provided below. - To enable the I2C communication, you might need to perform a really basic - initialization of the SN9C10X chip by using the write function declared - ahead. - Functions must return 0 on success, the appropriate error otherwise. -*/ -extern int sn9c102_probe_hv7131d(struct sn9c102_device* cam); -extern int sn9c102_probe_mi0343(struct sn9c102_device* cam); -extern int sn9c102_probe_ov7630(struct sn9c102_device* cam); -extern int sn9c102_probe_pas106b(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bca(struct sn9c102_device* cam); -extern int sn9c102_probe_pas202bcb(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5110c1b(struct sn9c102_device* cam); -extern int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam); - -/* - Add the above entries to this table. Be sure to add the entry in the right - place, since, on failure, the next probing routine is called according to - the order of the list below, from top to bottom. -*/ -#define SN9C102_SENSOR_TABLE \ -static int (*sn9c102_sensor_table[])(struct sn9c102_device*) = { \ - &sn9c102_probe_mi0343, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas106b, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas202bcb, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_hv7131d, /* strong detection based on SENSOR ids */ \ - &sn9c102_probe_pas202bca, /* detection mostly based on USB pid/vid */ \ - &sn9c102_probe_ov7630, /* detection mostly based on USB pid/vid */ \ - &sn9c102_probe_tas5110c1b, /* detection based on USB pid/vid */ \ - &sn9c102_probe_tas5130d1b, /* detection based on USB pid/vid */ \ - NULL, \ +enum sn9c102_bridge { + BRIDGE_SN9C101 = 0x01, + BRIDGE_SN9C102 = 0x02, + BRIDGE_SN9C103 = 0x04, + BRIDGE_SN9C105 = 0x08, + BRIDGE_SN9C120 = 0x10, }; -/* Device identification */ +/* Return the bridge name */ +enum sn9c102_bridge sn9c102_get_bridge(struct sn9c102_device* cam); + +/* Return a pointer the sensor struct attached to the camera */ +struct sn9c102_sensor* sn9c102_get_sensor(struct sn9c102_device* cam); + +/* Identify a device */ extern struct sn9c102_device* sn9c102_match_id(struct sn9c102_device* cam, const struct usb_device_id *id); @@ -99,68 +77,8 @@ sn9c102_attach_sensor(struct sn9c102_device* cam, struct sn9c102_sensor* sensor); /* - Each SN9C10x camera has proper PID/VID identifiers. - SN9C103 supports multiple interfaces, but we only handle the video class - interface. -*/ -#define SN9C102_USB_DEVICE(vend, prod, intclass) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = (vend), \ - .idProduct = (prod), \ - .bInterfaceClass = (intclass) - -#define SN9C102_ID_TABLE \ -static const struct usb_device_id sn9c102_id_table[] = { \ - { USB_DEVICE(0x0c45, 0x6001), }, /* TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6005), }, /* TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6007), }, \ - { USB_DEVICE(0x0c45, 0x6009), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x600d), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x6024), }, \ - { USB_DEVICE(0x0c45, 0x6025), }, /* TAS5130D1B and TAS5110C1B */ \ - { USB_DEVICE(0x0c45, 0x6028), }, /* PAS202BCB */ \ - { USB_DEVICE(0x0c45, 0x6029), }, /* PAS106B */ \ - { USB_DEVICE(0x0c45, 0x602a), }, /* HV7131D */ \ - { USB_DEVICE(0x0c45, 0x602b), }, /* MI-0343 */ \ - { USB_DEVICE(0x0c45, 0x602c), }, /* OV7630 */ \ - { USB_DEVICE(0x0c45, 0x602d), }, \ - { USB_DEVICE(0x0c45, 0x602e), }, /* OV7630 */ \ - { USB_DEVICE(0x0c45, 0x6030), }, /* MI03x */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6080, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x6082, 0xff), }, /* MI0343 & MI0360 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6083, 0xff), }, /* HV7131[D|E1] */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x6088, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608a, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608b, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x608c, 0xff), }, /* HV7131/R */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x608e, 0xff), }, /* CIS-VF10 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x608f, 0xff), }, /* OV7630 */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a0, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a2, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a3, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60a8, 0xff), }, /* PAS106B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60aa, 0xff), }, /* TAS5130D1B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ab, 0xff), }, /* TAS5110C1B */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ac, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ae, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60af, 0xff), }, /* PAS202BCB */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b0, 0xff), }, /* OV7630 (?) */ \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b2, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b3, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60b8, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60ba, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60bb, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60bc, 0xff), }, \ - { SN9C102_USB_DEVICE(0x0c45, 0x60be, 0xff), }, \ - { } \ -}; - -/*****************************************************************************/ - -/* Read/write routines: they always return -1 on error, 0 or the read value - otherwise. NOTE that a real read operation is not supported by the SN9C10X + otherwise. NOTE that a real read operation is not supported by the SN9C1XX chip for some of its registers. To work around this problem, a pseudo-read call is provided instead: it returns the last successfully written value on the register (0 if it has never been written), the usual -1 on error. @@ -176,7 +94,7 @@ extern int sn9c102_i2c_try_read(struct sn9c102_device*,struct sn9c102_sensor*, These must be used if and only if the sensor doesn't implement the standard I2C protocol. There are a number of good reasons why you must use the single-byte versions of these functions: do not abuse. The first function - writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C10X + writes n bytes, from data0 to datan, to registers 0x09 - 0x09+n of SN9C1XX chip. The second one programs the registers 0x09 and 0x10 with data0 and data1, and places the n bytes read from the sensor register table in the buffer pointed by 'buffer'. Both the functions return -1 on error; the write @@ -196,19 +114,17 @@ extern int sn9c102_i2c_write(struct sn9c102_device*, u8 address, u8 value); extern int sn9c102_i2c_read(struct sn9c102_device*, u8 address); /* I/O on registers in the bridge. Could be used by the sensor methods too */ -extern int sn9c102_write_regs(struct sn9c102_device*, u8* buff, u16 index); -extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); extern int sn9c102_pread_reg(struct sn9c102_device*, u16 index); - +extern int sn9c102_write_reg(struct sn9c102_device*, u8 value, u16 index); +extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], + int count); /* - NOTE: there are no exported debugging functions. To uniform the output you - must use the dev_info()/dev_warn()/dev_err() macros defined in device.h, - already included here, the argument being the struct device '&usbdev->dev' - of the sensor structure. Do NOT use these macros before the sensor is - attached or the kernel will crash! However, you should not need to notify - the user about common errors or other messages, since this is done by the - master module. -*/ + * Write multiple registers with constant values. For example: + * sn9c102_write_const_regs(cam, {0x00, 0x14}, {0x60, 0x17}, {0x0f, 0x18}); + */ +#define sn9c102_write_const_regs(device, data...) \ + ({ const static u8 _data[][2] = {data}; \ + sn9c102_write_regs(device, _data, ARRAY_SIZE(_data)); }) /*****************************************************************************/ @@ -227,17 +143,19 @@ enum sn9c102_i2c_interface { SN9C102_I2C_3WIRES, }; -#define SN9C102_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 +#define SN9C102_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) struct sn9c102_sensor { char name[32], /* sensor name */ maintainer[64]; /* name of the mantainer <email> */ + enum sn9c102_bridge supported_bridge; /* supported SN9C1xx bridges */ + /* Supported operations through the 'sysfs' interface */ enum sn9c102_i2c_sysfs_ops sysfs_ops; /* - These sensor capabilities must be provided if the SN9C10X controller + These sensor capabilities must be provided if the SN9C1XX controller needs to communicate through the sensor serial interface by using at least one of the i2c functions available. */ @@ -260,7 +178,7 @@ struct sn9c102_sensor { /* This function will be called after the sensor has been attached. It should be used to initialize the sensor only, but may also - configure part of the SN9C10X chip if necessary. You don't need to + configure part of the SN9C1XX chip if necessary. You don't need to setup picture settings like brightness, contrast, etc.. here, if the corrisponding controls are implemented (see below), since they are adjusted in the core driver by calling the set_ctrl() @@ -300,7 +218,7 @@ struct sn9c102_sensor { It is not always true that the largest achievable active window can cover the whole array of pixels. The V4L2 API defines another area called "source rectangle", which, in turn, is a subrectangle of - the active window. The SN9C10X chip is always programmed to read the + the active window. The SN9C1XX chip is always programmed to read the source rectangle. The bounds of both the active window and the source rectangle are specified in the cropcap substructures 'bounds' and 'defrect'. @@ -326,13 +244,13 @@ struct sn9c102_sensor { const struct v4l2_rect* rect); /* To be called on VIDIOC_C_SETCROP. The core module always calls a - default routine which configures the appropriate SN9C10X regs (also + default routine which configures the appropriate SN9C1XX regs (also scaling), but you may need to override/adjust specific stuff. 'rect' contains width and height values that are multiple of 16: in case you override the default function, you always have to program the chip to match those values; on error return the corresponding error code without rolling back. - NOTE: in case, you must program the SN9C10X chip to get rid of + NOTE: in case, you must program the SN9C1XX chip to get rid of blank pixels or blank lines at the _start_ of each line or frame after each HSYNC or VSYNC, so that the image starts with real RGB data (see regs 0x12, 0x13) (having set H_SIZE and, @@ -344,16 +262,16 @@ struct sn9c102_sensor { /* What you have to define here are: 1) initial 'width' and 'height' of the target rectangle 2) the initial 'pixelformat', which can be - either V4L2_PIX_FMT_SN9C10X (for compressed video) or - V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate the - number of bits per pixel for uncompressed video, 8 or 9 (despite the - current value of 'pixelformat'). + either V4L2_PIX_FMT_SN9C10X, V4L2_PIX_FMT_JPEG (for ompressed video) + or V4L2_PIX_FMT_SBGGR8 3) 'priv', which we'll be used to indicate + the number of bits per pixel for uncompressed video, 8 or 9 (despite + the current value of 'pixelformat'). NOTE 1: both 'width' and 'height' _must_ be either 1/1 or 1/2 or 1/4 of cropcap.defrect.width and cropcap.defrect.height. I suggest 1/1. NOTE 2: The initial compression quality is defined by the first bit of reg 0x17 during the initialization of the image sensor. - NOTE 3: as said above, you have to program the SN9C10X chip to get + NOTE 3: as said above, you have to program the SN9C1XX chip to get rid of any blank pixels, so that the output of the sensor matches the RGB bayer sequence (i.e. BGBGBG...GRGRGR). */ @@ -378,12 +296,12 @@ struct sn9c102_sensor { /*****************************************************************************/ /* Private ioctl's for control settings supported by some image sensors */ -#define SN9C102_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE -#define SN9C102_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 -#define SN9C102_V4L2_CID_RESET_LEVEL V4L2_CID_PRIVATE_BASE + 2 -#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE V4L2_CID_PRIVATE_BASE + 3 -#define SN9C102_V4L2_CID_GAMMA V4L2_CID_PRIVATE_BASE + 4 -#define SN9C102_V4L2_CID_BAND_FILTER V4L2_CID_PRIVATE_BASE + 5 -#define SN9C102_V4L2_CID_BRIGHT_LEVEL V4L2_CID_PRIVATE_BASE + 6 +#define SN9C102_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define SN9C102_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) +#define SN9C102_V4L2_CID_RESET_LEVEL (V4L2_CID_PRIVATE_BASE + 2) +#define SN9C102_V4L2_CID_PIXEL_BIAS_VOLTAGE (V4L2_CID_PRIVATE_BASE + 3) +#define SN9C102_V4L2_CID_GAMMA (V4L2_CID_PRIVATE_BASE + 4) +#define SN9C102_V4L2_CID_BAND_FILTER (V4L2_CID_PRIVATE_BASE + 5) +#define SN9C102_V4L2_CID_BRIGHT_LEVEL (V4L2_CID_PRIVATE_BASE + 6) #endif /* _SN9C102_SENSOR_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c index 294eb02..0e7ec86 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5110c1b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for TAS5110C1B image sensor connected to the SN9C10x PC Camera * + * Plug-in for TAS5110C1B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,21 +22,14 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5110c1b; - - static int tas5110c1b_init(struct sn9c102_device* cam) { int err = 0; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x44, 0x01); - err += sn9c102_write_reg(cam, 0x00, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x0a, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x06, 0x18); - err += sn9c102_write_reg(cam, 0xfb, 0x19); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x44, 0x01}, + {0x00, 0x10}, {0x00, 0x11}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); err += sn9c102_i2c_write(cam, 0xc0, 0x80); @@ -64,7 +57,7 @@ static int tas5110c1b_set_ctrl(struct sn9c102_device* cam, static int tas5110c1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &tas5110c1b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); int err = 0; u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; @@ -98,6 +91,7 @@ static int tas5110c1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5110c1b = { .name = "TAS5110C1B", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, diff --git a/drivers/media/video/sn9c102/sn9c102_tas5110d.c b/drivers/media/video/sn9c102/sn9c102_tas5110d.c new file mode 100644 index 0000000..83a39e8 --- /dev/null +++ b/drivers/media/video/sn9c102/sn9c102_tas5110d.c @@ -0,0 +1,118 @@ +/*************************************************************************** + * Plug-in for TAS5110D image sensor connected to the SN9C1xx PC Camera * + * Controllers * + * * + * Copyright (C) 2007 by Luca Risolia <luca.risolia@studio.unibo.it> * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the Free Software * + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * + ***************************************************************************/ + +#include "sn9c102_sensor.h" + + +static int tas5110d_init(struct sn9c102_device* cam) +{ + int err; + + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x04, 0x01}, + {0x0a, 0x14}, {0x60, 0x17}, + {0x06, 0x18}, {0xfb, 0x19}); + + err += sn9c102_i2c_write(cam, 0x9a, 0xca); + + return err; +} + + +static int tas5110d_set_crop(struct sn9c102_device* cam, + const struct v4l2_rect* rect) +{ + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); + int err = 0; + u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 69, + v_start = (u8)(rect->top - s->cropcap.bounds.top) + 9; + + err += sn9c102_write_reg(cam, h_start, 0x12); + err += sn9c102_write_reg(cam, v_start, 0x13); + + err += sn9c102_write_reg(cam, 0x14, 0x1a); + err += sn9c102_write_reg(cam, 0x0a, 0x1b); + + return err; +} + + +static int tas5110d_set_pix_format(struct sn9c102_device* cam, + const struct v4l2_pix_format* pix) +{ + int err = 0; + + if (pix->pixelformat == V4L2_PIX_FMT_SN9C10X) + err += sn9c102_write_reg(cam, 0x3b, 0x19); + else + err += sn9c102_write_reg(cam, 0xfb, 0x19); + + return err; +} + + +static struct sn9c102_sensor tas5110d = { + .name = "TAS5110D", + .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, + .sysfs_ops = SN9C102_I2C_WRITE, + .frequency = SN9C102_I2C_100KHZ, + .interface = SN9C102_I2C_2WIRES, + .i2c_slave_id = 0x61, + .init = &tas5110d_init, + .cropcap = { + .bounds = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + .defrect = { + .left = 0, + .top = 0, + .width = 352, + .height = 288, + }, + }, + .set_crop = &tas5110d_set_crop, + .pix_format = { + .width = 352, + .height = 288, + .pixelformat = V4L2_PIX_FMT_SBGGR8, + .priv = 8, + }, + .set_pix_format = &tas5110d_set_pix_format +}; + + +int sn9c102_probe_tas5110d(struct sn9c102_device* cam) +{ + const struct usb_device_id tas5110d_id_table[] = { + { USB_DEVICE(0x0c45, 0x6007), }, + { } + }; + + if (!sn9c102_match_id(cam, tas5110d_id_table)) + return -ENODEV; + + sn9c102_attach_sensor(cam, &tas5110d); + + return 0; +} diff --git a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c index 9ecb090..5040650 100644 --- a/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c +++ b/drivers/media/video/sn9c102/sn9c102_tas5130d1b.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the SN9C10x PC Camera * + * Plug-in for TAS5130D1B image sensor connected to the SN9C1xx PC Camera * * Controllers * * * - * Copyright (C) 2004-2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2004-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -22,21 +22,14 @@ #include "sn9c102_sensor.h" -static struct sn9c102_sensor tas5130d1b; - - static int tas5130d1b_init(struct sn9c102_device* cam) { - int err = 0; + int err; - err += sn9c102_write_reg(cam, 0x01, 0x01); - err += sn9c102_write_reg(cam, 0x20, 0x17); - err += sn9c102_write_reg(cam, 0x04, 0x01); - err += sn9c102_write_reg(cam, 0x01, 0x10); - err += sn9c102_write_reg(cam, 0x00, 0x11); - err += sn9c102_write_reg(cam, 0x00, 0x14); - err += sn9c102_write_reg(cam, 0x60, 0x17); - err += sn9c102_write_reg(cam, 0x07, 0x18); + err = sn9c102_write_const_regs(cam, {0x01, 0x01}, {0x20, 0x17}, + {0x04, 0x01}, {0x01, 0x10}, + {0x00, 0x11}, {0x00, 0x14}, + {0x60, 0x17}, {0x07, 0x18}); return err; } @@ -65,7 +58,7 @@ static int tas5130d1b_set_ctrl(struct sn9c102_device* cam, static int tas5130d1b_set_crop(struct sn9c102_device* cam, const struct v4l2_rect* rect) { - struct sn9c102_sensor* s = &tas5130d1b; + struct sn9c102_sensor* s = sn9c102_get_sensor(cam); u8 h_start = (u8)(rect->left - s->cropcap.bounds.left) + 104, v_start = (u8)(rect->top - s->cropcap.bounds.top) + 12; int err = 0; @@ -99,6 +92,7 @@ static int tas5130d1b_set_pix_format(struct sn9c102_device* cam, static struct sn9c102_sensor tas5130d1b = { .name = "TAS5130D1B", .maintainer = "Luca Risolia <luca.risolia@studio.unibo.it>", + .supported_bridge = BRIDGE_SN9C101 | BRIDGE_SN9C102, .sysfs_ops = SN9C102_I2C_WRITE, .frequency = SN9C102_I2C_100KHZ, .interface = SN9C102_I2C_3WIRES, @@ -154,6 +148,7 @@ static struct sn9c102_sensor tas5130d1b = { int sn9c102_probe_tas5130d1b(struct sn9c102_device* cam) { const struct usb_device_id tas5130d1b_id_table[] = { + { USB_DEVICE(0x0c45, 0x6024), }, { USB_DEVICE(0x0c45, 0x6025), }, { USB_DEVICE(0x0c45, 0x60aa), }, { } diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index d1ccc06..43225802 100644 --- a/drivers/media/video/tda7432.c +++ b/drivers/media/video/tda7432.c @@ -45,7 +45,6 @@ #include <linux/slab.h> #include <linux/videodev.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include <media/v4l2-common.h> #include <media/i2c-addr.h> diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index 027c8a0..1a1bef0 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -192,14 +192,52 @@ static struct tda827xa_data tda827xa_analog[] = { { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0} /* End */ }; +static void tda827xa_lna_gain(struct i2c_client *c, int high) +{ + struct tuner *t = i2c_get_clientdata(c); + unsigned char buf[] = {0x22, 0x01}; + int arg; + struct i2c_msg msg = {.addr = c->addr, .flags = 0, .buf = buf, .len = sizeof(buf)}; + if (t->config) { + if (high) + tuner_dbg("setting LNA to high gain\n"); + else + tuner_dbg("setting LNA to low gain\n"); + } + switch (t->config) { + case 0: /* no LNA */ + break; + case 1: /* switch is GPIO 0 of tda8290 */ + case 2: + /* turn Vsync on */ + if (t->std & V4L2_STD_MN) + arg = 1; + else + arg = 0; + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 1, arg); + buf[1] = high ? 0 : 1; + if (t->config == 2) + buf[1] = high ? 1 : 0; + i2c_transfer(c->adapter, &msg, 1); + break; + case 3: /* switch with GPIO of saa713x */ + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 0, high); + break; + } +} + static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) { - unsigned char tuner_reg[14]; - unsigned char reg2[2]; + unsigned char tuner_reg[11]; u32 N; int i; struct tuner *t = i2c_get_clientdata(c); - struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0}; + struct i2c_msg msg = {.addr = t->tda827x_addr, .flags = 0, .buf = tuner_reg}; + + tda827xa_lna_gain( c, 1); + msleep(10); if (t->mode == V4L2_TUNER_RADIO) freq = freq / 1000; @@ -222,48 +260,58 @@ static void tda827xa_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tuner_reg[5] = (tda827xa_analog[i].spd << 5) + (tda827xa_analog[i].svco << 3) + tda827xa_analog[i].sbs; tuner_reg[6] = 0x8b + (tda827xa_analog[i].gc3 << 4); - tuner_reg[7] = 0x0c; + tuner_reg[7] = 0x1c; tuner_reg[8] = 4; tuner_reg[9] = 0x20; - tuner_reg[10] = 0xff; - tuner_reg[11] = 0xe0; - tuner_reg[12] = 0; - tuner_reg[13] = 0x39 + (t->tda827x_lpsel << 1); + tuner_reg[10] = 0x00; + msg.len = 11; + i2c_transfer(c->adapter, &msg, 1); - msg.buf = tuner_reg; - msg.len = 14; + tuner_reg[0] = 0x90; + tuner_reg[1] = 0xff; + tuner_reg[2] = 0xe0; + tuner_reg[3] = 0; + tuner_reg[4] = 0x99 + (t->tda827x_lpsel << 1); + msg.len = 5; i2c_transfer(c->adapter, &msg, 1); - msg.buf= reg2; + tuner_reg[0] = 0xa0; + tuner_reg[1] = 0xc0; msg.len = 2; - reg2[0] = 0x60; - reg2[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xa0; - reg2[1] = 0xc0; + tuner_reg[0] = 0x30; + tuner_reg[1] = 0x10 + tda827xa_analog[i].scr; i2c_transfer(c->adapter, &msg, 1); - msleep(2); - reg2[0] = 0x30; - reg2[1] = 0x10 + tda827xa_analog[i].scr; + msg.flags = I2C_M_RD; + i2c_transfer(c->adapter, &msg, 1); + msg.flags = 0; + tuner_reg[1] >>= 4; + tuner_dbg("AGC2 gain is: %d\n", tuner_reg[1]); + if (tuner_reg[1] < 1) + tda827xa_lna_gain( c, 0); + + msleep(100); + tuner_reg[0] = 0x60; + tuner_reg[1] = 0x3c; i2c_transfer(c->adapter, &msg, 1); - msleep(550); - reg2[0] = 0x50; - reg2[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); + msleep(163); + tuner_reg[0] = 0x50; + tuner_reg[1] = 0x8f + (tda827xa_analog[i].gc3 << 4); i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0x80; - reg2[1] = 0x28; + tuner_reg[0] = 0x80; + tuner_reg[1] = 0x28; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xb0; - reg2[1] = 0x01; + tuner_reg[0] = 0xb0; + tuner_reg[1] = 0x01; i2c_transfer(c->adapter, &msg, 1); - reg2[0] = 0xc0; - reg2[1] = 0x19 + (t->tda827x_lpsel << 1); + tuner_reg[0] = 0xc0; + tuner_reg[1] = 0x19 + (t->tda827x_lpsel << 1); i2c_transfer(c->adapter, &msg, 1); } @@ -319,7 +367,9 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) unsigned char addr_pll_stat = 0x1b; unsigned char adc_sat, agc_stat, pll_stat; + int i; + tuner_dbg("tda827xa config is 0x%02x\n", t->config); i2c_master_send(c, easy_mode, 2); i2c_master_send(c, agc_out_on, 2); i2c_master_send(c, soft_reset, 2); @@ -340,17 +390,22 @@ static int tda8290_tune(struct i2c_client *c, u16 ifc, unsigned int freq) tda827xa_tune(c, ifc, freq); else tda827x_tune(c, ifc, freq); + for (i = 0; i < 3; i++) { + i2c_master_send(c, &addr_pll_stat, 1); + i2c_master_recv(c, &pll_stat, 1); + if (pll_stat & 0x80) { + i2c_master_send(c, &addr_adc_sat, 1); + i2c_master_recv(c, &adc_sat, 1); + i2c_master_send(c, &addr_agc_stat, 1); + i2c_master_recv(c, &agc_stat, 1); + tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); + break; + } else { + tuner_dbg("tda8290 not locked, no signal?\n"); + msleep(100); + } + } /* adjust headroom resp. gain */ - i2c_master_send(c, &addr_adc_sat, 1); - i2c_master_recv(c, &adc_sat, 1); - i2c_master_send(c, &addr_agc_stat, 1); - i2c_master_recv(c, &agc_stat, 1); - i2c_master_send(c, &addr_pll_stat, 1); - i2c_master_recv(c, &pll_stat, 1); - if (pll_stat & 0x80) - tuner_dbg("tda8290 is locked, AGC: %d\n", agc_stat); - else - tuner_dbg("tda8290 not locked, no signal?\n"); if ((agc_stat > 115) || (!(pll_stat & 0x80) && (adc_sat < 20))) { tuner_dbg("adjust gain, step 1. Agc: %d, ADC stat: %d, lock: %d\n", agc_stat, adc_sat, pll_stat & 0x80); @@ -407,7 +462,6 @@ static void set_audio(struct tuner *t) char* mode; t->tda827x_lpsel = 0; - mode = "xx"; if (t->std & V4L2_STD_MN) { t->sgIF = 92; t->tda8290_easy_mode = 0x01; @@ -437,8 +491,12 @@ static void set_audio(struct tuner *t) t->sgIF = 20; t->tda8290_easy_mode = 0x40; mode = "LC"; + } else { + t->sgIF = 124; + t->tda8290_easy_mode = 0x10; + mode = "xx"; } - tuner_dbg("setting tda8290 to system %s\n", mode); + tuner_dbg("setting tda8290 to system %s\n", mode); } static void set_tv_freq(struct i2c_client *c, unsigned int freq) @@ -487,11 +545,16 @@ static void standby(struct i2c_client *c) static void tda8290_init_if(struct i2c_client *c) { + struct tuner *t = i2c_get_clientdata(c); unsigned char set_VS[] = { 0x30, 0x6F }; + unsigned char set_GP00_CF[] = { 0x20, 0x01 }; unsigned char set_GP01_CF[] = { 0x20, 0x0B }; + if ((t->config == 1) || (t->config == 2)) + i2c_master_send(c, set_GP00_CF, 2); + else + i2c_master_send(c, set_GP01_CF, 2); i2c_master_send(c, set_VS, 2); - i2c_master_send(c, set_GP01_CF, 2); } static void tda8290_init_tuner(struct i2c_client *c) @@ -576,6 +639,7 @@ int tda8290_init(struct i2c_client *c) t->has_signal = has_signal; t->standby = standby; t->tda827x_lpsel = 0; + t->mode = V4L2_TUNER_ANALOG_TV; tda8290_init_tuner(c); tda8290_init_if(c); diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c index 00f0e8b..d110441 100644 --- a/drivers/media/video/tda9875.c +++ b/drivers/media/video/tda9875.c @@ -27,7 +27,6 @@ #include <linux/videodev.h> #include <media/v4l2-common.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include <linux/init.h> diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 7be73e3..505591a 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -144,7 +144,8 @@ static void set_freq(struct i2c_client *c, unsigned long freq) } static void set_type(struct i2c_client *c, unsigned int type, - unsigned int new_mode_mask) + unsigned int new_mode_mask, unsigned int new_config, + int (*tuner_callback) (void *dev, int command,int arg)) { struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; @@ -159,15 +160,20 @@ static void set_type(struct i2c_client *c, unsigned int type, return; } + t->type = type; + t->config = new_config; + if (tuner_callback != NULL) { + tuner_dbg("defining GPIO callback\n"); + t->tuner_callback = tuner_callback; + } + /* This code detects calls by card attach_inform */ if (NULL == t->i2c.dev.driver) { tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr); - t->type=type; return; } - t->type = type; switch (t->type) { case TUNER_MT2032: microtune_init(c); @@ -234,10 +240,11 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) tuner_dbg("set addr for type %i\n", t->type); - if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET && - (t->mode_mask & tun_setup->mode_mask)) || - tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask); + if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { + set_type(c, tun_setup->type, tun_setup->mode_mask, + tun_setup->config, tun_setup->tuner_callback); } } @@ -496,7 +503,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) register_client: tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); i2c_attach_client (&t->i2c); - set_type (&t->i2c,t->type, t->mode_mask); + set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback); return 0; } @@ -576,10 +583,11 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) switch (cmd) { /* --- configuration --- */ case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n", + tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", ((struct tuner_setup *)arg)->type, ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask); + ((struct tuner_setup *)arg)->mode_mask, + ((struct tuner_setup *)arg)->config); set_addr(client, (struct tuner_setup *)arg); break; @@ -804,9 +812,8 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) return 0; } -static int tuner_suspend(struct device *dev, pm_message_t state) +static int tuner_suspend(struct i2c_client *c, pm_message_t state) { - struct i2c_client *c = container_of (dev, struct i2c_client, dev); struct tuner *t = i2c_get_clientdata (c); tuner_dbg ("suspend\n"); @@ -814,9 +821,8 @@ static int tuner_suspend(struct device *dev, pm_message_t state) return 0; } -static int tuner_resume(struct device *dev) +static int tuner_resume(struct i2c_client *c) { - struct i2c_client *c = container_of (dev, struct i2c_client, dev); struct tuner *t = i2c_get_clientdata (c); tuner_dbg ("resume\n"); @@ -837,10 +843,10 @@ static struct i2c_driver driver = { .attach_adapter = tuner_probe, .detach_client = tuner_detach, .command = tuner_command, + .suspend = tuner_suspend, + .resume = tuner_resume, .driver = { .name = "tuner", - .suspend = tuner_suspend, - .resume = tuner_resume, }, }; static struct i2c_client client_template = { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index d506dfa..a2da5d2 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -25,7 +25,6 @@ #include <linux/slab.h> #include <linux/videodev.h> #include <linux/i2c.h> -#include <linux/i2c-algo-bit.h> #include <linux/init.h> #include <linux/smp_lock.h> #include <linux/kthread.h> @@ -33,6 +32,7 @@ #include <media/tvaudio.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/i2c-addr.h> @@ -1775,6 +1775,9 @@ static int chip_command(struct i2c_client *client, /* the thread will call checkmode() later */ } break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); } return 0; } diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index 4e7c1fa..a1136da 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -163,7 +163,7 @@ hauppauge_tuner[] = /* 60-69 */ { TUNER_PHILIPS_FM1216ME_MK3, "LG S001D MK3"}, { TUNER_ABSENT, "LG M001D MK3"}, - { TUNER_ABSENT, "LG S701D MK3"}, + { TUNER_PHILIPS_FM1216ME_MK3, "LG S701D MK3"}, { TUNER_ABSENT, "LG M701D MK3"}, { TUNER_ABSENT, "Temic 4146FM5"}, { TUNER_ABSENT, "Temic 4136FY5"}, @@ -229,6 +229,36 @@ hauppauge_tuner[] = /* 120-129 */ { TUNER_ABSENT, "Xceive XC3028"}, { TUNER_ABSENT, "Philips FQ1216LME MK5"}, + { TUNER_ABSENT, "Philips FQD1216LME"}, + { TUNER_ABSENT, "Conexant CX24118A"}, + { TUNER_ABSENT, "TCL DMF11WIP"}, + { TUNER_ABSENT, "TCL MFNM05_4H_E"}, + { TUNER_ABSENT, "TCL MNM05_4H_E"}, + { TUNER_ABSENT, "TCL MPE05_2H_E"}, + { TUNER_ABSENT, "TCL MQNM05_4_U"}, + { TUNER_ABSENT, "TCL M2523_5NH_E"}, + /* 130-139 */ + { TUNER_ABSENT, "TCL M2523_3DBH_E"}, + { TUNER_ABSENT, "TCL M2523_3DIH_E"}, + { TUNER_ABSENT, "TCL MFPE05_2_U"}, + { TUNER_ABSENT, "Philips FMD1216MEX"}, + { TUNER_ABSENT, "Philips FRH2036B"}, + { TUNER_ABSENT, "Panasonic ENGF75_01GF"}, + { TUNER_ABSENT, "MaxLinear MXL5005"}, + { TUNER_ABSENT, "MaxLinear MXL5003"}, + { TUNER_ABSENT, "Xceive XC2028"}, + { TUNER_ABSENT, "Microtune MT2131"}, + /* 140-149 */ + { TUNER_ABSENT, "Philips 8275A_8295"}, + { TUNER_ABSENT, "TCL MF02GIP_5N_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DB_E"}, + { TUNER_ABSENT, "TCL MF02GIP_3DI_E"}, + { TUNER_ABSENT, "Microtune MT2266"}, + { TUNER_ABSENT, "TCL MF10WPP_4N_E"}, + { TUNER_ABSENT, "LG TAPQ_H702F"}, + { TUNER_ABSENT, "TCL M09WPP_4N_E"}, + { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, + { TUNER_ABSENT, "Philips 18271_8295"}, }; static struct HAUPPAUGE_AUDIOIC @@ -280,11 +310,16 @@ audioIC[] = {AUDIO_CHIP_INTERNAL, "CX883"}, {AUDIO_CHIP_INTERNAL, "CX882"}, {AUDIO_CHIP_INTERNAL, "CX25840"}, - /* 35-38 */ + /* 35-39 */ {AUDIO_CHIP_INTERNAL, "CX25841"}, {AUDIO_CHIP_INTERNAL, "CX25842"}, {AUDIO_CHIP_INTERNAL, "CX25843"}, {AUDIO_CHIP_INTERNAL, "CX23418"}, + {AUDIO_CHIP_INTERNAL, "CX23885"}, + /* 40-42 */ + {AUDIO_CHIP_INTERNAL, "CX23888"}, + {AUDIO_CHIP_INTERNAL, "SAA7131"}, + {AUDIO_CHIP_INTERNAL, "CX23887"}, }; /* This list is supplied by Hauppauge. Thanks! */ @@ -301,8 +336,10 @@ static const char *decoderIC[] = { "CX880", "CX881", "CX883", "SAA7111", "SAA7113", /* 25-29 */ "CX882", "TVP5150A", "CX25840", "CX25841", "CX25842", - /* 30-31 */ - "CX25843", "CX23418", + /* 30-34 */ + "CX25843", "CX23418", "NEC61153", "CX23885", "CX23888", + /* 35-37 */ + "SAA7131", "CX25837", "CX23887" }; static int hasRadioTuner(int tunerType) diff --git a/drivers/media/video/tvmixer.c b/drivers/media/video/tvmixer.c index 7ea9132..3ae5a9c 100644 --- a/drivers/media/video/tvmixer.c +++ b/drivers/media/video/tvmixer.c @@ -212,8 +212,7 @@ static int tvmixer_release(struct inode *inode, struct file *file) return -ENODEV; } - if (client->adapter->owner) - module_put(client->adapter->owner); + module_put(client->adapter->owner); return 0; } diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index bc0a4fc..d5ec05f 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -950,25 +950,19 @@ static int tvp5150_command(struct i2c_client *c, } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_TVP5150) - return -EINVAL; - reg->val = tvp5150_read(c, reg->reg & 0xff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - - if (reg->i2c_id != I2C_DRIVERID_TVP5150) + if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = tvp5150_read(c, reg->reg & 0xff); + else + tvp5150_write(c, reg->reg & 0xff, reg->val & 0xff); break; } #endif diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index fc52201..0b2a961 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -27,6 +27,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/upd64031a.h> // --------------------- read registers functions define ----------------------- @@ -162,31 +163,26 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void * break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_UPD64031A) - return -EINVAL; - reg->val = upd64031a_read(client, reg->reg & 0xff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - u8 addr = reg->reg & 0xff; - u8 val = reg->val & 0xff; - - if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - upd64031a_write(client, addr, val); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = upd64031a_read(client, reg->reg & 0xff); + else + upd64031a_write(client, reg->reg & 0xff, reg->val & 0xff); break; } #endif + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0); + default: break; } diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index c3a7ffe..401bd21 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -26,6 +26,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #include <media/upd64083.h> MODULE_DESCRIPTION("uPD64083 driver"); @@ -139,30 +140,26 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_UPD64083) - return -EINVAL; - reg->val = upd64083_read(client, reg->reg & 0xff); - break; - } - - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - u8 addr = reg->reg & 0xff; - u8 val = reg->val & 0xff; - - if (reg->i2c_id != I2C_DRIVERID_UPD64083) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - upd64083_write(client, addr, val); + if (cmd == VIDIOC_DBG_G_REGISTER) + reg->val = upd64083_read(client, reg->reg & 0xff); + else + upd64083_write(client, reg->reg & 0xff, reg->val & 0xff); break; } #endif + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0); + default: break; } diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c index d34d8c8..687f026 100644 --- a/drivers/media/video/usbvideo/usbvideo.c +++ b/drivers/media/video/usbvideo/usbvideo.c @@ -628,24 +628,21 @@ EXPORT_SYMBOL(usbvideo_HexDump); /* ******************************************************************** */ /* XXX: this piece of crap really wants some error handling.. */ -static void usbvideo_ClientIncModCount(struct uvd *uvd) +static int usbvideo_ClientIncModCount(struct uvd *uvd) { if (uvd == NULL) { err("%s: uvd == NULL", __FUNCTION__); - return; + return -EINVAL; } if (uvd->handle == NULL) { err("%s: uvd->handle == NULL", __FUNCTION__); - return; - } - if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __FUNCTION__); - return; + return -EINVAL; } if (!try_module_get(uvd->handle->md_module)) { err("%s: try_module_get() == 0", __FUNCTION__); - return; + return -ENODEV; } + return 0; } static void usbvideo_ClientDecModCount(struct uvd *uvd) @@ -712,8 +709,6 @@ int usbvideo_register( cams->num_cameras = num_cams; cams->cam = (struct uvd *) &cams[1]; cams->md_module = md; - if (cams->md_module == NULL) - warn("%s: module == NULL!", __FUNCTION__); mutex_init(&cams->lock); /* to 1 == available */ for (i = 0; i < num_cams; i++) { @@ -1119,7 +1114,8 @@ static int usbvideo_v4l_open(struct inode *inode, struct file *file) if (uvd->debug > 1) info("%s($%p)", __FUNCTION__, dev); - usbvideo_ClientIncModCount(uvd); + if (0 < usbvideo_ClientIncModCount(uvd)) + return -ENODEV; mutex_lock(&uvd->lock); if (uvd->user) { diff --git a/drivers/media/video/usbvision/Kconfig b/drivers/media/video/usbvision/Kconfig index fc24ef0..c43a5d8 100644 --- a/drivers/media/video/usbvision/Kconfig +++ b/drivers/media/video/usbvision/Kconfig @@ -1,6 +1,6 @@ config VIDEO_USBVISION tristate "USB video devices based on Nogatech NT1003/1004/1005" - depends on I2C && VIDEO_V4L2 + depends on I2C && VIDEO_V4L2 && USB select VIDEO_TUNER select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO ---help--- diff --git a/drivers/media/video/usbvision/usbvision-cards.c b/drivers/media/video/usbvision/usbvision-cards.c index a40e583..13f69fe 100644 --- a/drivers/media/video/usbvision/usbvision-cards.c +++ b/drivers/media/video/usbvision/usbvision-cards.c @@ -1,6 +1,6 @@ /* - * USBVISION.H - * usbvision header file + * usbvision-cards.c + * usbvision cards definition file * * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> * @@ -28,129 +28,1060 @@ #include <media/v4l2-dev.h> #include <media/tuner.h> #include "usbvision.h" +#include "usbvision-cards.h" /* Supported Devices: A table for usbvision.c*/ struct usbvision_device_data_st usbvision_device_data[] = { - {0xFFF0, 0xFFF0, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Custom Dummy USBVision Device"}, - {0x0A6F, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "Xanboo"}, - {0x050D, 0x0208, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Belkin USBView II"}, - {0x0571, 0x0002, 0, CODEC_SAA7111, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, -1, -1, 7, "echoFX InterView Lite"}, - {0x0573, 0x0003, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "USBGear USBG-V1 resp. HAMA USB"}, - {0x0573, 0x0400, -1, CODEC_SAA7113, 4, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "D-Link V100"}, - {0x0573, 0x2000, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, -1, -1, -1, "X10 USB Camera"}, - {0x0573, 0x2d00, -1, CODEC_SAA7111, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, -1, 3, 7, "Osprey 50"}, - {0x0573, 0x2d01, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Hauppauge USB-Live Model 600"}, - {0x0573, 0x2101, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 2, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Zoran Co. PMD (Nogatech) AV-grabber Manhattan"}, - {0x0573, 0x4100, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Nogatech USB-TV (NTSC) FM"}, - {0x0573, 0x4110, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "PNY USB-TV (NTSC) FM"}, - {0x0573, 0x4450, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "PixelView PlayTv-USB PRO (PAL) FM"}, - {0x0573, 0x4550, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "ZTV ZT-721 2.4GHz USB A/V Receiver"}, - {0x0573, 0x4d00, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, 20, -1, "Hauppauge WinTv-USB USA"}, - {0x0573, 0x4d01, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {0x0573, 0x4d02, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC)"}, - {0x0573, 0x4d03, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (SECAM) "}, - {0x0573, 0x4d10, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (NTSC) FM"}, - {0x0573, 0x4d11, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d12, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB (PAL) FM"}, - {0x0573, 0x4d2a, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B285"}, - {0x0573, 0x4d2b, 0, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (NTSC) FM Model 602 40201 Rev B282"}, - {0x0573, 0x4d2c, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTv USB (PAL/SECAM) 40209 Rev E1A5"}, - {0x0573, 0x4d20, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226"}, - {0x0573, 0x4d21, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL)"}, - {0x0573, 0x4d22, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB II (PAL) MODEL 566"}, - {0x0573, 0x4d23, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) 4D23"}, - {0x0573, 0x4d25, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B234"}, - {0x0573, 0x4d26, -1, CODEC_SAA7113, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB (SECAM) Model 40209 Rev B243"}, - {0x0573, 0x4d27, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B281"}, - {0x0573, 0x4d28, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_ALPS_TSBE1_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40204 Rev B283"}, - {0x0573, 0x4d29, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB Model 40205 Rev B298"}, - {0x0573, 0x4d30, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB FM Model 40211 Rev B123"}, - {0x0573, 0x4d31, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 568"}, - {0x0573, 0x4d32, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 573"}, - {0x0573, 0x4d35, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_MICROTUNE_4049FM5, -1, -1, 0, 3, 7, "Hauppauge WinTv-USB III (PAL) FM Model 40219 Rev B252"}, - {0x0573, 0x4d37, 0, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_FM1216ME_MK3, -1, -1, 0, 3, 7, "Hauppauge WinTV USB device Model 40219 Rev E189"}, - {0x0768, 0x0006, -1, CODEC_SAA7113, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, 5, 5, -1, "Camtel Technology USB TV Genie Pro FM Model TVB330"}, - {0x07d0, 0x0001, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Digital Video Creator I"}, - {0x07d0, 0x0002, -1, CODEC_SAA7111, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 82, 20, 7, "Global Village GV-007 (NTSC)"}, - {0x07d0, 0x0003, 0, CODEC_SAA7113, 2, V4L2_STD_NTSC, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)"}, - {0x07d0, 0x0004, 0, CODEC_SAA7113, 2, V4L2_STD_PAL, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-80 Rev 1 (PAL)"}, - {0x07d0, 0x0005, 0, CODEC_SAA7113, 2, V4L2_STD_SECAM, 0, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)"}, - {0x2304, 0x010d, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 0, 1, TUNER_TEMIC_4066FY5_PAL_I, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (PAL)"}, - {0x2304, 0x0109, -1, CODEC_SAA7111, 3, V4L2_STD_SECAM, 1, 0, 1, 1, TUNER_PHILIPS_SECAM, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (SECAM)"}, - {0x2304, 0x0110, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_PHILIPS_PAL, -1, -1,128, 23, -1, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0111, -1, CODEC_SAA7111, 3, V4L2_STD_PAL, 1, 0, 1, 1, TUNER_PHILIPS_PAL, -1, -1, -1, -1, -1, "Miro PCTV USB"}, - {0x2304, 0x0112, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0210, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0212, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 1, 1, 1, TUNER_TEMIC_4039FR5_NTSC, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (NTSC) FM"}, - {0x2304, 0x0214, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle Studio PCTV USB (PAL) FM"}, - {0x2304, 0x0300, -1, CODEC_SAA7113, 2, V4L2_STD_NTSC, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (NTSC)"}, - {0x2304, 0x0301, -1, CODEC_SAA7113, 2, V4L2_STD_PAL, 1, 0, 1, 0, 0, -1, -1, 0, 3, 7, "Pinnacle Studio Linx Video input cable (PAL)"}, - {0x2304, 0x0419, -1, CODEC_SAA7113, 3, V4L2_STD_PAL, 1, 1, 1, 1, TUNER_TEMIC_4009FR5_PAL, -1, -1, 0, 3, 7, "Pinnacle PCTV Bungee USB (PAL) FM"}, - {0x2400, 0x4200, -1, CODEC_SAA7111, 3, V4L2_STD_NTSC, 1, 0, 1, 1, TUNER_PHILIPS_NTSC_M, -1, -1, -1, -1, -1, "Hauppauge WinTv-USB"}, - {} /* Terminating entry */ + [XANBOO] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Xanboo", + }, + [BELKIN_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II Adapter", + }, + [BELKIN_VIDEOBUS] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Belkin Components USB VideoBus", + }, + [BELKIN_USB_VIDEOBUS_II] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Belkin USB VideoBus II", + }, + [ECHOFX_INTERVIEW_LITE] = { + .Interface = 0, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "echoFX InterView Lite", + }, + [USBGEAR_USBG_V1] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "USBGear USBG-V1 resp. HAMA USB", + }, + [D_LINK_V100] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 4, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "D-Link V100", + }, + [X10_USB_CAMERA] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "X10 USB Camera", + }, + [HPG_WINTV_LIVE_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = -1, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live (PAL B/G)", + }, + [HPG_WINTV_LIVE_PRO_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Live Pro (NTSC M/N)", + }, + [ZORAN_PMD_NOGATECH] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 2, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Zoran Co. PMD (Nogatech) AV-grabber Manhattan", + }, + [NOGATECH_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "Nogatech USB-TV (NTSC) FM", + }, + [PNY_USB_TV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "PNY USB-TV (NTSC) FM", + }, + [PV_PLAYTV_USB_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "PixelView PlayTv-USB PRO (PAL) FM", + }, + [ZT_721] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "ZTV ZT-721 2.4GHz USB A/V Receiver", + }, + [HPG_WINTV_NTSC_MN] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = 20, + .ModelString = "Hauppauge WinTV USB (NTSC M/N)", + }, + [HPG_WINTV_PAL_BG] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G)", + }, + [HPG_WINTV_PAL_I] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL I)", + }, + [HPG_WINTV_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL/SECAM L)", + }, + [HPG_WINTV_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K)", + }, + [HPG_WINTV_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (NTSC FM)", + }, + [HPG_WINTV_PAL_BG_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL B/G FM)", + }, + [HPG_WINTV_PAL_I_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL I FM)", + }, + [HPG_WINTV_PAL_D_K_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTV USB (PAL D/K FM)", + }, + [HPG_WINTV_PRO_NTSC_MN] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N)", + }, + [HPG_WINTV_PRO_NTSC_MN_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V2", + }, + [HPG_WINTV_PRO_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L)", + }, + [HPG_WINTV_PRO_NTSC_MN_V3] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N) V3", + }, + [HPG_WINTV_PRO_PAL_BG] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G)", + }, + [HPG_WINTV_PRO_PAL_I] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I)", + }, + [HPG_WINTV_PRO_PAL_SECAM_L] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM L)", + }, + [HPG_WINTV_PRO_PAL_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K)", + }, + [HPG_WINTV_PRO_PAL_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L)", + }, + [HPG_WINTV_PRO_PAL_SECAM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM BGDK/I/L) V2", + }, + [HPG_WINTV_PRO_PAL_BG_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G) V2", + }, + [HPG_WINTV_PRO_PAL_BG_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_ALPS_TSBE1_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G,D/K)", + }, + [HPG_WINTV_PRO_PAL_I_D_K] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I,D/K)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM)", + }, + [HPG_WINTV_PRO_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_I_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL I FM)", + }, + [HPG_WINTV_PRO_PAL_D_K_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL D/K FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_TEMIC_PAL_BG_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_MICROTUNE_4049FM5, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (Temic PAL B/G FM)", + }, + [HPG_WINTV_PRO_PAL_FM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (PAL/SECAM B/G/I/D/K/L FM)", + }, + [HPG_WINTV_PRO_NTSC_MN_FM_V2] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Hauppauge WinTV USB Pro (NTSC M/N FM) V2", + }, + [CAMTEL_TVB330] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = 5, + .Y_Offset = 5, + .ModelString = "Camtel Technology USB TV Genie Pro FM Model TVB330", + }, + [DIGITAL_VIDEO_CREATOR_I] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Digital Video Creator I", + }, + [GLOBAL_VILLAGE_GV_007_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 82, + .Y_Offset = 20, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Global Village GV-007 (NTSC)", + }, + [DAZZLE_DVC_50_REV_1_NTSC] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-50 Rev 1 (NTSC)", + }, + [DAZZLE_DVC_80_REV_1_PAL] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-80 Rev 1 (PAL)", + }, + [DAZZLE_DVC_90_REV_1_SECAM] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 0, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)", + }, + [ESKAPE_LABS_MYTV2GO] = { + .Interface = 0, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_FM1216ME_MK3, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Eskape Labs MyTV2Go", + }, + [PINNA_PCTV_USB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 0, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4066FY5_PAL_I, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (PAL)", + }, + [PINNA_PCTV_USB_SECAM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_SECAM, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_SECAM, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (SECAM)", + }, + [PINNA_PCTV_USB_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = 128, + .Y_Offset = 23, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM", + }, + [MIRO_PCTV_USB] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_PAL, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Miro PCTV USB", + }, + [PINNA_PCTV_USB_NTSC_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM", + }, + [PINNA_PCTV_USB_PAL_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V2", + }, + [PINNA_PCTV_USB_NTSC_FM_V2] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4039FR5_NTSC, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (NTSC) FM V2", + }, + [PINNA_PCTV_USB_PAL_FM_V3] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio PCTV USB (PAL) FM V3", + }, + [PINNA_LINX_VD_IN_CAB_NTSC] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (NTSC)", + }, + [PINNA_LINX_VD_IN_CAB_PAL] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 2, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 0, + .TunerType = 0, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle Studio Linx Video input cable (PAL)", + }, + [PINNA_PCTV_BUNGEE_PAL_FM] = { + .Interface = -1, + .Codec = CODEC_SAA7113, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_PAL, + .AudioChannels = 1, + .Radio = 1, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_TEMIC_4009FR5_PAL, + .X_Offset = 0, + .Y_Offset = 3, + .Dvi_yuv_override = 1, + .Dvi_yuv = 7, + .ModelString = "Pinnacle PCTV Bungee USB (PAL) FM", + }, + [HPG_WINTV] = { + .Interface = -1, + .Codec = CODEC_SAA7111, + .VideoChannels = 3, + .VideoNorm = V4L2_STD_NTSC, + .AudioChannels = 1, + .Radio = 0, + .vbi = 1, + .Tuner = 1, + .TunerType = TUNER_PHILIPS_NTSC_M, + .X_Offset = -1, + .Y_Offset = -1, + .ModelString = "Hauppauge WinTv-USB", + }, }; +const int usbvision_device_data_size=ARRAY_SIZE(usbvision_device_data); /* Supported Devices */ struct usb_device_id usbvision_table [] = { - { USB_DEVICE(0xFFF0, 0xFFF0) }, /* Custom Dummy USBVision Device */ - { USB_DEVICE(0x0A6F, 0x0400) }, /* Xanboo */ - { USB_DEVICE(0x050d, 0x0208) }, /* Belkin USBView II */ - { USB_DEVICE(0x0571, 0x0002) }, /* echoFX InterView Lite */ - { USB_DEVICE(0x0573, 0x0003) }, /* USBGear USBG-V1 */ - { USB_DEVICE(0x0573, 0x0400) }, /* D-Link V100 */ - { USB_DEVICE(0x0573, 0x2000) }, /* X10 USB Camera */ - { USB_DEVICE(0x0573, 0x2d00) }, /* Osprey 50 */ - { USB_DEVICE(0x0573, 0x2d01) }, /* Hauppauge USB-Live Model 600 */ - { USB_DEVICE(0x0573, 0x2101) }, /* Zoran Co. PMD (Nogatech) AV-grabber Manhattan */ - { USB_DEVICE(0x0573, 0x4100) }, /* Nogatech USB-TV FM (NTSC) */ - { USB_DEVICE(0x0573, 0x4110) }, /* PNY USB-TV (NTSC) FM */ - { USB_DEVICE(0x0573, 0x4450) }, /* PixelView PlayTv-USB PRO (PAL) FM */ - { USB_DEVICE(0x0573, 0x4550) }, /* ZTV ZT-721 2.4GHz USB A/V Receiver */ - { USB_DEVICE(0x0573, 0x4d00) }, /* Hauppauge WinTv-USB USA */ - { USB_DEVICE(0x0573, 0x4d01) }, /* Hauppauge WinTv-USB */ - { USB_DEVICE(0x0573, 0x4d02) }, /* Hauppauge WinTv-USB UK */ - { USB_DEVICE(0x0573, 0x4d03) }, /* Hauppauge WinTv-USB France */ - { USB_DEVICE(0x0573, 0x4d10) }, /* Hauppauge WinTv-USB with FM USA radio */ - { USB_DEVICE(0x0573, 0x4d11) }, /* Hauppauge WinTv-USB (PAL) with FM radio */ - { USB_DEVICE(0x0573, 0x4d12) }, /* Hauppauge WinTv-USB UK with FM Radio */ - { USB_DEVICE(0x0573, 0x4d2a) }, /* Hauppague WinTv USB Model 602 40201 Rev B285 */ - { USB_DEVICE(0x0573, 0x4d2b) }, /* Hauppague WinTv USB Model 602 40201 Rev B282 */ - { USB_DEVICE(0x0573, 0x4d2c) }, /* Hauppague WinTv USB Model 40209 Rev. E1A5 PAL*/ - { USB_DEVICE(0x0573, 0x4d20) }, /* Hauppauge WinTv-USB II (PAL) FM Model 40201 Rev B226 */ - { USB_DEVICE(0x0573, 0x4d21) }, /* Hauppauge WinTv-USB II (PAL) with FM radio*/ - { USB_DEVICE(0x0573, 0x4d22) }, /* Hauppauge WinTv-USB II (PAL) Model 566 */ - { USB_DEVICE(0x0573, 0x4d23) }, /* Hauppauge WinTv-USB France 4D23*/ - { USB_DEVICE(0x0573, 0x4d25) }, /* Hauppauge WinTv-USB Model 40209 rev B234 */ - { USB_DEVICE(0x0573, 0x4d26) }, /* Hauppauge WinTv-USB Model 40209 Rev B243 */ - { USB_DEVICE(0x0573, 0x4d27) }, /* Hauppauge WinTv-USB Model 40204 Rev B281 */ - { USB_DEVICE(0x0573, 0x4d28) }, /* Hauppauge WinTv-USB Model 40204 Rev B283 */ - { USB_DEVICE(0x0573, 0x4d29) }, /* Hauppauge WinTv-USB Model 40205 Rev B298 */ - { USB_DEVICE(0x0573, 0x4d30) }, /* Hauppauge WinTv-USB FM Model 40211 Rev B123 */ - { USB_DEVICE(0x0573, 0x4d31) }, /* Hauppauge WinTv-USB III (PAL) with FM radio Model 568 */ - { USB_DEVICE(0x0573, 0x4d32) }, /* Hauppauge WinTv-USB III (PAL) FM Model 573 */ - { USB_DEVICE(0x0573, 0x4d35) }, /* Hauppauge WinTv-USB III (SECAM) FM Model 40219 Rev B252 */ - { USB_DEVICE(0x0573, 0x4d37) }, /* Hauppauge WinTv-USB Model 40219 Rev E189 */ - { USB_DEVICE(0x0768, 0x0006) }, /* Camtel Technology USB TV Genie Pro FM Model TVB330 */ - { USB_DEVICE(0x07d0, 0x0001) }, /* Digital Video Creator I */ - { USB_DEVICE(0x07d0, 0x0002) }, /* Global Village GV-007 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0003) }, /* Dazzle Fusion Model DVC-50 Rev 1 (NTSC) */ - { USB_DEVICE(0x07d0, 0x0004) }, /* Dazzle Fusion Model DVC-80 Rev 1 (PAL) */ - { USB_DEVICE(0x07d0, 0x0005) }, /* Dazzle Fusion Model DVC-90 Rev 1 (SECAM) */ - { USB_DEVICE(0x2304, 0x010d) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0109) }, /* Pinnacle Studio PCTV USB (SECAM) */ - { USB_DEVICE(0x2304, 0x0110) }, /* Pinnacle Studio PCTV USB (PAL) */ - { USB_DEVICE(0x2304, 0x0111) }, /* Miro PCTV USB */ - { USB_DEVICE(0x2304, 0x0112) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0210) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0212) }, /* Pinnacle Studio PCTV USB (NTSC) with FM radio */ - { USB_DEVICE(0x2304, 0x0214) }, /* Pinnacle Studio PCTV USB (PAL) with FM radio */ - { USB_DEVICE(0x2304, 0x0300) }, /* Pinnacle Studio Linx Video input cable (NTSC) */ - { USB_DEVICE(0x2304, 0x0301) }, /* Pinnacle Studio Linx Video input cable (PAL) */ - { USB_DEVICE(0x2304, 0x0419) }, /* Pinnacle PCTV Bungee USB (PAL) FM */ - { USB_DEVICE(0x2400, 0x4200) }, /* Hauppauge WinTv-USB2 Model 42012 */ - - { } /* Terminating entry */ + { USB_DEVICE(0x0a6f, 0x0400), .driver_info=XANBOO }, + { USB_DEVICE(0x050d, 0x0106), .driver_info=BELKIN_VIDEOBUS_II }, + { USB_DEVICE(0x050d, 0x0207), .driver_info=BELKIN_VIDEOBUS }, + { USB_DEVICE(0x050d, 0x0208), .driver_info=BELKIN_USB_VIDEOBUS_II }, + { USB_DEVICE(0x0571, 0x0002), .driver_info=ECHOFX_INTERVIEW_LITE }, + { USB_DEVICE(0x0573, 0x0003), .driver_info=USBGEAR_USBG_V1 }, + { USB_DEVICE(0x0573, 0x0400), .driver_info=D_LINK_V100 }, + { USB_DEVICE(0x0573, 0x2000), .driver_info=X10_USB_CAMERA }, + { USB_DEVICE(0x0573, 0x2d00), .driver_info=HPG_WINTV_LIVE_PAL_BG }, + { USB_DEVICE(0x0573, 0x2d01), .driver_info=HPG_WINTV_LIVE_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x2101), .driver_info=ZORAN_PMD_NOGATECH }, + { USB_DEVICE(0x0573, 0x4100), .driver_info=NOGATECH_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4110), .driver_info=PNY_USB_TV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4450), .driver_info=PV_PLAYTV_USB_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4550), .driver_info=ZT_721 }, + { USB_DEVICE(0x0573, 0x4d00), .driver_info=HPG_WINTV_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d01), .driver_info=HPG_WINTV_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d02), .driver_info=HPG_WINTV_PAL_I }, + { USB_DEVICE(0x0573, 0x4d03), .driver_info=HPG_WINTV_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d04), .driver_info=HPG_WINTV_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d10), .driver_info=HPG_WINTV_NTSC_FM }, + { USB_DEVICE(0x0573, 0x4d11), .driver_info=HPG_WINTV_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d12), .driver_info=HPG_WINTV_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d14), .driver_info=HPG_WINTV_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d2a), .driver_info=HPG_WINTV_PRO_NTSC_MN }, + { USB_DEVICE(0x0573, 0x4d2b), .driver_info=HPG_WINTV_PRO_NTSC_MN_V2 }, + { USB_DEVICE(0x0573, 0x4d2c), .driver_info=HPG_WINTV_PRO_PAL }, + { USB_DEVICE(0x0573, 0x4d20), .driver_info=HPG_WINTV_PRO_NTSC_MN_V3 }, + { USB_DEVICE(0x0573, 0x4d21), .driver_info=HPG_WINTV_PRO_PAL_BG }, + { USB_DEVICE(0x0573, 0x4d22), .driver_info=HPG_WINTV_PRO_PAL_I }, + { USB_DEVICE(0x0573, 0x4d23), .driver_info=HPG_WINTV_PRO_PAL_SECAM_L }, + { USB_DEVICE(0x0573, 0x4d24), .driver_info=HPG_WINTV_PRO_PAL_D_K }, + { USB_DEVICE(0x0573, 0x4d25), .driver_info=HPG_WINTV_PRO_PAL_SECAM }, + { USB_DEVICE(0x0573, 0x4d26), .driver_info=HPG_WINTV_PRO_PAL_SECAM_V2 }, + { USB_DEVICE(0x0573, 0x4d27), .driver_info=HPG_WINTV_PRO_PAL_BG_V2 }, + { USB_DEVICE(0x0573, 0x4d28), .driver_info=HPG_WINTV_PRO_PAL_BG_D_K }, + { USB_DEVICE(0x0573, 0x4d29), .driver_info=HPG_WINTV_PRO_PAL_I_D_K }, + { USB_DEVICE(0x0573, 0x4d30), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM }, + { USB_DEVICE(0x0573, 0x4d31), .driver_info=HPG_WINTV_PRO_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d32), .driver_info=HPG_WINTV_PRO_PAL_I_FM }, + { USB_DEVICE(0x0573, 0x4d34), .driver_info=HPG_WINTV_PRO_PAL_D_K_FM }, + { USB_DEVICE(0x0573, 0x4d35), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d36), .driver_info=HPG_WINTV_PRO_TEMIC_PAL_BG_FM }, + { USB_DEVICE(0x0573, 0x4d37), .driver_info=HPG_WINTV_PRO_PAL_FM }, + { USB_DEVICE(0x0573, 0x4d38), .driver_info=HPG_WINTV_PRO_NTSC_MN_FM_V2 }, + { USB_DEVICE(0x0768, 0x0006), .driver_info=CAMTEL_TVB330 }, + { USB_DEVICE(0x07d0, 0x0001), .driver_info=DIGITAL_VIDEO_CREATOR_I }, + { USB_DEVICE(0x07d0, 0x0002), .driver_info=GLOBAL_VILLAGE_GV_007_NTSC }, + { USB_DEVICE(0x07d0, 0x0003), .driver_info=DAZZLE_DVC_50_REV_1_NTSC }, + { USB_DEVICE(0x07d0, 0x0004), .driver_info=DAZZLE_DVC_80_REV_1_PAL }, + { USB_DEVICE(0x07d0, 0x0005), .driver_info=DAZZLE_DVC_90_REV_1_SECAM }, + { USB_DEVICE(0x07f8, 0x9104), .driver_info=ESKAPE_LABS_MYTV2GO }, + { USB_DEVICE(0x2304, 0x010d), .driver_info=PINNA_PCTV_USB_PAL }, + { USB_DEVICE(0x2304, 0x0109), .driver_info=PINNA_PCTV_USB_SECAM }, + { USB_DEVICE(0x2304, 0x0110), .driver_info=PINNA_PCTV_USB_PAL_FM }, + { USB_DEVICE(0x2304, 0x0111), .driver_info=MIRO_PCTV_USB }, + { USB_DEVICE(0x2304, 0x0112), .driver_info=PINNA_PCTV_USB_NTSC_FM }, + { USB_DEVICE(0x2304, 0x0210), .driver_info=PINNA_PCTV_USB_PAL_FM_V2 }, + { USB_DEVICE(0x2304, 0x0212), .driver_info=PINNA_PCTV_USB_NTSC_FM_V2 }, + { USB_DEVICE(0x2304, 0x0214), .driver_info=PINNA_PCTV_USB_PAL_FM_V3 }, + { USB_DEVICE(0x2304, 0x0300), .driver_info=PINNA_LINX_VD_IN_CAB_NTSC }, + { USB_DEVICE(0x2304, 0x0301), .driver_info=PINNA_LINX_VD_IN_CAB_PAL }, + { USB_DEVICE(0x2304, 0x0419), .driver_info=PINNA_PCTV_BUNGEE_PAL_FM }, + { USB_DEVICE(0x2400, 0x4200), .driver_info=HPG_WINTV }, }; MODULE_DEVICE_TABLE (usb, usbvision_table); diff --git a/drivers/media/video/usbvision/usbvision-cards.h b/drivers/media/video/usbvision/usbvision-cards.h new file mode 100644 index 0000000..512c5ce --- /dev/null +++ b/drivers/media/video/usbvision/usbvision-cards.h @@ -0,0 +1,66 @@ +#define XANBOO 0 +#define BELKIN_VIDEOBUS_II 1 +#define BELKIN_VIDEOBUS 2 +#define BELKIN_USB_VIDEOBUS_II 3 +#define ECHOFX_INTERVIEW_LITE 4 +#define USBGEAR_USBG_V1 5 +#define D_LINK_V100 6 +#define X10_USB_CAMERA 7 +#define HPG_WINTV_LIVE_PAL_BG 8 +#define HPG_WINTV_LIVE_PRO_NTSC_MN 9 +#define ZORAN_PMD_NOGATECH 10 +#define NOGATECH_USB_TV_NTSC_FM 11 +#define PNY_USB_TV_NTSC_FM 12 +#define PV_PLAYTV_USB_PRO_PAL_FM 13 +#define ZT_721 14 +#define HPG_WINTV_NTSC_MN 15 +#define HPG_WINTV_PAL_BG 16 +#define HPG_WINTV_PAL_I 17 +#define HPG_WINTV_PAL_SECAM_L 18 +#define HPG_WINTV_PAL_D_K 19 +#define HPG_WINTV_NTSC_FM 20 +#define HPG_WINTV_PAL_BG_FM 21 +#define HPG_WINTV_PAL_I_FM 22 +#define HPG_WINTV_PAL_D_K_FM 23 +#define HPG_WINTV_PRO_NTSC_MN 24 +#define HPG_WINTV_PRO_NTSC_MN_V2 25 +#define HPG_WINTV_PRO_PAL 26 +#define HPG_WINTV_PRO_NTSC_MN_V3 27 +#define HPG_WINTV_PRO_PAL_BG 28 +#define HPG_WINTV_PRO_PAL_I 29 +#define HPG_WINTV_PRO_PAL_SECAM_L 30 +#define HPG_WINTV_PRO_PAL_D_K 31 +#define HPG_WINTV_PRO_PAL_SECAM 32 +#define HPG_WINTV_PRO_PAL_SECAM_V2 33 +#define HPG_WINTV_PRO_PAL_BG_V2 34 +#define HPG_WINTV_PRO_PAL_BG_D_K 35 +#define HPG_WINTV_PRO_PAL_I_D_K 36 +#define HPG_WINTV_PRO_NTSC_MN_FM 37 +#define HPG_WINTV_PRO_PAL_BG_FM 38 +#define HPG_WINTV_PRO_PAL_I_FM 39 +#define HPG_WINTV_PRO_PAL_D_K_FM 40 +#define HPG_WINTV_PRO_TEMIC_PAL_FM 41 +#define HPG_WINTV_PRO_TEMIC_PAL_BG_FM 42 +#define HPG_WINTV_PRO_PAL_FM 43 +#define HPG_WINTV_PRO_NTSC_MN_FM_V2 44 +#define CAMTEL_TVB330 45 +#define DIGITAL_VIDEO_CREATOR_I 46 +#define GLOBAL_VILLAGE_GV_007_NTSC 47 +#define DAZZLE_DVC_50_REV_1_NTSC 48 +#define DAZZLE_DVC_80_REV_1_PAL 49 +#define DAZZLE_DVC_90_REV_1_SECAM 50 +#define ESKAPE_LABS_MYTV2GO 51 +#define PINNA_PCTV_USB_PAL 52 +#define PINNA_PCTV_USB_SECAM 53 +#define PINNA_PCTV_USB_PAL_FM 54 +#define MIRO_PCTV_USB 55 +#define PINNA_PCTV_USB_NTSC_FM 56 +#define PINNA_PCTV_USB_PAL_FM_V2 57 +#define PINNA_PCTV_USB_NTSC_FM_V2 58 +#define PINNA_PCTV_USB_PAL_FM_V3 59 +#define PINNA_LINX_VD_IN_CAB_NTSC 60 +#define PINNA_LINX_VD_IN_CAB_PAL 61 +#define PINNA_PCTV_BUNGEE_PAL_FM 62 +#define HPG_WINTV 63 + +extern const int usbvision_device_data_size; diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 901f664..bcb551a 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -138,7 +138,7 @@ static void *usbvision_rvmalloc(unsigned long size) return mem; } -void usbvision_rvfree(void *mem, unsigned long size) +static void usbvision_rvfree(void *mem, unsigned long size) { unsigned long adr; @@ -1852,28 +1852,33 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width, /* * usbvision_frames_alloc - * allocate the maximum frames this driver can manage + * allocate the required frames */ -int usbvision_frames_alloc(struct usb_usbvision *usbvision) +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames) { int i; - /* Allocate memory for the frame buffers */ - usbvision->max_frame_size = MAX_FRAME_SIZE; - usbvision->fbuf_size = USBVISION_NUMFRAMES * usbvision->max_frame_size; - usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size); + /*needs to be page aligned cause the buffers can be mapped individually! */ + usbvision->max_frame_size = PAGE_ALIGN(usbvision->curwidth * + usbvision->curheight * + usbvision->palette.bytes_per_pixel); - if(usbvision->fbuf == NULL) { - err("%s: unable to allocate %d bytes for fbuf ", - __FUNCTION__, usbvision->fbuf_size); - return -ENOMEM; + /* Try to do my best to allocate the frames the user want in the remaining memory */ + usbvision->num_frames = number_of_frames; + while (usbvision->num_frames > 0) { + usbvision->fbuf_size = usbvision->num_frames * usbvision->max_frame_size; + if((usbvision->fbuf = usbvision_rvmalloc(usbvision->fbuf_size))) { + break; + } + usbvision->num_frames--; } + spin_lock_init(&usbvision->queue_lock); init_waitqueue_head(&usbvision->wait_frame); init_waitqueue_head(&usbvision->wait_stream); /* Allocate all buffers */ - for (i = 0; i < USBVISION_NUMFRAMES; i++) { + for (i = 0; i < usbvision->num_frames; i++) { usbvision->frame[i].index = i; usbvision->frame[i].grabstate = FrameState_Unused; usbvision->frame[i].data = usbvision->fbuf + @@ -1887,7 +1892,8 @@ int usbvision_frames_alloc(struct usb_usbvision *usbvision) usbvision->frame[i].height = usbvision->curheight; usbvision->frame[i].bytes_read = 0; } - return 0; + PDEBUG(DBG_FUNC, "allocated %d frames (%d bytes per frame)",usbvision->num_frames,usbvision->max_frame_size); + return usbvision->num_frames; } /* @@ -1897,9 +1903,13 @@ int usbvision_frames_alloc(struct usb_usbvision *usbvision) void usbvision_frames_free(struct usb_usbvision *usbvision) { /* Have to free all that memory */ + PDEBUG(DBG_FUNC, "free %d frames",usbvision->num_frames); + if (usbvision->fbuf != NULL) { usbvision_rvfree(usbvision->fbuf, usbvision->fbuf_size); usbvision->fbuf = NULL; + + usbvision->num_frames = 0; } } /* @@ -2030,8 +2040,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision) return 0; /* Set input format expected from decoder*/ - if (usbvision_device_data[usbvision->DevModel].Vin_Reg1 >= 0) { - value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1 & 0xff; + if (usbvision_device_data[usbvision->DevModel].Vin_Reg1_override) { + value[0] = usbvision_device_data[usbvision->DevModel].Vin_Reg1; } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { /* SAA7113 uses 8 bit output */ value[0] = USBVISION_8_422_SYNC; @@ -2102,8 +2112,8 @@ int usbvision_set_input(struct usb_usbvision *usbvision) dvi_yuv_value = 0x00; /* U comes after V, Ya comes after U/V, Yb comes after Yb */ - if(usbvision_device_data[usbvision->DevModel].Dvi_yuv >= 0){ - dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv & 0xff; + if(usbvision_device_data[usbvision->DevModel].Dvi_yuv_override){ + dvi_yuv_value = usbvision_device_data[usbvision->DevModel].Dvi_yuv; } else if(usbvision_device_data[usbvision->DevModel].Codec == CODEC_SAA7113) { /* This changes as the fine sync control changes. Further investigation necessary */ @@ -2228,7 +2238,7 @@ static void call_usbvision_power_off(struct work_struct *work) PDEBUG(DBG_FUNC, ""); down_interruptible(&usbvision->lock); if(usbvision->user == 0) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; @@ -2351,6 +2361,32 @@ int usbvision_setup(struct usb_usbvision *usbvision,int format) return USBVISION_IS_OPERATIONAL(usbvision); } +int usbvision_set_alternate(struct usb_usbvision *dev) +{ + int errCode, prev_alt = dev->ifaceAlt; + int i; + + dev->ifaceAlt=0; + for(i=0;i< dev->num_alt; i++) + if(dev->alt_max_pkt_size[i]>dev->alt_max_pkt_size[dev->ifaceAlt]) + dev->ifaceAlt=i; + + if (dev->ifaceAlt != prev_alt) { + dev->isocPacketSize = dev->alt_max_pkt_size[dev->ifaceAlt]; + PDEBUG(DBG_FUNC,"setting alternate %d with wMaxPacketSize=%u", dev->ifaceAlt,dev->isocPacketSize); + errCode = usb_set_interface(dev->dev, dev->iface, dev->ifaceAlt); + if (errCode < 0) { + err ("cannot change alternate number to %d (error=%i)", + dev->ifaceAlt, errCode); + return errCode; + } + } + + PDEBUG(DBG_ISOC, "ISO Packet Length:%d", dev->isocPacketSize); + + return 0; +} + /* * usbvision_init_isoc() * @@ -2368,15 +2404,13 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) scratch_reset(usbvision); /* Alternate interface 1 is is the biggest frame size */ - errCode = usb_set_interface(dev, usbvision->iface, usbvision->ifaceAltActive); + errCode = usbvision_set_alternate(usbvision); if (errCode < 0) { usbvision->last_error = errCode; return -EBUSY; } regValue = (16 - usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F; - usbvision->isocPacketSize = (regValue == 0) ? 0 : (regValue * 64) - 1; - PDEBUG(DBG_ISOC, "ISO Packet Length:%d", usbvision->isocPacketSize); usbvision->usb_bandwidth = regValue >> 1; PDEBUG(DBG_ISOC, "USB Bandwidth Usage: %dMbit/Sec", usbvision->usb_bandwidth); @@ -2462,8 +2496,9 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision) if (!usbvision->remove_pending) { /* Set packet size to 0 */ + usbvision->ifaceAlt=0; errCode = usb_set_interface(usbvision->dev, usbvision->iface, - usbvision->ifaceAltInactive); + usbvision->ifaceAlt); if (errCode < 0) { err("%s: usb_set_interface() failed: error %d", __FUNCTION__, errCode); usbvision->last_error = errCode; @@ -2490,6 +2525,7 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) RESTRICT_TO_RANGE(channel, 0, usbvision->video_inputs); usbvision->ctl_input = channel; route.input = SAA7115_COMPOSITE1; + route.output = 0; call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route); call_i2c_clients(usbvision, VIDIOC_S_INPUT, &usbvision->ctl_input); diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c index 609e1fd..025be55 100644 --- a/drivers/media/video/usbvision/usbvision-i2c.c +++ b/drivers/media/video/usbvision/usbvision-i2c.c @@ -1,8 +1,8 @@ /* - * I2C_ALGO_USB.C + * usbvision_i2c.c * i2c algorithm for USB-I2C Bridges * - * Copyright (c) 1999-2005 Joerg Heckenbach <joerg@heckenbach-aw.de> + * Copyright (c) 1999-2007 Joerg Heckenbach <joerg@heckenbach-aw.de> * Dwaine Garden <dwainegarden@rogers.com> * * This module is part of usbvision driver project. @@ -39,7 +39,6 @@ #include "usbvision.h" #define DBG_I2C 1<<0 -#define DBG_ALGO 1<<1 static int i2c_debug = 0; @@ -49,22 +48,22 @@ MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); #define PDEBUG(level, fmt, args...) \ if (i2c_debug & (level)) info("[%s:%d] " fmt, __PRETTY_FUNCTION__, __LINE__ , ## args) -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len); -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len); static inline int try_write_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { - void *data; + struct usb_usbvision *usbvision; int i, ret = -1; char buf[4]; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); buf[0] = 0x00; for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_write(data, addr, buf, 1)); + ret = (usbvision_i2c_write(usbvision, addr, buf, 1)); if (ret == 1) break; /* success! */ udelay(5); @@ -73,8 +72,8 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap, udelay(10); } if (i) { - PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); + PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C,"Maybe there's no device at this address"); } return ret; } @@ -82,13 +81,13 @@ static inline int try_write_address(struct i2c_adapter *i2c_adap, static inline int try_read_address(struct i2c_adapter *i2c_adap, unsigned char addr, int retries) { - void *data; + struct usb_usbvision *usbvision; int i, ret = -1; char buf[4]; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); for (i = 0; i <= retries; i++) { - ret = (usbvision_i2c_read(data, addr, buf, 1)); + ret = (usbvision_i2c_read(usbvision, addr, buf, 1)); if (ret == 1) break; /* success! */ udelay(5); @@ -97,8 +96,8 @@ static inline int try_read_address(struct i2c_adapter *i2c_adap, udelay(10); } if (i) { - PDEBUG(DBG_ALGO,"Needed %d retries for address %#2x", i, addr); - PDEBUG(DBG_ALGO,"Maybe there's no device at this address"); + PDEBUG(DBG_I2C,"Needed %d retries for address %#2x", i, addr); + PDEBUG(DBG_I2C,"Maybe there's no device at this address"); } return ret; } @@ -152,32 +151,32 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap, } static int -usb_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +usbvision_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) { struct i2c_msg *pmsg; - void *data; + struct usb_usbvision *usbvision; int i, ret; unsigned char addr; - data = i2c_get_adapdata(i2c_adap); + usbvision = (struct usb_usbvision *)i2c_get_adapdata(i2c_adap); for (i = 0; i < num; i++) { pmsg = &msgs[i]; ret = usb_find_address(i2c_adap, pmsg, i2c_adap->retries, &addr); if (ret != 0) { - PDEBUG(DBG_ALGO,"got NAK from device, message #%d", i); + PDEBUG(DBG_I2C,"got NAK from device, message #%d", i); return (ret < 0) ? ret : -EREMOTEIO; } if (pmsg->flags & I2C_M_RD) { /* read bytes into buffer */ - ret = (usbvision_i2c_read(data, addr, pmsg->buf, pmsg->len)); + ret = (usbvision_i2c_read(usbvision, addr, pmsg->buf, pmsg->len)); if (ret < pmsg->len) { return (ret < 0) ? ret : -EREMOTEIO; } } else { /* write bytes from buffer */ - ret = (usbvision_i2c_write(data, addr, pmsg->buf, pmsg->len)); + ret = (usbvision_i2c_write(usbvision, addr, pmsg->buf, pmsg->len)); if (ret < pmsg->len) { return (ret < 0) ? ret : -EREMOTEIO; } @@ -191,7 +190,7 @@ static int algo_control(struct i2c_adapter *adapter, unsigned int cmd, unsigned return 0; } -static u32 usb_func(struct i2c_adapter *adap) +static u32 functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | I2C_FUNC_PROTOCOL_MANGLING; } @@ -199,11 +198,11 @@ static u32 usb_func(struct i2c_adapter *adap) /* -----exported algorithm data: ------------------------------------- */ -static struct i2c_algorithm i2c_usb_algo = { - .master_xfer = usb_xfer, +static struct i2c_algorithm usbvision_algo = { + .master_xfer = usbvision_i2c_xfer, .smbus_xfer = NULL, .algo_control = algo_control, - .functionality = usb_func, + .functionality = functionality, }; @@ -213,41 +212,29 @@ static struct i2c_algorithm i2c_usb_algo = { static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap) { PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]"); - PDEBUG(DBG_ALGO, "ALGO debugging is enabled [i2c]"); + PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]"); /* register new adapter to i2c module... */ - adap->algo = &i2c_usb_algo; + adap->algo = &usbvision_algo; adap->timeout = 100; /* default values, should */ adap->retries = 3; /* be replaced by defines */ i2c_add_adapter(adap); - PDEBUG(DBG_ALGO,"i2c bus for %s registered", adap->name); - - return 0; -} - - -int usbvision_i2c_usb_del_bus(struct i2c_adapter *adap) -{ - - i2c_del_adapter(adap); - - PDEBUG(DBG_ALGO,"i2c bus for %s unregistered", adap->name); + PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name); return 0; } - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ static struct i2c_adapter i2c_adap_template; static struct i2c_client i2c_client_template; -int usbvision_init_i2c(struct usb_usbvision *usbvision) +int usbvision_i2c_register(struct usb_usbvision *usbvision) { memcpy(&usbvision->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); @@ -265,7 +252,7 @@ int usbvision_init_i2c(struct usb_usbvision *usbvision) usbvision->i2c_client.adapter = &usbvision->i2c_adap; if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) { - printk(KERN_ERR "usbvision_init_i2c: can't write reg\n"); + printk(KERN_ERR "usbvision_register: can't write reg\n"); return -EBUSY; } @@ -287,6 +274,16 @@ int usbvision_init_i2c(struct usb_usbvision *usbvision) return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap); } +int usbvision_i2c_unregister(struct usb_usbvision *usbvision) +{ + + i2c_del_adapter(&(usbvision->i2c_adap)); + + PDEBUG(DBG_I2C,"i2c bus for %s unregistered", usbvision->i2c_adap.name); + + return 0; +} + void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd, void *arg) { @@ -300,19 +297,12 @@ static int attach_inform(struct i2c_client *client) usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter); switch (client->addr << 1) { - case 0x43: - case 0x4b: - { - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup); - + case 0x42 << 1: + case 0x43 << 1: + case 0x4a << 1: + case 0x4b << 1: + PDEBUG(DBG_I2C,"attach_inform: tda9887 detected."); break; - } case 0x42: PDEBUG(DBG_I2C,"attach_inform: saa7114 detected."); break; @@ -480,7 +470,7 @@ static int usbvision_i2c_write_max4(struct usb_usbvision *usbvision, return len; } -static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_write(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) { char *bufPtr = buf; @@ -488,7 +478,6 @@ static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, int wrcount = 0; int count; int maxLen = 4; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; while (len > 0) { count = (len > maxLen) ? maxLen : len; @@ -503,14 +492,13 @@ static int usbvision_i2c_write(void *data, unsigned char addr, char *buf, return wrcount; } -static int usbvision_i2c_read(void *data, unsigned char addr, char *buf, +static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char addr, char *buf, short len) { char temp[4]; int retval, i; int rdcount = 0; int count; - struct usb_usbvision *usbvision = (struct usb_usbvision *) data; while (len > 0) { count = (len > 3) ? 4 : len; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index af33653..2167041 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -76,6 +76,7 @@ #endif #include "usbvision.h" +#include "usbvision-cards.h" #define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>" #define DRIVER_NAME "usbvision" @@ -150,7 +151,6 @@ static int PowerOnAtOpen = 1; // Set the default device to power on at startu static int video_nr = -1; // Sequential Number of Video Device static int radio_nr = -1; // Sequential Number of Radio Device static int vbi_nr = -1; // Sequential Number of VBI Device -static char *CustomDevice=NULL; // Set as nothing.... // Grab parameters for the device driver @@ -161,7 +161,6 @@ module_param(PowerOnAtOpen, int, 0444); module_param(video_nr, int, 0444); module_param(radio_nr, int, 0444); module_param(vbi_nr, int, 0444); -module_param(CustomDevice, charp, 0444); #else // Old Style MODULE_PARAM(isocMode, "i"); MODULE_PARM(video_debug, "i"); // Grab the Debug Mode of the device driver @@ -171,7 +170,6 @@ MODULE_PARM(SwitchSVideoInput, "i"); // To help people with Black and White ou MODULE_PARM(video_nr, "i"); // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...) MODULE_PARM(radio_nr, "i"); // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...) MODULE_PARM(vbi_nr, "i"); // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...) -MODULE_PARM(CustomDevice, "s"); // .... CustomDevice #endif MODULE_PARM_DESC(isocMode, " Set the default format for ISOC endpoint. Default: 0x60 (Compression On)"); @@ -180,7 +178,6 @@ MODULE_PARM_DESC(PowerOnAtOpen, " Set the default device to power on when device MODULE_PARM_DESC(video_nr, "Set video device number (/dev/videoX). Default: -1 (autodetect)"); MODULE_PARM_DESC(radio_nr, "Set radio device number (/dev/radioX). Default: -1 (autodetect)"); MODULE_PARM_DESC(vbi_nr, "Set vbi device number (/dev/vbiX). Default: -1 (autodetect)"); -MODULE_PARM_DESC(CustomDevice, " Define the fine tuning parameters for the device. Default: null"); // Misc stuff @@ -230,7 +227,7 @@ static ssize_t show_hue(struct class_device *cd, char *buf) ctrl.value = 0; if(usbvision->user) call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); - return sprintf(buf, "%d\n", ctrl.value >> 8); + return sprintf(buf, "%d\n", ctrl.value); } static CLASS_DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL); @@ -243,7 +240,7 @@ static ssize_t show_contrast(struct class_device *cd, char *buf) ctrl.value = 0; if(usbvision->user) call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); - return sprintf(buf, "%d\n", ctrl.value >> 8); + return sprintf(buf, "%d\n", ctrl.value); } static CLASS_DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL); @@ -256,7 +253,7 @@ static ssize_t show_brightness(struct class_device *cd, char *buf) ctrl.value = 0; if(usbvision->user) call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); - return sprintf(buf, "%d\n", ctrl.value >> 8); + return sprintf(buf, "%d\n", ctrl.value); } static CLASS_DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL); @@ -269,7 +266,7 @@ static ssize_t show_saturation(struct class_device *cd, char *buf) ctrl.value = 0; if(usbvision->user) call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl); - return sprintf(buf, "%d\n", ctrl.value >> 8); + return sprintf(buf, "%d\n", ctrl.value); } static CLASS_DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL); @@ -391,19 +388,14 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) if (usbvision->user) errCode = -EBUSY; else { - /* Allocate memory for the frame buffers */ - errCode = usbvision_frames_alloc(usbvision); - if(!errCode) { - /* Allocate memory for the scratch ring buffer */ - errCode = usbvision_scratch_alloc(usbvision); - if ((!errCode) && (isocMode==ISOC_MODE_COMPRESS)) { - /* Allocate intermediate decompression buffers only if needed */ - errCode = usbvision_decompress_alloc(usbvision); - } + /* Allocate memory for the scratch ring buffer */ + errCode = usbvision_scratch_alloc(usbvision); + if (isocMode==ISOC_MODE_COMPRESS) { + /* Allocate intermediate decompression buffers only if needed */ + errCode = usbvision_decompress_alloc(usbvision); } if (errCode) { /* Deallocate all buffers if trouble */ - usbvision_frames_free(usbvision); usbvision_scratch_free(usbvision); usbvision_decompress_free(usbvision); } @@ -414,7 +406,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) down(&usbvision->lock); if (usbvision->power == 0) { usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } /* Send init sequence only once, it's large! */ @@ -436,7 +428,7 @@ static int usbvision_v4l2_open(struct inode *inode, struct file *file) } else { if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; } @@ -476,6 +468,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) usbvision_decompress_free(usbvision); usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); usbvision_scratch_free(usbvision); usbvision->user--; @@ -489,7 +482,7 @@ static int usbvision_v4l2_close(struct inode *inode, struct file *file) up(&usbvision->lock); if (usbvision->remove_pending) { - info("%s: Final disconnect", __FUNCTION__); + printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__); usbvision_release(usbvision); } @@ -519,44 +512,32 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, #ifdef CONFIG_VIDEO_ADV_DEBUG /* ioctls to allow direct acces to the NT100x registers */ - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; int errCode; - if (reg->i2c_id != 0) - return -EINVAL; - /* NT100x has a 8-bit register space */ - errCode = usbvision_read_reg(usbvision, reg->reg&0xff); - if (errCode < 0) { - err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode); - } - else { - reg->val=(unsigned char)errCode; - PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X", - (unsigned int)reg->reg, reg->val); - errCode = 0; // No error - } - return errCode; - } - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *reg = arg; - int errCode; - - if (reg->i2c_id != 0) + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); + /* NT100x has a 8-bit register space */ + if (cmd == VIDIOC_DBG_G_REGISTER) + errCode = usbvision_read_reg(usbvision, reg->reg&0xff); + else + errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); if (errCode < 0) { - err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode); - } - else { - PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X", - (unsigned int)reg->reg, reg->val); - errCode = 0; + err("%s: VIDIOC_DBG_%c_REGISTER failed: error %d", __FUNCTION__, + cmd == VIDIOC_DBG_G_REGISTER ? 'G' : 'S', errCode); + return errCode; } + if (cmd == VIDIOC_DBG_S_REGISTER) + reg->val = (u8)errCode; + + PDEBUG(DBG_IOCTL, "VIDIOC_DBG_%c_REGISTER reg=0x%02X, value=0x%02X", + cmd == VIDIOC_DBG_G_REGISTER ? 'G' : 'S', + (unsigned int)reg->reg, (unsigned int)reg->val); return 0; } #endif @@ -792,8 +773,8 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_CTRL: { struct v4l2_control *ctrl = arg; - PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl); + PDEBUG(DBG_IOCTL,"VIDIOC_G_CTRL id=%x value=%x",ctrl->id,ctrl->value); return 0; } case VIDIOC_S_CTRL: @@ -821,7 +802,9 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, return ret; } + usbvision_frames_free(usbvision); usbvision_empty_framequeues(usbvision); + vr->count = usbvision_frames_alloc(usbvision,vr->count); usbvision->curFrame = NULL; @@ -838,7 +821,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { return -EINVAL; } - if(vb->index>=USBVISION_NUMFRAMES) { + if(vb->index>=usbvision->num_frames) { return -EINVAL; } // Updating the corresponding frame state @@ -852,7 +835,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, vb->flags |= V4L2_BUF_FLAG_MAPPED; vb->memory = V4L2_MEMORY_MMAP; - vb->m.offset = vb->index*usbvision->max_frame_size; + vb->m.offset = vb->index*PAGE_ALIGN(usbvision->max_frame_size); vb->memory = V4L2_MEMORY_MMAP; vb->field = V4L2_FIELD_NONE; @@ -871,7 +854,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { return -EINVAL; } - if(vb->index>=USBVISION_NUMFRAMES) { + if(vb->index>=usbvision->num_frames) { return -EINVAL; } @@ -1041,6 +1024,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, if ((ret = usbvision_stream_interrupt(usbvision))) return ret; } + usbvision_frames_free(usbvision); usbvision_empty_framequeues(usbvision); usbvision->curFrame = NULL; @@ -1087,12 +1071,24 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf, if (!USBVISION_IS_OPERATIONAL(usbvision) || (buf == NULL)) return -EFAULT; - /* no stream is running, make it running ! */ - usbvision->streaming = Stream_On; - call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); + /* This entry point is compatible with the mmap routines so that a user can do either + VIDIOC_QBUF/VIDIOC_DQBUF to get frames or call read on the device. */ + if(!usbvision->num_frames) { + /* First, allocate some frames to work with if this has not been done with + VIDIOC_REQBUF */ + usbvision_frames_free(usbvision); + usbvision_empty_framequeues(usbvision); + usbvision_frames_alloc(usbvision,USBVISION_NUMFRAMES); + } - /* First, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ - for(i=0;i<USBVISION_NUMFRAMES;i++) { + if(usbvision->streaming != Stream_On) { + /* no stream is running, make it running ! */ + usbvision->streaming = Stream_On; + call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL); + } + + /* Then, enqueue as many frames as possible (like a user of VIDIOC_QBUF would do) */ + for(i=0;i<usbvision->num_frames;i++) { frame = &usbvision->frame[i]; if(frame->grabstate == FrameState_Unused) { /* Mark it as ready and enqueue frame */ @@ -1169,6 +1165,8 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) struct video_device *dev = video_devdata(file); struct usb_usbvision *usbvision = (struct usb_usbvision *) video_get_drvdata(dev); + PDEBUG(DBG_MMAP, "mmap"); + down(&usbvision->lock); if (!USBVISION_IS_OPERATIONAL(usbvision)) { @@ -1177,16 +1175,16 @@ static int usbvision_v4l2_mmap(struct file *file, struct vm_area_struct *vma) } if (!(vma->vm_flags & VM_WRITE) || - size != PAGE_ALIGN(usbvision->curwidth*usbvision->curheight*usbvision->palette.bytes_per_pixel)) { + size != PAGE_ALIGN(usbvision->max_frame_size)) { up(&usbvision->lock); return -EINVAL; } - for (i = 0; i < USBVISION_NUMFRAMES; i++) { - if (((usbvision->max_frame_size*i) >> PAGE_SHIFT) == vma->vm_pgoff) + for (i = 0; i < usbvision->num_frames; i++) { + if (((PAGE_ALIGN(usbvision->max_frame_size)*i) >> PAGE_SHIFT) == vma->vm_pgoff) break; } - if (i == USBVISION_NUMFRAMES) { + if (i == usbvision->num_frames) { PDEBUG(DBG_MMAP, "mmap: user supplied mapping address is out of range"); up(&usbvision->lock); return -EINVAL; @@ -1238,10 +1236,17 @@ static int usbvision_radio_open(struct inode *inode, struct file *file) usbvision_reset_powerOffTimer(usbvision); if (usbvision->power == 0) { usbvision_power_on(usbvision); - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } } + /* Alternate interface 1 is is the biggest frame size */ + errCode = usbvision_set_alternate(usbvision); + if (errCode < 0) { + usbvision->last_error = errCode; + return -EBUSY; + } + // If so far no errors then we shall start the radio usbvision->radio = 1; call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type); @@ -1253,7 +1258,7 @@ static int usbvision_radio_open(struct inode *inode, struct file *file) if (errCode) { if (PowerOnAtOpen) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); usbvision->initialized = 0; } @@ -1273,6 +1278,11 @@ static int usbvision_radio_close(struct inode *inode, struct file *file) down(&usbvision->lock); + /* Set packet size to 0 */ + usbvision->ifaceAlt=0; + errCode = usb_set_interface(usbvision->dev, usbvision->iface, + usbvision->ifaceAlt); + usbvision_audio_off(usbvision); usbvision->radio=0; usbvision->user--; @@ -1285,7 +1295,7 @@ static int usbvision_radio_close(struct inode *inode, struct file *file) up(&usbvision->lock); if (usbvision->remove_pending) { - info("%s: Final disconnect", __FUNCTION__); + printk(KERN_INFO "%s: Final disconnect\n", __FUNCTION__); usbvision_release(usbvision); } @@ -1611,7 +1621,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) if (video_register_device(usbvision->vdev, VFL_TYPE_GRABBER, video_nr)<0) { goto err_exit; } - info("USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]", usbvision->nr,usbvision->vdev->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2]\n", usbvision->nr,usbvision->vdev->minor & 0x1f); // Radio Device: if (usbvision_device_data[usbvision->DevModel].Radio) { @@ -1623,7 +1633,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) if (video_register_device(usbvision->rdev, VFL_TYPE_RADIO, radio_nr)<0) { goto err_exit; } - info("USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]", usbvision->nr, usbvision->rdev->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2]\n", usbvision->nr, usbvision->rdev->minor & 0x1f); } // vbi Device: if (usbvision_device_data[usbvision->DevModel].vbi) { @@ -1634,7 +1644,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) if (video_register_device(usbvision->vbi, VFL_TYPE_VBI, vbi_nr)<0) { goto err_exit; } - info("USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)", usbvision->nr,usbvision->vbi->minor & 0x1f); + printk(KERN_INFO "USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!)\n", usbvision->nr,usbvision->vbi->minor & 0x1f); } // all done return 0; @@ -1731,8 +1741,8 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) model = usbvision->DevModel; usbvision->palette = usbvision_v4l2_format[2]; // V4L2_PIX_FMT_RGB24; - if (usbvision_device_data[usbvision->DevModel].Vin_Reg2 >= 0) { - usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2 & 0xff; + if (usbvision_device_data[usbvision->DevModel].Vin_Reg2_override) { + usbvision->Vin_Reg2_Preset = usbvision_device_data[usbvision->DevModel].Vin_Reg2; } else { usbvision->Vin_Reg2_Preset = 0; } @@ -1751,7 +1761,7 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) usbvision_audio_off(usbvision); //first switch off audio if (!PowerOnAtOpen) { usbvision_power_on(usbvision); //and then power up the noisy tuner - usbvision_init_i2c(usbvision); + usbvision_i2c_register(usbvision); } } @@ -1762,34 +1772,29 @@ static void usbvision_configure_video(struct usb_usbvision *usbvision) * if it looks like USBVISION video device * */ -static int __devinit usbvision_probe(struct usb_interface *intf, const struct usb_device_id *devid) +static int __devinit usbvision_probe(struct usb_interface *intf, + const struct usb_device_id *devid) { - struct usb_device *dev = interface_to_usbdev(intf); + struct usb_device *dev = usb_get_dev(interface_to_usbdev(intf)); + struct usb_interface *uif; __u8 ifnum = intf->altsetting->desc.bInterfaceNumber; const struct usb_host_interface *interface; struct usb_usbvision *usbvision = NULL; const struct usb_endpoint_descriptor *endpoint; - int model; + int model,i; PDEBUG(DBG_PROBE, "VID=%#04x, PID=%#04x, ifnum=%u", - dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum); - /* Is it an USBVISION video dev? */ - model = 0; - for(model = 0; usbvision_device_data[model].idVendor; model++) { - if (le16_to_cpu(dev->descriptor.idVendor) != usbvision_device_data[model].idVendor) { - continue; - } - if (le16_to_cpu(dev->descriptor.idProduct) != usbvision_device_data[model].idProduct) { - continue; - } + dev->descriptor.idVendor, + dev->descriptor.idProduct, ifnum); - info("%s: %s found", __FUNCTION__, usbvision_device_data[model].ModelString); - break; + model = devid->driver_info; + if ( (model<0) || (model>=usbvision_device_data_size) ) { + PDEBUG(DBG_PROBE, "model out of bounds %d",model); + return -ENODEV; } + printk(KERN_INFO "%s: %s found\n", __FUNCTION__, + usbvision_device_data[model].ModelString); - if (usbvision_device_data[model].idVendor == 0) { - return -ENODEV; //no matching device - } if (usbvision_device_data[model].Interface >= 0) { interface = &dev->actconfig->interface[usbvision_device_data[model].Interface]->altsetting[0]; } @@ -1799,7 +1804,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us endpoint = &interface->endpoint[1].desc; if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) { err("%s: interface %d. has non-ISO endpoint!", __FUNCTION__, ifnum); - err("%s: Endpoint attribures %d", __FUNCTION__, endpoint->bmAttributes); + err("%s: Endpoint attributes %d", __FUNCTION__, endpoint->bmAttributes); return -ENODEV; } if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) { @@ -1807,16 +1812,15 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us return -ENODEV; } - usb_get_dev(dev); - if ((usbvision = usbvision_alloc(dev)) == NULL) { err("%s: couldn't allocate USBVision struct", __FUNCTION__); return -ENOMEM; } + if (dev->descriptor.bNumConfigurations > 1) { usbvision->bridgeType = BRIDGE_NT1004; } - else if (usbvision_device_data[model].ModelString == "Dazzle Fusion Model DVC-90 Rev 1 (SECAM)") { + else if (model == DAZZLE_DVC_90_REV_1_SECAM) { usbvision->bridgeType = BRIDGE_NT1005; } else { @@ -1826,6 +1830,28 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us down(&usbvision->lock); + /* compute alternate max packet sizes */ + uif = dev->actconfig->interface[0]; + + usbvision->num_alt=uif->num_altsetting; + PDEBUG(DBG_PROBE, "Alternate settings: %i",usbvision->num_alt); + usbvision->alt_max_pkt_size = kmalloc(32* + usbvision->num_alt,GFP_KERNEL); + if (usbvision->alt_max_pkt_size == NULL) { + err("usbvision: out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < usbvision->num_alt ; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[1].desc. + wMaxPacketSize); + usbvision->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + PDEBUG(DBG_PROBE, "Alternate setting %i, max size= %i",i, + usbvision->alt_max_pkt_size[i]); + } + + usbvision->nr = usbvision_nr++; usbvision->have_tuner = usbvision_device_data[model].Tuner; @@ -1838,8 +1864,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf, const struct us usbvision->DevModel = model; usbvision->remove_pending = 0; usbvision->iface = ifnum; - usbvision->ifaceAltInactive = 0; - usbvision->ifaceAltActive = 1; + usbvision->ifaceAlt = 0; usbvision->video_endp = endpoint->bEndpointAddress; usbvision->isocPacketSize = 0; usbvision->usb_bandwidth = 0; @@ -1884,7 +1909,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) usbvision_stop_isoc(usbvision); if (usbvision->power) { - usbvision_i2c_usb_del_bus(&usbvision->i2c_adap); + usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); } usbvision->remove_pending = 1; // Now all ISO data will be ignored @@ -1895,7 +1920,7 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) up(&usbvision->lock); if (usbvision->user) { - info("%s: In use, disconnect pending", __FUNCTION__); + printk(KERN_INFO "%s: In use, disconnect pending\n", __FUNCTION__); wake_up_interruptible(&usbvision->wait_frame); wake_up_interruptible(&usbvision->wait_stream); } @@ -1915,124 +1940,6 @@ static struct usb_driver usbvision_driver = { }; /* - * customdevice_process() - * - * This procedure preprocesses CustomDevice parameter if any - * - */ -static void customdevice_process(void) -{ - usbvision_device_data[0]=usbvision_device_data[1]; - usbvision_table[0]=usbvision_table[1]; - - if(CustomDevice) - { - char *parse=CustomDevice; - - PDEBUG(DBG_PROBE, "CustomDevide=%s", CustomDevice); - - /*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1" - usbvision_device_data[0].idVendor; - usbvision_device_data[0].idProduct; - usbvision_device_data[0].Interface; - usbvision_device_data[0].Codec; - usbvision_device_data[0].VideoChannels; - usbvision_device_data[0].VideoNorm; - usbvision_device_data[0].AudioChannels; - usbvision_device_data[0].Radio; - usbvision_device_data[0].Tuner; - usbvision_device_data[0].TunerType; - usbvision_device_data[0].Vin_Reg1; - usbvision_device_data[0].Vin_Reg2; - usbvision_device_data[0].X_Offset; - usbvision_device_data[0].Y_Offset; - usbvision_device_data[0].Dvi_yuv; - usbvision_device_data[0].ModelString; - */ - - rmspace(parse); - usbvision_device_data[0].ModelString="USBVISION Custom Device"; - - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idVendor); - goto2next(parse); - PDEBUG(DBG_PROBE, "idVendor=0x%.4X", usbvision_device_data[0].idVendor); - parse+=2; - sscanf(parse,"%x",&usbvision_device_data[0].idProduct); - goto2next(parse); - PDEBUG(DBG_PROBE, "idProduct=0x%.4X", usbvision_device_data[0].idProduct); - sscanf(parse,"%d",&usbvision_device_data[0].Interface); - goto2next(parse); - PDEBUG(DBG_PROBE, "Interface=%d", usbvision_device_data[0].Interface); - sscanf(parse,"%d",&usbvision_device_data[0].Codec); - goto2next(parse); - PDEBUG(DBG_PROBE, "Codec=%d", usbvision_device_data[0].Codec); - sscanf(parse,"%d",&usbvision_device_data[0].VideoChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "VideoChannels=%d", usbvision_device_data[0].VideoChannels); - - switch(*parse) - { - case 'P': - PDEBUG(DBG_PROBE, "VideoNorm=PAL"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - - case 'S': - PDEBUG(DBG_PROBE, "VideoNorm=SECAM"); - usbvision_device_data[0].VideoNorm=V4L2_STD_SECAM; - break; - - case 'N': - PDEBUG(DBG_PROBE, "VideoNorm=NTSC"); - usbvision_device_data[0].VideoNorm=V4L2_STD_NTSC; - break; - - default: - PDEBUG(DBG_PROBE, "VideoNorm=PAL (by default)"); - usbvision_device_data[0].VideoNorm=V4L2_STD_PAL; - break; - } - goto2next(parse); - - sscanf(parse,"%d",&usbvision_device_data[0].AudioChannels); - goto2next(parse); - PDEBUG(DBG_PROBE, "AudioChannels=%d", usbvision_device_data[0].AudioChannels); - sscanf(parse,"%d",&usbvision_device_data[0].Radio); - goto2next(parse); - PDEBUG(DBG_PROBE, "Radio=%d", usbvision_device_data[0].Radio); - sscanf(parse,"%d",&usbvision_device_data[0].Tuner); - goto2next(parse); - PDEBUG(DBG_PROBE, "Tuner=%d", usbvision_device_data[0].Tuner); - sscanf(parse,"%d",&usbvision_device_data[0].TunerType); - goto2next(parse); - PDEBUG(DBG_PROBE, "TunerType=%d", usbvision_device_data[0].TunerType); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg1); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg1=%d", usbvision_device_data[0].Vin_Reg1); - sscanf(parse,"%d",&usbvision_device_data[0].Vin_Reg2); - goto2next(parse); - PDEBUG(DBG_PROBE, "Vin_Reg2=%d", usbvision_device_data[0].Vin_Reg2); - sscanf(parse,"%d",&usbvision_device_data[0].X_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "X_Offset=%d", usbvision_device_data[0].X_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Y_Offset); - goto2next(parse); - PDEBUG(DBG_PROBE, "Y_Offset=%d", usbvision_device_data[0].Y_Offset); - sscanf(parse,"%d",&usbvision_device_data[0].Dvi_yuv); - PDEBUG(DBG_PROBE, "Dvi_yuv=%d", usbvision_device_data[0].Dvi_yuv); - - //add to usbvision_table also - usbvision_table[0].match_flags=USB_DEVICE_ID_MATCH_DEVICE; - usbvision_table[0].idVendor=usbvision_device_data[0].idVendor; - usbvision_table[0].idProduct=usbvision_device_data[0].idProduct; - - } -} - - - -/* * usbvision_init() * * This code is run to initialize the driver. @@ -2056,12 +1963,10 @@ static int __init usbvision_init(void) usbvision_v4l2_format[7].supported = 0; // V4L2_PIX_FMT_YUV422P } - customdevice_process(); - errCode = usb_register(&usbvision_driver); if (errCode == 0) { - info(DRIVER_DESC " : " USBVISION_VERSION_STRING); + printk(KERN_INFO DRIVER_DESC " : " USBVISION_VERSION_STRING "\n"); PDEBUG(DBG_PROBE, "success"); } return errCode; diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h index e2bcaba..bd6f642 100644 --- a/drivers/media/video/usbvision/usbvision.h +++ b/drivers/media/video/usbvision/usbvision.h @@ -33,6 +33,7 @@ #include <linux/list.h> #include <linux/usb.h> +#include <linux/i2c.h> #include <media/v4l2-common.h> #include <media/tuner.h> #include <linux/videodev2.h> @@ -341,23 +342,24 @@ struct usbvision_frame { #define BRIDGE_NT1005 1005 struct usbvision_device_data_st { - int idVendor; - int idProduct; - int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ - int Codec; - int VideoChannels; __u64 VideoNorm; - int AudioChannels; - int Radio; - int vbi; - int Tuner; - int TunerType; - int Vin_Reg1; - int Vin_Reg2; - int X_Offset; - int Y_Offset; - int Dvi_yuv; - char *ModelString; + const char *ModelString; + int Interface; /* to handle special interface number like BELKIN and Hauppauge WinTV-USB II */ + __u16 Codec; + unsigned VideoChannels:3; + unsigned AudioChannels:2; + unsigned Radio:1; + unsigned vbi:1; + unsigned Tuner:1; + unsigned Vin_Reg1_override:1; /* Override default value with */ + unsigned Vin_Reg2_override:1; /* Vin_Reg1, Vin_Reg2, etc. */ + unsigned Dvi_yuv_override:1; + __u8 Vin_Reg1; + __u8 Vin_Reg2; + __u8 Dvi_yuv; + __u8 TunerType; + __s16 X_Offset; + __s16 Y_Offset; }; /* Declared on usbvision-cards.c */ @@ -396,8 +398,11 @@ struct usb_usbvision { /* Device structure */ struct usb_device *dev; + /* usb transfer */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ unsigned char iface; /* Video interface number */ - unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */ + unsigned char ifaceAlt; /* Alt settings */ unsigned char Vin_Reg2_Preset; struct semaphore lock; struct timer_list powerOffTimer; @@ -421,6 +426,7 @@ struct usb_usbvision { wait_queue_head_t wait_stream; /* Processes waiting */ struct usbvision_frame *curFrame; // pointer to current frame, set by usbvision_find_header struct usbvision_frame frame[USBVISION_NUMFRAMES]; // frame buffer + int num_frames; // number of frames allocated struct usbvision_sbuf sbuf[USBVISION_NUMSBUF]; // S buffering volatile int remove_pending; /* If set then about to exit */ @@ -476,22 +482,19 @@ struct usb_usbvision { /* i2c-algo-usb declaration */ /* --------------------------------------------------------------- */ -int usbvision_i2c_usb_del_bus(struct i2c_adapter *); - - /* ----------------------------------------------------------------------- */ /* usbvision specific I2C functions */ /* ----------------------------------------------------------------------- */ -int usbvision_init_i2c(struct usb_usbvision *usbvision); +int usbvision_i2c_register(struct usb_usbvision *usbvision); +int usbvision_i2c_unregister(struct usb_usbvision *usbvision); void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg); /* defined in usbvision-core.c */ -void usbvision_rvfree(void *mem, unsigned long size); int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg); int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg, unsigned char value); -int usbvision_frames_alloc(struct usb_usbvision *usbvision); +int usbvision_frames_alloc(struct usb_usbvision *usbvision, int number_of_frames); void usbvision_frames_free(struct usb_usbvision *usbvision); int usbvision_scratch_alloc(struct usb_usbvision *usbvision); void usbvision_scratch_free(struct usb_usbvision *usbvision); @@ -502,6 +505,7 @@ int usbvision_setup(struct usb_usbvision *usbvision,int format); int usbvision_init_isoc(struct usb_usbvision *usbvision); int usbvision_restart_isoc(struct usb_usbvision *usbvision); void usbvision_stop_isoc(struct usb_usbvision *usbvision); +int usbvision_set_alternate(struct usb_usbvision *dev); int usbvision_set_audio(struct usb_usbvision *usbvision, int AudioChannel); int usbvision_audio_off(struct usb_usbvision *usbvision); diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c index 8a13e59..d2c1ae0 100644 --- a/drivers/media/video/v4l1-compat.c +++ b/drivers/media/video/v4l1-compat.c @@ -11,7 +11,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Bill Dirks <bdirks@pacbell.net> + * Author: Bill Dirks <bill@thedirks.org> * et al. * */ diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index b8ee37d..49f1df7 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -12,7 +12,7 @@ * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Author: Bill Dirks <bdirks@pacbell.net> + * Author: Bill Dirks <bill@thedirks.org> * based on code by Alan Cox, <alan@cymru.net> * */ @@ -51,6 +51,7 @@ #include <linux/mm.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/i2c.h> #include <asm/uaccess.h> #include <asm/system.h> #include <asm/pgtable.h> @@ -59,6 +60,7 @@ #include <linux/video_decoder.h> #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> #ifdef CONFIG_KMOD #include <linux/kmod.h> @@ -259,6 +261,8 @@ char *v4l2_field_names[] = { [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; char *v4l2_type_names[] = { @@ -268,14 +272,10 @@ char *v4l2_type_names[] = { [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", - [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", }; -static char *v4l2_memory_names[] = { - [V4L2_MEMORY_MMAP] = "mmap", - [V4L2_MEMORY_USERPTR] = "userptr", - [V4L2_MEMORY_OVERLAY] = "overlay", -}; #define prt_names(a,arr) (((a)>=0)&&((a)<ARRAY_SIZE(arr)))?arr[a]:"unknown" @@ -370,13 +370,23 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", -#if 1 [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", -#endif [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", - [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS" + [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", +#if 1 + [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", + [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", + [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", + [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", + [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", + + [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", + [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", + + [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", +#endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -401,32 +411,24 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG", [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", - [_IOC_NR(VIDIOC_INT_S_REGISTER)] = "VIDIOC_INT_S_REGISTER", - [_IOC_NR(VIDIOC_INT_G_REGISTER)] = "VIDIOC_INT_G_REGISTER", [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", [_IOC_NR(VIDIOC_INT_AUDIO_CLOCK_FREQ)] = "VIDIOC_INT_AUDIO_CLOCK_FREQ", [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", - [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ" + [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ", + [_IOC_NR(VIDIOC_INT_INIT)] = "VIDIOC_INT_INIT", + [_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)] = "VIDIOC_INT_G_STD_OUTPUT", + [_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)] = "VIDIOC_INT_S_STD_OUTPUT", }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) -static void v4l_print_pix_fmt (char *s, struct v4l2_pix_format *fmt) -{ - printk ("%s: width=%d, height=%d, format=%d, field=%s, " - "bytesperline=%d sizeimage=%d, colorspace=%d\n", s, - fmt->width,fmt->height,fmt->pixelformat, - prt_names(fmt->field,v4l2_field_names), - fmt->bytesperline,fmt->sizeimage,fmt->colorspace); -}; /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ @@ -466,576 +468,6 @@ void v4l_printk_ioctl(unsigned int cmd) } } -/* Common ioctl debug function. This function can be used by - external ioctl messages as well as internal V4L ioctl and its - arguments */ -void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) -{ - printk(s); - printk(": "); - v4l_printk_ioctl(cmd); - switch (cmd) { - case VIDIOC_INT_G_CHIP_IDENT: - { - enum v4l2_chip_ident *p=arg; - printk ("%s: chip ident=%d\n", s, *p); - break; - } - case VIDIOC_G_PRIORITY: - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *p=arg; - printk ("%s: priority=%d\n", s, *p); - break; - } - case VIDIOC_INT_S_TUNER_MODE: - { - enum v4l2_tuner_type *p=arg; - printk ("%s: tuner type=%d\n", s, *p); - break; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case DECODER_SET_VBI_BYPASS: - case DECODER_ENABLE_OUTPUT: - case DECODER_GET_STATUS: - case DECODER_SET_OUTPUT: - case DECODER_SET_INPUT: - case DECODER_SET_GPIO: - case DECODER_SET_NORM: - case VIDIOCCAPTURE: - case VIDIOCSYNC: - case VIDIOCSWRITEMODE: -#endif - case TUNER_SET_TYPE_ADDR: - case TUNER_SET_STANDBY: - case TDA9887_SET_CONFIG: -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY_OLD: -#endif - case VIDIOC_STREAMOFF: - case VIDIOC_G_OUTPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_STREAMON: - case VIDIOC_G_INPUT: - case VIDIOC_OVERLAY: - case VIDIOC_S_INPUT: - { - int *p=arg; - printk ("%s: value=%d\n", s, *p); - break; - } - case VIDIOC_G_AUDIO: - case VIDIOC_S_AUDIO: - case VIDIOC_ENUMAUDIO: -#ifdef __OLD_VIDIOC_ - case VIDIOC_G_AUDIO_OLD: -#endif - { - struct v4l2_audio *p=arg; - - printk ("%s: index=%d, name=%s, capability=%d, mode=%d\n", - s,p->index, p->name,p->capability, p->mode); - break; - } - case VIDIOC_G_AUDOUT: - case VIDIOC_S_AUDOUT: - case VIDIOC_ENUMAUDOUT: -#ifdef __OLD_VIDIOC_ - case VIDIOC_G_AUDOUT_OLD: -#endif - { - struct v4l2_audioout *p=arg; - printk ("%s: index=%d, name=%s, capability=%d, mode=%d\n", s, - p->index, p->name, p->capability,p->mode); - break; - } - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *p=arg; - struct v4l2_timecode *tc=&p->timecode; - printk ("%s: %02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08x, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n", - s, - (p->timestamp.tv_sec/3600), - (int)(p->timestamp.tv_sec/60)%60, - (int)(p->timestamp.tv_sec%60), - p->timestamp.tv_usec, - p->index, - prt_names(p->type,v4l2_type_names), - p->bytesused,p->flags, - p->field,p->sequence, - prt_names(p->memory,v4l2_memory_names), - p->m.userptr); - printk ("%s: timecode= %02d:%02d:%02d type=%d, " - "flags=0x%08x, frames=%d, userbits=0x%08x\n", - s,tc->hours,tc->minutes,tc->seconds, - tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits); - break; - } - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *p=arg; - printk ("%s: driver=%s, card=%s, bus=%s, version=0x%08x, " - "capabilities=0x%08x\n", s, - p->driver,p->card,p->bus_info, - p->version, - p->capabilities); - break; - } - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: -#ifdef __OLD_VIDIOC_ - case VIDIOC_S_CTRL_OLD: -#endif - { - struct v4l2_control *p=arg; - printk ("%s: id=%d, value=%d\n", s, p->id, p->value); - break; - } - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - int i; - - printk("%s: ctrl_class=%d, count=%d\n", s, p->ctrl_class, p->count); - for (i = 0; i < p->count; i++) { - struct v4l2_ext_control *c = &p->controls[i]; - if (cmd == VIDIOC_G_EXT_CTRLS) - printk("%s: id=%d\n", s, c->id); - else - printk("%s: id=%d, value=%d\n", s, c->id, c->value); - } - break; - } - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - { - struct v4l2_crop *p=arg; - /*FIXME: Should also show rect structs */ - printk ("%s: type=%d\n", s, p->type); - break; - } - case VIDIOC_CROPCAP: -#ifdef __OLD_VIDIOC_ - case VIDIOC_CROPCAP_OLD: -#endif - { - struct v4l2_cropcap *p=arg; - /*FIXME: Should also show rect structs */ - printk ("%s: type=%d\n", s, p->type); - break; - } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *p=arg; - printk ("%s: is_second_field=%d, ptr=0x%08lx, line=%d, " - "type=%d\n", s, - p->is_second_field,(unsigned long)p->p,p->line,p->type); - break; - } - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *p=arg; - printk ("%s: index=%d, type=%d, flags=%d, description=%s," - " pixelformat=%d\n", s, - p->index, p->type, p->flags,p->description, - p->pixelformat); - - break; - } - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - { - struct v4l2_format *p=arg; - printk ("%s: type=%s\n", s, - prt_names(p->type,v4l2_type_names)); - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - v4l_print_pix_fmt (s, &p->fmt.pix); - break; - default: - break; - } - } - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *p=arg; - printk ("%s: capability=%d, flags=%d, base=0x%08lx\n", s, - p->capability,p->flags, (unsigned long)p->base); - v4l_print_pix_fmt (s, &p->fmt); - break; - } - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *p=arg; - printk ("%s: tuner=%d, type=%d, frequency=%d\n", s, - p->tuner,p->type,p->frequency); - break; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *p=arg; - printk ("%s: index=%d, name=%s, type=%d, audioset=%d, " - "tuner=%d, std=%Ld, status=%d\n", s, - p->index,p->name,p->type,p->audioset, - p->tuner, - (unsigned long long)p->std, - p->status); - break; - } - case VIDIOC_G_JPEGCOMP: - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *p=arg; - printk ("%s: quality=%d, APPn=%d, APP_len=%d, COM_len=%d," - " jpeg_markers=%d\n", s, - p->quality,p->APPn,p->APP_len, - p->COM_len,p->jpeg_markers); - break; - } - case VIDIOC_G_MODULATOR: - case VIDIOC_S_MODULATOR: - { - struct v4l2_modulator *p=arg; - printk ("%s: index=%d, name=%s, capability=%d, rangelow=%d," - " rangehigh=%d, txsubchans=%d\n", s, - p->index, p->name,p->capability,p->rangelow, - p->rangehigh,p->txsubchans); - break; - } - case VIDIOC_G_MPEGCOMP: - case VIDIOC_S_MPEGCOMP: - { - struct v4l2_mpeg_compression *p=arg; - /*FIXME: Several fields not shown */ - printk ("%s: ts_pid_pmt=%d, ts_pid_audio=%d, ts_pid_video=%d, " - "ts_pid_pcr=%d, ps_size=%d, au_sample_rate=%d, " - "au_pesid=%c, vi_frame_rate=%d, vi_frames_per_gop=%d, " - "vi_bframes_count=%d, vi_pesid=%c\n", s, - p->ts_pid_pmt,p->ts_pid_audio, p->ts_pid_video, - p->ts_pid_pcr, p->ps_size, p->au_sample_rate, - p->au_pesid, p->vi_frame_rate, - p->vi_frames_per_gop, p->vi_bframes_count, - p->vi_pesid); - break; - } - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *p=arg; - printk ("%s: index=%d, name=%s,type=%d, audioset=%d, " - "modulator=%d, std=%Ld\n", - s,p->index,p->name,p->type,p->audioset, - p->modulator, - (unsigned long long)p->std); - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *p=arg; - printk ("%s: id=%d, type=%d, name=%s, min/max=%d/%d," - " step=%d, default=%d, flags=0x%08x\n", s, - p->id,p->type,p->name,p->minimum,p->maximum, - p->step,p->default_value,p->flags); - break; - } - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *p=arg; - printk ("%s: id=%d, index=%d, name=%s\n", s, - p->id,p->index,p->name); - break; - } - case VIDIOC_INT_G_REGISTER: - case VIDIOC_INT_S_REGISTER: - { - struct v4l2_register *p=arg; - printk ("%s: i2c_id=%d, reg=%lu, val=%d\n", s, - p->i2c_id,p->reg,p->val); - - break; - } - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *p=arg; - printk ("%s: count=%d, type=%s, memory=%s\n", s, - p->count, - prt_names(p->type,v4l2_type_names), - prt_names(p->memory,v4l2_memory_names)); - break; - } - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_S_VIDEO_ROUTING: - case VIDIOC_INT_G_AUDIO_ROUTING: - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *p=arg; - printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output); - break; - } - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *p=arg; - printk ("%s: freq=%u, flags=0x%x\n", s, p->freq, p->flags); - break; - } - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *p=arg; - printk ("%s: service_set=%d\n", s, - p->service_set); - break; - } - case VIDIOC_INT_S_VBI_DATA: - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *p=arg; - printk ("%s: id=%d, field=%d, line=%d\n", s, - p->id, p->field, p->line); - break; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *p=arg; - printk ("%s: index=%d, id=%Ld, name=%s, fps=%d/%d, " - "framelines=%d\n", s, p->index, - (unsigned long long)p->id, p->name, - p->frameperiod.numerator, - p->frameperiod.denominator, - p->framelines); - - break; - } - case VIDIOC_G_PARM: - case VIDIOC_S_PARM: -#ifdef __OLD_VIDIOC_ - case VIDIOC_S_PARM_OLD: -#endif - { - struct v4l2_streamparm *p=arg; - printk ("%s: type=%d\n", s, p->type); - - break; - } - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *p=arg; - printk ("%s: index=%d, name=%s, type=%d, capability=%d, " - "rangelow=%d, rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=%d, audmode=%d\n", s, - p->index, p->name, p->type, - p->capability, p->rangelow,p->rangehigh, - p->rxsubchans, p->audmode, p->signal, - p->afc); - break; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGVBIFMT: - case VIDIOCSVBIFMT: - { - struct vbi_format *p=arg; - printk ("%s: sampling_rate=%d, samples_per_line=%d, " - "sample_format=%d, start=%d/%d, count=%d/%d, flags=%d\n", s, - p->sampling_rate,p->samples_per_line, - p->sample_format,p->start[0],p->start[1], - p->count[0],p->count[1],p->flags); - break; - } - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - { - struct video_audio *p=arg; - printk ("%s: audio=%d, volume=%d, bass=%d, treble=%d, " - "flags=%d, name=%s, mode=%d, balance=%d, step=%d\n", - s,p->audio,p->volume,p->bass, p->treble, - p->flags,p->name,p->mode,p->balance,p->step); - break; - } - case VIDIOCGFBUF: - case VIDIOCSFBUF: - { - struct video_buffer *p=arg; - printk ("%s: base=%08lx, height=%d, width=%d, depth=%d, " - "bytesperline=%d\n", s, - (unsigned long) p->base, p->height, p->width, - p->depth,p->bytesperline); - break; - } - case VIDIOCGCAP: - { - struct video_capability *p=arg; - printk ("%s: name=%s, type=%d, channels=%d, audios=%d, " - "maxwidth=%d, maxheight=%d, minwidth=%d, minheight=%d\n", - s,p->name,p->type,p->channels,p->audios, - p->maxwidth,p->maxheight,p->minwidth, - p->minheight); - - break; - } - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - { - struct video_capture *p=arg; - printk ("%s: x=%d, y=%d, width=%d, height=%d, decimation=%d," - " flags=%d\n", s, - p->x, p->y,p->width, p->height, - p->decimation,p->flags); - break; - } - case VIDIOCGCHAN: - case VIDIOCSCHAN: - { - struct video_channel *p=arg; - printk ("%s: channel=%d, name=%s, tuners=%d, flags=%d, " - "type=%d, norm=%d\n", s, - p->channel,p->name,p->tuners, - p->flags,p->type,p->norm); - - break; - } - case VIDIOCSMICROCODE: - { - struct video_code *p=arg; - printk ("%s: loadwhat=%s, datasize=%d\n", s, - p->loadwhat,p->datasize); - break; - } - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *p=arg; - printk ("%s: flags=%d, inputs=%d, outputs=%d\n", s, - p->flags,p->inputs,p->outputs); - break; - } - case DECODER_INIT: - { - struct video_decoder_init *p=arg; - printk ("%s: len=%c\n", s, p->len); - break; - } - case VIDIOCGPLAYINFO: - { - struct video_info *p=arg; - printk ("%s: frame_count=%d, h_size=%d, v_size=%d, " - "smpte_timecode=%d, picture_type=%d, " - "temporal_reference=%d, user_data=%s\n", s, - p->frame_count, p->h_size, - p->v_size, p->smpte_timecode, - p->picture_type, p->temporal_reference, - p->user_data); - break; - } - case VIDIOCKEY: - { - struct video_key *p=arg; - printk ("%s: key=%s, flags=%d\n", s, - p->key, p->flags); - break; - } - case VIDIOCGMBUF: - { - struct video_mbuf *p=arg; - printk ("%s: size=%d, frames=%d, offsets=0x%08lx\n", s, - p->size, - p->frames, - (unsigned long)p->offsets); - break; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *p=arg; - printk ("%s: frame=%d, height=%d, width=%d, format=%d\n", s, - p->frame, - p->height, p->width, - p->format); - break; - } - case VIDIOCGPICT: - case VIDIOCSPICT: - case DECODER_SET_PICTURE: - { - struct video_picture *p=arg; - - printk ("%s: brightness=%d, hue=%d, colour=%d, contrast=%d," - " whiteness=%d, depth=%d, palette=%d\n", s, - p->brightness, p->hue, p->colour, - p->contrast, p->whiteness, p->depth, - p->palette); - break; - } - case VIDIOCSPLAYMODE: - { - struct video_play_mode *p=arg; - printk ("%s: mode=%d, p1=%d, p2=%d\n", s, - p->mode,p->p1,p->p2); - break; - } - case VIDIOCGTUNER: - case VIDIOCSTUNER: - { - struct video_tuner *p=arg; - printk ("%s: tuner=%d, name=%s, rangelow=%ld, rangehigh=%ld, " - "flags=%d, mode=%d, signal=%d\n", s, - p->tuner, p->name,p->rangelow, p->rangehigh, - p->flags,p->mode, p->signal); - break; - } - case VIDIOCGUNIT: - { - struct video_unit *p=arg; - printk ("%s: video=%d, vbi=%d, radio=%d, audio=%d, " - "teletext=%d\n", s, - p->video,p->vbi,p->radio,p->audio,p->teletext); - break; - } - case VIDIOCGWIN: - case VIDIOCSWIN: - { - struct video_window *p=arg; - printk ("%s: x=%d, y=%d, width=%d, height=%d, chromakey=%d," - " flags=%d, clipcount=%d\n", s, - p->x, p->y,p->width, p->height, - p->chromakey,p->flags, - p->clipcount); - break; - } - case VIDIOCGFREQ: - case VIDIOCSFREQ: - { - unsigned long *p=arg; - printk ("%s: value=%lu\n", s, *p); - break; - } -#endif - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - case VIDIOC_INT_I2S_CLOCK_FREQ: - case VIDIOC_INT_S_STANDBY: - case VIDIOC_INT_RESET: - { - u32 *p=arg; - - printk ("%s: value=%d\n", s, *p); - break; - } - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_QUERYSTD: - { - v4l2_std_id *p=arg; - - printk ("%s: value=%Lu\n", s, (unsigned long long)*p); - break; - } - } -} /* ----------------------------------------------------------------- */ @@ -1256,6 +688,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break; + case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break; case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break; case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break; @@ -1266,6 +699,8 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break; case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break; + case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break; case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break; case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break; case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break; @@ -1281,6 +716,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; @@ -1414,6 +850,8 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_CRC_NONE, V4L2_MPEG_AUDIO_CRC_CRC16, 1, V4L2_MPEG_AUDIO_CRC_NONE); + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, @@ -1443,6 +881,10 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, @@ -1529,6 +971,44 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id) return **ctrl_classes; } +int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 match_type, u32 match_chip) +{ + switch (match_type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + return (c != NULL && c->driver != NULL && c->driver->id == match_chip); + case V4L2_CHIP_MATCH_I2C_ADDR: + return (c != NULL && c->addr == match_chip); + default: + return 0; + } +} + +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, + u32 ident, u32 revision) +{ + if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip)) + return 0; + if (chip->ident == V4L2_IDENT_NONE) { + chip->ident = ident; + chip->revision = revision; + } + else { + chip->ident = V4L2_IDENT_AMBIGUOUS; + chip->revision = 0; + } + return 0; +} + +int v4l2_chip_match_host(u32 match_type, u32 match_chip) +{ + switch (match_type) { + case V4L2_CHIP_MATCH_HOST: + return match_chip == 0; + default: + return 0; + } +} + /* ----------------------------------------------------------------- */ EXPORT_SYMBOL(v4l2_norm_to_name); @@ -1544,7 +1024,6 @@ EXPORT_SYMBOL(v4l2_prio_check); EXPORT_SYMBOL(v4l2_field_names); EXPORT_SYMBOL(v4l2_type_names); EXPORT_SYMBOL(v4l_printk_ioctl); -EXPORT_SYMBOL(v4l_printk_ioctl_arg); EXPORT_SYMBOL(v4l2_ctrl_next); EXPORT_SYMBOL(v4l2_ctrl_check); @@ -1553,6 +1032,10 @@ EXPORT_SYMBOL(v4l2_ctrl_query_menu); EXPORT_SYMBOL(v4l2_ctrl_query_fill); EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); +EXPORT_SYMBOL(v4l2_chip_match_i2c_client); +EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); +EXPORT_SYMBOL(v4l2_chip_match_host); + /* * Local variables: * c-basic-offset: 8 diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 6504a58..459786f 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -148,6 +148,8 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", data,size,dma->nr_pages); + dma->varea = (void *) data; + down_read(¤t->mm->mmap_sem); err = get_user_pages(current,current->mm, data & PAGE_MASK, dma->nr_pages, @@ -285,6 +287,7 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) vfree(dma->vmalloc); dma->vmalloc = NULL; + dma->varea = NULL; if (dma->bus_addr) { dma->bus_addr = 0; diff --git a/drivers/media/video/videocodec.c b/drivers/media/video/videocodec.c index 290e641..f2bbd7a 100644 --- a/drivers/media/video/videocodec.c +++ b/drivers/media/video/videocodec.c @@ -348,6 +348,9 @@ videocodec_build_table (void) kfree(videocodec_buf); videocodec_buf = kmalloc(size, GFP_KERNEL); + if (!videocodec_buf) + return 0; + i = 0; i += scnprintf(videocodec_buf + i, size - 1, "<S>lave or attached <M>aster name type flags magic "); diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index a786c1f..80ac5f8 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -318,6 +318,7 @@ static char *v4l2_type_names_FIXME[] = { [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", [V4L2_BUF_TYPE_PRIVATE] = "private", }; @@ -330,6 +331,8 @@ static char *v4l2_field_names_FIXME[] = { [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; #define prt_names(a,arr) (((a)>=0)&&((a)<ARRAY_SIZE(arr)))?arr[a]:"unknown" @@ -411,6 +414,10 @@ static int check_fmt (struct video_device *vfd, enum v4l2_buf_type type) if (vfd->vidioc_try_fmt_vbi_output) return (0); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + return (0); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_try_fmt_type_private) return (0); @@ -525,6 +532,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_enum_fmt_vbi_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_enum_fmt_output_overlay) + ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_enum_fmt_type_private) ret=vfd->vidioc_enum_fmt_type_private(file, @@ -582,6 +593,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_g_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_g_fmt_output_overlay) + ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_g_fmt_vbi_output) ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); @@ -630,6 +645,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_s_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_s_fmt_output_overlay) + ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_s_fmt_vbi_output) ret=vfd->vidioc_s_fmt_vbi_output(file, @@ -680,6 +699,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_try_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_try_fmt_vbi_output) ret=vfd->vidioc_try_fmt_vbi_output(file, @@ -1342,9 +1365,50 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_s_jpegcomp(file, fh, p); break; } + case VIDIOC_G_ENC_INDEX: + { + struct v4l2_enc_idx *p=arg; + + if (!vfd->vidioc_g_enc_index) + break; + ret=vfd->vidioc_g_enc_index(file, fh, p); + if (!ret) + dbgarg (cmd, "entries=%d, entries_cap=%d\n", + p->entries,p->entries_cap); + break; + } + case VIDIOC_ENCODER_CMD: + { + struct v4l2_encoder_cmd *p=arg; + + if (!vfd->vidioc_encoder_cmd) + break; + ret=vfd->vidioc_encoder_cmd(file, fh, p); + if (!ret) + dbgarg (cmd, "cmd=%d, flags=%d\n", + p->cmd,p->flags); + break; + } + case VIDIOC_TRY_ENCODER_CMD: + { + struct v4l2_encoder_cmd *p=arg; + + if (!vfd->vidioc_try_encoder_cmd) + break; + ret=vfd->vidioc_try_encoder_cmd(file, fh, p); + if (!ret) + dbgarg (cmd, "cmd=%d, flags=%d\n", + p->cmd,p->flags); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p=arg; + __u32 type=p->type; + + memset(p,0,sizeof(*p)); + p->type=type; + if (vfd->vidioc_g_parm) { ret=vfd->vidioc_g_parm(file, fh, p); } else { @@ -1356,8 +1420,6 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, v4l2_video_std_construct(&s, vfd->current_norm, v4l2_norm_to_name(vfd->current_norm)); - memset(p,0,sizeof(*p)); - p->parm.capture.timeperframe = s.frameperiod; ret=0; } @@ -1453,6 +1515,36 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_log_status(file, fh); break; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_DBG_G_REGISTER: + { + struct v4l2_register *p=arg; + if (!capable(CAP_SYS_ADMIN)) + ret=-EPERM; + else if (vfd->vidioc_g_register) + ret=vfd->vidioc_g_register(file, fh, p); + break; + } + case VIDIOC_DBG_S_REGISTER: + { + struct v4l2_register *p=arg; + if (!capable(CAP_SYS_ADMIN)) + ret=-EPERM; + else if (vfd->vidioc_s_register) + ret=vfd->vidioc_s_register(file, fh, p); + break; + } +#endif + case VIDIOC_G_CHIP_IDENT: + { + struct v4l2_chip_ident *p=arg; + if (!vfd->vidioc_g_chip_ident) + break; + ret=vfd->vidioc_g_chip_ident(file, fh, p); + if (!ret) + dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index cfb6b1f..f7e1d19 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -145,7 +145,9 @@ struct vivi_buffer { struct vivi_fmt *fmt; +#ifdef CONFIG_VIVI_SCATTER struct sg_to_addr *to_addr; +#endif }; struct vivi_dmaqueue { @@ -230,6 +232,7 @@ static u8 bars[8][3] = { #define TSTAMP_MAX_Y TSTAMP_MIN_Y+15 #define TSTAMP_MIN_X 64 +#ifdef CONFIG_VIVI_SCATTER static void prep_to_addr(struct sg_to_addr to_addr[], struct videobuf_buffer *vb) { @@ -262,14 +265,24 @@ static int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) return (p1); } +#endif +#ifdef CONFIG_VIVI_SCATTER static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, int hmax, int line, char *timestr) +#else +static void gen_line(char *basep,int inipos,int wmax, + int hmax, int line, char *timestr) +#endif { - int w,i,j,pos=inipos,pgpos,oldpg,y; - char *p,*s,*basep; - struct page *pg; + int w,i,j,pos=inipos,y; + char *p,*s; u8 chr,r,g,b,color; +#ifdef CONFIG_VIVI_SCATTER + int pgpos,oldpg; + char *basep; + struct page *pg; + unsigned long flags; spinlock_t spinlock; @@ -280,6 +293,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, pg=pfn_to_page(sg_dma_address(to_addr[oldpg].sg) >> PAGE_SHIFT); spin_lock_irqsave(&spinlock,flags); basep = kmap_atomic(pg, KM_BOUNCE_READ)+to_addr[oldpg].sg->offset; +#endif /* We will just duplicate the second pixel at the packet */ wmax/=2; @@ -291,6 +305,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, b=bars[w*7/wmax][2]; for (color=0;color<4;color++) { +#ifdef CONFIG_VIVI_SCATTER pgpos=get_addr_pos(pos,pages,to_addr); if (pgpos!=oldpg) { pg=pfn_to_page(sg_dma_address(to_addr[pgpos].sg) >> PAGE_SHIFT); @@ -299,6 +314,9 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, oldpg=pgpos; } p=basep+pos-to_addr[pgpos].pos; +#else + p=basep+pos; +#endif switch (color) { case 0: @@ -343,6 +361,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, pos=inipos+j*2; for (color=0;color<4;color++) { +#ifdef CONFIG_VIVI_SCATTER pgpos=get_addr_pos(pos,pages,to_addr); if (pgpos!=oldpg) { pg=pfn_to_page(sg_dma_address( @@ -356,6 +375,9 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, oldpg=pgpos; } p=basep+pos-to_addr[pgpos].pos; +#else + p=basep+pos; +#endif y=TO_Y(r,g,b); @@ -380,19 +402,27 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, end: +#ifdef CONFIG_VIVI_SCATTER kunmap_atomic(basep, KM_BOUNCE_READ); spin_unlock_irqrestore(&spinlock,flags); - +#else + return; +#endif } static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) { int h,pos=0; int hmax = buf->vb.height; int wmax = buf->vb.width; - struct videobuf_buffer *vb=&buf->vb; - struct sg_to_addr *to_addr=buf->to_addr; struct timeval ts; +#ifdef CONFIG_VIVI_SCATTER + struct sg_to_addr *to_addr=buf->to_addr; + struct videobuf_buffer *vb=&buf->vb; +#else + char *tmpbuf; +#endif +#ifdef CONFIG_VIVI_SCATTER /* Test if DMA mapping is ready */ if (!sg_dma_address(&vb->dma.sglist[0])) return; @@ -401,9 +431,28 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) /* Check if there is enough memory */ BUG_ON(buf->vb.dma.nr_pages << PAGE_SHIFT < (buf->vb.width*buf->vb.height)*2); +#else + if (buf->vb.dma.varea) { + tmpbuf=kmalloc (wmax*2, GFP_KERNEL); + } else { + tmpbuf=buf->vb.dma.vmalloc; + } + +#endif for (h=0;h<hmax;h++) { +#ifdef CONFIG_VIVI_SCATTER gen_line(to_addr,pos,vb->dma.nr_pages,wmax,hmax,h,dev->timestr); +#else + if (buf->vb.dma.varea) { + gen_line(tmpbuf,0,wmax,hmax,h,dev->timestr); + /* FIXME: replacing to __copy_to_user */ + if (copy_to_user(buf->vb.dma.varea+pos,tmpbuf,wmax*2)!=0) + dprintk(2,"vivifill copy_to_user failed.\n"); + } else { + gen_line(tmpbuf,pos,wmax,hmax,h,dev->timestr); + } +#endif pos += wmax*2; } @@ -429,7 +478,7 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) dev->h,dev->m,dev->s,(dev->us+500)/1000); dprintk(2,"vivifill at %s: Buffer 0x%08lx size= %d\n",dev->timestr, - (unsigned long)buf->vb.dma.vmalloc,pos); + (unsigned long)buf->vb.dma.varea,pos); /* Advice that buffer was filled */ buf->vb.state = STATE_DONE; @@ -471,11 +520,12 @@ static void vivi_thread_tick(struct vivi_dmaqueue *dma_q) /* Fill buffer */ vivi_fillbuff(dev,buf); - } - if (list_empty(&dma_q->active)) { - del_timer(&dma_q->timeout); - } else { - mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + + if (list_empty(&dma_q->active)) { + del_timer(&dma_q->timeout); + } else { + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + } } if (bc != 1) dprintk(1,"%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc); @@ -522,6 +572,8 @@ static int vivi_thread(void *data) dprintk(1,"thread started\n"); + mod_timer(&dma_q->timeout, jiffies+BUFFER_TIMEOUT); + for (;;) { vivi_sleep(dma_q); @@ -538,7 +590,6 @@ static int vivi_start_thread(struct vivi_dmaqueue *dma_q) dma_q->ini_jiffies=jiffies; dprintk(1,"%s\n",__FUNCTION__); - init_waitqueue_head(&dma_q->wq); dma_q->kthread = kthread_run(vivi_thread, dma_q, "vivi"); @@ -546,6 +597,9 @@ static int vivi_start_thread(struct vivi_dmaqueue *dma_q) printk(KERN_ERR "vivi: kernel_thread() failed\n"); return PTR_ERR(dma_q->kthread); } + /* Wakes thread */ + wake_up_interruptible(&dma_q->wq); + dprintk(1,"returning from %s\n",__FUNCTION__); return 0; } @@ -663,9 +717,11 @@ static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) if (in_interrupt()) BUG(); +#ifdef CONFIG_VIVI_SCATTER /*FIXME: Maybe a spinlock is required here */ kfree(buf->to_addr); buf->to_addr=NULL; +#endif videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(vq, &buf->vb.dma); @@ -711,11 +767,12 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, buf->vb.state = STATE_PREPARED; +#ifdef CONFIG_VIVI_SCATTER if (NULL == (buf->to_addr = kmalloc(sizeof(*buf->to_addr) * vb->dma.nr_pages,GFP_KERNEL))) { rc=-ENOMEM; goto fail; } - +#endif return 0; fail: @@ -780,6 +837,7 @@ static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb free_buffer(vq,buf); } +#ifdef CONFIG_VIVI_SCATTER static int vivi_map_sg(void *dev, struct scatterlist *sg, int nents, int direction) { @@ -812,6 +870,7 @@ static int vivi_dma_sync_sg(void *dev,struct scatterlist *sglist, int nr_pages, // flush_write_buffers(); return 0; } +#endif static struct videobuf_queue_ops vivi_video_qops = { .buf_setup = buffer_setup, @@ -820,9 +879,9 @@ static struct videobuf_queue_ops vivi_video_qops = { .buf_release = buffer_release, /* Non-pci handling routines */ - .vb_map_sg = vivi_map_sg, - .vb_dma_sync_sg = vivi_dma_sync_sg, - .vb_unmap_sg = vivi_unmap_sg, +// .vb_map_sg = vivi_map_sg, +// .vb_dma_sync_sg = vivi_dma_sync_sg, +// .vb_unmap_sg = vivi_unmap_sg, }; /* ------------------------------------------------------------------ @@ -1200,11 +1259,19 @@ static int vivi_open(struct inode *inode, struct file *file) sprintf(dev->timestr,"%02d:%02d:%02d:%03d", dev->h,dev->m,dev->s,(dev->us+500)/1000); +#ifdef CONFIG_VIVI_SCATTER + videobuf_queue_init(&fh->vb_vidq,VIDEOBUF_DMA_SCATTER, &vivi_video_qops, + NULL, NULL, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct vivi_buffer),fh); +#else videobuf_queue_init(&fh->vb_vidq, &vivi_video_qops, NULL, NULL, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct vivi_buffer),fh); +#endif return 0; } @@ -1352,6 +1419,7 @@ static int __init vivi_init(void) /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + init_waitqueue_head(&dev->vidq.wq); /* initialize locks */ init_MUTEX(&dev->lock); diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a9b59c3..8f6741a 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -29,6 +29,7 @@ #include <linux/i2c-id.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> MODULE_DESCRIPTION("wm8739 driver"); MODULE_AUTHOR("T. Adachi, Hans Verkuil"); @@ -236,6 +237,9 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg return -EINVAL; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index d81a88b..4df5d30 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -33,6 +33,7 @@ #include <linux/i2c-id.h> #include <linux/videodev.h> #include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); @@ -124,6 +125,9 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd, wm8775_write(client, R21, 0x100 + state->input); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Input: %d%s\n", state->input, state->muted ? " (muted)" : ""); diff --git a/drivers/media/video/zc0301/zc0301.h b/drivers/media/video/zc0301/zc0301.h index b9c93b8..710f12e 100644 --- a/drivers/media/video/zc0301/zc0301.h +++ b/drivers/media/video/zc0301/zc0301.h @@ -1,7 +1,7 @@ /*************************************************************************** - * V4L2 driver for ZC0301 Image Processor and Control Chip * + * V4L2 driver for ZC0301[P] Image Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * diff --git a/drivers/media/video/zc0301/zc0301_core.c b/drivers/media/video/zc0301/zc0301_core.c index 8da7f15..f112055 100644 --- a/drivers/media/video/zc0301/zc0301_core.c +++ b/drivers/media/video/zc0301/zc0301_core.c @@ -1,7 +1,7 @@ /*************************************************************************** * Video4Linux2 driver for ZC0301[P] Image Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * Informations about the chip internals needed to enable the I2C protocol * * have been taken from the documentation of the ZC030x Video4Linux1 * @@ -52,8 +52,8 @@ #define ZC0301_MODULE_AUTHOR "(C) 2006 Luca Risolia" #define ZC0301_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define ZC0301_MODULE_LICENSE "GPL" -#define ZC0301_MODULE_VERSION "1:1.05" -#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 0, 5) +#define ZC0301_MODULE_VERSION "1:1.07" +#define ZC0301_MODULE_VERSION_CODE KERNEL_VERSION(1, 1, 7) /*****************************************************************************/ @@ -89,7 +89,7 @@ MODULE_PARM_DESC(force_munmap, "\ndetected camera." "\n 0 = do not force memory unmapping" "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(SN9C102_FORCE_MUNMAP)"." + "\nDefault value is "__MODULE_STRING(ZC0301_FORCE_MUNMAP)"." "\n"); static unsigned int frame_timeout[] = {[0 ... ZC0301_MAX_DEVICES-1] = @@ -136,7 +136,8 @@ zc0301_request_buffers(struct zc0301_device* cam, u32 count, cam->nbuffers = count; while (cam->nbuffers > 0) { - if ((buff = vmalloc_32(cam->nbuffers * PAGE_ALIGN(imagesize)))) + if ((buff = vmalloc_32_user(cam->nbuffers * + PAGE_ALIGN(imagesize)))) break; cam->nbuffers--; } @@ -430,7 +431,8 @@ static int zc0301_start_transfer(struct zc0301_device* cam) struct usb_host_interface* altsetting = usb_altnum_to_altsetting( usb_ifnum_to_if(udev, 0), ZC0301_ALTERNATE_SETTING); - const unsigned int psz = altsetting->endpoint[0].desc.wMaxPacketSize; + const unsigned int psz = le16_to_cpu(altsetting-> + endpoint[0].desc.wMaxPacketSize); struct urb* urb; s8 i, j; int err = 0; @@ -489,7 +491,7 @@ static int zc0301_start_transfer(struct zc0301_device* cam) return 0; free_urbs: - for (i = 0; i < ZC0301_URBS; i++) + for (i = 0; (i < ZC0301_URBS) && cam->urb[i]; i++) usb_free_urb(cam->urb[i]); free_buffers: @@ -1288,6 +1290,35 @@ zc0301_vidioc_s_crop(struct zc0301_device* cam, void __user * arg) static int +zc0301_vidioc_enum_framesizes(struct zc0301_device* cam, void __user * arg) +{ + struct v4l2_frmsizeenum frmsize; + + if (copy_from_user(&frmsize, arg, sizeof(frmsize))) + return -EFAULT; + + if (frmsize.index != 0 && frmsize.index != 1) + return -EINVAL; + + if (frmsize.pixel_format != V4L2_PIX_FMT_JPEG) + return -EINVAL; + + frmsize.type = V4L2_FRMSIZE_TYPE_DISCRETE; + + if (frmsize.index == 1) { + frmsize.discrete.width = cam->sensor.cropcap.defrect.width; + frmsize.discrete.height = cam->sensor.cropcap.defrect.height; + } + memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); + + if (copy_to_user(arg, &frmsize, sizeof(frmsize))) + return -EFAULT; + + return 0; +} + + +static int zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) { struct v4l2_fmtdesc fmtd; @@ -1295,6 +1326,9 @@ zc0301_vidioc_enum_fmt(struct zc0301_device* cam, void __user * arg) if (copy_from_user(&fmtd, arg, sizeof(fmtd))) return -EFAULT; + if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (fmtd.index == 0) { strcpy(fmtd.description, "JPEG"); fmtd.pixelformat = V4L2_PIX_FMT_JPEG; @@ -1795,6 +1829,9 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_S_FMT: return zc0301_vidioc_try_s_fmt(cam, cmd, arg); + case VIDIOC_ENUM_FRAMESIZES: + return zc0301_vidioc_enum_framesizes(cam, arg); + case VIDIOC_G_JPEGCOMP: return zc0301_vidioc_g_jpegcomp(cam, arg); @@ -1830,6 +1867,7 @@ static int zc0301_ioctl_v4l2(struct inode* inode, struct file* filp, case VIDIOC_QUERYSTD: case VIDIOC_ENUMSTD: case VIDIOC_QUERYMENU: + case VIDIOC_ENUM_FRAMEINTERVALS: return -EINVAL; default: @@ -1876,6 +1914,7 @@ static const struct file_operations zc0301_fops = { .open = zc0301_open, .release = zc0301_release, .ioctl = zc0301_ioctl, + .compat_ioctl = v4l_compat_ioctl32, .read = zc0301_read, .poll = zc0301_poll, .mmap = zc0301_mmap, @@ -1913,7 +1952,7 @@ zc0301_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) mutex_init(&cam->dev_mutex); DBG(2, "ZC0301[P] Image Processor and Control Chip detected " - "(vid/pid 0x%04X/0x%04X)",id->idVendor, id->idProduct); + "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); for (i = 0; zc0301_sensor_table[i]; i++) { err = zc0301_sensor_table[i](cam); diff --git a/drivers/media/video/zc0301/zc0301_pas202bcb.c b/drivers/media/video/zc0301/zc0301_pas202bcb.c index ecfd39a..3efb92a 100644 --- a/drivers/media/video/zc0301/zc0301_pas202bcb.c +++ b/drivers/media/video/zc0301/zc0301_pas202bcb.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PAS202BCB image sensor connected to the ZC0301[P] Image * + * Plug-in for PAS202BCB image sensor connected to the ZC0301 Image * * Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard <mxhaard@magic.fr> * diff --git a/drivers/media/video/zc0301/zc0301_pb0330.c b/drivers/media/video/zc0301/zc0301_pb0330.c index ed8542e..5784b1d 100644 --- a/drivers/media/video/zc0301/zc0301_pb0330.c +++ b/drivers/media/video/zc0301/zc0301_pb0330.c @@ -1,8 +1,8 @@ /*************************************************************************** - * Plug-in for PB-0330 image sensor connected to the ZC0301[P] Image * + * Plug-in for PB-0330 image sensor connected to the ZC0301P Image * * Processor and Control Chip * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * Initialization values of the ZC0301[P] have been taken from the SPCA5XX * * driver maintained by Michel Xhaard <mxhaard@magic.fr> * diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h index 3daf049..44e82cf 100644 --- a/drivers/media/video/zc0301/zc0301_sensor.h +++ b/drivers/media/video/zc0301/zc0301_sensor.h @@ -1,8 +1,8 @@ /*************************************************************************** - * API for image sensors connected to the ZC0301 Image Processor and * + * API for image sensors connected to the ZC0301[P] Image Processor and * * Control Chip * * * - * Copyright (C) 2006 by Luca Risolia <luca.risolia@studio.unibo.it> * + * Copyright (C) 2006-2007 by Luca Risolia <luca.risolia@studio.unibo.it> * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -70,7 +70,7 @@ static const struct usb_device_id zc0301_id_table[] = { \ { ZC0301_USB_DEVICE(0x041e, 0x4036, 0xff), }, /* HV7131 */ \ { ZC0301_USB_DEVICE(0x041e, 0x403a, 0xff), }, /* HV7131 */ \ { ZC0301_USB_DEVICE(0x0458, 0x7007, 0xff), }, /* TAS5130 */ \ - { ZC0301_USB_DEVICE(0x0458, 0x700C, 0xff), }, /* TAS5130 */ \ + { ZC0301_USB_DEVICE(0x0458, 0x700c, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x0458, 0x700f, 0xff), }, /* TAS5130 */ \ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \ { ZC0301_USB_DEVICE(0x055f, 0xd003, 0xff), }, /* TAS5130 */ \ @@ -93,9 +93,9 @@ extern int zc0301_i2c_read(struct zc0301_device*, u16 address, u8 length); /*****************************************************************************/ -#define ZC0301_MAX_CTRLS V4L2_CID_LASTP1-V4L2_CID_BASE+10 -#define ZC0301_V4L2_CID_DAC_MAGNITUDE V4L2_CID_PRIVATE_BASE -#define ZC0301_V4L2_CID_GREEN_BALANCE V4L2_CID_PRIVATE_BASE + 1 +#define ZC0301_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) +#define ZC0301_V4L2_CID_DAC_MAGNITUDE (V4L2_CID_PRIVATE_BASE + 0) +#define ZC0301_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 1) struct zc0301_sensor { char name[32]; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c new file mode 100644 index 0000000..b5d3364 --- /dev/null +++ b/drivers/media/video/zr364xx.c @@ -0,0 +1,929 @@ +/* + * Zoran 364xx based USB webcam module version 0.72 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy developpement ! + * + * Copyright (C) 2004 Antoine Jacquet <royale@zerezo.com> + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include <linux/version.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/vmalloc.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/highmem.h> +#include <media/v4l2-common.h> + + +/* Version Information */ +#define DRIVER_VERSION "v0.72" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 2 +#define MAX_FRAME_SIZE 100000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + + +/* Debug macro */ +#define DBG(x...) if (debug) info(x) + + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 + + +/* Module parameters */ +static int debug = 0; +static int mode = 0; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct video_device *vdev; /* v4l video device */ + u8 *framebuf; + int nb; + unsigned char *buffer; + int skip; + int brightness; + int width; + int height; + int method; + struct mutex lock; +}; + + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); + if (!transfer_buffer) { + info("kmalloc(%d) failed", size); + return -ENOMEM; + } + + memcpy(transfer_buffer, cp, size); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + + if (status < 0) + info("Failed sending control message, error %d.", status); + + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[3] = { m0, m1, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 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, + 0xFF, 0xC4, 0x00, 0xB5, 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, 0xFF, 0xC4, 0x00, 0x1F, + 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, 0xFF, 0xC4, 0x00, 0xB5, + 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, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + + + +/********************/ +/* V4L2 integration */ +/********************/ + +/* this function reads a full JPEG picture synchronously + * TODO: do it asynchronously... */ +static int read_frame(struct zr364xx_camera *cam, int framenum) +{ + int i, n, temp, head, size, actual_length; + unsigned char *ptr = NULL, *jpeg; + + redo: + /* hardware brightness */ + n = send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - cam->brightness; + n = send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + + /* during the first loop we are going to insert JPEG header */ + head = 0; + /* this is the place in memory where we are going to build + * the JPEG image */ + jpeg = cam->framebuf + framenum * MAX_FRAME_SIZE; + /* read data... */ + do { + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, 0x81), + cam->buffer, BUFFER_SIZE, &actual_length, + CTRL_TIMEOUT); + DBG("buffer : %d %d", cam->buffer[0], cam->buffer[1]); + DBG("bulk : n=%d size=%d", n, actual_length); + if (n < 0) { + info("error reading bulk msg"); + return 0; + } + if (actual_length < 0 || actual_length > BUFFER_SIZE) { + info("wrong number of bytes"); + return 0; + } + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16*)cam->buffer; + for (i = 0; i < BUFFER_SIZE/2; i++) + swab16s(buf + i); + } + + /* write the JPEG header */ + if (!head) { + DBG("jpeg header"); + ptr = jpeg; + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, cam->buffer + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, cam->buffer + 128, + actual_length - 128); + ptr += actual_length - 128; + head = 1; + DBG("header : %d %d %d %d %d %d %d %d %d", + cam->buffer[0], cam->buffer[1], cam->buffer[2], + cam->buffer[3], cam->buffer[4], cam->buffer[5], + cam->buffer[6], cam->buffer[7], cam->buffer[8]); + } else { + memcpy(ptr, cam->buffer, actual_length); + ptr += actual_length; + } + } + /* ... until there is no more */ + while (actual_length == BUFFER_SIZE); + + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) { + cam->skip--; + goto redo; + } + + /* go back to find the JPEG EOI marker */ + size = ptr - jpeg; + ptr -= 2; + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == jpeg) + DBG("No EOI marker"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > jpeg) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != jpeg) { + DBG("Bogus frame ? %d", cam->nb); + goto redo; + } + + DBG("jpeg : %d %d %d %d %d %d %d %d", + jpeg[0], jpeg[1], jpeg[2], jpeg[3], + jpeg[4], jpeg[5], jpeg[6], jpeg[7]); + + return size; +} + + +static ssize_t zr364xx_read(struct file *file, char *buf, size_t cnt, + loff_t * ppos) +{ + unsigned long count = cnt; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_read: read %d bytes.", (int) count); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + /* NoMan Sux ! */ + count = read_frame(cam, 0); + + if (copy_to_user(buf, cam->framebuf, count)) + return -EFAULT; + + return count; +} + + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + memset(cap, 0, sizeof(*cap)); + strcpy(cap->driver, DRIVER_DESC); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + memset(i, 0, sizeof(*i)); + i->index = 0; + strcpy(i->name, DRIVER_DESC " Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->type = V4L2_CTRL_TYPE_INTEGER; + strcpy(c->name, "Brightness"); + c->minimum = 0; + c->maximum = 127; + c->step = 1; + c->default_value = cam->brightness; + c->flags = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + cam->brightness = c->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *c) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = cam->brightness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int zr364xx_vidioc_enum_fmt_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = 0; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, "JPEG"); + f->pixelformat = V4L2_PIX_FMT_JPEG; + return 0; +} + +static int zr364xx_vidioc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_g_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_s_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) + return -EINVAL; + if (f->fmt.pix.field != V4L2_FIELD_ANY && + f->fmt.pix.field != V4L2_FIELD_NONE) + return -EINVAL; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + DBG("ok!"); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + return 0; +} + + +/* open the camera */ +static int zr364xx_open(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam = video_get_drvdata(vdev); + struct usb_device *udev = cam->udev; + int i, err; + + DBG("zr364xx_open"); + + cam->skip = 2; + + err = video_exclusive_open(inode, file); + if (err < 0) + return err; + + if (!cam->framebuf) { + cam->framebuf = vmalloc_32(MAX_FRAME_SIZE * FRAMES); + if (!cam->framebuf) { + info("vmalloc_32 failed!"); + return -ENOMEM; + } + } + + mutex_lock(&cam->lock); + for (i = 0; init[cam->method][i].size != -1; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during open sequence: %d", i); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = vdev; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +/* release the camera */ +static int zr364xx_release(struct inode *inode, struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + struct usb_device *udev; + int i, err; + + DBG("zr364xx_release"); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + udev = cam->udev; + + mutex_lock(&cam->lock); + for (i = 0; i < 2; i++) { + err = + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[i][cam->method].bytes, + init[cam->method][i].size); + if (err < 0) { + info("error during release sequence"); + mutex_unlock(&cam->lock); + return err; + } + } + + file->private_data = NULL; + video_exclusive_release(inode, file); + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + + mutex_unlock(&cam->lock); + return 0; +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + void *pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end - vma->vm_start; + struct video_device *vdev = video_devdata(file); + struct zr364xx_camera *cam; + + DBG("zr364xx_mmap: %ld\n", size); + + if (vdev == NULL) + return -ENODEV; + cam = video_get_drvdata(vdev); + + pos = cam->framebuf; + while (size > 0) { + if (vm_insert_page(vma, start, vmalloc_to_page(pos))) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + + +static struct file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_release, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .ioctl = video_ioctl2, + .llseek = no_llseek, +}; + +static struct video_device zr364xx_template = { + .owner = THIS_MODULE, + .name = DRIVER_DESC, + .type = VID_TYPE_CAPTURE, + .fops = &zr364xx_fops, + .release = video_device_release, + .minor = -1, + + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_cap = zr364xx_vidioc_enum_fmt_cap, + .vidioc_try_fmt_cap = zr364xx_vidioc_try_fmt_cap, + .vidioc_s_fmt_cap = zr364xx_vidioc_s_fmt_cap, + .vidioc_g_fmt_cap = zr364xx_vidioc_g_fmt_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_queryctrl = zr364xx_vidioc_queryctrl, + .vidioc_g_ctrl = zr364xx_vidioc_g_ctrl, + .vidioc_s_ctrl = zr364xx_vidioc_s_ctrl, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + + DBG("probing..."); + + info(DRIVER_DESC " compatible webcam plugged"); + info("model %04x:%04x detected", udev->descriptor.idVendor, + udev->descriptor.idProduct); + + if ((cam = + kmalloc(sizeof(struct zr364xx_camera), GFP_KERNEL)) == NULL) { + info("cam: out of memory !"); + return -ENODEV; + } + memset(cam, 0x00, sizeof(struct zr364xx_camera)); + /* save the init method used by this camera */ + cam->method = id->driver_info; + + cam->vdev = video_device_alloc(); + if (cam->vdev == NULL) { + info("cam->vdev: out of memory !"); + kfree(cam); + return -ENODEV; + } + memcpy(cam->vdev, &zr364xx_template, sizeof(zr364xx_template)); + video_set_drvdata(cam->vdev, cam); + if (debug) + cam->vdev->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + + cam->udev = udev; + + if ((cam->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL)) == NULL) { + info("cam->buffer: out of memory !"); + video_device_release(cam->vdev); + kfree(cam); + return -ENODEV; + } + + switch (mode) { + case 1: + info("160x120 mode selected"); + cam->width = 160; + cam->height = 120; + break; + case 2: + info("640x480 mode selected"); + cam->width = 640; + cam->height = 480; + break; + default: + info("320x240 mode selected"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + cam->brightness = 64; + mutex_init(&cam->lock); + + if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, -1) == -1) { + info("video_register_device failed"); + video_device_release(cam->vdev); + kfree(cam->buffer); + kfree(cam); + return -ENODEV; + } + + usb_set_intfdata(intf, cam); + + info(DRIVER_DESC " controlling video device %d", cam->vdev->minor); + return 0; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + dev_set_drvdata(&intf->dev, NULL); + info(DRIVER_DESC " webcam unplugged"); + if (cam->vdev) + video_unregister_device(cam->vdev); + cam->vdev = NULL; + kfree(cam->buffer); + if (cam->framebuf) + vfree(cam->framebuf); + kfree(cam); +} + + + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, + .id_table = device_table +}; + + +static int __init zr364xx_init(void) +{ + int retval; + retval = usb_register(&zr364xx_driver) < 0; + if (retval) + info("usb_register failed!"); + else + info(DRIVER_DESC " module loaded"); + return retval; +} + + +static void __exit zr364xx_exit(void) +{ + info(DRIVER_DESC " module unloaded"); + usb_deregister(&zr364xx_driver); +} + + +module_init(zr364xx_init); +module_exit(zr364xx_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c index b691292..7dd34bd 100644 --- a/drivers/message/fusion/mptlan.c +++ b/drivers/message/fusion/mptlan.c @@ -714,6 +714,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) LANSendRequest_t *pSendReq; SGETransaction32_t *pTrans; SGESimple64_t *pSimple; + const unsigned char *mac; dma_addr_t dma; unsigned long flags; int ctx; @@ -753,7 +754,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) /* Set the mac.raw pointer, since this apparently isn't getting * done before we get the skb. Pull the data pointer past the mac data. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, 12); dma = pci_map_single(mpt_dev->pcidev, skb->data, skb->len, @@ -784,6 +785,7 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) // IOC_AND_NETDEV_NAMES_s_s(dev), // ctx, skb, skb->data)); + mac = skb_mac_header(skb); #ifdef QLOGIC_NAA_WORKAROUND { struct NAA_Hosed *nh; @@ -793,12 +795,12 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) drops. */ read_lock_irq(&bad_naa_lock); for (nh = mpt_bad_naa; nh != NULL; nh=nh->next) { - if ((nh->ieee[0] == skb->mac.raw[0]) && - (nh->ieee[1] == skb->mac.raw[1]) && - (nh->ieee[2] == skb->mac.raw[2]) && - (nh->ieee[3] == skb->mac.raw[3]) && - (nh->ieee[4] == skb->mac.raw[4]) && - (nh->ieee[5] == skb->mac.raw[5])) { + if ((nh->ieee[0] == mac[0]) && + (nh->ieee[1] == mac[1]) && + (nh->ieee[2] == mac[2]) && + (nh->ieee[3] == mac[3]) && + (nh->ieee[4] == mac[4]) && + (nh->ieee[5] == mac[5])) { cur_naa = nh->NAA; dlprintk ((KERN_INFO "mptlan/sdu_send: using NAA value " "= %04x.\n", cur_naa)); @@ -810,12 +812,12 @@ mpt_lan_sdu_send (struct sk_buff *skb, struct net_device *dev) #endif pTrans->TransactionDetails[0] = cpu_to_le32((cur_naa << 16) | - (skb->mac.raw[0] << 8) | - (skb->mac.raw[1] << 0)); - pTrans->TransactionDetails[1] = cpu_to_le32((skb->mac.raw[2] << 24) | - (skb->mac.raw[3] << 16) | - (skb->mac.raw[4] << 8) | - (skb->mac.raw[5] << 0)); + (mac[0] << 8) | + (mac[1] << 0)); + pTrans->TransactionDetails[1] = cpu_to_le32((mac[2] << 24) | + (mac[3] << 16) | + (mac[4] << 8) | + (mac[5] << 0)); pSimple = (SGESimple64_t *) &pTrans->TransactionDetails[2]; @@ -930,7 +932,7 @@ mpt_lan_receive_post_turbo(struct net_device *dev, u32 tmsg) pci_dma_sync_single_for_cpu(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, len), old_skb->data, len); + skb_copy_from_linear_data(old_skb, skb_put(skb, len), len); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); @@ -1091,7 +1093,7 @@ mpt_lan_receive_post_reply(struct net_device *dev, priv->RcvCtl[ctx].dma, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, l), old_skb->data, l); + skb_copy_from_linear_data(old_skb, skb_put(skb, l), l); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, @@ -1120,7 +1122,7 @@ mpt_lan_receive_post_reply(struct net_device *dev, priv->RcvCtl[ctx].len, PCI_DMA_FROMDEVICE); - memcpy(skb_put(skb, len), old_skb->data, len); + skb_copy_from_linear_data(old_skb, skb_put(skb, len), len); pci_dma_sync_single_for_device(mpt_dev->pcidev, priv->RcvCtl[ctx].dma, @@ -1549,7 +1551,7 @@ mpt_lan_type_trans(struct sk_buff *skb, struct net_device *dev) struct mpt_lan_ohdr *fch = (struct mpt_lan_ohdr *)skb->data; struct fcllc *fcllc; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, sizeof(struct mpt_lan_ohdr)); if (fch->dtype == htons(0xffff)) { diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 404c014..1d2d03f 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -815,7 +815,7 @@ mptsas_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) static int mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) { - MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + MPT_SCSI_HOST *hd; struct mptsas_target_reset_event *target_reset_list, *n; int rc; @@ -827,7 +827,10 @@ mptsas_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) if (reset_phase != MPT_IOC_POST_RESET) goto out; - if (!hd || !hd->ioc) + if (!ioc->sh || !ioc->sh->hostdata) + goto out; + hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + if (!hd->ioc) goto out; if (list_empty(&hd->target_reset_list)) diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c index da9859f..b17c4b2 100644 --- a/drivers/message/i2o/i2o_block.c +++ b/drivers/message/i2o/i2o_block.c @@ -390,13 +390,6 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) return BLKPREP_KILL; } - /* request is already processed by us, so return */ - if (blk_special_request(req)) { - osm_debug("REQ_SPECIAL already set!\n"); - req->cmd_flags |= REQ_DONTPREP; - return BLKPREP_OK; - } - /* connect the i2o_block_request to the request */ if (!req->special) { ireq = i2o_block_request_alloc(); @@ -408,11 +401,8 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req) ireq->i2o_blk_dev = i2o_blk_dev; req->special = ireq; ireq->req = req; - } else - ireq = req->special; - + } /* do not come back here */ - req->cmd_type = REQ_TYPE_SPECIAL; req->cmd_flags |= REQ_DONTPREP; return BLKPREP_OK; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fc3c885..ab6e985 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2,6 +2,20 @@ # Multifunction miscellaneous devices # +menu "Multifunction device drivers" + +config MFD_SM501 + tristate "Support for Silicon Motion SM501" + ---help--- + This is the core driver for the Silicon Motion SM501 multimedia + companion chip. This device is a multifunction device which may + provide numerous interfaces including USB host controller USB gadget, + Asyncronous Serial ports, Audio functions and a dual display video + interface. The device may be connected by PCI or local bus with + varying functions enabled. + +endmenu + menu "Multimedia Capabilities Port drivers" depends on ARCH_SA1100 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index adb29b5..5143209 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -2,6 +2,8 @@ # Makefile for multifunction miscellaneous devices # +obj-$(CONFIG_MFD_SM501) += sm501.o + obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c new file mode 100644 index 0000000..b0b4458 --- /dev/null +++ b/drivers/mfd/sm501.c @@ -0,0 +1,1148 @@ +/* linux/drivers/mfd/sm501.c + * + * Copyright (C) 2006 Simtec Electronics + * Ben Dooks <ben@simtec.co.uk> + * Vincent Sanders <vince@simtec.co.uk> + * + * 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. + * + * SM501 MFD driver +*/ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/pci.h> + +#include <linux/sm501.h> +#include <linux/sm501-regs.h> + +#include <asm/io.h> + +struct sm501_device { + struct list_head list; + struct platform_device pdev; +}; + +struct sm501_devdata { + spinlock_t reg_lock; + struct mutex clock_lock; + struct list_head devices; + + struct device *dev; + struct resource *io_res; + struct resource *mem_res; + struct resource *regs_claim; + struct sm501_platdata *platdata; + + int unit_power[20]; + unsigned int pdev_id; + unsigned int irq; + void __iomem *regs; +}; + +#define MHZ (1000 * 1000) + +#ifdef DEBUG +static const unsigned int misc_div[] = { + [0] = 1, + [1] = 2, + [2] = 4, + [3] = 8, + [4] = 16, + [5] = 32, + [6] = 64, + [7] = 128, + [8] = 3, + [9] = 6, + [10] = 12, + [11] = 24, + [12] = 48, + [13] = 96, + [14] = 192, + [15] = 384, +}; + +static const unsigned int px_div[] = { + [0] = 1, + [1] = 2, + [2] = 4, + [3] = 8, + [4] = 16, + [5] = 32, + [6] = 64, + [7] = 128, + [8] = 3, + [9] = 6, + [10] = 12, + [11] = 24, + [12] = 48, + [13] = 96, + [14] = 192, + [15] = 384, + [16] = 5, + [17] = 10, + [18] = 20, + [19] = 40, + [20] = 80, + [21] = 160, + [22] = 320, + [23] = 604, +}; + +static unsigned long decode_div(unsigned long pll2, unsigned long val, + unsigned int lshft, unsigned int selbit, + unsigned long mask, const unsigned int *dtab) +{ + if (val & selbit) + pll2 = 288 * MHZ; + + return pll2 / dtab[(val >> lshft) & mask]; +} + +#define fmt_freq(x) ((x) / MHZ), ((x) % MHZ), (x) + +/* sm501_dump_clk + * + * Print out the current clock configuration for the device +*/ + +static void sm501_dump_clk(struct sm501_devdata *sm) +{ + unsigned long misct = readl(sm->regs + SM501_MISC_TIMING); + unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK); + unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK); + unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL); + unsigned long sdclk0, sdclk1; + unsigned long pll2 = 0; + + switch (misct & 0x30) { + case 0x00: + pll2 = 336 * MHZ; + break; + case 0x10: + pll2 = 288 * MHZ; + break; + case 0x20: + pll2 = 240 * MHZ; + break; + case 0x30: + pll2 = 192 * MHZ; + break; + } + + sdclk0 = (misct & (1<<12)) ? pll2 : 288 * MHZ; + sdclk0 /= misc_div[((misct >> 8) & 0xf)]; + + sdclk1 = (misct & (1<<20)) ? pll2 : 288 * MHZ; + sdclk1 /= misc_div[((misct >> 16) & 0xf)]; + + dev_dbg(sm->dev, "MISCT=%08lx, PM0=%08lx, PM1=%08lx\n", + misct, pm0, pm1); + + dev_dbg(sm->dev, "PLL2 = %ld.%ld MHz (%ld), SDCLK0=%08lx, SDCLK1=%08lx\n", + fmt_freq(pll2), sdclk0, sdclk1); + + dev_dbg(sm->dev, "SDRAM: PM0=%ld, PM1=%ld\n", sdclk0, sdclk1); + + dev_dbg(sm->dev, "PM0[%c]: " + "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " +x "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", + (pmc & 3 ) == 0 ? '*' : '-', + fmt_freq(decode_div(pll2, pm0, 24, 1<<29, 31, px_div)), + fmt_freq(decode_div(pll2, pm0, 16, 1<<20, 15, misc_div)), + fmt_freq(decode_div(pll2, pm0, 8, 1<<12, 15, misc_div)), + fmt_freq(decode_div(pll2, pm0, 0, 1<<4, 15, misc_div))); + + dev_dbg(sm->dev, "PM1[%c]: " + "P2 %ld.%ld MHz (%ld), V2 %ld.%ld (%ld), " + "M %ld.%ld (%ld), MX1 %ld.%ld (%ld)\n", + (pmc & 3 ) == 1 ? '*' : '-', + fmt_freq(decode_div(pll2, pm1, 24, 1<<29, 31, px_div)), + fmt_freq(decode_div(pll2, pm1, 16, 1<<20, 15, misc_div)), + fmt_freq(decode_div(pll2, pm1, 8, 1<<12, 15, misc_div)), + fmt_freq(decode_div(pll2, pm1, 0, 1<<4, 15, misc_div))); +} +#else +static void sm501_dump_clk(struct sm501_devdata *sm) +{ +} +#endif + +/* sm501_sync_regs + * + * ensure the +*/ + +static void sm501_sync_regs(struct sm501_devdata *sm) +{ + readl(sm->regs); +} + +/* sm501_misc_control + * + * alters the misceleneous control parameters +*/ + +int sm501_misc_control(struct device *dev, + unsigned long set, unsigned long clear) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long misc; + unsigned long save; + unsigned long to; + + spin_lock_irqsave(&sm->reg_lock, save); + + misc = readl(sm->regs + SM501_MISC_CONTROL); + to = (misc & ~clear) | set; + + if (to != misc) { + writel(to, sm->regs + SM501_MISC_CONTROL); + sm501_sync_regs(sm); + + dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); + } + + spin_unlock_irqrestore(&sm->reg_lock, save); + return to; +} + +EXPORT_SYMBOL_GPL(sm501_misc_control); + +/* sm501_modify_reg + * + * Modify a register in the SM501 which may be shared with other + * drivers. +*/ + +unsigned long sm501_modify_reg(struct device *dev, + unsigned long reg, + unsigned long set, + unsigned long clear) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long data; + unsigned long save; + + spin_lock_irqsave(&sm->reg_lock, save); + + data = readl(sm->regs + reg); + data |= set; + data &= ~clear; + + writel(data, sm->regs + reg); + sm501_sync_regs(sm); + + spin_unlock_irqrestore(&sm->reg_lock, save); + + return data; +} + +EXPORT_SYMBOL_GPL(sm501_modify_reg); + +unsigned long sm501_gpio_get(struct device *dev, + unsigned long gpio) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long result; + unsigned long reg; + + reg = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; + result = readl(sm->regs + reg); + + result >>= (gpio & 31); + return result & 1UL; +} + +EXPORT_SYMBOL_GPL(sm501_gpio_get); + +void sm501_gpio_set(struct device *dev, + unsigned long gpio, + unsigned int to, + unsigned int dir) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + + unsigned long bit = 1 << (gpio & 31); + unsigned long base; + unsigned long save; + unsigned long val; + + base = (gpio > 32) ? SM501_GPIO_DATA_HIGH : SM501_GPIO_DATA_LOW; + base += SM501_GPIO; + + spin_lock_irqsave(&sm->reg_lock, save); + + val = readl(sm->regs + base) & ~bit; + if (to) + val |= bit; + writel(val, sm->regs + base); + + val = readl(sm->regs + SM501_GPIO_DDR_LOW) & ~bit; + if (dir) + val |= bit; + + writel(val, sm->regs + SM501_GPIO_DDR_LOW); + sm501_sync_regs(sm); + + spin_unlock_irqrestore(&sm->reg_lock, save); + +} + +EXPORT_SYMBOL_GPL(sm501_gpio_set); + + +/* sm501_unit_power + * + * alters the power active gate to set specific units on or off + */ + +int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long mode; + unsigned long gate; + unsigned long clock; + + mutex_lock(&sm->clock_lock); + + mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); + gate = readl(sm->regs + SM501_CURRENT_GATE); + clock = readl(sm->regs + SM501_CURRENT_CLOCK); + + mode &= 3; /* get current power mode */ + + if (unit >= ARRAY_SIZE(sm->unit_power)) { + dev_err(dev, "%s: bad unit %d\n", __FUNCTION__, unit); + goto already; + } + + dev_dbg(sm->dev, "%s: unit %d, cur %d, to %d\n", __FUNCTION__, unit, + sm->unit_power[unit], to); + + if (to == 0 && sm->unit_power[unit] == 0) { + dev_err(sm->dev, "unit %d is already shutdown\n", unit); + goto already; + } + + sm->unit_power[unit] += to ? 1 : -1; + to = sm->unit_power[unit] ? 1 : 0; + + if (to) { + if (gate & (1 << unit)) + goto already; + gate |= (1 << unit); + } else { + if (!(gate & (1 << unit))) + goto already; + gate &= ~(1 << unit); + } + + switch (mode) { + case 1: + writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); + writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); + mode = 0; + break; + case 2: + case 0: + writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); + writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); + mode = 1; + break; + + default: + return -1; + } + + writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); + sm501_sync_regs(sm); + + dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", + gate, clock, mode); + + msleep(16); + + already: + mutex_unlock(&sm->clock_lock); + return gate; +} + +EXPORT_SYMBOL_GPL(sm501_unit_power); + + +/* Perform a rounded division. */ +static long sm501fb_round_div(long num, long denom) +{ + /* n / d + 1 / 2 = (2n + d) / 2d */ + return (2 * num + denom) / (2 * denom); +} + +/* clock value structure. */ +struct sm501_clock { + unsigned long mclk; + int divider; + int shift; +}; + +/* sm501_select_clock + * + * selects nearest discrete clock frequency the SM501 can achive + * the maximum divisor is 3 or 5 + */ +static unsigned long sm501_select_clock(unsigned long freq, + struct sm501_clock *clock, + int max_div) +{ + unsigned long mclk; + int divider; + int shift; + long diff; + long best_diff = 999999999; + + /* Try 288MHz and 336MHz clocks. */ + for (mclk = 288000000; mclk <= 336000000; mclk += 48000000) { + /* try dividers 1 and 3 for CRT and for panel, + try divider 5 for panel only.*/ + + for (divider = 1; divider <= max_div; divider += 2) { + /* try all 8 shift values.*/ + for (shift = 0; shift < 8; shift++) { + /* Calculate difference to requested clock */ + diff = sm501fb_round_div(mclk, divider << shift) - freq; + if (diff < 0) + diff = -diff; + + /* If it is less than the current, use it */ + if (diff < best_diff) { + best_diff = diff; + + clock->mclk = mclk; + clock->divider = divider; + clock->shift = shift; + } + } + } + } + + /* Return best clock. */ + return clock->mclk / (clock->divider << clock->shift); +} + +/* sm501_set_clock + * + * set one of the four clock sources to the closest available frequency to + * the one specified +*/ + +unsigned long sm501_set_clock(struct device *dev, + int clksrc, + unsigned long req_freq) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev); + unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); + unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE); + unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK); + unsigned char reg; + unsigned long sm501_freq; /* the actual frequency acheived */ + + struct sm501_clock to; + + /* find achivable discrete frequency and setup register value + * accordingly, V2XCLK, MCLK and M1XCLK are the same P2XCLK + * has an extra bit for the divider */ + + switch (clksrc) { + case SM501_CLOCK_P2XCLK: + /* This clock is divided in half so to achive the + * requested frequency the value must be multiplied by + * 2. This clock also has an additional pre divisor */ + + sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); + reg=to.shift & 0x07;/* bottom 3 bits are shift */ + if (to.divider == 3) + reg |= 0x08; /* /3 divider required */ + else if (to.divider == 5) + reg |= 0x10; /* /5 divider required */ + if (to.mclk != 288000000) + reg |= 0x20; /* which mclk pll is source */ + break; + + case SM501_CLOCK_V2XCLK: + /* This clock is divided in half so to achive the + * requested frequency the value must be multiplied by 2. */ + + sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); + reg=to.shift & 0x07; /* bottom 3 bits are shift */ + if (to.divider == 3) + reg |= 0x08; /* /3 divider required */ + if (to.mclk != 288000000) + reg |= 0x10; /* which mclk pll is source */ + break; + + case SM501_CLOCK_MCLK: + case SM501_CLOCK_M1XCLK: + /* These clocks are the same and not further divided */ + + sm501_freq = sm501_select_clock( req_freq, &to, 3); + reg=to.shift & 0x07; /* bottom 3 bits are shift */ + if (to.divider == 3) + reg |= 0x08; /* /3 divider required */ + if (to.mclk != 288000000) + reg |= 0x10; /* which mclk pll is source */ + break; + + default: + return 0; /* this is bad */ + } + + mutex_lock(&sm->clock_lock); + + mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); + gate = readl(sm->regs + SM501_CURRENT_GATE); + clock = readl(sm->regs + SM501_CURRENT_CLOCK); + + clock = clock & ~(0xFF << clksrc); + clock |= reg<<clksrc; + + mode &= 3; /* find current mode */ + + switch (mode) { + case 1: + writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); + writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); + mode = 0; + break; + case 2: + case 0: + writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); + writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); + mode = 1; + break; + + default: + mutex_unlock(&sm->clock_lock); + return -1; + } + + writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); + sm501_sync_regs(sm); + + dev_info(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", + gate, clock, mode); + + msleep(16); + mutex_unlock(&sm->clock_lock); + + sm501_dump_clk(sm); + + return sm501_freq; +} + +EXPORT_SYMBOL_GPL(sm501_set_clock); + +/* sm501_find_clock + * + * finds the closest available frequency for a given clock +*/ + +unsigned long sm501_find_clock(int clksrc, + unsigned long req_freq) +{ + unsigned long sm501_freq; /* the frequency achiveable by the 501 */ + struct sm501_clock to; + + switch (clksrc) { + case SM501_CLOCK_P2XCLK: + sm501_freq = (sm501_select_clock(2 * req_freq, &to, 5) / 2); + break; + + case SM501_CLOCK_V2XCLK: + sm501_freq = (sm501_select_clock(2 * req_freq, &to, 3) / 2); + break; + + case SM501_CLOCK_MCLK: + case SM501_CLOCK_M1XCLK: + sm501_freq = sm501_select_clock(req_freq, &to, 3); + break; + + default: + sm501_freq = 0; /* error */ + } + + return sm501_freq; +} + +EXPORT_SYMBOL_GPL(sm501_find_clock); + +static struct sm501_device *to_sm_device(struct platform_device *pdev) +{ + return container_of(pdev, struct sm501_device, pdev); +} + +/* sm501_device_release + * + * A release function for the platform devices we create to allow us to + * free any items we allocated +*/ + +static void sm501_device_release(struct device *dev) +{ + kfree(to_sm_device(to_platform_device(dev))); +} + +/* sm501_create_subdev + * + * Create a skeleton platform device with resources for passing to a + * sub-driver +*/ + +static struct platform_device * +sm501_create_subdev(struct sm501_devdata *sm, + char *name, unsigned int res_count) +{ + struct sm501_device *smdev; + + smdev = kzalloc(sizeof(struct sm501_device) + + sizeof(struct resource) * res_count, GFP_KERNEL); + if (!smdev) + return NULL; + + smdev->pdev.dev.release = sm501_device_release; + + smdev->pdev.name = name; + smdev->pdev.id = sm->pdev_id; + smdev->pdev.resource = (struct resource *)(smdev+1); + smdev->pdev.num_resources = res_count; + + smdev->pdev.dev.parent = sm->dev; + + return &smdev->pdev; +} + +/* sm501_register_device + * + * Register a platform device created with sm501_create_subdev() +*/ + +static int sm501_register_device(struct sm501_devdata *sm, + struct platform_device *pdev) +{ + struct sm501_device *smdev = to_sm_device(pdev); + int ptr; + int ret; + + for (ptr = 0; ptr < pdev->num_resources; ptr++) { + printk("%s[%d] flags %08lx: %08llx..%08llx\n", + pdev->name, ptr, + pdev->resource[ptr].flags, + (unsigned long long)pdev->resource[ptr].start, + (unsigned long long)pdev->resource[ptr].end); + } + + ret = platform_device_register(pdev); + + if (ret >= 0) { + dev_dbg(sm->dev, "registered %s\n", pdev->name); + list_add_tail(&smdev->list, &sm->devices); + } else + dev_err(sm->dev, "error registering %s (%d)\n", + pdev->name, ret); + + return ret; +} + +/* sm501_create_subio + * + * Fill in an IO resource for a sub device +*/ + +static void sm501_create_subio(struct sm501_devdata *sm, + struct resource *res, + resource_size_t offs, + resource_size_t size) +{ + res->flags = IORESOURCE_MEM; + res->parent = sm->io_res; + res->start = sm->io_res->start + offs; + res->end = res->start + size - 1; +} + +/* sm501_create_mem + * + * Fill in an MEM resource for a sub device +*/ + +static void sm501_create_mem(struct sm501_devdata *sm, + struct resource *res, + resource_size_t *offs, + resource_size_t size) +{ + *offs -= size; /* adjust memory size */ + + res->flags = IORESOURCE_MEM; + res->parent = sm->mem_res; + res->start = sm->mem_res->start + *offs; + res->end = res->start + size - 1; +} + +/* sm501_create_irq + * + * Fill in an IRQ resource for a sub device +*/ + +static void sm501_create_irq(struct sm501_devdata *sm, + struct resource *res) +{ + res->flags = IORESOURCE_IRQ; + res->parent = NULL; + res->start = res->end = sm->irq; +} + +static int sm501_register_usbhost(struct sm501_devdata *sm, + resource_size_t *mem_avail) +{ + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "sm501-usb", 3); + if (!pdev) + return -ENOMEM; + + sm501_create_subio(sm, &pdev->resource[0], 0x40000, 0x20000); + sm501_create_mem(sm, &pdev->resource[1], mem_avail, 256*1024); + sm501_create_irq(sm, &pdev->resource[2]); + + return sm501_register_device(sm, pdev); +} + +static int sm501_register_display(struct sm501_devdata *sm, + resource_size_t *mem_avail) +{ + struct platform_device *pdev; + + pdev = sm501_create_subdev(sm, "sm501-fb", 4); + if (!pdev) + return -ENOMEM; + + sm501_create_subio(sm, &pdev->resource[0], 0x80000, 0x10000); + sm501_create_subio(sm, &pdev->resource[1], 0x100000, 0x50000); + sm501_create_mem(sm, &pdev->resource[2], mem_avail, *mem_avail); + sm501_create_irq(sm, &pdev->resource[3]); + + return sm501_register_device(sm, pdev); +} + +/* sm501_dbg_regs + * + * Debug attribute to attach to parent device to show core registers +*/ + +static ssize_t sm501_dbg_regs(struct device *dev, + struct device_attribute *attr, char *buff) +{ + struct sm501_devdata *sm = dev_get_drvdata(dev) ; + unsigned int reg; + char *ptr = buff; + int ret; + + for (reg = 0x00; reg < 0x70; reg += 4) { + ret = sprintf(ptr, "%08x = %08x\n", + reg, readl(sm->regs + reg)); + ptr += ret; + } + + return ptr - buff; +} + + +static DEVICE_ATTR(dbg_regs, 0666, sm501_dbg_regs, NULL); + +/* sm501_init_reg + * + * Helper function for the init code to setup a register +*/ + +static inline void sm501_init_reg(struct sm501_devdata *sm, + unsigned long reg, + struct sm501_reg_init *r) +{ + unsigned long tmp; + + tmp = readl(sm->regs + reg); + tmp |= r->set; + tmp &= ~r->mask; + writel(tmp, sm->regs + reg); +} + +/* sm501_init_regs + * + * Setup core register values +*/ + +static void sm501_init_regs(struct sm501_devdata *sm, + struct sm501_initdata *init) +{ + sm501_misc_control(sm->dev, + init->misc_control.set, + init->misc_control.mask); + + sm501_init_reg(sm, SM501_MISC_TIMING, &init->misc_timing); + sm501_init_reg(sm, SM501_GPIO31_0_CONTROL, &init->gpio_low); + sm501_init_reg(sm, SM501_GPIO63_32_CONTROL, &init->gpio_high); + + if (init->mclk) { + dev_info(sm->dev, "setting MCLK to %ld\n", init->mclk); + sm501_set_clock(sm->dev, SM501_CLOCK_MCLK, init->mclk); + } + + if (init->m1xclk) { + dev_info(sm->dev, "setting M1XCLK to %ld\n", init->m1xclk); + sm501_set_clock(sm->dev, SM501_CLOCK_M1XCLK, init->m1xclk); + } +} + +static unsigned int sm501_mem_local[] = { + [0] = 4*1024*1024, + [1] = 8*1024*1024, + [2] = 16*1024*1024, + [3] = 32*1024*1024, + [4] = 64*1024*1024, + [5] = 2*1024*1024, +}; + +/* sm501_init_dev + * + * Common init code for an SM501 +*/ + +static int sm501_init_dev(struct sm501_devdata *sm) +{ + resource_size_t mem_avail; + unsigned long dramctrl; + int ret; + + mutex_init(&sm->clock_lock); + spin_lock_init(&sm->reg_lock); + + INIT_LIST_HEAD(&sm->devices); + + dramctrl = readl(sm->regs + SM501_DRAM_CONTROL); + + mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; + + dev_info(sm->dev, "SM501 At %p: Version %08x, %ld Mb, IRQ %d\n", + sm->regs, readl(sm->regs + SM501_DEVICEID), + (unsigned long)mem_avail >> 20, sm->irq); + + dev_info(sm->dev, "CurrentGate %08x\n", readl(sm->regs+0x38)); + dev_info(sm->dev, "CurrentClock %08x\n", readl(sm->regs+0x3c)); + dev_info(sm->dev, "PowerModeControl %08x\n", readl(sm->regs+0x54)); + + ret = device_create_file(sm->dev, &dev_attr_dbg_regs); + if (ret) + dev_err(sm->dev, "failed to create debug regs file\n"); + + sm501_dump_clk(sm); + + /* check to see if we have some device initialisation */ + + if (sm->platdata) { + struct sm501_platdata *pdata = sm->platdata; + + if (pdata->init) { + sm501_init_regs(sm, sm->platdata->init); + + if (pdata->init->devices & SM501_USE_USB_HOST) + sm501_register_usbhost(sm, &mem_avail); + } + } + + /* always create a framebuffer */ + sm501_register_display(sm, &mem_avail); + + return 0; +} + +static int sm501_plat_probe(struct platform_device *dev) +{ + struct sm501_devdata *sm; + int err; + + sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); + if (sm == NULL) { + dev_err(&dev->dev, "no memory for device data\n"); + err = -ENOMEM; + goto err1; + } + + sm->dev = &dev->dev; + sm->pdev_id = dev->id; + sm->irq = platform_get_irq(dev, 0); + sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); + sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + sm->platdata = dev->dev.platform_data; + + if (sm->irq < 0) { + dev_err(&dev->dev, "failed to get irq resource\n"); + err = sm->irq; + goto err_res; + } + + if (sm->io_res == NULL || sm->mem_res == NULL) { + dev_err(&dev->dev, "failed to get IO resource\n"); + err = -ENOENT; + goto err_res; + } + + sm->regs_claim = request_mem_region(sm->io_res->start, + 0x100, "sm501"); + + if (sm->regs_claim == NULL) { + dev_err(&dev->dev, "cannot claim registers\n"); + err= -EBUSY; + goto err_res; + } + + platform_set_drvdata(dev, sm); + + sm->regs = ioremap(sm->io_res->start, + (sm->io_res->end - sm->io_res->start) - 1); + + if (sm->regs == NULL) { + dev_err(&dev->dev, "cannot remap registers\n"); + err = -EIO; + goto err_claim; + } + + return sm501_init_dev(sm); + + err_claim: + release_resource(sm->regs_claim); + kfree(sm->regs_claim); + err_res: + kfree(sm); + err1: + return err; + +} + +/* Initialisation data for PCI devices */ + +static struct sm501_initdata sm501_pci_initdata = { + .gpio_high = { + .set = 0x3F000000, /* 24bit panel */ + .mask = 0x0, + }, + .misc_timing = { + .set = 0x010100, /* SDRAM timing */ + .mask = 0x1F1F00, + }, + .misc_control = { + .set = SM501_MISC_PNL_24BIT, + .mask = 0, + }, + + .devices = SM501_USE_ALL, + .mclk = 100 * MHZ, + .m1xclk = 160 * MHZ, +}; + +static struct sm501_platdata_fbsub sm501_pdata_fbsub = { + .flags = (SM501FB_FLAG_USE_INIT_MODE | + SM501FB_FLAG_USE_HWCURSOR | + SM501FB_FLAG_USE_HWACCEL | + SM501FB_FLAG_DISABLE_AT_EXIT), +}; + +static struct sm501_platdata_fb sm501_fb_pdata = { + .fb_route = SM501_FB_OWN, + .fb_crt = &sm501_pdata_fbsub, + .fb_pnl = &sm501_pdata_fbsub, +}; + +static struct sm501_platdata sm501_pci_platdata = { + .init = &sm501_pci_initdata, + .fb = &sm501_fb_pdata, +}; + +static int sm501_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct sm501_devdata *sm; + int err; + + sm = kzalloc(sizeof(struct sm501_devdata), GFP_KERNEL); + if (sm == NULL) { + dev_err(&dev->dev, "no memory for device data\n"); + err = -ENOMEM; + goto err1; + } + + /* set a default set of platform data */ + dev->dev.platform_data = sm->platdata = &sm501_pci_platdata; + + /* set a hopefully unique id for our child platform devices */ + sm->pdev_id = 32 + dev->devfn; + + pci_set_drvdata(dev, sm); + + err = pci_enable_device(dev); + if (err) { + dev_err(&dev->dev, "cannot enable device\n"); + goto err2; + } + + sm->dev = &dev->dev; + sm->irq = dev->irq; + +#ifdef __BIG_ENDIAN + /* if the system is big-endian, we most probably have a + * translation in the IO layer making the PCI bus little endian + * so make the framebuffer swapped pixels */ + + sm501_fb_pdata.flags |= SM501_FBPD_SWAP_FB_ENDIAN; +#endif + + /* check our resources */ + + if (!(pci_resource_flags(dev, 0) & IORESOURCE_MEM)) { + dev_err(&dev->dev, "region #0 is not memory?\n"); + err = -EINVAL; + goto err3; + } + + if (!(pci_resource_flags(dev, 1) & IORESOURCE_MEM)) { + dev_err(&dev->dev, "region #1 is not memory?\n"); + err = -EINVAL; + goto err3; + } + + /* make our resources ready for sharing */ + + sm->io_res = &dev->resource[1]; + sm->mem_res = &dev->resource[0]; + + sm->regs_claim = request_mem_region(sm->io_res->start, + 0x100, "sm501"); + if (sm->regs_claim == NULL) { + dev_err(&dev->dev, "cannot claim registers\n"); + err= -EBUSY; + goto err3; + } + + sm->regs = ioremap(pci_resource_start(dev, 1), + pci_resource_len(dev, 1)); + + if (sm->regs == NULL) { + dev_err(&dev->dev, "cannot remap registers\n"); + err = -EIO; + goto err4; + } + + sm501_init_dev(sm); + return 0; + + err4: + release_resource(sm->regs_claim); + kfree(sm->regs_claim); + err3: + pci_disable_device(dev); + err2: + pci_set_drvdata(dev, NULL); + kfree(sm); + err1: + return err; +} + +static void sm501_remove_sub(struct sm501_devdata *sm, + struct sm501_device *smdev) +{ + list_del(&smdev->list); + platform_device_unregister(&smdev->pdev); +} + +static void sm501_dev_remove(struct sm501_devdata *sm) +{ + struct sm501_device *smdev, *tmp; + + list_for_each_entry_safe(smdev, tmp, &sm->devices, list) + sm501_remove_sub(sm, smdev); + + device_remove_file(sm->dev, &dev_attr_dbg_regs); +} + +static void sm501_pci_remove(struct pci_dev *dev) +{ + struct sm501_devdata *sm = pci_get_drvdata(dev); + + sm501_dev_remove(sm); + iounmap(sm->regs); + + release_resource(sm->regs_claim); + kfree(sm->regs_claim); + + pci_set_drvdata(dev, NULL); + pci_disable_device(dev); +} + +static int sm501_plat_remove(struct platform_device *dev) +{ + struct sm501_devdata *sm = platform_get_drvdata(dev); + + sm501_dev_remove(sm); + iounmap(sm->regs); + + release_resource(sm->regs_claim); + kfree(sm->regs_claim); + + return 0; +} + +static struct pci_device_id sm501_pci_tbl[] = { + { 0x126f, 0x0501, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, sm501_pci_tbl); + +static struct pci_driver sm501_pci_drv = { + .name = "sm501", + .id_table = sm501_pci_tbl, + .probe = sm501_pci_probe, + .remove = sm501_pci_remove, +}; + +static struct platform_driver sm501_plat_drv = { + .driver = { + .name = "sm501", + .owner = THIS_MODULE, + }, + .probe = sm501_plat_probe, + .remove = sm501_plat_remove, +}; + +static int __init sm501_base_init(void) +{ + platform_driver_register(&sm501_plat_drv); + return pci_register_driver(&sm501_pci_drv); +} + +static void __exit sm501_base_exit(void) +{ + platform_driver_unregister(&sm501_plat_drv); + pci_unregister_driver(&sm501_pci_drv); +} + +module_init(sm501_base_init); +module_exit(sm501_base_exit); + +MODULE_DESCRIPTION("SM501 Core Driver"); +MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>, Vincent Sanders"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index bedae4a..a3c525b 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -107,4 +107,75 @@ config MSI_LAPTOP If you have an MSI S270 laptop, say Y or M here. +config SONY_LAPTOP + tristate "Sony Laptop Extras" + depends on X86 && ACPI + select BACKLIGHT_CLASS_DEVICE + ---help--- + This mini-driver drives the SNC and SPIC devices present in the ACPI + BIOS of the Sony Vaio laptops. + + It gives access to some extra laptop functionalities like Bluetooth, + screen brightness control, Fn keys and allows powering on/off some + devices. + + Read <file:Documentation/sony-laptop.txt> for more information. + +config SONY_LAPTOP_OLD + bool "Sonypi compatibility" + depends on SONY_LAPTOP + ---help--- + Build the sonypi driver compatibility code into the sony-laptop driver. + +config THINKPAD_ACPI + tristate "ThinkPad ACPI Laptop Extras" + depends on X86 && ACPI + select BACKLIGHT_CLASS_DEVICE + select HWMON + ---help--- + This is a driver for the IBM and Lenovo ThinkPad laptops. It adds + support for Fn-Fx key combinations, Bluetooth control, video + output switching, ThinkLight control, UltraBay eject and more. + For more information about this driver see + <file:Documentation/thinkpad-acpi.txt> and <http://ibm-acpi.sf.net/> . + + This driver was formely known as ibm-acpi. + + If you have an IBM or Lenovo ThinkPad laptop, say Y or M here. + +config THINKPAD_ACPI_DEBUG + bool "Verbose debug mode" + depends on THINKPAD_ACPI + default n + ---help--- + Enables extra debugging information, at the expense of a slightly + increase in driver size. + + If you are not sure, say N here. + +config THINKPAD_ACPI_DOCK + bool "Legacy Docking Station Support" + depends on THINKPAD_ACPI + depends on ACPI_DOCK=n + default n + ---help--- + Allows the thinkpad_acpi driver to handle docking station events. + This support was made obsolete by the generic ACPI docking station + support (CONFIG_ACPI_DOCK). It will allow locking and removing the + laptop from the docking station, but will not properly connect PCI + devices. + + If you are not sure, say N here. + +config THINKPAD_ACPI_BAY + bool "Legacy Removable Bay Support" + depends on THINKPAD_ACPI + default y + ---help--- + Allows the thinkpad_acpi driver to handle removable bays. It will + eletrically disable the device in the bay, and also generate + notifications when the bay lever is ejected or inserted. + + If you are not sure, say Y here. + endmenu diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 35da53c..e325164 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -11,3 +11,5 @@ obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_SGI_IOC4) += ioc4.o +obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o +obj-$(CONFIG_THINKPAD_ACPI) += thinkpad_acpi.o diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 861c399..65c32a9 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -3,7 +3,7 @@ * * * Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor - * Copyright (C) 2006 Corentin Chary + * Copyright (C) 2006-2007 Corentin Chary * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,7 +48,7 @@ #include <acpi/acpi_bus.h> #include <asm/uaccess.h> -#define ASUS_LAPTOP_VERSION "0.40" +#define ASUS_LAPTOP_VERSION "0.41" #define ASUS_HOTK_NAME "Asus Laptop Support" #define ASUS_HOTK_CLASS "hotkey" @@ -81,7 +81,8 @@ #define TLED_ON 0x08 //touchpad LED #define RLED_ON 0x10 //Record LED #define PLED_ON 0x20 //Phone LED -#define LCD_ON 0x40 //LCD backlight +#define GLED_ON 0x40 //Gaming LED +#define LCD_ON 0x80 //LCD backlight #define ASUS_LOG ASUS_HOTK_FILE ": " #define ASUS_ERR KERN_ERR ASUS_LOG @@ -94,6 +95,19 @@ MODULE_AUTHOR("Julien Lerouge, Karol Kozimor, Corentin Chary"); MODULE_DESCRIPTION(ASUS_HOTK_NAME); MODULE_LICENSE("GPL"); +/* WAPF defines the behavior of the Fn+Fx wlan key + * The significance of values is yet to be found, but + * most of the time: + * 0x0 will do nothing + * 0x1 will allow to control the device with Fn+Fx key. + * 0x4 will send an ACPI event (0x88) while pressing the Fn+Fx key + * 0x5 like 0x1 or 0x4 + * So, if something doesn't work as you want, just try other values =) + */ +static uint wapf = 1; +module_param(wapf, uint, 0644); +MODULE_PARM_DESC(wapf, "WAPF value"); + #define ASUS_HANDLE(object, paths...) \ static acpi_handle object##_handle = NULL; \ static char *object##_paths[] = { paths } @@ -103,6 +117,7 @@ ASUS_HANDLE(mled_set, ASUS_HOTK_PREFIX "MLED"); ASUS_HANDLE(tled_set, ASUS_HOTK_PREFIX "TLED"); ASUS_HANDLE(rled_set, ASUS_HOTK_PREFIX "RLED"); /* W1JC */ ASUS_HANDLE(pled_set, ASUS_HOTK_PREFIX "PLED"); /* A7J */ +ASUS_HANDLE(gled_set, ASUS_HOTK_PREFIX "GLED"); /* G1, G2 (probably) */ /* LEDD */ ASUS_HANDLE(ledd_set, ASUS_HOTK_PREFIX "SLCM"); @@ -195,11 +210,9 @@ static struct backlight_device *asus_backlight_device; */ static int read_brightness(struct backlight_device *bd); static int update_bl_status(struct backlight_device *bd); -static struct backlight_properties asusbl_data = { - .owner = THIS_MODULE, +static struct backlight_ops asusbl_ops = { .get_brightness = read_brightness, .update_status = update_bl_status, - .max_brightness = 15, }; /* These functions actually update the LED's, and are called from a @@ -213,7 +226,7 @@ static struct workqueue_struct *led_workqueue; enum led_brightness value); \ static void object##_led_update(struct work_struct *ignored); \ static int object##_led_wk; \ - DECLARE_WORK(object##_led_work, object##_led_update); \ + static DECLARE_WORK(object##_led_work, object##_led_update); \ static struct led_classdev object##_led = { \ .name = "asus:" ledname, \ .brightness_set = object##_led_set, \ @@ -223,6 +236,7 @@ ASUS_LED(mled, "mail"); ASUS_LED(tled, "touchpad"); ASUS_LED(rled, "record"); ASUS_LED(pled, "phone"); +ASUS_LED(gled, "gaming"); /* * This function evaluates an ACPI method, given an int as parameter, the @@ -247,32 +261,19 @@ static int write_acpi_int(acpi_handle handle, const char *method, int val, return (status == AE_OK); } -static int read_acpi_int(acpi_handle handle, const char *method, int *val, - struct acpi_object_list *params) -{ - struct acpi_buffer output; - union acpi_object out_obj; - acpi_status status; - - output.length = sizeof(out_obj); - output.pointer = &out_obj; - - status = acpi_evaluate_object(handle, (char *)method, params, &output); - *val = out_obj.integer.value; - return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); -} - static int read_wireless_status(int mask) { - int status; + ulong status; + acpi_status rv = AE_OK; if (!wireless_status_handle) return (hotk->status & mask) ? 1 : 0; - if (read_acpi_int(wireless_status_handle, NULL, &status, NULL)) { - return (status & mask) ? 1 : 0; - } else + rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading Wireless status\n"); + else + return (status & mask) ? 1 : 0; return (hotk->status & mask) ? 1 : 0; } @@ -287,19 +288,28 @@ static int read_status(int mask) return (hotk->status & mask) ? 1 : 0; } -static void write_status(acpi_handle handle, int out, int mask, int invert) +static void write_status(acpi_handle handle, int out, int mask) { hotk->status = (out) ? (hotk->status | mask) : (hotk->status & ~mask); - if (invert) /* invert target value */ + switch (mask) { + case MLED_ON: out = !out & 0x1; + break; + case GLED_ON: + out = (out & 0x1) + 1; + break; + default: + out &= 0x1; + break; + } if (handle && !write_acpi_int(handle, NULL, out, NULL)) - printk(ASUS_WARNING " write failed\n"); + printk(ASUS_WARNING " write failed %x\n", mask); } /* /sys/class/led handlers */ -#define ASUS_LED_HANDLER(object, mask, invert) \ +#define ASUS_LED_HANDLER(object, mask) \ static void object##_led_set(struct led_classdev *led_cdev, \ enum led_brightness value) \ { \ @@ -309,13 +319,14 @@ static void write_status(acpi_handle handle, int out, int mask, int invert) static void object##_led_update(struct work_struct *ignored) \ { \ int value = object##_led_wk; \ - write_status(object##_set_handle, value, (mask), (invert)); \ + write_status(object##_set_handle, value, (mask)); \ } -ASUS_LED_HANDLER(mled, MLED_ON, 1); -ASUS_LED_HANDLER(pled, PLED_ON, 0); -ASUS_LED_HANDLER(rled, RLED_ON, 0); -ASUS_LED_HANDLER(tled, TLED_ON, 0); +ASUS_LED_HANDLER(mled, MLED_ON); +ASUS_LED_HANDLER(pled, PLED_ON); +ASUS_LED_HANDLER(rled, RLED_ON); +ASUS_LED_HANDLER(tled, TLED_ON); +ASUS_LED_HANDLER(gled, GLED_ON); static int get_lcd_state(void) { @@ -340,7 +351,7 @@ static int set_lcd_state(int value) printk(ASUS_WARNING "Error switching LCD\n"); } - write_status(NULL, lcd, LCD_ON, 0); + write_status(NULL, lcd, LCD_ON); return 0; } @@ -349,21 +360,18 @@ static void lcd_blank(int blank) struct backlight_device *bd = asus_backlight_device; if (bd) { - down(&bd->sem); - if (likely(bd->props)) { - bd->props->power = blank; - if (likely(bd->props->update_status)) - bd->props->update_status(bd); - } - up(&bd->sem); + bd->props.power = blank; + backlight_update_status(bd); } } static int read_brightness(struct backlight_device *bd) { - int value; + ulong value; + acpi_status rv = AE_OK; - if (!read_acpi_int(brightness_get_handle, NULL, &value, NULL)) + rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading brightness\n"); return value; @@ -387,13 +395,13 @@ static int set_brightness(struct backlight_device *bd, int value) static int update_bl_status(struct backlight_device *bd) { int rv; - int value = bd->props->brightness; + int value = bd->props.brightness; rv = set_brightness(bd, value); if (rv) return rv; - value = (bd->props->power == FB_BLANK_UNBLANK) ? 1 : 0; + value = (bd->props.power == FB_BLANK_UNBLANK) ? 1 : 0; return set_lcd_state(value); } @@ -410,8 +418,10 @@ static ssize_t show_infos(struct device *dev, struct device_attribute *attr, char *page) { int len = 0; - int temp; + ulong temp; char buf[16]; //enough for all info + acpi_status rv = AE_OK; + /* * We use the easy way, we don't care of off and count, so we don't set eof * to 1 @@ -425,9 +435,10 @@ static ssize_t show_infos(struct device *dev, * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. * The significance of others is yet to be found. */ - if (read_acpi_int(hotk->handle, "SFUN", &temp, NULL)) - len += - sprintf(page + len, "SFUN value : 0x%04x\n", temp); + rv = acpi_evaluate_integer(hotk->handle, "SFUN", NULL, &temp); + if (!ACPI_FAILURE(rv)) + len += sprintf(page + len, "SFUN value : 0x%04x\n", + (uint) temp); /* * Another value for userspace: the ASYM method returns 0x02 for * battery low and 0x04 for battery critical, its readings tend to be @@ -435,9 +446,10 @@ static ssize_t show_infos(struct device *dev, * Note: since not all the laptops provide this method, errors are * silently ignored. */ - if (read_acpi_int(hotk->handle, "ASYM", &temp, NULL)) - len += - sprintf(page + len, "ASYM value : 0x%04x\n", temp); + rv = acpi_evaluate_integer(hotk->handle, "ASYM", NULL, &temp); + if (!ACPI_FAILURE(rv)) + len += sprintf(page + len, "ASYM value : 0x%04x\n", + (uint) temp); if (asus_info) { snprintf(buf, 16, "%d", asus_info->length); len += sprintf(page + len, "DSDT length : %s\n", buf); @@ -472,7 +484,7 @@ static int parse_arg(const char *buf, unsigned long count, int *val) } static ssize_t store_status(const char *buf, size_t count, - acpi_handle handle, int mask, int invert) + acpi_handle handle, int mask) { int rv, value; int out = 0; @@ -481,7 +493,7 @@ static ssize_t store_status(const char *buf, size_t count, if (rv > 0) out = value ? 1 : 0; - write_status(handle, out, mask, invert); + write_status(handle, out, mask); return rv; } @@ -522,7 +534,7 @@ static ssize_t show_wlan(struct device *dev, static ssize_t store_wlan(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - return store_status(buf, count, wl_switch_handle, WL_ON, 0); + return store_status(buf, count, wl_switch_handle, WL_ON); } /* @@ -538,7 +550,7 @@ static ssize_t store_bluetooth(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - return store_status(buf, count, bt_switch_handle, BT_ON, 0); + return store_status(buf, count, bt_switch_handle, BT_ON); } /* @@ -554,12 +566,15 @@ static void set_display(int value) static int read_display(void) { - int value = 0; + ulong value = 0; + acpi_status rv = AE_OK; /* In most of the case, we know how to set the display, but sometime we can't read it */ if (display_get_handle) { - if (!read_acpi_int(display_get_handle, NULL, &value, NULL)) + rv = acpi_evaluate_integer(display_get_handle, NULL, + NULL, &value); + if (ACPI_FAILURE(rv)) printk(ASUS_WARNING "Error reading display status\n"); } @@ -663,10 +678,10 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) * switched */ if (event == ATKD_LCD_ON) { - write_status(NULL, 1, LCD_ON, 0); + write_status(NULL, 1, LCD_ON); lcd_blank(FB_BLANK_UNBLANK); } else if (event == ATKD_LCD_OFF) { - write_status(NULL, 0, LCD_ON, 0); + write_status(NULL, 0, LCD_ON); lcd_blank(FB_BLANK_POWERDOWN); } @@ -778,7 +793,7 @@ static int asus_hotk_get_info(void) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *model = NULL; - int bsts_result, hwrs_result; + ulong bsts_result, hwrs_result; char *string = NULL; acpi_status status; @@ -801,11 +816,16 @@ static int asus_hotk_get_info(void) } /* This needs to be called for some laptops to init properly */ - if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result, NULL)) + status = + acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); + if (ACPI_FAILURE(status)) printk(ASUS_WARNING "Error calling BSTS\n"); else if (bsts_result) printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n", - bsts_result); + (uint) bsts_result); + + /* This too ... */ + write_acpi_int(hotk->handle, "CWAP", wapf, NULL); /* * Try to match the object returned by INIT to the specific model. @@ -838,6 +858,7 @@ static int asus_hotk_get_info(void) ASUS_HANDLE_INIT(tled_set); ASUS_HANDLE_INIT(rled_set); ASUS_HANDLE_INIT(pled_set); + ASUS_HANDLE_INIT(gled_set); ASUS_HANDLE_INIT(ledd_set); @@ -847,7 +868,9 @@ static int asus_hotk_get_info(void) * The significance of others is yet to be found. * If we don't find the method, we assume the device are present. */ - if (!read_acpi_int(hotk->handle, "HRWS", &hwrs_result, NULL)) + status = + acpi_evaluate_integer(hotk->handle, "HRWS", NULL, &hwrs_result); + if (ACPI_FAILURE(status)) hwrs_result = WL_HWRS | BT_HWRS; if (hwrs_result & WL_HWRS) @@ -935,11 +958,15 @@ static int asus_hotk_add(struct acpi_device *device) asus_hotk_found = 1; /* WLED and BLED are on by default */ - write_status(bt_switch_handle, 1, BT_ON, 0); - write_status(wl_switch_handle, 1, WL_ON, 0); + write_status(bt_switch_handle, 1, BT_ON); + write_status(wl_switch_handle, 1, WL_ON); + + /* If the h/w switch is off, we need to check the real status */ + write_status(NULL, read_status(BT_ON), BT_ON); + write_status(NULL, read_status(WL_ON), WL_ON); /* LCD Backlight is on by default */ - write_status(NULL, 1, LCD_ON, 0); + write_status(NULL, 1, LCD_ON); /* LED display is off by default */ hotk->ledd_status = 0xFFF; @@ -998,6 +1025,7 @@ static void asus_led_exit(void) ASUS_LED_UNREGISTER(tled); ASUS_LED_UNREGISTER(pled); ASUS_LED_UNREGISTER(rled); + ASUS_LED_UNREGISTER(gled); destroy_workqueue(led_workqueue); } @@ -1019,7 +1047,7 @@ static int asus_backlight_init(struct device *dev) if (brightness_set_handle && lcd_switch_handle) { bd = backlight_device_register(ASUS_HOTK_FILE, dev, - NULL, &asusbl_data); + NULL, &asusbl_ops); if (IS_ERR(bd)) { printk(ASUS_ERR "Could not register asus backlight device\n"); @@ -1029,14 +1057,10 @@ static int asus_backlight_init(struct device *dev) asus_backlight_device = bd; - down(&bd->sem); - if (likely(bd->props)) { - bd->props->brightness = read_brightness(NULL); - bd->props->power = FB_BLANK_UNBLANK; - if (likely(bd->props->update_status)) - bd->props->update_status(bd); - } - up(&bd->sem); + bd->props.max_brightness = 15; + bd->props.brightness = read_brightness(NULL); + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); } return 0; } @@ -1073,6 +1097,10 @@ static int asus_led_init(struct device *dev) if (rv) return rv; + rv = ASUS_LED_REGISTER(gled, dev); + if (rv) + return rv; + led_workqueue = create_singlethread_workqueue("led_workqueue"); if (!led_workqueue) return -ENOMEM; @@ -1088,11 +1116,6 @@ static int __init asus_laptop_init(void) if (acpi_disabled) return -ENODEV; - if (!acpi_specific_hotkey_enabled) { - printk(ASUS_ERR "Using generic hotkey driver\n"); - return -ENODEV; - } - result = acpi_bus_register_driver(&asus_hotk_driver); if (result < 0) return result; diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index 8e5e07e..68c4b58 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -157,14 +157,12 @@ static int bl_get_brightness(struct backlight_device *b) static int bl_update_status(struct backlight_device *b) { - return set_lcd_level(b->props->brightness); + return set_lcd_level(b->props.brightness); } -static struct backlight_properties msibl_props = { - .owner = THIS_MODULE, +static struct backlight_ops msibl_ops = { .get_brightness = bl_get_brightness, .update_status = bl_update_status, - .max_brightness = MSI_LCD_LEVEL_MAX-1, }; static struct backlight_device *msibl_device; @@ -318,10 +316,12 @@ static int __init msi_init(void) /* Register backlight stuff */ msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, - &msibl_props); + &msibl_ops); if (IS_ERR(msibl_device)) return PTR_ERR(msibl_device); + msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1, + ret = platform_driver_register(&msipf_driver); if (ret) goto fail_backlight; diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c new file mode 100644 index 0000000..c15c1f6 --- /dev/null +++ b/drivers/misc/sony-laptop.c @@ -0,0 +1,2291 @@ +/* + * ACPI Sony Notebook Control Driver (SNC and SPIC) + * + * Copyright (C) 2004-2005 Stelian Pop <stelian@popies.net> + * Copyright (C) 2007 Mattia Dongili <malattia@linux.it> + * + * Parts of this driver inspired from asus_acpi.c and ibm_acpi.c + * which are copyrighted by their respective authors. + * + * The SNY6001 driver part is based on the sonypi driver which includes + * material from: + * + * Copyright (C) 2001-2005 Stelian Pop <stelian@popies.net> + * + * Copyright (C) 2005 Narayanan R S <nars@kadamba.org> + * + * Copyright (C) 2001-2002 Alcôve <www.alcove.com> + * + * Copyright (C) 2001 Michael Ashley <m.ashley@unsw.edu.au> + * + * Copyright (C) 2001 Junichi Morita <jun1m@mars.dti.ne.jp> + * + * Copyright (C) 2000 Takaya Kinjo <t-kinjo@tc4.so-net.ne.jp> + * + * Copyright (C) 2000 Andrew Tridgell <tridge@valinux.com> + * + * Earlier work by Werner Almesberger, Paul `Rusty' Russell and Paul Mackerras. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/backlight.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/dmi.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/kfifo.h> +#include <linux/workqueue.h> +#include <linux/acpi.h> +#include <acpi/acpi_drivers.h> +#include <acpi/acpi_bus.h> +#include <asm/uaccess.h> +#include <linux/sonypi.h> +#include <linux/sony-laptop.h> +#ifdef CONFIG_SONY_LAPTOP_OLD +#include <linux/poll.h> +#include <linux/miscdevice.h> +#endif + +#define DRV_PFX "sony-laptop: " +#define dprintk(msg...) do { \ + if (debug) printk(KERN_WARNING DRV_PFX msg); \ +} while (0) + +#define SONY_LAPTOP_DRIVER_VERSION "0.5" + +#define SONY_NC_CLASS "sony-nc" +#define SONY_NC_HID "SNY5001" +#define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" + +#define SONY_PIC_CLASS "sony-pic" +#define SONY_PIC_HID "SNY6001" +#define SONY_PIC_DRIVER_NAME "Sony Programmable IO Control Driver" + +MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); +MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); + +static int debug; +module_param(debug, int, 0); +MODULE_PARM_DESC(debug, "set this to 1 (and RTFM) if you want to help " + "the development of this driver"); + +static int no_spic; /* = 0 */ +module_param(no_spic, int, 0444); +MODULE_PARM_DESC(no_spic, + "set this if you don't want to enable the SPIC device"); + +static int compat; /* = 0 */ +module_param(compat, int, 0444); +MODULE_PARM_DESC(compat, + "set this if you want to enable backward compatibility mode"); + +static unsigned long mask = 0xffffffff; +module_param(mask, ulong, 0644); +MODULE_PARM_DESC(mask, + "set this to the mask of event you want to enable (see doc)"); + +static int camera; /* = 0 */ +module_param(camera, int, 0444); +MODULE_PARM_DESC(camera, + "set this to 1 to enable Motion Eye camera controls " + "(only use it if you have a C1VE or C1VN model)"); + +#ifdef CONFIG_SONY_LAPTOP_OLD +static int minor = -1; +module_param(minor, int, 0); +MODULE_PARM_DESC(minor, + "minor number of the misc device for the SPIC compatibility code, " + "default is -1 (automatic)"); +#endif + +/*********** Input Devices ***********/ + +#define SONY_LAPTOP_BUF_SIZE 128 +struct sony_laptop_input_s { + atomic_t users; + struct input_dev *jog_dev; + struct input_dev *key_dev; + struct kfifo *fifo; + spinlock_t fifo_lock; + struct workqueue_struct *wq; +}; +static struct sony_laptop_input_s sony_laptop_input = { + .users = ATOMIC_INIT(0), +}; + +struct sony_laptop_keypress { + struct input_dev *dev; + int key; +}; + +/* Correspondance table between sonypi events and input layer events */ +static struct { + int sonypiev; + int inputev; +} sony_laptop_inputkeys[] = { + { SONYPI_EVENT_CAPTURE_PRESSED, KEY_CAMERA }, + { SONYPI_EVENT_FNKEY_ONLY, KEY_FN }, + { SONYPI_EVENT_FNKEY_ESC, KEY_FN_ESC }, + { SONYPI_EVENT_FNKEY_F1, KEY_FN_F1 }, + { SONYPI_EVENT_FNKEY_F2, KEY_FN_F2 }, + { SONYPI_EVENT_FNKEY_F3, KEY_FN_F3 }, + { SONYPI_EVENT_FNKEY_F4, KEY_FN_F4 }, + { SONYPI_EVENT_FNKEY_F5, KEY_FN_F5 }, + { SONYPI_EVENT_FNKEY_F6, KEY_FN_F6 }, + { SONYPI_EVENT_FNKEY_F7, KEY_FN_F7 }, + { SONYPI_EVENT_FNKEY_F8, KEY_FN_F8 }, + { SONYPI_EVENT_FNKEY_F9, KEY_FN_F9 }, + { SONYPI_EVENT_FNKEY_F10, KEY_FN_F10 }, + { SONYPI_EVENT_FNKEY_F11, KEY_FN_F11 }, + { SONYPI_EVENT_FNKEY_F12, KEY_FN_F12 }, + { SONYPI_EVENT_FNKEY_1, KEY_FN_1 }, + { SONYPI_EVENT_FNKEY_2, KEY_FN_2 }, + { SONYPI_EVENT_FNKEY_D, KEY_FN_D }, + { SONYPI_EVENT_FNKEY_E, KEY_FN_E }, + { SONYPI_EVENT_FNKEY_F, KEY_FN_F }, + { SONYPI_EVENT_FNKEY_S, KEY_FN_S }, + { SONYPI_EVENT_FNKEY_B, KEY_FN_B }, + { SONYPI_EVENT_BLUETOOTH_PRESSED, KEY_BLUE }, + { SONYPI_EVENT_BLUETOOTH_ON, KEY_BLUE }, + { SONYPI_EVENT_PKEY_P1, KEY_PROG1 }, + { SONYPI_EVENT_PKEY_P2, KEY_PROG2 }, + { SONYPI_EVENT_PKEY_P3, KEY_PROG3 }, + { SONYPI_EVENT_BACK_PRESSED, KEY_BACK }, + { SONYPI_EVENT_HELP_PRESSED, KEY_HELP }, + { SONYPI_EVENT_ZOOM_PRESSED, KEY_ZOOM }, + { SONYPI_EVENT_THUMBPHRASE_PRESSED, BTN_THUMB }, + { 0, 0 }, +}; + +/* release buttons after a short delay if pressed */ +static void do_sony_laptop_release_key(struct work_struct *work) +{ + struct sony_laptop_keypress kp; + + while (kfifo_get(sony_laptop_input.fifo, (unsigned char *)&kp, + sizeof(kp)) == sizeof(kp)) { + msleep(10); + input_report_key(kp.dev, kp.key, 0); + input_sync(kp.dev); + } +} +static DECLARE_WORK(sony_laptop_release_key_work, + do_sony_laptop_release_key); + +/* forward event to the input subsytem */ +static void sony_laptop_report_input_event(u8 event) +{ + struct input_dev *jog_dev = sony_laptop_input.jog_dev; + struct input_dev *key_dev = sony_laptop_input.key_dev; + struct sony_laptop_keypress kp = { NULL }; + int i; + + if (event == SONYPI_EVENT_FNKEY_RELEASED) { + /* Nothing, not all VAIOs generate this event */ + return; + } + + /* report events */ + switch (event) { + /* jog_dev events */ + case SONYPI_EVENT_JOGDIAL_UP: + case SONYPI_EVENT_JOGDIAL_UP_PRESSED: + input_report_rel(jog_dev, REL_WHEEL, 1); + input_sync(jog_dev); + return; + + case SONYPI_EVENT_JOGDIAL_DOWN: + case SONYPI_EVENT_JOGDIAL_DOWN_PRESSED: + input_report_rel(jog_dev, REL_WHEEL, -1); + input_sync(jog_dev); + return; + + /* key_dev events */ + case SONYPI_EVENT_JOGDIAL_PRESSED: + kp.key = BTN_MIDDLE; + kp.dev = jog_dev; + break; + + default: + for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) + if (event == sony_laptop_inputkeys[i].sonypiev) { + kp.dev = key_dev; + kp.key = sony_laptop_inputkeys[i].inputev; + break; + } + break; + } + + if (kp.dev) { + input_report_key(kp.dev, kp.key, 1); + input_sync(kp.dev); + kfifo_put(sony_laptop_input.fifo, + (unsigned char *)&kp, sizeof(kp)); + + if (!work_pending(&sony_laptop_release_key_work)) + queue_work(sony_laptop_input.wq, + &sony_laptop_release_key_work); + } else + dprintk("unknown input event %.2x\n", event); +} + +static int sony_laptop_setup_input(void) +{ + struct input_dev *jog_dev; + struct input_dev *key_dev; + int i; + int error; + + /* don't run again if already initialized */ + if (atomic_add_return(1, &sony_laptop_input.users) > 1) + return 0; + + /* kfifo */ + spin_lock_init(&sony_laptop_input.fifo_lock); + sony_laptop_input.fifo = + kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, + &sony_laptop_input.fifo_lock); + if (IS_ERR(sony_laptop_input.fifo)) { + printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + error = PTR_ERR(sony_laptop_input.fifo); + goto err_dec_users; + } + + /* init workqueue */ + sony_laptop_input.wq = create_singlethread_workqueue("sony-laptop"); + if (!sony_laptop_input.wq) { + printk(KERN_ERR DRV_PFX + "Unabe to create workqueue.\n"); + error = -ENXIO; + goto err_free_kfifo; + } + + /* input keys */ + key_dev = input_allocate_device(); + if (!key_dev) { + error = -ENOMEM; + goto err_destroy_wq; + } + + key_dev->name = "Sony Vaio Keys"; + key_dev->id.bustype = BUS_ISA; + key_dev->id.vendor = PCI_VENDOR_ID_SONY; + + /* Initialize the Input Drivers: special keys */ + key_dev->evbit[0] = BIT(EV_KEY); + for (i = 0; sony_laptop_inputkeys[i].sonypiev; i++) + if (sony_laptop_inputkeys[i].inputev) + set_bit(sony_laptop_inputkeys[i].inputev, + key_dev->keybit); + + error = input_register_device(key_dev); + if (error) + goto err_free_keydev; + + sony_laptop_input.key_dev = key_dev; + + /* jogdial */ + jog_dev = input_allocate_device(); + if (!jog_dev) { + error = -ENOMEM; + goto err_unregister_keydev; + } + + jog_dev->name = "Sony Vaio Jogdial"; + jog_dev->id.bustype = BUS_ISA; + jog_dev->id.vendor = PCI_VENDOR_ID_SONY; + + jog_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + jog_dev->keybit[LONG(BTN_MOUSE)] = BIT(BTN_MIDDLE); + jog_dev->relbit[0] = BIT(REL_WHEEL); + + error = input_register_device(jog_dev); + if (error) + goto err_free_jogdev; + + sony_laptop_input.jog_dev = jog_dev; + + return 0; + +err_free_jogdev: + input_free_device(jog_dev); + +err_unregister_keydev: + input_unregister_device(key_dev); + /* to avoid kref underflow below at input_free_device */ + key_dev = NULL; + +err_free_keydev: + input_free_device(key_dev); + +err_destroy_wq: + destroy_workqueue(sony_laptop_input.wq); + +err_free_kfifo: + kfifo_free(sony_laptop_input.fifo); + +err_dec_users: + atomic_dec(&sony_laptop_input.users); + return error; +} + +static void sony_laptop_remove_input(void) +{ + /* cleanup only after the last user has gone */ + if (!atomic_dec_and_test(&sony_laptop_input.users)) + return; + + /* flush workqueue first */ + flush_workqueue(sony_laptop_input.wq); + + /* destroy input devs */ + input_unregister_device(sony_laptop_input.key_dev); + sony_laptop_input.key_dev = NULL; + + if (sony_laptop_input.jog_dev) { + input_unregister_device(sony_laptop_input.jog_dev); + sony_laptop_input.jog_dev = NULL; + } + + destroy_workqueue(sony_laptop_input.wq); + kfifo_free(sony_laptop_input.fifo); +} + +/*********** Platform Device ***********/ + +static atomic_t sony_pf_users = ATOMIC_INIT(0); +static struct platform_driver sony_pf_driver = { + .driver = { + .name = "sony-laptop", + .owner = THIS_MODULE, + } +}; +static struct platform_device *sony_pf_device; + +static int sony_pf_add(void) +{ + int ret = 0; + + /* don't run again if already initialized */ + if (atomic_add_return(1, &sony_pf_users) > 1) + return 0; + + ret = platform_driver_register(&sony_pf_driver); + if (ret) + goto out; + + sony_pf_device = platform_device_alloc("sony-laptop", -1); + if (!sony_pf_device) { + ret = -ENOMEM; + goto out_platform_registered; + } + + ret = platform_device_add(sony_pf_device); + if (ret) + goto out_platform_alloced; + + return 0; + + out_platform_alloced: + platform_device_put(sony_pf_device); + sony_pf_device = NULL; + out_platform_registered: + platform_driver_unregister(&sony_pf_driver); + out: + atomic_dec(&sony_pf_users); + return ret; +} + +static void sony_pf_remove(void) +{ + /* deregister only after the last user has gone */ + if (!atomic_dec_and_test(&sony_pf_users)) + return; + + platform_device_del(sony_pf_device); + platform_device_put(sony_pf_device); + platform_driver_unregister(&sony_pf_driver); +} + +/*********** SNC (SNY5001) Device ***********/ + +/* the device uses 1-based values, while the backlight subsystem uses + 0-based values */ +#define SONY_MAX_BRIGHTNESS 8 + +#define SNC_VALIDATE_IN 0 +#define SNC_VALIDATE_OUT 1 + +static ssize_t sony_nc_sysfs_show(struct device *, struct device_attribute *, + char *); +static ssize_t sony_nc_sysfs_store(struct device *, struct device_attribute *, + const char *, size_t); +static int boolean_validate(const int, const int); +static int brightness_default_validate(const int, const int); + +struct sony_nc_value { + char *name; /* name of the entry */ + char **acpiget; /* names of the ACPI get function */ + char **acpiset; /* names of the ACPI set function */ + int (*validate)(const int, const int); /* input/output validation */ + int value; /* current setting */ + int valid; /* Has ever been set */ + int debug; /* active only in debug mode ? */ + struct device_attribute devattr; /* sysfs atribute */ +}; + +#define SNC_HANDLE_NAMES(_name, _values...) \ + static char *snc_##_name[] = { _values, NULL } + +#define SNC_HANDLE(_name, _getters, _setters, _validate, _debug) \ + { \ + .name = __stringify(_name), \ + .acpiget = _getters, \ + .acpiset = _setters, \ + .validate = _validate, \ + .debug = _debug, \ + .devattr = __ATTR(_name, 0, sony_nc_sysfs_show, sony_nc_sysfs_store), \ + } + +#define SNC_HANDLE_NULL { .name = NULL } + +SNC_HANDLE_NAMES(fnkey_get, "GHKE"); + +SNC_HANDLE_NAMES(brightness_def_get, "GPBR"); +SNC_HANDLE_NAMES(brightness_def_set, "SPBR"); + +SNC_HANDLE_NAMES(cdpower_get, "GCDP"); +SNC_HANDLE_NAMES(cdpower_set, "SCDP", "CDPW"); + +SNC_HANDLE_NAMES(audiopower_get, "GAZP"); +SNC_HANDLE_NAMES(audiopower_set, "AZPW"); + +SNC_HANDLE_NAMES(lanpower_get, "GLNP"); +SNC_HANDLE_NAMES(lanpower_set, "LNPW"); + +SNC_HANDLE_NAMES(PID_get, "GPID"); + +SNC_HANDLE_NAMES(CTR_get, "GCTR"); +SNC_HANDLE_NAMES(CTR_set, "SCTR"); + +SNC_HANDLE_NAMES(PCR_get, "GPCR"); +SNC_HANDLE_NAMES(PCR_set, "SPCR"); + +SNC_HANDLE_NAMES(CMI_get, "GCMI"); +SNC_HANDLE_NAMES(CMI_set, "SCMI"); + +static struct sony_nc_value sony_nc_values[] = { + SNC_HANDLE(brightness_default, snc_brightness_def_get, + snc_brightness_def_set, brightness_default_validate, 0), + SNC_HANDLE(fnkey, snc_fnkey_get, NULL, NULL, 0), + SNC_HANDLE(cdpower, snc_cdpower_get, snc_cdpower_set, boolean_validate, 0), + SNC_HANDLE(audiopower, snc_audiopower_get, snc_audiopower_set, + boolean_validate, 0), + SNC_HANDLE(lanpower, snc_lanpower_get, snc_lanpower_set, + boolean_validate, 1), + /* unknown methods */ + SNC_HANDLE(PID, snc_PID_get, NULL, NULL, 1), + SNC_HANDLE(CTR, snc_CTR_get, snc_CTR_set, NULL, 1), + SNC_HANDLE(PCR, snc_PCR_get, snc_PCR_set, NULL, 1), + SNC_HANDLE(CMI, snc_CMI_get, snc_CMI_set, NULL, 1), + SNC_HANDLE_NULL +}; + +static acpi_handle sony_nc_acpi_handle; +static struct acpi_device *sony_nc_acpi_device = NULL; + +/* + * acpi_evaluate_object wrappers + */ +static int acpi_callgetfunc(acpi_handle handle, char *name, int *result) +{ + struct acpi_buffer output; + union acpi_object out_obj; + acpi_status status; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + + status = acpi_evaluate_object(handle, name, NULL, &output); + if ((status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER)) { + *result = out_obj.integer.value; + return 0; + } + + printk(KERN_WARNING DRV_PFX "acpi_callreadfunc failed\n"); + + return -1; +} + +static int acpi_callsetfunc(acpi_handle handle, char *name, int value, + int *result) +{ + struct acpi_object_list params; + union acpi_object in_obj; + struct acpi_buffer output; + union acpi_object out_obj; + acpi_status status; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = value; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + + status = acpi_evaluate_object(handle, name, ¶ms, &output); + if (status == AE_OK) { + if (result != NULL) { + if (out_obj.type != ACPI_TYPE_INTEGER) { + printk(KERN_WARNING DRV_PFX "acpi_evaluate_object bad " + "return type\n"); + return -1; + } + *result = out_obj.integer.value; + } + return 0; + } + + printk(KERN_WARNING DRV_PFX "acpi_evaluate_object failed\n"); + + return -1; +} + +/* + * sony_nc_values input/output validate functions + */ + +/* brightness_default_validate: + * + * manipulate input output values to keep consistency with the + * backlight framework for which brightness values are 0-based. + */ +static int brightness_default_validate(const int direction, const int value) +{ + switch (direction) { + case SNC_VALIDATE_OUT: + return value - 1; + case SNC_VALIDATE_IN: + if (value >= 0 && value < SONY_MAX_BRIGHTNESS) + return value + 1; + } + return -EINVAL; +} + +/* boolean_validate: + * + * on input validate boolean values 0/1, on output just pass the + * received value. + */ +static int boolean_validate(const int direction, const int value) +{ + if (direction == SNC_VALIDATE_IN) { + if (value != 0 && value != 1) + return -EINVAL; + } + return value; +} + +/* + * Sysfs show/store common to all sony_nc_values + */ +static ssize_t sony_nc_sysfs_show(struct device *dev, struct device_attribute *attr, + char *buffer) +{ + int value; + struct sony_nc_value *item = + container_of(attr, struct sony_nc_value, devattr); + + if (!*item->acpiget) + return -EIO; + + if (acpi_callgetfunc(sony_nc_acpi_handle, *item->acpiget, &value) < 0) + return -EIO; + + if (item->validate) + value = item->validate(SNC_VALIDATE_OUT, value); + + return snprintf(buffer, PAGE_SIZE, "%d\n", value); +} + +static ssize_t sony_nc_sysfs_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + int value; + struct sony_nc_value *item = + container_of(attr, struct sony_nc_value, devattr); + + if (!item->acpiset) + return -EIO; + + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + + if (item->validate) + value = item->validate(SNC_VALIDATE_IN, value); + + if (value < 0) + return value; + + if (acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, value, NULL) < 0) + return -EIO; + item->value = value; + item->valid = 1; + return count; +} + + +/* + * Backlight device + */ +static int sony_backlight_update_status(struct backlight_device *bd) +{ + return acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", + bd->props.brightness + 1, NULL); +} + +static int sony_backlight_get_brightness(struct backlight_device *bd) +{ + int value; + + if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) + return 0; + /* brightness levels are 1-based, while backlight ones are 0-based */ + return value - 1; +} + +static struct backlight_device *sony_backlight_device; +static struct backlight_ops sony_backlight_ops = { + .update_status = sony_backlight_update_status, + .get_brightness = sony_backlight_get_brightness, +}; + +/* + * ACPI callbacks + */ +static void sony_acpi_notify(acpi_handle handle, u32 event, void *data) +{ + dprintk("sony_acpi_notify, event: %d\n", event); + sony_laptop_report_input_event(event); + acpi_bus_generate_event(sony_nc_acpi_device, 1, event); +} + +static acpi_status sony_walk_callback(acpi_handle handle, u32 level, + void *context, void **return_value) +{ + struct acpi_namespace_node *node; + union acpi_operand_object *operand; + + node = (struct acpi_namespace_node *)handle; + operand = (union acpi_operand_object *)node->object; + + printk(KERN_WARNING DRV_PFX "method: name: %4.4s, args %X\n", node->name.ascii, + (u32) operand->method.param_count); + + return AE_OK; +} + +/* + * ACPI device + */ +static int sony_nc_resume(struct acpi_device *device) +{ + struct sony_nc_value *item; + + for (item = sony_nc_values; item->name; item++) { + int ret; + + if (!item->valid) + continue; + ret = acpi_callsetfunc(sony_nc_acpi_handle, *item->acpiset, + item->value, NULL); + if (ret < 0) { + printk("%s: %d\n", __FUNCTION__, ret); + break; + } + } + return 0; +} + +static int sony_nc_add(struct acpi_device *device) +{ + acpi_status status; + int result = 0; + acpi_handle handle; + struct sony_nc_value *item; + + printk(KERN_INFO DRV_PFX "%s v%s.\n", + SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + + sony_nc_acpi_device = device; + strcpy(acpi_device_class(device), "sony/hotkey"); + + sony_nc_acpi_handle = device->handle; + + if (debug) { + status = acpi_walk_namespace(ACPI_TYPE_METHOD, sony_nc_acpi_handle, + 1, sony_walk_callback, NULL, NULL); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING DRV_PFX "unable to walk acpi resources\n"); + result = -ENODEV; + goto outwalk; + } + } + + /* setup input devices and helper fifo */ + result = sony_laptop_setup_input(); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to create input devices.\n"); + goto outwalk; + } + + status = acpi_install_notify_handler(sony_nc_acpi_handle, + ACPI_DEVICE_NOTIFY, + sony_acpi_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING DRV_PFX "unable to install notify handler\n"); + result = -ENODEV; + goto outinput; + } + + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { + sony_backlight_device = backlight_device_register("sony", NULL, + NULL, + &sony_backlight_ops); + + if (IS_ERR(sony_backlight_device)) { + printk(KERN_WARNING DRV_PFX "unable to register backlight device\n"); + sony_backlight_device = NULL; + } else { + sony_backlight_device->props.brightness = + sony_backlight_get_brightness + (sony_backlight_device); + sony_backlight_device->props.max_brightness = + SONY_MAX_BRIGHTNESS - 1; + } + + } + + result = sony_pf_add(); + if (result) + goto outbacklight; + + /* create sony_pf sysfs attributes related to the SNC device */ + for (item = sony_nc_values; item->name; ++item) { + + if (!debug && item->debug) + continue; + + /* find the available acpiget as described in the DSDT */ + for (; item->acpiget && *item->acpiget; ++item->acpiget) { + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, + *item->acpiget, + &handle))) { + dprintk("Found %s getter: %s\n", + item->name, *item->acpiget); + item->devattr.attr.mode |= S_IRUGO; + break; + } + } + + /* find the available acpiset as described in the DSDT */ + for (; item->acpiset && *item->acpiset; ++item->acpiset) { + if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, + *item->acpiset, + &handle))) { + dprintk("Found %s setter: %s\n", + item->name, *item->acpiset); + item->devattr.attr.mode |= S_IWUSR; + break; + } + } + + if (item->devattr.attr.mode != 0) { + result = + device_create_file(&sony_pf_device->dev, + &item->devattr); + if (result) + goto out_sysfs; + } + } + + return 0; + + out_sysfs: + for (item = sony_nc_values; item->name; ++item) { + device_remove_file(&sony_pf_device->dev, &item->devattr); + } + sony_pf_remove(); + + outbacklight: + if (sony_backlight_device) + backlight_device_unregister(sony_backlight_device); + + status = acpi_remove_notify_handler(sony_nc_acpi_handle, + ACPI_DEVICE_NOTIFY, + sony_acpi_notify); + if (ACPI_FAILURE(status)) + printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n"); + + outinput: + sony_laptop_remove_input(); + + outwalk: + return result; +} + +static int sony_nc_remove(struct acpi_device *device, int type) +{ + acpi_status status; + struct sony_nc_value *item; + + if (sony_backlight_device) + backlight_device_unregister(sony_backlight_device); + + sony_nc_acpi_device = NULL; + + status = acpi_remove_notify_handler(sony_nc_acpi_handle, + ACPI_DEVICE_NOTIFY, + sony_acpi_notify); + if (ACPI_FAILURE(status)) + printk(KERN_WARNING DRV_PFX "unable to remove notify handler\n"); + + for (item = sony_nc_values; item->name; ++item) { + device_remove_file(&sony_pf_device->dev, &item->devattr); + } + + sony_pf_remove(); + sony_laptop_remove_input(); + dprintk(SONY_NC_DRIVER_NAME " removed.\n"); + + return 0; +} + +static struct acpi_driver sony_nc_driver = { + .name = SONY_NC_DRIVER_NAME, + .class = SONY_NC_CLASS, + .ids = SONY_NC_HID, + .owner = THIS_MODULE, + .ops = { + .add = sony_nc_add, + .remove = sony_nc_remove, + .resume = sony_nc_resume, + }, +}; + +/*********** SPIC (SNY6001) Device ***********/ + +#define SONYPI_DEVICE_TYPE1 0x00000001 +#define SONYPI_DEVICE_TYPE2 0x00000002 +#define SONYPI_DEVICE_TYPE3 0x00000004 + +#define SONY_PIC_EV_MASK 0xff + +struct sony_pic_ioport { + struct acpi_resource_io io; + struct list_head list; +}; + +struct sony_pic_irq { + struct acpi_resource_irq irq; + struct list_head list; +}; + +struct sony_pic_dev { + int model; + u8 camera_power; + u8 bluetooth_power; + u8 wwan_power; + struct acpi_device *acpi_dev; + struct sony_pic_irq *cur_irq; + struct sony_pic_ioport *cur_ioport; + struct list_head interrupts; + struct list_head ioports; + struct mutex lock; +}; + +static struct sony_pic_dev spic_dev = { + .interrupts = LIST_HEAD_INIT(spic_dev.interrupts), + .ioports = LIST_HEAD_INIT(spic_dev.ioports), +}; + +/* Event masks */ +#define SONYPI_JOGGER_MASK 0x00000001 +#define SONYPI_CAPTURE_MASK 0x00000002 +#define SONYPI_FNKEY_MASK 0x00000004 +#define SONYPI_BLUETOOTH_MASK 0x00000008 +#define SONYPI_PKEY_MASK 0x00000010 +#define SONYPI_BACK_MASK 0x00000020 +#define SONYPI_HELP_MASK 0x00000040 +#define SONYPI_LID_MASK 0x00000080 +#define SONYPI_ZOOM_MASK 0x00000100 +#define SONYPI_THUMBPHRASE_MASK 0x00000200 +#define SONYPI_MEYE_MASK 0x00000400 +#define SONYPI_MEMORYSTICK_MASK 0x00000800 +#define SONYPI_BATTERY_MASK 0x00001000 +#define SONYPI_WIRELESS_MASK 0x00002000 + +struct sonypi_event { + u8 data; + u8 event; +}; + +/* The set of possible button release events */ +static struct sonypi_event sonypi_releaseev[] = { + { 0x00, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0, 0 } +}; + +/* The set of possible jogger events */ +static struct sonypi_event sonypi_joggerev[] = { + { 0x1f, SONYPI_EVENT_JOGDIAL_UP }, + { 0x01, SONYPI_EVENT_JOGDIAL_DOWN }, + { 0x5f, SONYPI_EVENT_JOGDIAL_UP_PRESSED }, + { 0x41, SONYPI_EVENT_JOGDIAL_DOWN_PRESSED }, + { 0x1e, SONYPI_EVENT_JOGDIAL_FAST_UP }, + { 0x02, SONYPI_EVENT_JOGDIAL_FAST_DOWN }, + { 0x5e, SONYPI_EVENT_JOGDIAL_FAST_UP_PRESSED }, + { 0x42, SONYPI_EVENT_JOGDIAL_FAST_DOWN_PRESSED }, + { 0x1d, SONYPI_EVENT_JOGDIAL_VFAST_UP }, + { 0x03, SONYPI_EVENT_JOGDIAL_VFAST_DOWN }, + { 0x5d, SONYPI_EVENT_JOGDIAL_VFAST_UP_PRESSED }, + { 0x43, SONYPI_EVENT_JOGDIAL_VFAST_DOWN_PRESSED }, + { 0x40, SONYPI_EVENT_JOGDIAL_PRESSED }, + { 0, 0 } +}; + +/* The set of possible capture button events */ +static struct sonypi_event sonypi_captureev[] = { + { 0x05, SONYPI_EVENT_CAPTURE_PARTIALPRESSED }, + { 0x07, SONYPI_EVENT_CAPTURE_PRESSED }, + { 0x01, SONYPI_EVENT_CAPTURE_PARTIALRELEASED }, + { 0, 0 } +}; + +/* The set of possible fnkeys events */ +static struct sonypi_event sonypi_fnkeyev[] = { + { 0x10, SONYPI_EVENT_FNKEY_ESC }, + { 0x11, SONYPI_EVENT_FNKEY_F1 }, + { 0x12, SONYPI_EVENT_FNKEY_F2 }, + { 0x13, SONYPI_EVENT_FNKEY_F3 }, + { 0x14, SONYPI_EVENT_FNKEY_F4 }, + { 0x15, SONYPI_EVENT_FNKEY_F5 }, + { 0x16, SONYPI_EVENT_FNKEY_F6 }, + { 0x17, SONYPI_EVENT_FNKEY_F7 }, + { 0x18, SONYPI_EVENT_FNKEY_F8 }, + { 0x19, SONYPI_EVENT_FNKEY_F9 }, + { 0x1a, SONYPI_EVENT_FNKEY_F10 }, + { 0x1b, SONYPI_EVENT_FNKEY_F11 }, + { 0x1c, SONYPI_EVENT_FNKEY_F12 }, + { 0x1f, SONYPI_EVENT_FNKEY_RELEASED }, + { 0x21, SONYPI_EVENT_FNKEY_1 }, + { 0x22, SONYPI_EVENT_FNKEY_2 }, + { 0x31, SONYPI_EVENT_FNKEY_D }, + { 0x32, SONYPI_EVENT_FNKEY_E }, + { 0x33, SONYPI_EVENT_FNKEY_F }, + { 0x34, SONYPI_EVENT_FNKEY_S }, + { 0x35, SONYPI_EVENT_FNKEY_B }, + { 0x36, SONYPI_EVENT_FNKEY_ONLY }, + { 0, 0 } +}; + +/* The set of possible program key events */ +static struct sonypi_event sonypi_pkeyev[] = { + { 0x01, SONYPI_EVENT_PKEY_P1 }, + { 0x02, SONYPI_EVENT_PKEY_P2 }, + { 0x04, SONYPI_EVENT_PKEY_P3 }, + { 0x5c, SONYPI_EVENT_PKEY_P1 }, + { 0, 0 } +}; + +/* The set of possible bluetooth events */ +static struct sonypi_event sonypi_blueev[] = { + { 0x55, SONYPI_EVENT_BLUETOOTH_PRESSED }, + { 0x59, SONYPI_EVENT_BLUETOOTH_ON }, + { 0x5a, SONYPI_EVENT_BLUETOOTH_OFF }, + { 0, 0 } +}; + +/* The set of possible wireless events */ +static struct sonypi_event sonypi_wlessev[] = { + { 0x59, SONYPI_EVENT_WIRELESS_ON }, + { 0x5a, SONYPI_EVENT_WIRELESS_OFF }, + { 0, 0 } +}; + +/* The set of possible back button events */ +static struct sonypi_event sonypi_backev[] = { + { 0x20, SONYPI_EVENT_BACK_PRESSED }, + { 0, 0 } +}; + +/* The set of possible help button events */ +static struct sonypi_event sonypi_helpev[] = { + { 0x3b, SONYPI_EVENT_HELP_PRESSED }, + { 0, 0 } +}; + + +/* The set of possible lid events */ +static struct sonypi_event sonypi_lidev[] = { + { 0x51, SONYPI_EVENT_LID_CLOSED }, + { 0x50, SONYPI_EVENT_LID_OPENED }, + { 0, 0 } +}; + +/* The set of possible zoom events */ +static struct sonypi_event sonypi_zoomev[] = { + { 0x39, SONYPI_EVENT_ZOOM_PRESSED }, + { 0, 0 } +}; + +/* The set of possible thumbphrase events */ +static struct sonypi_event sonypi_thumbphraseev[] = { + { 0x3a, SONYPI_EVENT_THUMBPHRASE_PRESSED }, + { 0, 0 } +}; + +/* The set of possible motioneye camera events */ +static struct sonypi_event sonypi_meyeev[] = { + { 0x00, SONYPI_EVENT_MEYE_FACE }, + { 0x01, SONYPI_EVENT_MEYE_OPPOSITE }, + { 0, 0 } +}; + +/* The set of possible memorystick events */ +static struct sonypi_event sonypi_memorystickev[] = { + { 0x53, SONYPI_EVENT_MEMORYSTICK_INSERT }, + { 0x54, SONYPI_EVENT_MEMORYSTICK_EJECT }, + { 0, 0 } +}; + +/* The set of possible battery events */ +static struct sonypi_event sonypi_batteryev[] = { + { 0x20, SONYPI_EVENT_BATTERY_INSERT }, + { 0x30, SONYPI_EVENT_BATTERY_REMOVE }, + { 0, 0 } +}; + +static struct sonypi_eventtypes { + int model; + u8 data; + unsigned long mask; + struct sonypi_event * events; +} sony_pic_eventtypes[] = { + { SONYPI_DEVICE_TYPE1, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE1, 0x70, SONYPI_MEYE_MASK, sonypi_meyeev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_TYPE1, 0x60, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_TYPE1, 0x10, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_TYPE1, 0x20, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { SONYPI_DEVICE_TYPE1, 0x30, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE1, 0x40, SONYPI_BATTERY_MASK, sonypi_batteryev }, + + { SONYPI_DEVICE_TYPE2, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE2, 0x38, SONYPI_LID_MASK, sonypi_lidev }, + { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_JOGGER_MASK, sonypi_joggerev }, + { SONYPI_DEVICE_TYPE2, 0x61, SONYPI_CAPTURE_MASK, sonypi_captureev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_BLUETOOTH_MASK, sonypi_blueev }, + { SONYPI_DEVICE_TYPE2, 0x08, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { SONYPI_DEVICE_TYPE2, 0x11, SONYPI_BACK_MASK, sonypi_backev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_HELP_MASK, sonypi_helpev }, + { SONYPI_DEVICE_TYPE2, 0x21, SONYPI_ZOOM_MASK, sonypi_zoomev }, + { SONYPI_DEVICE_TYPE2, 0x20, SONYPI_THUMBPHRASE_MASK, sonypi_thumbphraseev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE2, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { SONYPI_DEVICE_TYPE2, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + + { SONYPI_DEVICE_TYPE3, 0, 0xffffffff, sonypi_releaseev }, + { SONYPI_DEVICE_TYPE3, 0x21, SONYPI_FNKEY_MASK, sonypi_fnkeyev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_WIRELESS_MASK, sonypi_wlessev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_MEMORYSTICK_MASK, sonypi_memorystickev }, + { SONYPI_DEVICE_TYPE3, 0x41, SONYPI_BATTERY_MASK, sonypi_batteryev }, + { SONYPI_DEVICE_TYPE3, 0x31, SONYPI_PKEY_MASK, sonypi_pkeyev }, + { 0 } +}; + +static int sony_pic_detect_device_type(void) +{ + struct pci_dev *pcidev; + int model = 0; + + if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL))) + model = SONYPI_DEVICE_TYPE1; + + else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH6_1, NULL))) + model = SONYPI_DEVICE_TYPE3; + + else if ((pcidev = pci_get_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_ICH7_1, NULL))) + model = SONYPI_DEVICE_TYPE3; + + else + model = SONYPI_DEVICE_TYPE2; + + if (pcidev) + pci_dev_put(pcidev); + + printk(KERN_INFO DRV_PFX "detected Type%d model\n", + model == SONYPI_DEVICE_TYPE1 ? 1 : + model == SONYPI_DEVICE_TYPE2 ? 2 : 3); + return model; +} + +#define ITERATIONS_LONG 10000 +#define ITERATIONS_SHORT 10 +#define wait_on_command(command, iterations) { \ + unsigned int n = iterations; \ + while (--n && (command)) \ + udelay(1); \ + if (!n) \ + dprintk("command failed at %s : %s (line %d)\n", \ + __FILE__, __FUNCTION__, __LINE__); \ +} + +static u8 sony_pic_call1(u8 dev) +{ + u8 v1, v2; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + v1 = inb_p(spic_dev.cur_ioport->io.minimum + 4); + v2 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call1: 0x%.4x\n", (v2 << 8) | v1); + return v2; +} + +static u8 sony_pic_call2(u8 dev, u8 fn) +{ + u8 v1; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, + ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io.minimum); + v1 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call2: 0x%.4x\n", v1); + return v1; +} + +static u8 sony_pic_call3(u8 dev, u8 fn, u8 v) +{ + u8 v1; + + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(dev, spic_dev.cur_ioport->io.minimum + 4); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(fn, spic_dev.cur_ioport->io.minimum); + wait_on_command(inb_p(spic_dev.cur_ioport->io.minimum + 4) & 2, ITERATIONS_LONG); + outb(v, spic_dev.cur_ioport->io.minimum); + v1 = inb_p(spic_dev.cur_ioport->io.minimum); + dprintk("sony_pic_call3: 0x%.4x\n", v1); + return v1; +} + +/* camera tests and poweron/poweroff */ +#define SONYPI_CAMERA_PICTURE 5 +#define SONYPI_CAMERA_CONTROL 0x10 + +#define SONYPI_CAMERA_BRIGHTNESS 0 +#define SONYPI_CAMERA_CONTRAST 1 +#define SONYPI_CAMERA_HUE 2 +#define SONYPI_CAMERA_COLOR 3 +#define SONYPI_CAMERA_SHARPNESS 4 + +#define SONYPI_CAMERA_EXPOSURE_MASK 0xC +#define SONYPI_CAMERA_WHITE_BALANCE_MASK 0x3 +#define SONYPI_CAMERA_PICTURE_MODE_MASK 0x30 +#define SONYPI_CAMERA_MUTE_MASK 0x40 + +/* the rest don't need a loop until not 0xff */ +#define SONYPI_CAMERA_AGC 6 +#define SONYPI_CAMERA_AGC_MASK 0x30 +#define SONYPI_CAMERA_SHUTTER_MASK 0x7 + +#define SONYPI_CAMERA_SHUTDOWN_REQUEST 7 +#define SONYPI_CAMERA_CONTROL 0x10 + +#define SONYPI_CAMERA_STATUS 7 +#define SONYPI_CAMERA_STATUS_READY 0x2 +#define SONYPI_CAMERA_STATUS_POSITION 0x4 + +#define SONYPI_DIRECTION_BACKWARDS 0x4 + +#define SONYPI_CAMERA_REVISION 8 +#define SONYPI_CAMERA_ROMVERSION 9 + +static int __sony_pic_camera_ready(void) +{ + u8 v; + + v = sony_pic_call2(0x8f, SONYPI_CAMERA_STATUS); + return (v != 0xff && (v & SONYPI_CAMERA_STATUS_READY)); +} + +static int __sony_pic_camera_off(void) +{ + if (!camera) { + printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + return -ENODEV; + } + + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, + SONYPI_CAMERA_MUTE_MASK), + ITERATIONS_SHORT); + + if (spic_dev.camera_power) { + sony_pic_call2(0x91, 0); + spic_dev.camera_power = 0; + } + return 0; +} + +static int __sony_pic_camera_on(void) +{ + int i, j, x; + + if (!camera) { + printk(KERN_WARNING DRV_PFX "camera control not enabled\n"); + return -ENODEV; + } + + if (spic_dev.camera_power) + return 0; + + for (j = 5; j > 0; j--) { + + for (x = 0; x < 100 && sony_pic_call2(0x91, 0x1); x++) + msleep(10); + sony_pic_call1(0x93); + + for (i = 400; i > 0; i--) { + if (__sony_pic_camera_ready()) + break; + msleep(10); + } + if (i) + break; + } + + if (j == 0) { + printk(KERN_WARNING DRV_PFX "failed to power on camera\n"); + return -ENODEV; + } + + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTROL, + 0x5a), + ITERATIONS_SHORT); + + spic_dev.camera_power = 1; + return 0; +} + +/* External camera command (exported to the motion eye v4l driver) */ +int sony_pic_camera_command(int command, u8 value) +{ + if (!camera) + return -EIO; + + mutex_lock(&spic_dev.lock); + + switch (command) { + case SONY_PIC_COMMAND_SETCAMERA: + if (value) + __sony_pic_camera_on(); + else + __sony_pic_camera_off(); + break; + case SONY_PIC_COMMAND_SETCAMERABRIGHTNESS: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_BRIGHTNESS, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERACONTRAST: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_CONTRAST, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAHUE: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_HUE, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERACOLOR: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_COLOR, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERASHARPNESS: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_SHARPNESS, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAPICTURE: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_PICTURE, value), + ITERATIONS_SHORT); + break; + case SONY_PIC_COMMAND_SETCAMERAAGC: + wait_on_command(sony_pic_call3(0x90, SONYPI_CAMERA_AGC, value), + ITERATIONS_SHORT); + break; + default: + printk(KERN_ERR DRV_PFX "sony_pic_camera_command invalid: %d\n", + command); + break; + } + mutex_unlock(&spic_dev.lock); + return 0; +} +EXPORT_SYMBOL(sony_pic_camera_command); + +/* gprs/edge modem (SZ460N and SZ210P), thanks to Joshua Wise */ +static void sony_pic_set_wwanpower(u8 state) +{ + state = !!state; + mutex_lock(&spic_dev.lock); + if (spic_dev.wwan_power == state) { + mutex_unlock(&spic_dev.lock); + return; + } + sony_pic_call2(0xB0, state); + spic_dev.wwan_power = state; + mutex_unlock(&spic_dev.lock); +} + +static ssize_t sony_pic_wwanpower_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + sony_pic_set_wwanpower(value); + + return count; +} + +static ssize_t sony_pic_wwanpower_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count; + mutex_lock(&spic_dev.lock); + count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.wwan_power); + mutex_unlock(&spic_dev.lock); + return count; +} + +/* bluetooth subsystem power state */ +static void __sony_pic_set_bluetoothpower(u8 state) +{ + state = !!state; + if (spic_dev.bluetooth_power == state) + return; + sony_pic_call2(0x96, state); + sony_pic_call1(0x82); + spic_dev.bluetooth_power = state; +} + +static ssize_t sony_pic_bluetoothpower_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + mutex_lock(&spic_dev.lock); + __sony_pic_set_bluetoothpower(value); + mutex_unlock(&spic_dev.lock); + + return count; +} + +static ssize_t sony_pic_bluetoothpower_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + ssize_t count = 0; + mutex_lock(&spic_dev.lock); + count = snprintf(buffer, PAGE_SIZE, "%d\n", spic_dev.bluetooth_power); + mutex_unlock(&spic_dev.lock); + return count; +} + +/* fan speed */ +/* FAN0 information (reverse engineered from ACPI tables) */ +#define SONY_PIC_FAN0_STATUS 0x93 +static int sony_pic_set_fanspeed(unsigned long value) +{ + return ec_write(SONY_PIC_FAN0_STATUS, value); +} + +static int sony_pic_get_fanspeed(u8 *value) +{ + return ec_read(SONY_PIC_FAN0_STATUS, value); +} + +static ssize_t sony_pic_fanspeed_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned long value; + if (count > 31) + return -EINVAL; + + value = simple_strtoul(buffer, NULL, 10); + if (sony_pic_set_fanspeed(value)) + return -EIO; + + return count; +} + +static ssize_t sony_pic_fanspeed_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + u8 value = 0; + if (sony_pic_get_fanspeed(&value)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", value); +} + +#define SPIC_ATTR(_name, _mode) \ +struct device_attribute spic_attr_##_name = __ATTR(_name, \ + _mode, sony_pic_## _name ##_show, \ + sony_pic_## _name ##_store) + +static SPIC_ATTR(bluetoothpower, 0644); +static SPIC_ATTR(wwanpower, 0644); +static SPIC_ATTR(fanspeed, 0644); + +static struct attribute *spic_attributes[] = { + &spic_attr_bluetoothpower.attr, + &spic_attr_wwanpower.attr, + &spic_attr_fanspeed.attr, + NULL +}; + +static struct attribute_group spic_attribute_group = { + .attrs = spic_attributes +}; + +/******** SONYPI compatibility **********/ +#ifdef CONFIG_SONY_LAPTOP_OLD + +/* battery / brightness / temperature addresses */ +#define SONYPI_BAT_FLAGS 0x81 +#define SONYPI_LCD_LIGHT 0x96 +#define SONYPI_BAT1_PCTRM 0xa0 +#define SONYPI_BAT1_LEFT 0xa2 +#define SONYPI_BAT1_MAXRT 0xa4 +#define SONYPI_BAT2_PCTRM 0xa8 +#define SONYPI_BAT2_LEFT 0xaa +#define SONYPI_BAT2_MAXRT 0xac +#define SONYPI_BAT1_MAXTK 0xb0 +#define SONYPI_BAT1_FULL 0xb2 +#define SONYPI_BAT2_MAXTK 0xb8 +#define SONYPI_BAT2_FULL 0xba +#define SONYPI_TEMP_STATUS 0xC1 + +struct sonypi_compat_s { + struct fasync_struct *fifo_async; + struct kfifo *fifo; + spinlock_t fifo_lock; + wait_queue_head_t fifo_proc_list; + atomic_t open_count; +}; +static struct sonypi_compat_s sonypi_compat = { + .open_count = ATOMIC_INIT(0), +}; + +static int sonypi_misc_fasync(int fd, struct file *filp, int on) +{ + int retval; + + retval = fasync_helper(fd, filp, on, &sonypi_compat.fifo_async); + if (retval < 0) + return retval; + return 0; +} + +static int sonypi_misc_release(struct inode *inode, struct file *file) +{ + sonypi_misc_fasync(-1, file, 0); + atomic_dec(&sonypi_compat.open_count); + return 0; +} + +static int sonypi_misc_open(struct inode *inode, struct file *file) +{ + /* Flush input queue on first open */ + if (atomic_inc_return(&sonypi_compat.open_count) == 1) + kfifo_reset(sonypi_compat.fifo); + return 0; +} + +static ssize_t sonypi_misc_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + ssize_t ret; + unsigned char c; + + if ((kfifo_len(sonypi_compat.fifo) == 0) && + (file->f_flags & O_NONBLOCK)) + return -EAGAIN; + + ret = wait_event_interruptible(sonypi_compat.fifo_proc_list, + kfifo_len(sonypi_compat.fifo) != 0); + if (ret) + return ret; + + while (ret < count && + (kfifo_get(sonypi_compat.fifo, &c, sizeof(c)) == sizeof(c))) { + if (put_user(c, buf++)) + return -EFAULT; + ret++; + } + + if (ret > 0) { + struct inode *inode = file->f_path.dentry->d_inode; + inode->i_atime = current_fs_time(inode->i_sb); + } + + return ret; +} + +static unsigned int sonypi_misc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &sonypi_compat.fifo_proc_list, wait); + if (kfifo_len(sonypi_compat.fifo)) + return POLLIN | POLLRDNORM; + return 0; +} + +static int ec_read16(u8 addr, u16 *value) +{ + u8 val_lb, val_hb; + if (ec_read(addr, &val_lb)) + return -1; + if (ec_read(addr + 1, &val_hb)) + return -1; + *value = val_lb | (val_hb << 8); + return 0; +} + +static int sonypi_misc_ioctl(struct inode *ip, struct file *fp, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + void __user *argp = (void __user *)arg; + u8 val8; + u16 val16; + int value; + + mutex_lock(&spic_dev.lock); + switch (cmd) { + case SONYPI_IOCGBRT: + if (sony_backlight_device == NULL) { + ret = -EIO; + break; + } + if (acpi_callgetfunc(sony_nc_acpi_handle, "GBRT", &value)) { + ret = -EIO; + break; + } + val8 = ((value & 0xff) - 1) << 5; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSBRT: + if (sony_backlight_device == NULL) { + ret = -EIO; + break; + } + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + if (acpi_callsetfunc(sony_nc_acpi_handle, "SBRT", + (val8 >> 5) + 1, NULL)) { + ret = -EIO; + break; + } + /* sync the backlight device status */ + sony_backlight_device->props.brightness = + sony_backlight_get_brightness(sony_backlight_device); + break; + case SONYPI_IOCGBAT1CAP: + if (ec_read16(SONYPI_BAT1_FULL, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT1REM: + if (ec_read16(SONYPI_BAT1_LEFT, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT2CAP: + if (ec_read16(SONYPI_BAT2_FULL, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBAT2REM: + if (ec_read16(SONYPI_BAT2_LEFT, &val16)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val16, sizeof(val16))) + ret = -EFAULT; + break; + case SONYPI_IOCGBATFLAGS: + if (ec_read(SONYPI_BAT_FLAGS, &val8)) { + ret = -EIO; + break; + } + val8 &= 0x07; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCGBLUE: + val8 = spic_dev.bluetooth_power; + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSBLUE: + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + __sony_pic_set_bluetoothpower(val8); + break; + /* FAN Controls */ + case SONYPI_IOCGFAN: + if (sony_pic_get_fanspeed(&val8)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + case SONYPI_IOCSFAN: + if (copy_from_user(&val8, argp, sizeof(val8))) { + ret = -EFAULT; + break; + } + if (sony_pic_set_fanspeed(val8)) + ret = -EIO; + break; + /* GET Temperature (useful under APM) */ + case SONYPI_IOCGTEMP: + if (ec_read(SONYPI_TEMP_STATUS, &val8)) { + ret = -EIO; + break; + } + if (copy_to_user(argp, &val8, sizeof(val8))) + ret = -EFAULT; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&spic_dev.lock); + return ret; +} + +static const struct file_operations sonypi_misc_fops = { + .owner = THIS_MODULE, + .read = sonypi_misc_read, + .poll = sonypi_misc_poll, + .open = sonypi_misc_open, + .release = sonypi_misc_release, + .fasync = sonypi_misc_fasync, + .ioctl = sonypi_misc_ioctl, +}; + +static struct miscdevice sonypi_misc_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "sonypi", + .fops = &sonypi_misc_fops, +}; + +static void sonypi_compat_report_event(u8 event) +{ + kfifo_put(sonypi_compat.fifo, (unsigned char *)&event, sizeof(event)); + kill_fasync(&sonypi_compat.fifo_async, SIGIO, POLL_IN); + wake_up_interruptible(&sonypi_compat.fifo_proc_list); +} + +static int sonypi_compat_init(void) +{ + int error; + + spin_lock_init(&sonypi_compat.fifo_lock); + sonypi_compat.fifo = kfifo_alloc(SONY_LAPTOP_BUF_SIZE, GFP_KERNEL, + &sonypi_compat.fifo_lock); + if (IS_ERR(sonypi_compat.fifo)) { + printk(KERN_ERR DRV_PFX "kfifo_alloc failed\n"); + return PTR_ERR(sonypi_compat.fifo); + } + + init_waitqueue_head(&sonypi_compat.fifo_proc_list); + + if (minor != -1) + sonypi_misc_device.minor = minor; + error = misc_register(&sonypi_misc_device); + if (error) { + printk(KERN_ERR DRV_PFX "misc_register failed\n"); + goto err_free_kfifo; + } + if (minor == -1) + printk(KERN_INFO DRV_PFX "device allocated minor is %d\n", + sonypi_misc_device.minor); + + return 0; + +err_free_kfifo: + kfifo_free(sonypi_compat.fifo); + return error; +} + +static void sonypi_compat_exit(void) +{ + misc_deregister(&sonypi_misc_device); + kfifo_free(sonypi_compat.fifo); +} +#else +static int sonypi_compat_init(void) { return 0; } +static void sonypi_compat_exit(void) { } +static void sonypi_compat_report_event(u8 event) { } +#endif /* CONFIG_SONY_LAPTOP_OLD */ + +/* + * ACPI callbacks + */ +static acpi_status +sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) +{ + u32 i; + struct sony_pic_dev *dev = (struct sony_pic_dev *)context; + + switch (resource->type) { + case ACPI_RESOURCE_TYPE_START_DEPENDENT: + case ACPI_RESOURCE_TYPE_END_DEPENDENT: + return AE_OK; + + case ACPI_RESOURCE_TYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + struct sony_pic_irq *interrupt = NULL; + if (!p || !p->interrupt_count) { + /* + * IRQ descriptors may have no IRQ# bits set, + * particularly those those w/ _STA disabled + */ + dprintk("Blank IRQ resource\n"); + return AE_OK; + } + for (i = 0; i < p->interrupt_count; i++) { + if (!p->interrupts[i]) { + printk(KERN_WARNING DRV_PFX + "Invalid IRQ %d\n", + p->interrupts[i]); + continue; + } + interrupt = kzalloc(sizeof(*interrupt), + GFP_KERNEL); + if (!interrupt) + return AE_ERROR; + + list_add_tail(&interrupt->list, &dev->interrupts); + interrupt->irq.triggering = p->triggering; + interrupt->irq.polarity = p->polarity; + interrupt->irq.sharable = p->sharable; + interrupt->irq.interrupt_count = 1; + interrupt->irq.interrupts[0] = p->interrupts[i]; + } + return AE_OK; + } + case ACPI_RESOURCE_TYPE_IO: + { + struct acpi_resource_io *io = &resource->data.io; + struct sony_pic_ioport *ioport = NULL; + if (!io) { + dprintk("Blank IO resource\n"); + return AE_OK; + } + + ioport = kzalloc(sizeof(*ioport), GFP_KERNEL); + if (!ioport) + return AE_ERROR; + + list_add_tail(&ioport->list, &dev->ioports); + memcpy(&ioport->io, io, sizeof(*io)); + return AE_OK; + } + default: + dprintk("Resource %d isn't an IRQ nor an IO port\n", + resource->type); + + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + } + return AE_CTRL_TERMINATE; +} + +static int sony_pic_possible_resources(struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + + if (!device) + return -EINVAL; + + /* get device status */ + /* see acpi_pci_link_get_current acpi_pci_link_get_possible */ + dprintk("Evaluating _STA\n"); + result = acpi_bus_get_status(device); + if (result) { + printk(KERN_WARNING DRV_PFX "Unable to read status\n"); + goto end; + } + + if (!device->status.enabled) + dprintk("Device disabled\n"); + else + dprintk("Device enabled\n"); + + /* + * Query and parse 'method' + */ + dprintk("Evaluating %s\n", METHOD_NAME__PRS); + status = acpi_walk_resources(device->handle, METHOD_NAME__PRS, + sony_pic_read_possible_resource, &spic_dev); + if (ACPI_FAILURE(status)) { + printk(KERN_WARNING DRV_PFX + "Failure evaluating %s\n", + METHOD_NAME__PRS); + result = -ENODEV; + } +end: + return result; +} + +/* + * Disable the spic device by calling its _DIS method + */ +static int sony_pic_disable(struct acpi_device *device) +{ + if (ACPI_FAILURE(acpi_evaluate_object(device->handle, "_DIS", 0, NULL))) + return -ENXIO; + + dprintk("Device disabled\n"); + return 0; +} + + +/* + * Based on drivers/acpi/pci_link.c:acpi_pci_link_set + * + * Call _SRS to set current resources + */ +static int sony_pic_enable(struct acpi_device *device, + struct sony_pic_ioport *ioport, struct sony_pic_irq *irq) +{ + acpi_status status; + int result = 0; + struct { + struct acpi_resource io_res; + struct acpi_resource irq_res; + struct acpi_resource end; + } *resource; + struct acpi_buffer buffer = { 0, NULL }; + + if (!ioport || !irq) + return -EINVAL; + + /* init acpi_buffer */ + resource = kzalloc(sizeof(*resource) + 1, GFP_KERNEL); + if (!resource) + return -ENOMEM; + + buffer.length = sizeof(*resource) + 1; + buffer.pointer = resource; + + /* setup io resource */ + resource->io_res.type = ACPI_RESOURCE_TYPE_IO; + resource->io_res.length = sizeof(struct acpi_resource); + memcpy(&resource->io_res.data.io, &ioport->io, + sizeof(struct acpi_resource_io)); + + /* setup irq resource */ + resource->irq_res.type = ACPI_RESOURCE_TYPE_IRQ; + resource->irq_res.length = sizeof(struct acpi_resource); + memcpy(&resource->irq_res.data.irq, &irq->irq, + sizeof(struct acpi_resource_irq)); + /* we requested a shared irq */ + resource->irq_res.data.irq.sharable = ACPI_SHARED; + + resource->end.type = ACPI_RESOURCE_TYPE_END_TAG; + + /* Attempt to set the resource */ + dprintk("Evaluating _SRS\n"); + status = acpi_set_current_resources(device->handle, &buffer); + + /* check for total failure */ + if (ACPI_FAILURE(status)) { + printk(KERN_ERR DRV_PFX "Error evaluating _SRS"); + result = -ENODEV; + goto end; + } + + /* Necessary device initializations calls (from sonypi) */ + sony_pic_call1(0x82); + sony_pic_call2(0x81, 0xff); + sony_pic_call1(compat ? 0x92 : 0x82); + +end: + kfree(resource); + return result; +} + +/***************** + * + * ISR: some event is available + * + *****************/ +static irqreturn_t sony_pic_irq(int irq, void *dev_id) +{ + int i, j; + u32 port_val = 0; + u8 ev = 0; + u8 data_mask = 0; + u8 device_event = 0; + + struct sony_pic_dev *dev = (struct sony_pic_dev *) dev_id; + + acpi_os_read_port(dev->cur_ioport->io.minimum, &port_val, + dev->cur_ioport->io.address_length); + ev = port_val & SONY_PIC_EV_MASK; + data_mask = 0xff & (port_val >> (dev->cur_ioport->io.address_length - 8)); + + dprintk("event (0x%.8x [%.2x] [%.2x]) at port 0x%.4x\n", + port_val, ev, data_mask, dev->cur_ioport->io.minimum); + + if (ev == 0x00 || ev == 0xff) + return IRQ_HANDLED; + + for (i = 0; sony_pic_eventtypes[i].model; i++) { + + if (spic_dev.model != sony_pic_eventtypes[i].model) + continue; + + if ((data_mask & sony_pic_eventtypes[i].data) != + sony_pic_eventtypes[i].data) + continue; + + if (!(mask & sony_pic_eventtypes[i].mask)) + continue; + + for (j = 0; sony_pic_eventtypes[i].events[j].event; j++) { + if (ev == sony_pic_eventtypes[i].events[j].data) { + device_event = + sony_pic_eventtypes[i].events[j].event; + goto found; + } + } + } + return IRQ_HANDLED; + +found: + sony_laptop_report_input_event(device_event); + acpi_bus_generate_event(spic_dev.acpi_dev, 1, device_event); + sonypi_compat_report_event(device_event); + + return IRQ_HANDLED; +} + +/***************** + * + * ACPI driver + * + *****************/ +static int sony_pic_remove(struct acpi_device *device, int type) +{ + struct sony_pic_ioport *io, *tmp_io; + struct sony_pic_irq *irq, *tmp_irq; + + sonypi_compat_exit(); + + if (sony_pic_disable(device)) { + printk(KERN_ERR DRV_PFX "Couldn't disable device.\n"); + return -ENXIO; + } + + free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); + release_region(spic_dev.cur_ioport->io.minimum, + spic_dev.cur_ioport->io.address_length); + + sony_laptop_remove_input(); + + /* pf attrs */ + sysfs_remove_group(&sony_pf_device->dev.kobj, &spic_attribute_group); + sony_pf_remove(); + + list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { + list_del(&io->list); + kfree(io); + } + list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { + list_del(&irq->list); + kfree(irq); + } + spic_dev.cur_ioport = NULL; + spic_dev.cur_irq = NULL; + + dprintk(SONY_PIC_DRIVER_NAME " removed.\n"); + return 0; +} + +static int sony_pic_add(struct acpi_device *device) +{ + int result; + struct sony_pic_ioport *io, *tmp_io; + struct sony_pic_irq *irq, *tmp_irq; + + printk(KERN_INFO DRV_PFX "%s v%s.\n", + SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); + + spic_dev.acpi_dev = device; + strcpy(acpi_device_class(device), "sony/hotkey"); + spic_dev.model = sony_pic_detect_device_type(); + mutex_init(&spic_dev.lock); + + /* read _PRS resources */ + result = sony_pic_possible_resources(device); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to read possible resources.\n"); + goto err_free_resources; + } + + /* setup input devices and helper fifo */ + result = sony_laptop_setup_input(); + if (result) { + printk(KERN_ERR DRV_PFX + "Unabe to create input devices.\n"); + goto err_free_resources; + } + + /* request io port */ + list_for_each_entry(io, &spic_dev.ioports, list) { + if (request_region(io->io.minimum, io->io.address_length, + "Sony Programable I/O Device")) { + dprintk("I/O port: 0x%.4x (0x%.4x) + 0x%.2x\n", + io->io.minimum, io->io.maximum, + io->io.address_length); + spic_dev.cur_ioport = io; + break; + } + } + if (!spic_dev.cur_ioport) { + printk(KERN_ERR DRV_PFX "Failed to request_region.\n"); + result = -ENODEV; + goto err_remove_input; + } + + /* request IRQ */ + list_for_each_entry(irq, &spic_dev.interrupts, list) { + if (!request_irq(irq->irq.interrupts[0], sony_pic_irq, + IRQF_SHARED, "sony-laptop", &spic_dev)) { + dprintk("IRQ: %d - triggering: %d - " + "polarity: %d - shr: %d\n", + irq->irq.interrupts[0], + irq->irq.triggering, + irq->irq.polarity, + irq->irq.sharable); + spic_dev.cur_irq = irq; + break; + } + } + if (!spic_dev.cur_irq) { + printk(KERN_ERR DRV_PFX "Failed to request_irq.\n"); + result = -ENODEV; + goto err_release_region; + } + + /* set resource status _SRS */ + result = sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + if (result) { + printk(KERN_ERR DRV_PFX "Couldn't enable device.\n"); + goto err_free_irq; + } + + spic_dev.bluetooth_power = -1; + /* create device attributes */ + result = sony_pf_add(); + if (result) + goto err_disable_device; + + result = sysfs_create_group(&sony_pf_device->dev.kobj, &spic_attribute_group); + if (result) + goto err_remove_pf; + + if (sonypi_compat_init()) + goto err_remove_pf; + + return 0; + +err_remove_pf: + sony_pf_remove(); + +err_disable_device: + sony_pic_disable(device); + +err_free_irq: + free_irq(spic_dev.cur_irq->irq.interrupts[0], &spic_dev); + +err_release_region: + release_region(spic_dev.cur_ioport->io.minimum, + spic_dev.cur_ioport->io.address_length); + +err_remove_input: + sony_laptop_remove_input(); + +err_free_resources: + list_for_each_entry_safe(io, tmp_io, &spic_dev.ioports, list) { + list_del(&io->list); + kfree(io); + } + list_for_each_entry_safe(irq, tmp_irq, &spic_dev.interrupts, list) { + list_del(&irq->list); + kfree(irq); + } + spic_dev.cur_ioport = NULL; + spic_dev.cur_irq = NULL; + + return result; +} + +static int sony_pic_suspend(struct acpi_device *device, pm_message_t state) +{ + if (sony_pic_disable(device)) + return -ENXIO; + return 0; +} + +static int sony_pic_resume(struct acpi_device *device) +{ + sony_pic_enable(device, spic_dev.cur_ioport, spic_dev.cur_irq); + return 0; +} + +static struct acpi_driver sony_pic_driver = { + .name = SONY_PIC_DRIVER_NAME, + .class = SONY_PIC_CLASS, + .ids = SONY_PIC_HID, + .owner = THIS_MODULE, + .ops = { + .add = sony_pic_add, + .remove = sony_pic_remove, + .suspend = sony_pic_suspend, + .resume = sony_pic_resume, + }, +}; + +static struct dmi_system_id __initdata sonypi_dmi_table[] = { + { + .ident = "Sony Vaio", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "PCG-"), + }, + }, + { + .ident = "Sony Vaio", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-"), + }, + }, + { } +}; + +static int __init sony_laptop_init(void) +{ + int result; + + if (!no_spic && dmi_check_system(sonypi_dmi_table)) { + result = acpi_bus_register_driver(&sony_pic_driver); + if (result) { + printk(KERN_ERR DRV_PFX + "Unable to register SPIC driver."); + goto out; + } + } + + result = acpi_bus_register_driver(&sony_nc_driver); + if (result) { + printk(KERN_ERR DRV_PFX "Unable to register SNC driver."); + goto out_unregister_pic; + } + + return 0; + +out_unregister_pic: + if (!no_spic) + acpi_bus_unregister_driver(&sony_pic_driver); +out: + return result; +} + +static void __exit sony_laptop_exit(void) +{ + acpi_bus_unregister_driver(&sony_nc_driver); + if (!no_spic) + acpi_bus_unregister_driver(&sony_pic_driver); +} + +module_init(sony_laptop_init); +module_exit(sony_laptop_exit); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c new file mode 100644 index 0000000..6c36a55 --- /dev/null +++ b/drivers/misc/thinkpad_acpi.c @@ -0,0 +1,4312 @@ +/* + * thinkpad_acpi.c - ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#define IBM_VERSION "0.14" +#define TPACPI_SYSFS_VERSION 0x000100 + +/* + * Changelog: + * 2007-03-27 0.14 renamed to thinkpad_acpi and moved to + * drivers/misc. + * + * 2006-11-22 0.13 new maintainer + * changelog now lives in git commit history, and will + * not be updated further in-file. + * + * 2005-08-17 0.12 fix compilation on 2.6.13-rc kernels + * 2005-03-17 0.11 support for 600e, 770x + * thanks to Jamie Lentin <lentinj@dial.pipex.com> + * support for 770e, G41 + * G40 and G41 don't have a thinklight + * temperatures no longer experimental + * experimental brightness control + * experimental volume control + * experimental fan enable/disable + * 2005-01-16 0.10 fix module loading on R30, R31 + * 2005-01-16 0.9 support for 570, R30, R31 + * ultrabay support on A22p, A3x + * limit arg for cmos, led, beep, drop experimental status + * more capable led control on A21e, A22p, T20-22, X20 + * experimental temperatures and fan speed + * experimental embedded controller register dump + * mark more functions as __init, drop incorrect __exit + * use MODULE_VERSION + * thanks to Henrik Brix Andersen <brix@gentoo.org> + * fix parameter passing on module loading + * thanks to Rusty Russell <rusty@rustcorp.com.au> + * thanks to Jim Radford <radford@blackbean.org> + * 2004-11-08 0.8 fix init error case, don't return from a macro + * thanks to Chris Wright <chrisw@osdl.org> + * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 + * fix led control on A21e + * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device + * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 + * proc file format changed + * video_switch command + * experimental cmos control + * experimental led control + * experimental acpi sounds + * 2004-09-16 0.4 support for module parameters + * hotkey mask can be prefixed by 0x + * video output switching + * video expansion control + * ultrabay eject support + * removed lcd brightness/on/off control, didn't work + * 2004-08-17 0.3 support for R40 + * lcd off, brightness control + * thinklight on/off + * 2004-08-14 0.2 support for T series, X20 + * bluetooth enable/disable + * hotkey events disabled by default + * removed fan control, currently useless + * 2004-08-09 0.1 initial release, support for X series + */ + +#include "thinkpad_acpi.h" + +MODULE_AUTHOR("Borislav Deianov, Henrique de Moraes Holschuh"); +MODULE_DESCRIPTION(IBM_DESC); +MODULE_VERSION(IBM_VERSION); +MODULE_LICENSE("GPL"); + +/* Please remove this in year 2009 */ +MODULE_ALIAS("ibm_acpi"); + +#define __unused __attribute__ ((unused)) + +/**************************************************************************** + **************************************************************************** + * + * ACPI Helpers and device model + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * ACPI basic handles + */ + +static acpi_handle root_handle = NULL; + +#define IBM_HANDLE(object, parent, paths...) \ + static acpi_handle object##_handle; \ + static acpi_handle *object##_parent = &parent##_handle; \ + static char *object##_path; \ + static char *object##_paths[] = { paths } + +IBM_HANDLE(ec, root, "\\_SB.PCI0.ISA.EC0", /* 240, 240x */ + "\\_SB.PCI.ISA.EC", /* 570 */ + "\\_SB.PCI0.ISA0.EC0", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */ + "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */ + "\\_SB.PCI0.ICH3.EC0", /* R31 */ + "\\_SB.PCI0.LPC.EC", /* all others */ + ); + +IBM_HANDLE(ecrd, ec, "ECRD"); /* 570 */ +IBM_HANDLE(ecwr, ec, "ECWR"); /* 570 */ + + +/************************************************************************* + * Misc ACPI handles + */ + +IBM_HANDLE(cmos, root, "\\UCMS", /* R50, R50e, R50p, R51, T4x, X31, X40 */ + "\\CMOS", /* A3x, G4x, R32, T23, T30, X22-24, X30 */ + "\\CMS", /* R40, R40e */ + ); /* all others */ + +IBM_HANDLE(hkey, ec, "\\_SB.HKEY", /* 600e/x, 770e, 770x */ + "^HKEY", /* R30, R31 */ + "HKEY", /* all others */ + ); /* 570 */ + + +/************************************************************************* + * ACPI helpers + */ + +static int acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...) +{ + char *fmt0 = fmt; + struct acpi_object_list params; + union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; + struct acpi_buffer result, *resultp; + union acpi_object out_obj; + acpi_status status; + va_list ap; + char res_type; + int success; + int quiet; + + if (!*fmt) { + printk(IBM_ERR "acpi_evalf() called with empty format\n"); + return 0; + } + + if (*fmt == 'q') { + quiet = 1; + fmt++; + } else + quiet = 0; + + res_type = *(fmt++); + + params.count = 0; + params.pointer = &in_objs[0]; + + va_start(ap, fmt); + while (*fmt) { + char c = *(fmt++); + switch (c) { + case 'd': /* int */ + in_objs[params.count].integer.value = va_arg(ap, int); + in_objs[params.count++].type = ACPI_TYPE_INTEGER; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", c); + return 0; + } + } + va_end(ap); + + if (res_type != 'v') { + result.length = sizeof(out_obj); + result.pointer = &out_obj; + resultp = &result; + } else + resultp = NULL; + + status = acpi_evaluate_object(handle, method, ¶ms, resultp); + + switch (res_type) { + case 'd': /* int */ + if (res) + *(int *)res = out_obj.integer.value; + success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; + break; + case 'v': /* void */ + success = status == AE_OK; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", res_type); + return 0; + } + + if (!success && !quiet) + printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", + method, fmt0, status); + + return success; +} + +static void __unused acpi_print_int(acpi_handle handle, char *method) +{ + int i; + + if (acpi_evalf(handle, &i, method, "d")) + printk(IBM_INFO "%s = 0x%x\n", method, i); + else + printk(IBM_ERR "error calling %s\n", method); +} + +static int acpi_ec_read(int i, u8 * p) +{ + int v; + + if (ecrd_handle) { + if (!acpi_evalf(ecrd_handle, &v, NULL, "dd", i)) + return 0; + *p = v; + } else { + if (ec_read(i, p) < 0) + return 0; + } + + return 1; +} + +static int acpi_ec_write(int i, u8 v) +{ + if (ecwr_handle) { + if (!acpi_evalf(ecwr_handle, NULL, NULL, "vdd", i, v)) + return 0; + } else { + if (ec_write(i, v) < 0) + return 0; + } + + return 1; +} + +static int _sta(acpi_handle handle) +{ + int status; + + if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) + status = 0; + + return status; +} + +static int issue_thinkpad_cmos_command(int cmos_cmd) +{ + if (!cmos_handle) + return -ENXIO; + + if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) + return -EIO; + + return 0; +} + +/************************************************************************* + * ACPI device model + */ + +static void drv_acpi_handle_init(char *name, + acpi_handle *handle, acpi_handle parent, + char **paths, int num_paths, char **path) +{ + int i; + acpi_status status; + + vdbg_printk(TPACPI_DBG_INIT, "trying to locate ACPI handle for %s\n", + name); + + for (i = 0; i < num_paths; i++) { + status = acpi_get_handle(parent, paths[i], handle); + if (ACPI_SUCCESS(status)) { + *path = paths[i]; + dbg_printk(TPACPI_DBG_INIT, + "Found ACPI handle %s for %s\n", + *path, name); + return; + } + } + + vdbg_printk(TPACPI_DBG_INIT, "ACPI handle for %s not found\n", + name); + *handle = NULL; +} + +static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) +{ + struct ibm_struct *ibm = data; + + if (!ibm || !ibm->acpi || !ibm->acpi->notify) + return; + + ibm->acpi->notify(ibm, event); +} + +static int __init setup_acpi_notify(struct ibm_struct *ibm) +{ + acpi_status status; + int rc; + + BUG_ON(!ibm->acpi); + + if (!*ibm->acpi->handle) + return 0; + + vdbg_printk(TPACPI_DBG_INIT, + "setting up ACPI notify for %s\n", ibm->name); + + rc = acpi_bus_get_device(*ibm->acpi->handle, &ibm->acpi->device); + if (rc < 0) { + printk(IBM_ERR "acpi_bus_get_device(%s) failed: %d\n", + ibm->name, rc); + return -ENODEV; + } + + acpi_driver_data(ibm->acpi->device) = ibm; + sprintf(acpi_device_class(ibm->acpi->device), "%s/%s", + IBM_ACPI_EVENT_PREFIX, + ibm->name); + + status = acpi_install_notify_handler(*ibm->acpi->handle, + ibm->acpi->type, dispatch_acpi_notify, ibm); + if (ACPI_FAILURE(status)) { + if (status == AE_ALREADY_EXISTS) { + printk(IBM_NOTICE "another device driver is already handling %s events\n", + ibm->name); + } else { + printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", + ibm->name, status); + } + return -ENODEV; + } + ibm->flags.acpi_notify_installed = 1; + return 0; +} + +static int __init tpacpi_device_add(struct acpi_device *device) +{ + return 0; +} + +static int __init register_tpacpi_subdriver(struct ibm_struct *ibm) +{ + int rc; + + dbg_printk(TPACPI_DBG_INIT, + "registering %s as an ACPI driver\n", ibm->name); + + BUG_ON(!ibm->acpi); + + ibm->acpi->driver = kzalloc(sizeof(struct acpi_driver), GFP_KERNEL); + if (!ibm->acpi->driver) { + printk(IBM_ERR "kzalloc(ibm->driver) failed\n"); + return -ENOMEM; + } + + sprintf(ibm->acpi->driver->name, "%s_%s", IBM_NAME, ibm->name); + ibm->acpi->driver->ids = ibm->acpi->hid; + ibm->acpi->driver->ops.add = &tpacpi_device_add; + + rc = acpi_bus_register_driver(ibm->acpi->driver); + if (rc < 0) { + printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", + ibm->acpi->hid, rc); + kfree(ibm->acpi->driver); + ibm->acpi->driver = NULL; + } else if (!rc) + ibm->flags.acpi_driver_registered = 1; + + return rc; +} + + +/**************************************************************************** + **************************************************************************** + * + * Procfs Helpers + * + **************************************************************************** + ****************************************************************************/ + +static int dispatch_procfs_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + struct ibm_struct *ibm = data; + int len; + + if (!ibm || !ibm->read) + return -EINVAL; + + len = ibm->read(page); + if (len < 0) + return len; + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +static int dispatch_procfs_write(struct file *file, + const char __user * userbuf, + unsigned long count, void *data) +{ + struct ibm_struct *ibm = data; + char *kernbuf; + int ret; + + if (!ibm || !ibm->write) + return -EINVAL; + + kernbuf = kmalloc(count + 2, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (copy_from_user(kernbuf, userbuf, count)) { + kfree(kernbuf); + return -EFAULT; + } + + kernbuf[count] = 0; + strcat(kernbuf, ","); + ret = ibm->write(kernbuf); + if (ret == 0) + ret = count; + + kfree(kernbuf); + + return ret; +} + +static char *next_cmd(char **cmds) +{ + char *start = *cmds; + char *end; + + while ((end = strchr(start, ',')) && end == start) + start = end + 1; + + if (!end) + return NULL; + + *end = 0; + *cmds = end + 1; + return start; +} + + +/**************************************************************************** + **************************************************************************** + * + * Device model: hwmon and platform + * + **************************************************************************** + ****************************************************************************/ + +static struct platform_device *tpacpi_pdev = NULL; +static struct class_device *tpacpi_hwmon = NULL; + +static struct platform_driver tpacpi_pdriver = { + .driver = { + .name = IBM_DRVR_NAME, + .owner = THIS_MODULE, + }, +}; + + +/************************************************************************* + * thinkpad-acpi driver attributes + */ + +/* interface_version --------------------------------------------------- */ +static ssize_t tpacpi_driver_interface_version_show( + struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%08x\n", TPACPI_SYSFS_VERSION); +} + +static DRIVER_ATTR(interface_version, S_IRUGO, + tpacpi_driver_interface_version_show, NULL); + +/* debug_level --------------------------------------------------------- */ +static ssize_t tpacpi_driver_debug_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", dbg_level); +} + +static ssize_t tpacpi_driver_debug_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 0xffff, &t)) + return -EINVAL; + + dbg_level = t; + + return count; +} + +static DRIVER_ATTR(debug_level, S_IWUSR | S_IRUGO, + tpacpi_driver_debug_show, tpacpi_driver_debug_store); + +/* version ------------------------------------------------------------- */ +static ssize_t tpacpi_driver_version_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s v%s\n", IBM_DESC, IBM_VERSION); +} + +static DRIVER_ATTR(version, S_IRUGO, + tpacpi_driver_version_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct driver_attribute* tpacpi_driver_attributes[] = { + &driver_attr_debug_level, &driver_attr_version, + &driver_attr_interface_version, +}; + +static int __init tpacpi_create_driver_attributes(struct device_driver *drv) +{ + int i, res; + + i = 0; + res = 0; + while (!res && i < ARRAY_SIZE(tpacpi_driver_attributes)) { + res = driver_create_file(drv, tpacpi_driver_attributes[i]); + i++; + } + + return res; +} + +static void tpacpi_remove_driver_attributes(struct device_driver *drv) +{ + int i; + + for(i = 0; i < ARRAY_SIZE(tpacpi_driver_attributes); i++) + driver_remove_file(drv, tpacpi_driver_attributes[i]); +} + +/************************************************************************* + * sysfs support helpers + */ + +struct attribute_set_obj { + struct attribute_set s; + struct attribute *a; +} __attribute__((packed)); + +static struct attribute_set *create_attr_set(unsigned int max_members, + const char* name) +{ + struct attribute_set_obj *sobj; + + if (max_members == 0) + return NULL; + + /* Allocates space for implicit NULL at the end too */ + sobj = kzalloc(sizeof(struct attribute_set_obj) + + max_members * sizeof(struct attribute *), + GFP_KERNEL); + if (!sobj) + return NULL; + sobj->s.max_members = max_members; + sobj->s.group.attrs = &sobj->a; + sobj->s.group.name = name; + + return &sobj->s; +} + +/* not multi-threaded safe, use it in a single thread per set */ +static int add_to_attr_set(struct attribute_set* s, struct attribute *attr) +{ + if (!s || !attr) + return -EINVAL; + + if (s->members >= s->max_members) + return -ENOMEM; + + s->group.attrs[s->members] = attr; + s->members++; + + return 0; +} + +static int add_many_to_attr_set(struct attribute_set* s, + struct attribute **attr, + unsigned int count) +{ + int i, res; + + for (i = 0; i < count; i++) { + res = add_to_attr_set(s, attr[i]); + if (res) + return res; + } + + return 0; +} + +static void delete_attr_set(struct attribute_set* s, struct kobject *kobj) +{ + sysfs_remove_group(kobj, &s->group); + destroy_attr_set(s); +} + +static int parse_strtoul(const char *buf, + unsigned long max, unsigned long *value) +{ + char *endp; + + *value = simple_strtoul(buf, &endp, 0); + while (*endp && isspace(*endp)) + endp++; + if (*endp || *value > max) + return -EINVAL; + + return 0; +} + +/**************************************************************************** + **************************************************************************** + * + * Subdrivers + * + **************************************************************************** + ****************************************************************************/ + +/************************************************************************* + * thinkpad-acpi init subdriver + */ + +static int __init thinkpad_acpi_driver_init(struct ibm_init_struct *iibm) +{ + printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); + printk(IBM_INFO "%s\n", IBM_URL); + + if (ibm_thinkpad_ec_found) + printk(IBM_INFO "ThinkPad EC firmware %s\n", + ibm_thinkpad_ec_found); + + return 0; +} + +static int thinkpad_acpi_driver_read(char *p) +{ + int len = 0; + + len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); + len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); + + return len; +} + +static struct ibm_struct thinkpad_acpi_driver_data = { + .name = "driver", + .read = thinkpad_acpi_driver_read, +}; + +/************************************************************************* + * Hotkey subdriver + */ + +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static struct attribute_set *hotkey_dev_attributes = NULL; + +/* sysfs hotkey enable ------------------------------------------------- */ +static ssize_t hotkey_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static ssize_t hotkey_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + res = hotkey_set(t, mask); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + hotkey_enable_show, hotkey_enable_store); + +/* sysfs hotkey mask --------------------------------------------------- */ +static ssize_t hotkey_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, status, mask; + + res = hotkey_get(&status, &mask); + if (res) + return res; + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", mask); +} + +static ssize_t hotkey_mask_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, status, mask; + + if (parse_strtoul(buf, 0xffff, &t)) + return -EINVAL; + + res = hotkey_get(&status, &mask); + if (!res) + hotkey_set(status, t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_hotkey_mask = + __ATTR(mask, S_IWUSR | S_IRUGO, + hotkey_mask_show, hotkey_mask_store); + +/* sysfs hotkey bios_enabled ------------------------------------------- */ +static ssize_t hotkey_bios_enabled_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", hotkey_orig_status); +} + +static struct device_attribute dev_attr_hotkey_bios_enabled = + __ATTR(bios_enabled, S_IRUGO, hotkey_bios_enabled_show, NULL); + +/* sysfs hotkey bios_mask ---------------------------------------------- */ +static ssize_t hotkey_bios_mask_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "0x%04x\n", hotkey_orig_mask); +} + +static struct device_attribute dev_attr_hotkey_bios_mask = + __ATTR(bios_mask, S_IRUGO, hotkey_bios_mask_show, NULL); + +/* --------------------------------------------------------------------- */ + +static struct attribute *hotkey_mask_attributes[] = { + &dev_attr_hotkey_mask.attr, + &dev_attr_hotkey_bios_enabled.attr, + &dev_attr_hotkey_bios_mask.attr, +}; + +static int __init hotkey_init(struct ibm_init_struct *iibm) +{ + int res; + + vdbg_printk(TPACPI_DBG_INIT, "initializing hotkey subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + mutex_init(&hotkey_mutex); + + /* hotkey not supported on 570 */ + tp_features.hotkey = hkey_handle != NULL; + + vdbg_printk(TPACPI_DBG_INIT, "hotkeys are %s\n", + str_supported(tp_features.hotkey)); + + if (tp_features.hotkey) { + hotkey_dev_attributes = create_attr_set(4, + TPACPI_HOTKEY_SYSFS_GROUP); + if (!hotkey_dev_attributes) + return -ENOMEM; + res = add_to_attr_set(hotkey_dev_attributes, + &dev_attr_hotkey_enable.attr); + if (res) + return res; + + /* mask not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + A30, R30, R31, T20-22, X20-21, X22-24 */ + tp_features.hotkey_mask = + acpi_evalf(hkey_handle, NULL, "DHKN", "qv"); + + vdbg_printk(TPACPI_DBG_INIT, "hotkey masks are %s\n", + str_supported(tp_features.hotkey_mask)); + + res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask); + if (!res && tp_features.hotkey_mask) { + res = add_many_to_attr_set(hotkey_dev_attributes, + hotkey_mask_attributes, + ARRAY_SIZE(hotkey_mask_attributes)); + } + if (!res) + res = register_attr_set_with_sysfs( + hotkey_dev_attributes, + &tpacpi_pdev->dev.kobj); + + if (res) + return res; + } + + return (tp_features.hotkey)? 0 : 1; +} + +static void hotkey_exit(void) +{ + int res; + + if (tp_features.hotkey) { + dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n"); + res = hotkey_set(hotkey_orig_status, hotkey_orig_mask); + if (res) + printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n"); + } + + if (hotkey_dev_attributes) { + delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj); + hotkey_dev_attributes = NULL; + } +} + +static void hotkey_notify(struct ibm_struct *ibm, u32 event) +{ + int hkey; + + if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) + acpi_bus_generate_event(ibm->acpi->device, event, hkey); + else { + printk(IBM_ERR "unknown hotkey event %d\n", event); + acpi_bus_generate_event(ibm->acpi->device, event, 0); + } +} + +/* + * Call with hotkey_mutex held + */ +static int hotkey_get(int *status, int *mask) +{ + if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) + return -EIO; + + if (tp_features.hotkey_mask) + if (!acpi_evalf(hkey_handle, mask, "DHKN", "d")) + return -EIO; + + return 0; +} + +/* + * Call with hotkey_mutex held + */ +static int hotkey_set(int status, int mask) +{ + int i; + + if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) + return -EIO; + + if (tp_features.hotkey_mask) + for (i = 0; i < 32; i++) { + int bit = ((1 << i) & mask) != 0; + if (!acpi_evalf(hkey_handle, + NULL, "MHKM", "vdd", i + 1, bit)) + return -EIO; + } + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int hotkey_read(char *p) +{ + int res, status, mask; + int len = 0; + + if (!tp_features.hotkey) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + res = hotkey_get(&status, &mask); + mutex_unlock(&hotkey_mutex); + if (res) + return res; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); + if (tp_features.hotkey_mask) { + len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); + len += sprintf(p + len, + "commands:\tenable, disable, reset, <mask>\n"); + } else { + len += sprintf(p + len, "mask:\t\tnot supported\n"); + len += sprintf(p + len, "commands:\tenable, disable, reset\n"); + } + + return len; +} + +static int hotkey_write(char *buf) +{ + int res, status, mask; + char *cmd; + int do_cmd = 0; + + if (!tp_features.hotkey) + return -ENODEV; + + res = mutex_lock_interruptible(&hotkey_mutex); + if (res < 0) + return res; + + res = hotkey_get(&status, &mask); + if (res) + goto errexit; + + res = 0; + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status = 1; + } else if (strlencmp(cmd, "disable") == 0) { + status = 0; + } else if (strlencmp(cmd, "reset") == 0) { + status = hotkey_orig_status; + mask = hotkey_orig_mask; + } else if (sscanf(cmd, "0x%x", &mask) == 1) { + /* mask set */ + } else if (sscanf(cmd, "%x", &mask) == 1) { + /* mask set */ + } else { + res = -EINVAL; + goto errexit; + } + do_cmd = 1; + } + + if (do_cmd) + res = hotkey_set(status, mask); + +errexit: + mutex_unlock(&hotkey_mutex); + return res; +} + +static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = { + .hid = IBM_HKEY_HID, + .notify = hotkey_notify, + .handle = &hkey_handle, + .type = ACPI_DEVICE_NOTIFY, +}; + +static struct ibm_struct hotkey_driver_data = { + .name = "hotkey", + .read = hotkey_read, + .write = hotkey_write, + .exit = hotkey_exit, + .acpi = &ibm_hotkey_acpidriver, +}; + +/************************************************************************* + * Bluetooth subdriver + */ + +/* sysfs bluetooth enable ---------------------------------------------- */ +static ssize_t bluetooth_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = bluetooth_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t bluetooth_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = bluetooth_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_bluetooth_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + bluetooth_enable_show, bluetooth_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *bluetooth_attributes[] = { + &dev_attr_bluetooth_enable.attr, + NULL +}; + +static const struct attribute_group bluetooth_attr_group = { + .name = TPACPI_BLUETH_SYSFS_GROUP, + .attrs = bluetooth_attributes, +}; + +static int __init bluetooth_init(struct ibm_init_struct *iibm) +{ + int res; + int status = 0; + + vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + + /* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + G4x, R30, R31, R40e, R50e, T20-22, X20-21 */ + tp_features.bluetooth = hkey_handle && + acpi_evalf(hkey_handle, &status, "GBDC", "qd"); + + vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n", + str_supported(tp_features.bluetooth), + status); + + if (tp_features.bluetooth) { + if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) { + /* no bluetooth hardware present in system */ + tp_features.bluetooth = 0; + dbg_printk(TPACPI_DBG_INIT, + "bluetooth hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); + if (res) + return res; + } + } + + return (tp_features.bluetooth)? 0 : 1; +} + +static void bluetooth_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &bluetooth_attr_group); +} + +static int bluetooth_get_radiosw(void) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + + return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0); +} + +static int bluetooth_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.bluetooth) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GBDC", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_BLUETOOTH_RADIOSSW; + else + status &= ~TP_ACPI_BLUETOOTH_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) + return -EIO; + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int bluetooth_read(char *p) +{ + int len = 0; + int status = bluetooth_get_radiosw(); + + if (!tp_features.bluetooth) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", + (status)? "enabled" : "disabled"); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int bluetooth_write(char *buf) +{ + char *cmd; + + if (!tp_features.bluetooth) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + bluetooth_set_radiosw(1); + } else if (strlencmp(cmd, "disable") == 0) { + bluetooth_set_radiosw(0); + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct bluetooth_driver_data = { + .name = "bluetooth", + .read = bluetooth_read, + .write = bluetooth_write, + .exit = bluetooth_exit, +}; + +/************************************************************************* + * Wan subdriver + */ + +/* sysfs wan enable ---------------------------------------------------- */ +static ssize_t wan_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int status; + + status = wan_get_radiosw(); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0); +} + +static ssize_t wan_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res; + + if (parse_strtoul(buf, 1, &t)) + return -EINVAL; + + res = wan_set_radiosw(t); + + return (res) ? res : count; +} + +static struct device_attribute dev_attr_wan_enable = + __ATTR(enable, S_IWUSR | S_IRUGO, + wan_enable_show, wan_enable_store); + +/* --------------------------------------------------------------------- */ + +static struct attribute *wan_attributes[] = { + &dev_attr_wan_enable.attr, + NULL +}; + +static const struct attribute_group wan_attr_group = { + .name = TPACPI_WAN_SYSFS_GROUP, + .attrs = wan_attributes, +}; + +static int __init wan_init(struct ibm_init_struct *iibm) +{ + int res; + int status = 0; + + vdbg_printk(TPACPI_DBG_INIT, "initializing wan subdriver\n"); + + IBM_ACPIHANDLE_INIT(hkey); + + tp_features.wan = hkey_handle && + acpi_evalf(hkey_handle, &status, "GWAN", "qd"); + + vdbg_printk(TPACPI_DBG_INIT, "wan is %s, status 0x%02x\n", + str_supported(tp_features.wan), + status); + + if (tp_features.wan) { + if (!(status & TP_ACPI_WANCARD_HWPRESENT)) { + /* no wan hardware present in system */ + tp_features.wan = 0; + dbg_printk(TPACPI_DBG_INIT, + "wan hardware not installed\n"); + } else { + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); + if (res) + return res; + } + } + + return (tp_features.wan)? 0 : 1; +} + +static void wan_exit(void) +{ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &wan_attr_group); +} + +static int wan_get_radiosw(void) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + + return ((status & TP_ACPI_WANCARD_RADIOSSW) != 0); +} + +static int wan_set_radiosw(int radio_on) +{ + int status; + + if (!tp_features.wan) + return -ENODEV; + + if (!acpi_evalf(hkey_handle, &status, "GWAN", "d")) + return -EIO; + if (radio_on) + status |= TP_ACPI_WANCARD_RADIOSSW; + else + status &= ~TP_ACPI_WANCARD_RADIOSSW; + if (!acpi_evalf(hkey_handle, NULL, "SWAN", "vd", status)) + return -EIO; + + return 0; +} + +/* procfs -------------------------------------------------------------- */ +static int wan_read(char *p) +{ + int len = 0; + int status = wan_get_radiosw(); + + if (!tp_features.wan) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", + (status)? "enabled" : "disabled"); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int wan_write(char *buf) +{ + char *cmd; + + if (!tp_features.wan) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + wan_set_radiosw(1); + } else if (strlencmp(cmd, "disable") == 0) { + wan_set_radiosw(0); + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct wan_driver_data = { + .name = "wan", + .read = wan_read, + .write = wan_write, + .exit = wan_exit, + .flags.experimental = 1, +}; + +/************************************************************************* + * Video subdriver + */ + +static enum video_access_mode video_supported; +static int video_orig_autosw; + +IBM_HANDLE(vid, root, "\\_SB.PCI.AGP.VGA", /* 570 */ + "\\_SB.PCI0.AGP0.VID0", /* 600e/x, 770x */ + "\\_SB.PCI0.VID0", /* 770e */ + "\\_SB.PCI0.VID", /* A21e, G4x, R50e, X30, X40 */ + "\\_SB.PCI0.AGP.VID", /* all others */ + ); /* R30, R31 */ + +IBM_HANDLE(vid2, root, "\\_SB.PCI0.AGPB.VID"); /* G41 */ + +static int __init video_init(struct ibm_init_struct *iibm) +{ + int ivga; + + vdbg_printk(TPACPI_DBG_INIT, "initializing video subdriver\n"); + + IBM_ACPIHANDLE_INIT(vid); + IBM_ACPIHANDLE_INIT(vid2); + + if (vid2_handle && acpi_evalf(NULL, &ivga, "\\IVGA", "d") && ivga) + /* G41, assume IVGA doesn't change */ + vid_handle = vid2_handle; + + if (!vid_handle) + /* video switching not supported on R30, R31 */ + video_supported = TPACPI_VIDEO_NONE; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "SWIT", "qd")) + /* 570 */ + video_supported = TPACPI_VIDEO_570; + else if (acpi_evalf(vid_handle, &video_orig_autosw, "^VADL", "qd")) + /* 600e/x, 770e, 770x */ + video_supported = TPACPI_VIDEO_770; + else + /* all others */ + video_supported = TPACPI_VIDEO_NEW; + + vdbg_printk(TPACPI_DBG_INIT, "video is %s, mode %d\n", + str_supported(video_supported != TPACPI_VIDEO_NONE), + video_supported); + + return (video_supported != TPACPI_VIDEO_NONE)? 0 : 1; +} + +static void video_exit(void) +{ + dbg_printk(TPACPI_DBG_EXIT, + "restoring original video autoswitch mode\n"); + if (video_autosw_set(video_orig_autosw)) + printk(IBM_ERR "error while trying to restore original " + "video autoswitch mode\n"); +} + +static int video_outputsw_get(void) +{ + int status = 0; + int i; + + switch (video_supported) { + case TPACPI_VIDEO_570: + if (!acpi_evalf(NULL, &i, "\\_SB.PHS", "dd", + TP_ACPI_VIDEO_570_PHSCMD)) + return -EIO; + status = i & TP_ACPI_VIDEO_570_PHSMASK; + break; + case TPACPI_VIDEO_770: + if (!acpi_evalf(NULL, &i, "\\VCDL", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_LCD; + if (!acpi_evalf(NULL, &i, "\\VCDC", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_CRT; + break; + case TPACPI_VIDEO_NEW: + if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1) || + !acpi_evalf(NULL, &i, "\\VCDC", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_CRT; + + if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0) || + !acpi_evalf(NULL, &i, "\\VCDL", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_LCD; + if (!acpi_evalf(NULL, &i, "\\VCDD", "d")) + return -EIO; + if (i) + status |= TP_ACPI_VIDEO_S_DVI; + break; + default: + return -ENOSYS; + } + + return status; +} + +static int video_outputsw_set(int status) +{ + int autosw; + int res = 0; + + switch (video_supported) { + case TPACPI_VIDEO_570: + res = acpi_evalf(NULL, NULL, + "\\_SB.PHS2", "vdd", + TP_ACPI_VIDEO_570_PHS2CMD, + status | TP_ACPI_VIDEO_570_PHS2SET); + break; + case TPACPI_VIDEO_770: + autosw = video_autosw_get(); + if (autosw < 0) + return autosw; + + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(vid_handle, NULL, + "ASWT", "vdd", status * 0x100, 0); + if (!autosw && video_autosw_set(autosw)) { + printk(IBM_ERR "video auto-switch left enabled due to error\n"); + return -EIO; + } + break; + case TPACPI_VIDEO_NEW: + res = acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80) && + acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1); + break; + default: + return -ENOSYS; + } + + return (res)? 0 : -EIO; +} + +static int video_autosw_get(void) +{ + int autosw = 0; + + switch (video_supported) { + case TPACPI_VIDEO_570: + if (!acpi_evalf(vid_handle, &autosw, "SWIT", "d")) + return -EIO; + break; + case TPACPI_VIDEO_770: + case TPACPI_VIDEO_NEW: + if (!acpi_evalf(vid_handle, &autosw, "^VDEE", "d")) + return -EIO; + break; + default: + return -ENOSYS; + } + + return autosw & 1; +} + +static int video_autosw_set(int enable) +{ + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", (enable)? 1 : 0)) + return -EIO; + return 0; +} + +static int video_outputsw_cycle(void) +{ + int autosw = video_autosw_get(); + int res; + + if (autosw < 0) + return autosw; + + switch (video_supported) { + case TPACPI_VIDEO_570: + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(ec_handle, NULL, "_Q16", "v"); + break; + case TPACPI_VIDEO_770: + case TPACPI_VIDEO_NEW: + res = video_autosw_set(1); + if (res) + return res; + res = acpi_evalf(vid_handle, NULL, "VSWT", "v"); + break; + default: + return -ENOSYS; + } + if (!autosw && video_autosw_set(autosw)) { + printk(IBM_ERR "video auto-switch left enabled due to error\n"); + return -EIO; + } + + return (res)? 0 : -EIO; +} + +static int video_expand_toggle(void) +{ + switch (video_supported) { + case TPACPI_VIDEO_570: + return acpi_evalf(ec_handle, NULL, "_Q17", "v")? + 0 : -EIO; + case TPACPI_VIDEO_770: + return acpi_evalf(vid_handle, NULL, "VEXP", "v")? + 0 : -EIO; + case TPACPI_VIDEO_NEW: + return acpi_evalf(NULL, NULL, "\\VEXP", "v")? + 0 : -EIO; + default: + return -ENOSYS; + } + /* not reached */ +} + +static int video_read(char *p) +{ + int status, autosw; + int len = 0; + + if (video_supported == TPACPI_VIDEO_NONE) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + + status = video_outputsw_get(); + if (status < 0) + return status; + + autosw = video_autosw_get(); + if (autosw < 0) + return autosw; + + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); + len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); + if (video_supported == TPACPI_VIDEO_NEW) + len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); + len += sprintf(p + len, "auto:\t\t%s\n", enabled(autosw, 0)); + len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable\n"); + len += sprintf(p + len, "commands:\tcrt_enable, crt_disable\n"); + if (video_supported == TPACPI_VIDEO_NEW) + len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable\n"); + len += sprintf(p + len, "commands:\tauto_enable, auto_disable\n"); + len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); + + return len; +} + +static int video_write(char *buf) +{ + char *cmd; + int enable, disable, status; + int res; + + if (video_supported == TPACPI_VIDEO_NONE) + return -ENODEV; + + enable = 0; + disable = 0; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "lcd_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_LCD; + } else if (strlencmp(cmd, "lcd_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_LCD; + } else if (strlencmp(cmd, "crt_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_CRT; + } else if (strlencmp(cmd, "crt_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_CRT; + } else if (video_supported == TPACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_enable") == 0) { + enable |= TP_ACPI_VIDEO_S_DVI; + } else if (video_supported == TPACPI_VIDEO_NEW && + strlencmp(cmd, "dvi_disable") == 0) { + disable |= TP_ACPI_VIDEO_S_DVI; + } else if (strlencmp(cmd, "auto_enable") == 0) { + res = video_autosw_set(1); + if (res) + return res; + } else if (strlencmp(cmd, "auto_disable") == 0) { + res = video_autosw_set(0); + if (res) + return res; + } else if (strlencmp(cmd, "video_switch") == 0) { + res = video_outputsw_cycle(); + if (res) + return res; + } else if (strlencmp(cmd, "expand_toggle") == 0) { + res = video_expand_toggle(); + if (res) + return res; + } else + return -EINVAL; + } + + if (enable || disable) { + status = video_outputsw_get(); + if (status < 0) + return status; + res = video_outputsw_set((status & ~disable) | enable); + if (res) + return res; + } + + return 0; +} + +static struct ibm_struct video_driver_data = { + .name = "video", + .read = video_read, + .write = video_write, + .exit = video_exit, +}; + +/************************************************************************* + * Light (thinklight) subdriver + */ + +IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A2xm/p, T20-22, X20-21 */ +IBM_HANDLE(ledb, ec, "LEDB"); /* G4x */ + +static int __init light_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing light subdriver\n"); + + IBM_ACPIHANDLE_INIT(ledb); + IBM_ACPIHANDLE_INIT(lght); + IBM_ACPIHANDLE_INIT(cmos); + + /* light not supported on 570, 600e/x, 770e, 770x, G4x, R30, R31 */ + tp_features.light = (cmos_handle || lght_handle) && !ledb_handle; + + if (tp_features.light) + /* light status not supported on + 570, 600e/x, 770e, 770x, G4x, R30, R31, R32, X20 */ + tp_features.light_status = + acpi_evalf(ec_handle, NULL, "KBLT", "qv"); + + vdbg_printk(TPACPI_DBG_INIT, "light is %s\n", + str_supported(tp_features.light)); + + return (tp_features.light)? 0 : 1; +} + +static int light_read(char *p) +{ + int len = 0; + int status = 0; + + if (!tp_features.light) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + } else if (!tp_features.light_status) { + len += sprintf(p + len, "status:\t\tunknown\n"); + len += sprintf(p + len, "commands:\ton, off\n"); + } else { + if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) + return -EIO; + len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); + len += sprintf(p + len, "commands:\ton, off\n"); + } + + return len; +} + +static int light_write(char *buf) +{ + int cmos_cmd, lght_cmd; + char *cmd; + int success; + + if (!tp_features.light) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "on") == 0) { + cmos_cmd = 0x0c; + lght_cmd = 1; + } else if (strlencmp(cmd, "off") == 0) { + cmos_cmd = 0x0d; + lght_cmd = 0; + } else + return -EINVAL; + + success = cmos_handle ? + acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : + acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); + if (!success) + return -EIO; + } + + return 0; +} + +static struct ibm_struct light_driver_data = { + .name = "light", + .read = light_read, + .write = light_write, +}; + +/************************************************************************* + * Dock subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_DOCK + +IBM_HANDLE(dock, root, "\\_SB.GDCK", /* X30, X31, X40 */ + "\\_SB.PCI0.DOCK", /* 600e/x,770e,770x,A2xm/p,T20-22,X20-21 */ + "\\_SB.PCI0.PCI1.DOCK", /* all others */ + "\\_SB.PCI.ISA.SLCE", /* 570 */ + ); /* A21e,G4x,R30,R31,R32,R40,R40e,R50e */ + +/* don't list other alternatives as we install a notify handler on the 570 */ +IBM_HANDLE(pci, root, "\\_SB.PCI"); /* 570 */ + +static struct tp_acpi_drv_struct ibm_dock_acpidriver[2] = { + { + .notify = dock_notify, + .handle = &dock_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, + { + .hid = IBM_PCI_HID, + .notify = dock_notify, + .handle = &pci_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, +}; + +static struct ibm_struct dock_driver_data[2] = { + { + .name = "dock", + .read = dock_read, + .write = dock_write, + .acpi = &ibm_dock_acpidriver[0], + }, + { + .name = "dock", + .acpi = &ibm_dock_acpidriver[1], + }, +}; + +#define dock_docked() (_sta(dock_handle) & 1) + +static int __init dock_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver\n"); + + IBM_ACPIHANDLE_INIT(dock); + + vdbg_printk(TPACPI_DBG_INIT, "dock is %s\n", + str_supported(dock_handle != NULL)); + + return (dock_handle)? 0 : 1; +} + +static int __init dock_init2(struct ibm_init_struct *iibm) +{ + int dock2_needed; + + vdbg_printk(TPACPI_DBG_INIT, "initializing dock subdriver part 2\n"); + + if (dock_driver_data[0].flags.acpi_driver_registered && + dock_driver_data[0].flags.acpi_notify_installed) { + IBM_ACPIHANDLE_INIT(pci); + dock2_needed = (pci_handle != NULL); + vdbg_printk(TPACPI_DBG_INIT, + "dock PCI handler for the TP 570 is %s\n", + str_supported(dock2_needed)); + } else { + vdbg_printk(TPACPI_DBG_INIT, + "dock subdriver part 2 not required\n"); + dock2_needed = 0; + } + + return (dock2_needed)? 0 : 1; +} + +static void dock_notify(struct ibm_struct *ibm, u32 event) +{ + int docked = dock_docked(); + int pci = ibm->acpi->hid && strstr(ibm->acpi->hid, IBM_PCI_HID); + + if (event == 1 && !pci) /* 570 */ + acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ + else if (event == 1 && pci) /* 570 */ + acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */ + else if (event == 3 && docked) + acpi_bus_generate_event(ibm->acpi->device, event, 1); /* button */ + else if (event == 3 && !docked) + acpi_bus_generate_event(ibm->acpi->device, event, 2); /* undock */ + else if (event == 0 && docked) + acpi_bus_generate_event(ibm->acpi->device, event, 3); /* dock */ + else { + printk(IBM_ERR "unknown dock event %d, status %d\n", + event, _sta(dock_handle)); + acpi_bus_generate_event(ibm->acpi->device, event, 0); /* unknown */ + } +} + +static int dock_read(char *p) +{ + int len = 0; + int docked = dock_docked(); + + if (!dock_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!docked) + len += sprintf(p + len, "status:\t\tundocked\n"); + else { + len += sprintf(p + len, "status:\t\tdocked\n"); + len += sprintf(p + len, "commands:\tdock, undock\n"); + } + + return len; +} + +static int dock_write(char *buf) +{ + char *cmd; + + if (!dock_docked()) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "undock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0) || + !acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "dock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +#endif /* CONFIG_THINKPAD_ACPI_DOCK */ + +/************************************************************************* + * Bay subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_BAY +IBM_HANDLE(bay, root, "\\_SB.PCI.IDE.SECN.MAST", /* 570 */ + "\\_SB.PCI0.IDE0.IDES.IDSM", /* 600e/x, 770e, 770x */ + "\\_SB.PCI0.SATA.SCND.MSTR", /* T60, X60, Z60 */ + "\\_SB.PCI0.IDE0.SCND.MSTR", /* all others */ + ); /* A21e, R30, R31 */ +IBM_HANDLE(bay_ej, bay, "_EJ3", /* 600e/x, A2xm/p, A3x */ + "_EJ0", /* all others */ + ); /* 570,A21e,G4x,R30,R31,R32,R40e,R50e */ +IBM_HANDLE(bay2, root, "\\_SB.PCI0.IDE0.PRIM.SLAV", /* A3x, R32 */ + "\\_SB.PCI0.IDE0.IDEP.IDPS", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(bay2_ej, bay2, "_EJ3", /* 600e/x, 770e, A3x */ + "_EJ0", /* 770x */ + ); /* all others */ + +static int __init bay_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing bay subdriver\n"); + + IBM_ACPIHANDLE_INIT(bay); + if (bay_handle) + IBM_ACPIHANDLE_INIT(bay_ej); + IBM_ACPIHANDLE_INIT(bay2); + if (bay2_handle) + IBM_ACPIHANDLE_INIT(bay2_ej); + + tp_features.bay_status = bay_handle && + acpi_evalf(bay_handle, NULL, "_STA", "qv"); + tp_features.bay_status2 = bay2_handle && + acpi_evalf(bay2_handle, NULL, "_STA", "qv"); + + tp_features.bay_eject = bay_handle && bay_ej_handle && + (strlencmp(bay_ej_path, "_EJ0") == 0 || experimental); + tp_features.bay_eject2 = bay2_handle && bay2_ej_handle && + (strlencmp(bay2_ej_path, "_EJ0") == 0 || experimental); + + vdbg_printk(TPACPI_DBG_INIT, + "bay 1: status %s, eject %s; bay 2: status %s, eject %s\n", + str_supported(tp_features.bay_status), + str_supported(tp_features.bay_eject), + str_supported(tp_features.bay_status2), + str_supported(tp_features.bay_eject2)); + + return (tp_features.bay_status || tp_features.bay_eject || + tp_features.bay_status2 || tp_features.bay_eject2)? 0 : 1; +} + +static void bay_notify(struct ibm_struct *ibm, u32 event) +{ + acpi_bus_generate_event(ibm->acpi->device, event, 0); +} + +#define bay_occupied(b) (_sta(b##_handle) & 1) + +static int bay_read(char *p) +{ + int len = 0; + int occupied = bay_occupied(bay); + int occupied2 = bay_occupied(bay2); + int eject, eject2; + + len += sprintf(p + len, "status:\t\t%s\n", + tp_features.bay_status ? + (occupied ? "occupied" : "unoccupied") : + "not supported"); + if (tp_features.bay_status2) + len += sprintf(p + len, "status2:\t%s\n", occupied2 ? + "occupied" : "unoccupied"); + + eject = tp_features.bay_eject && occupied; + eject2 = tp_features.bay_eject2 && occupied2; + + if (eject && eject2) + len += sprintf(p + len, "commands:\teject, eject2\n"); + else if (eject) + len += sprintf(p + len, "commands:\teject\n"); + else if (eject2) + len += sprintf(p + len, "commands:\teject2\n"); + + return len; +} + +static int bay_write(char *buf) +{ + char *cmd; + + if (!tp_features.bay_eject && !tp_features.bay_eject2) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (tp_features.bay_eject && strlencmp(cmd, "eject") == 0) { + if (!acpi_evalf(bay_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else if (tp_features.bay_eject2 && + strlencmp(cmd, "eject2") == 0) { + if (!acpi_evalf(bay2_ej_handle, NULL, NULL, "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static struct tp_acpi_drv_struct ibm_bay_acpidriver = { + .notify = bay_notify, + .handle = &bay_handle, + .type = ACPI_SYSTEM_NOTIFY, +}; + +static struct ibm_struct bay_driver_data = { + .name = "bay", + .read = bay_read, + .write = bay_write, + .acpi = &ibm_bay_acpidriver, +}; + +#endif /* CONFIG_THINKPAD_ACPI_BAY */ + +/************************************************************************* + * CMOS subdriver + */ + +/* sysfs cmos_command -------------------------------------------------- */ +static ssize_t cmos_command_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long cmos_cmd; + int res; + + if (parse_strtoul(buf, 21, &cmos_cmd)) + return -EINVAL; + + res = issue_thinkpad_cmos_command(cmos_cmd); + return (res)? res : count; +} + +static struct device_attribute dev_attr_cmos_command = + __ATTR(cmos_command, S_IWUSR, NULL, cmos_command_store); + +/* --------------------------------------------------------------------- */ + +static int __init cmos_init(struct ibm_init_struct *iibm) +{ + int res; + + vdbg_printk(TPACPI_DBG_INIT, + "initializing cmos commands subdriver\n"); + + IBM_ACPIHANDLE_INIT(cmos); + + vdbg_printk(TPACPI_DBG_INIT, "cmos commands are %s\n", + str_supported(cmos_handle != NULL)); + + res = device_create_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); + if (res) + return res; + + return (cmos_handle)? 0 : 1; +} + +static void cmos_exit(void) +{ + device_remove_file(&tpacpi_pdev->dev, &dev_attr_cmos_command); +} + +static int cmos_read(char *p) +{ + int len = 0; + + /* cmos not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p, + R30, R31, T20-22, X20-21 */ + if (!cmos_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-21)\n"); + } + + return len; +} + +static int cmos_write(char *buf) +{ + char *cmd; + int cmos_cmd, res; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &cmos_cmd) == 1 && + cmos_cmd >= 0 && cmos_cmd <= 21) { + /* cmos_cmd set */ + } else + return -EINVAL; + + res = issue_thinkpad_cmos_command(cmos_cmd); + if (res) + return res; + } + + return 0; +} + +static struct ibm_struct cmos_driver_data = { + .name = "cmos", + .read = cmos_read, + .write = cmos_write, + .exit = cmos_exit, +}; + +/************************************************************************* + * LED subdriver + */ + +static enum led_access_mode led_supported; + +IBM_HANDLE(led, ec, "SLED", /* 570 */ + "SYSL", /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + "LED", /* all others */ + ); /* R30, R31 */ + +static int __init led_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); + + IBM_ACPIHANDLE_INIT(led); + + if (!led_handle) + /* led not supported on R30, R31 */ + led_supported = TPACPI_LED_NONE; + else if (strlencmp(led_path, "SLED") == 0) + /* 570 */ + led_supported = TPACPI_LED_570; + else if (strlencmp(led_path, "SYSL") == 0) + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + led_supported = TPACPI_LED_OLD; + else + /* all others */ + led_supported = TPACPI_LED_NEW; + + vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", + str_supported(led_supported), led_supported); + + return (led_supported != TPACPI_LED_NONE)? 0 : 1; +} + +#define led_status(s) ((s) == 0 ? "off" : ((s) == 1 ? "on" : "blinking")) + +static int led_read(char *p) +{ + int len = 0; + + if (!led_supported) { + len += sprintf(p + len, "status:\t\tnot supported\n"); + return len; + } + len += sprintf(p + len, "status:\t\tsupported\n"); + + if (led_supported == TPACPI_LED_570) { + /* 570 */ + int i, status; + for (i = 0; i < 8; i++) { + if (!acpi_evalf(ec_handle, + &status, "GLED", "dd", 1 << i)) + return -EIO; + len += sprintf(p + len, "%d:\t\t%s\n", + i, led_status(status)); + } + } + + len += sprintf(p + len, "commands:\t" + "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); + + return len; +} + +/* off, on, blink */ +static const int led_sled_arg1[] = { 0, 1, 3 }; +static const int led_exp_hlbl[] = { 0, 0, 1 }; /* led# * */ +static const int led_exp_hlcl[] = { 0, 1, 1 }; /* led# * */ +static const int led_led_arg1[] = { 0, 0x80, 0xc0 }; + +static int led_write(char *buf) +{ + char *cmd; + int led, ind, ret; + + if (!led_supported) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) + return -EINVAL; + + if (strstr(cmd, "off")) { + ind = 0; + } else if (strstr(cmd, "on")) { + ind = 1; + } else if (strstr(cmd, "blink")) { + ind = 2; + } else + return -EINVAL; + + if (led_supported == TPACPI_LED_570) { + /* 570 */ + led = 1 << led; + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_sled_arg1[ind])) + return -EIO; + } else if (led_supported == TPACPI_LED_OLD) { + /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20 */ + led = 1 << led; + ret = ec_write(TPACPI_LED_EC_HLMS, led); + if (ret >= 0) + ret = + ec_write(TPACPI_LED_EC_HLBL, + led * led_exp_hlbl[ind]); + if (ret >= 0) + ret = + ec_write(TPACPI_LED_EC_HLCL, + led * led_exp_hlcl[ind]); + if (ret < 0) + return ret; + } else { + /* all others */ + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_led_arg1[ind])) + return -EIO; + } + } + + return 0; +} + +static struct ibm_struct led_driver_data = { + .name = "led", + .read = led_read, + .write = led_write, +}; + +/************************************************************************* + * Beep subdriver + */ + +IBM_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ + +static int __init beep_init(struct ibm_init_struct *iibm) +{ + vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); + + IBM_ACPIHANDLE_INIT(beep); + + vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", + str_supported(beep_handle != NULL)); + + return (beep_handle)? 0 : 1; +} + +static int beep_read(char *p) +{ + int len = 0; + + if (!beep_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t<cmd> (<cmd> is 0-17)\n"); + } + + return len; +} + +static int beep_write(char *buf) +{ + char *cmd; + int beep_cmd; + + if (!beep_handle) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &beep_cmd) == 1 && + beep_cmd >= 0 && beep_cmd <= 17) { + /* beep_cmd set */ + } else + return -EINVAL; + if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) + return -EIO; + } + + return 0; +} + +static struct ibm_struct beep_driver_data = { + .name = "beep", + .read = beep_read, + .write = beep_write, +}; + +/************************************************************************* + * Thermal subdriver + */ + +static enum thermal_access_mode thermal_read_mode; + +/* sysfs temp##_input -------------------------------------------------- */ + +static ssize_t thermal_temp_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = + to_sensor_dev_attr(attr); + int idx = sensor_attr->index; + s32 value; + int res; + + res = thermal_get_sensor(idx, &value); + if (res) + return res; + if (value == TP_EC_THERMAL_TMP_NA * 1000) + return -ENXIO; + + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +#define THERMAL_SENSOR_ATTR_TEMP(_idxA, _idxB) \ + SENSOR_ATTR(temp##_idxA##_input, S_IRUGO, thermal_temp_input_show, NULL, _idxB) + +static struct sensor_device_attribute sensor_dev_attr_thermal_temp_input[] = { + THERMAL_SENSOR_ATTR_TEMP(1, 0), + THERMAL_SENSOR_ATTR_TEMP(2, 1), + THERMAL_SENSOR_ATTR_TEMP(3, 2), + THERMAL_SENSOR_ATTR_TEMP(4, 3), + THERMAL_SENSOR_ATTR_TEMP(5, 4), + THERMAL_SENSOR_ATTR_TEMP(6, 5), + THERMAL_SENSOR_ATTR_TEMP(7, 6), + THERMAL_SENSOR_ATTR_TEMP(8, 7), + THERMAL_SENSOR_ATTR_TEMP(9, 8), + THERMAL_SENSOR_ATTR_TEMP(10, 9), + THERMAL_SENSOR_ATTR_TEMP(11, 10), + THERMAL_SENSOR_ATTR_TEMP(12, 11), + THERMAL_SENSOR_ATTR_TEMP(13, 12), + THERMAL_SENSOR_ATTR_TEMP(14, 13), + THERMAL_SENSOR_ATTR_TEMP(15, 14), + THERMAL_SENSOR_ATTR_TEMP(16, 15), +}; + +#define THERMAL_ATTRS(X) \ + &sensor_dev_attr_thermal_temp_input[X].dev_attr.attr + +static struct attribute *thermal_temp_input_attr[] = { + THERMAL_ATTRS(8), + THERMAL_ATTRS(9), + THERMAL_ATTRS(10), + THERMAL_ATTRS(11), + THERMAL_ATTRS(12), + THERMAL_ATTRS(13), + THERMAL_ATTRS(14), + THERMAL_ATTRS(15), + THERMAL_ATTRS(0), + THERMAL_ATTRS(1), + THERMAL_ATTRS(2), + THERMAL_ATTRS(3), + THERMAL_ATTRS(4), + THERMAL_ATTRS(5), + THERMAL_ATTRS(6), + THERMAL_ATTRS(7), + NULL +}; + +static const struct attribute_group thermal_temp_input16_group = { + .attrs = thermal_temp_input_attr +}; + +static const struct attribute_group thermal_temp_input8_group = { + .attrs = &thermal_temp_input_attr[8] +}; + +#undef THERMAL_SENSOR_ATTR_TEMP +#undef THERMAL_ATTRS + +/* --------------------------------------------------------------------- */ + +static int __init thermal_init(struct ibm_init_struct *iibm) +{ + u8 t, ta1, ta2; + int i; + int acpi_tmp7; + int res; + + vdbg_printk(TPACPI_DBG_INIT, "initializing thermal subdriver\n"); + + acpi_tmp7 = acpi_evalf(ec_handle, NULL, "TMP7", "qv"); + + if (ibm_thinkpad_ec_found && experimental) { + /* + * Direct EC access mode: sensors at registers + * 0x78-0x7F, 0xC0-0xC7. Registers return 0x00 for + * non-implemented, thermal sensors return 0x80 when + * not available + */ + + ta1 = ta2 = 0; + for (i = 0; i < 8; i++) { + if (acpi_ec_read(TP_EC_THERMAL_TMP0 + i, &t)) { + ta1 |= t; + } else { + ta1 = 0; + break; + } + if (acpi_ec_read(TP_EC_THERMAL_TMP8 + i, &t)) { + ta2 |= t; + } else { + ta1 = 0; + break; + } + } + if (ta1 == 0) { + /* This is sheer paranoia, but we handle it anyway */ + if (acpi_tmp7) { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "falling back to ACPI TMPx access mode\n"); + thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "disabling thermal sensors access\n"); + thermal_read_mode = TPACPI_THERMAL_NONE; + } + } else { + thermal_read_mode = + (ta2 != 0) ? + TPACPI_THERMAL_TPEC_16 : TPACPI_THERMAL_TPEC_8; + } + } else if (acpi_tmp7) { + if (acpi_evalf(ec_handle, NULL, "UPDT", "qv")) { + /* 600e/x, 770e, 770x */ + thermal_read_mode = TPACPI_THERMAL_ACPI_UPDT; + } else { + /* Standard ACPI TMPx access, max 8 sensors */ + thermal_read_mode = TPACPI_THERMAL_ACPI_TMP07; + } + } else { + /* temperatures not supported on 570, G4x, R30, R31, R32 */ + thermal_read_mode = TPACPI_THERMAL_NONE; + } + + vdbg_printk(TPACPI_DBG_INIT, "thermal is %s, mode %d\n", + str_supported(thermal_read_mode != TPACPI_THERMAL_NONE), + thermal_read_mode); + + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + if (res) + return res; + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + res = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input8_group); + if (res) + return res; + break; + case TPACPI_THERMAL_NONE: + default: + return 1; + } + + return 0; +} + +static void thermal_exit(void) +{ + switch(thermal_read_mode) { + case TPACPI_THERMAL_TPEC_16: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_TPEC_8: + case TPACPI_THERMAL_ACPI_TMP07: + case TPACPI_THERMAL_ACPI_UPDT: + sysfs_remove_group(&tpacpi_pdev->dev.kobj, + &thermal_temp_input16_group); + break; + case TPACPI_THERMAL_NONE: + default: + break; + } +} + +/* idx is zero-based */ +static int thermal_get_sensor(int idx, s32 *value) +{ + int t; + s8 tmp; + char tmpi[5]; + + t = TP_EC_THERMAL_TMP0; + + switch (thermal_read_mode) { +#if TPACPI_MAX_THERMAL_SENSORS >= 16 + case TPACPI_THERMAL_TPEC_16: + if (idx >= 8 && idx <= 15) { + t = TP_EC_THERMAL_TMP8; + idx -= 8; + } + /* fallthrough */ +#endif + case TPACPI_THERMAL_TPEC_8: + if (idx <= 7) { + if (!acpi_ec_read(t + idx, &tmp)) + return -EIO; + *value = tmp * 1000; + return 0; + } + break; + + case TPACPI_THERMAL_ACPI_UPDT: + if (idx <= 7) { + snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); + if (!acpi_evalf(ec_handle, NULL, "UPDT", "v")) + return -EIO; + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + *value = (t - 2732) * 100; + return 0; + } + break; + + case TPACPI_THERMAL_ACPI_TMP07: + if (idx <= 7) { + snprintf(tmpi, sizeof(tmpi), "TMP%c", '0' + idx); + if (!acpi_evalf(ec_handle, &t, tmpi, "d")) + return -EIO; + *value = t * 1000; + return 0; + } + break; + + case TPACPI_THERMAL_NONE: + default: + return -ENOSYS; + } + + return -EINVAL; +} + +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s) +{ + int res, i; + int n; + + n = 8; + i = 0; + + if (!s) + return -EINVAL; + + if (thermal_read_mode == TPACPI_THERMAL_TPEC_16) + n = 16; + + for(i = 0 ; i < n; i++) { + res = thermal_get_sensor(i, &s->temp[i]); + if (res) + return res; + } + + return n; +} + +static int thermal_read(char *p) +{ + int len = 0; + int n, i; + struct ibm_thermal_sensors_struct t; + + n = thermal_get_sensors(&t); + if (unlikely(n < 0)) + return n; + + len += sprintf(p + len, "temperatures:\t"); + + if (n > 0) { + for (i = 0; i < (n - 1); i++) + len += sprintf(p + len, "%d ", t.temp[i] / 1000); + len += sprintf(p + len, "%d\n", t.temp[i] / 1000); + } else + len += sprintf(p + len, "not supported\n"); + + return len; +} + +static struct ibm_struct thermal_driver_data = { + .name = "thermal", + .read = thermal_read, + .exit = thermal_exit, +}; + +/************************************************************************* + * EC Dump subdriver + */ + +static u8 ecdump_regs[256]; + +static int ecdump_read(char *p) +{ + int len = 0; + int i, j; + u8 v; + + len += sprintf(p + len, "EC " + " +00 +01 +02 +03 +04 +05 +06 +07" + " +08 +09 +0a +0b +0c +0d +0e +0f\n"); + for (i = 0; i < 256; i += 16) { + len += sprintf(p + len, "EC 0x%02x:", i); + for (j = 0; j < 16; j++) { + if (!acpi_ec_read(i + j, &v)) + break; + if (v != ecdump_regs[i + j]) + len += sprintf(p + len, " *%02x", v); + else + len += sprintf(p + len, " %02x", v); + ecdump_regs[i + j] = v; + } + len += sprintf(p + len, "\n"); + if (j != 16) + break; + } + + /* These are way too dangerous to advertise openly... */ +#if 0 + len += sprintf(p + len, "commands:\t0x<offset> 0x<value>" + " (<offset> is 00-ff, <value> is 00-ff)\n"); + len += sprintf(p + len, "commands:\t0x<offset> <value> " + " (<offset> is 00-ff, <value> is 0-255)\n"); +#endif + return len; +} + +static int ecdump_write(char *buf) +{ + char *cmd; + int i, v; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "0x%x 0x%x", &i, &v) == 2) { + /* i and v set */ + } else if (sscanf(cmd, "0x%x %u", &i, &v) == 2) { + /* i and v set */ + } else + return -EINVAL; + if (i >= 0 && i < 256 && v >= 0 && v < 256) { + if (!acpi_ec_write(i, v)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static struct ibm_struct ecdump_driver_data = { + .name = "ecdump", + .read = ecdump_read, + .write = ecdump_write, + .flags.experimental = 1, +}; + +/************************************************************************* + * Backlight/brightness subdriver + */ + +static struct backlight_device *ibm_backlight_device = NULL; + +static struct backlight_ops ibm_backlight_data = { + .get_brightness = brightness_get, + .update_status = brightness_update_status, +}; + +static int __init brightness_init(struct ibm_init_struct *iibm) +{ + int b; + + vdbg_printk(TPACPI_DBG_INIT, "initializing brightness subdriver\n"); + + b = brightness_get(NULL); + if (b < 0) + return b; + + ibm_backlight_device = backlight_device_register( + TPACPI_BACKLIGHT_DEV_NAME, NULL, NULL, + &ibm_backlight_data); + if (IS_ERR(ibm_backlight_device)) { + printk(IBM_ERR "Could not register backlight device\n"); + return PTR_ERR(ibm_backlight_device); + } + vdbg_printk(TPACPI_DBG_INIT, "brightness is supported\n"); + + ibm_backlight_device->props.max_brightness = 7; + ibm_backlight_device->props.brightness = b; + backlight_update_status(ibm_backlight_device); + + return 0; +} + +static void brightness_exit(void) +{ + if (ibm_backlight_device) { + vdbg_printk(TPACPI_DBG_EXIT, + "calling backlight_device_unregister()\n"); + backlight_device_unregister(ibm_backlight_device); + ibm_backlight_device = NULL; + } +} + +static int brightness_update_status(struct backlight_device *bd) +{ + return brightness_set( + (bd->props.fb_blank == FB_BLANK_UNBLANK && + bd->props.power == FB_BLANK_UNBLANK) ? + bd->props.brightness : 0); +} + +static int brightness_get(struct backlight_device *bd) +{ + u8 level; + if (!acpi_ec_read(brightness_offset, &level)) + return -EIO; + + level &= 0x7; + + return level; +} + +static int brightness_set(int value) +{ + int cmos_cmd, inc, i; + int current_value = brightness_get(NULL); + + value &= 7; + + cmos_cmd = value > current_value ? TP_CMOS_BRIGHTNESS_UP : TP_CMOS_BRIGHTNESS_DOWN; + inc = value > current_value ? 1 : -1; + for (i = current_value; i != value; i += inc) { + if (issue_thinkpad_cmos_command(cmos_cmd)) + return -EIO; + if (!acpi_ec_write(brightness_offset, i + inc)) + return -EIO; + } + + return 0; +} + +static int brightness_read(char *p) +{ + int len = 0; + int level; + + if ((level = brightness_get(NULL)) < 0) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0x7); + len += sprintf(p + len, "commands:\tup, down\n"); + len += sprintf(p + len, "commands:\tlevel <level>" + " (<level> is 0-7)\n"); + } + + return len; +} + +static int brightness_write(char *buf) +{ + int level; + int new_level; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if ((level = brightness_get(NULL)) < 0) + return level; + level &= 7; + + if (strlencmp(cmd, "up") == 0) { + new_level = level == 7 ? 7 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 7) { + /* new_level set */ + } else + return -EINVAL; + + brightness_set(new_level); + } + + return 0; +} + +static struct ibm_struct brightness_driver_data = { + .name = "brightness", + .read = brightness_read, + .write = brightness_write, + .exit = brightness_exit, +}; + +/************************************************************************* + * Volume subdriver + */ + +static int volume_read(char *p) +{ + int len = 0; + u8 level; + + if (!acpi_ec_read(volume_offset, &level)) { + len += sprintf(p + len, "level:\t\tunreadable\n"); + } else { + len += sprintf(p + len, "level:\t\t%d\n", level & 0xf); + len += sprintf(p + len, "mute:\t\t%s\n", onoff(level, 6)); + len += sprintf(p + len, "commands:\tup, down, mute\n"); + len += sprintf(p + len, "commands:\tlevel <level>" + " (<level> is 0-15)\n"); + } + + return len; +} + +static int volume_write(char *buf) +{ + int cmos_cmd, inc, i; + u8 level, mute; + int new_level, new_mute; + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if (!acpi_ec_read(volume_offset, &level)) + return -EIO; + new_mute = mute = level & 0x40; + new_level = level = level & 0xf; + + if (strlencmp(cmd, "up") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 15 ? 15 : level + 1; + } else if (strlencmp(cmd, "down") == 0) { + if (mute) + new_mute = 0; + else + new_level = level == 0 ? 0 : level - 1; + } else if (sscanf(cmd, "level %d", &new_level) == 1 && + new_level >= 0 && new_level <= 15) { + /* new_level set */ + } else if (strlencmp(cmd, "mute") == 0) { + new_mute = 0x40; + } else + return -EINVAL; + + if (new_level != level) { /* mute doesn't change */ + cmos_cmd = new_level > level ? TP_CMOS_VOLUME_UP : TP_CMOS_VOLUME_DOWN; + inc = new_level > level ? 1 : -1; + + if (mute && (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, level))) + return -EIO; + + for (i = level; i != new_level; i += inc) + if (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, i + inc)) + return -EIO; + + if (mute && (issue_thinkpad_cmos_command(TP_CMOS_VOLUME_MUTE) || + !acpi_ec_write(volume_offset, + new_level + mute))) + return -EIO; + } + + if (new_mute != mute) { /* level doesn't change */ + cmos_cmd = new_mute ? TP_CMOS_VOLUME_MUTE : TP_CMOS_VOLUME_UP; + + if (issue_thinkpad_cmos_command(cmos_cmd) || + !acpi_ec_write(volume_offset, level + new_mute)) + return -EIO; + } + } + + return 0; +} + +static struct ibm_struct volume_driver_data = { + .name = "volume", + .read = volume_read, + .write = volume_write, +}; + +/************************************************************************* + * Fan subdriver + */ + +/* + * FAN ACCESS MODES + * + * TPACPI_FAN_RD_ACPI_GFAN: + * ACPI GFAN method: returns fan level + * + * see TPACPI_FAN_WR_ACPI_SFAN + * EC 0x2f (HFSP) not available if GFAN exists + * + * TPACPI_FAN_WR_ACPI_SFAN: + * ACPI SFAN method: sets fan level, 0 (stop) to 7 (max) + * + * EC 0x2f (HFSP) might be available *for reading*, but do not use + * it for writing. + * + * TPACPI_FAN_WR_TPEC: + * ThinkPad EC register 0x2f (HFSP): fan control loop mode + * Supported on almost all ThinkPads + * + * Fan speed changes of any sort (including those caused by the + * disengaged mode) are usually done slowly by the firmware as the + * maximum ammount of fan duty cycle change per second seems to be + * limited. + * + * Reading is not available if GFAN exists. + * Writing is not available if SFAN exists. + * + * Bits + * 7 automatic mode engaged; + * (default operation mode of the ThinkPad) + * fan level is ignored in this mode. + * 6 full speed mode (takes precedence over bit 7); + * not available on all thinkpads. May disable + * the tachometer while the fan controller ramps up + * the speed (which can take up to a few *minutes*). + * Speeds up fan to 100% duty-cycle, which is far above + * the standard RPM levels. It is not impossible that + * it could cause hardware damage. + * 5-3 unused in some models. Extra bits for fan level + * in others, but still useless as all values above + * 7 map to the same speed as level 7 in these models. + * 2-0 fan level (0..7 usually) + * 0x00 = stop + * 0x07 = max (set when temperatures critical) + * Some ThinkPads may have other levels, see + * TPACPI_FAN_WR_ACPI_FANS (X31/X40/X41) + * + * FIRMWARE BUG: on some models, EC 0x2f might not be initialized at + * boot. Apparently the EC does not intialize it, so unless ACPI DSDT + * does so, its initial value is meaningless (0x07). + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * ---- + * + * ThinkPad EC register 0x84 (LSB), 0x85 (MSB): + * Main fan tachometer reading (in RPM) + * + * This register is present on all ThinkPads with a new-style EC, and + * it is known not to be present on the A21m/e, and T22, as there is + * something else in offset 0x84 according to the ACPI DSDT. Other + * ThinkPads from this same time period (and earlier) probably lack the + * tachometer as well. + * + * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * was never fixed by IBM to report the EC firmware version string + * probably support the tachometer (like the early X models), so + * detecting it is quite hard. We need more data to know for sure. + * + * FIRMWARE BUG: always read 0x84 first, otherwise incorrect readings + * might result. + * + * FIRMWARE BUG: may go stale while the EC is switching to full speed + * mode. + * + * For firmware bugs, refer to: + * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues + * + * TPACPI_FAN_WR_ACPI_FANS: + * ThinkPad X31, X40, X41. Not available in the X60. + * + * FANS ACPI handle: takes three arguments: low speed, medium speed, + * high speed. ACPI DSDT seems to map these three speeds to levels + * as follows: STOP LOW LOW MED MED HIGH HIGH HIGH HIGH + * (this map is stored on FAN0..FAN8 as "0,1,1,2,2,3,3,3,3") + * + * The speeds are stored on handles + * (FANA:FAN9), (FANC:FANB), (FANE:FAND). + * + * There are three default speed sets, acessible as handles: + * FS1L,FS1M,FS1H; FS2L,FS2M,FS2H; FS3L,FS3M,FS3H + * + * ACPI DSDT switches which set is in use depending on various + * factors. + * + * TPACPI_FAN_WR_TPEC is also available and should be used to + * command the fan. The X31/X40/X41 seems to have 8 fan levels, + * but the ACPI tables just mention level 7. + */ + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; + +static u8 fan_control_initial_status; +static u8 fan_control_desired_level; + +static void fan_watchdog_fire(struct work_struct *ignored); +static int fan_watchdog_maxinterval; +static DECLARE_DELAYED_WORK(fan_watchdog_task, fan_watchdog_fire); + +IBM_HANDLE(fans, ec, "FANS"); /* X31, X40, X41 */ +IBM_HANDLE(gfan, ec, "GFAN", /* 570 */ + "\\FSPD", /* 600e/x, 770e, 770x */ + ); /* all others */ +IBM_HANDLE(sfan, ec, "SFAN", /* 570 */ + "JFNS", /* 770x-JL */ + ); /* all others */ + +/* + * SYSFS fan layout: hwmon compatible (device) + * + * pwm*_enable: + * 0: "disengaged" mode + * 1: manual mode + * 2: native EC "auto" mode (recommended, hardware default) + * + * pwm*: set speed in manual mode, ignored otherwise. + * 0 is level 0; 255 is level 7. Intermediate points done with linear + * interpolation. + * + * fan*_input: tachometer reading, RPM + * + * + * SYSFS fan layout: extensions + * + * fan_watchdog (driver): + * fan watchdog interval in seconds, 0 disables (default), max 120 + */ + +/* sysfs fan pwm1_enable ----------------------------------------------- */ +static ssize_t fan_pwm1_enable_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res, mode; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + /* Return most likely status. In fact, it + * might be the only possible status */ + status = TP_EC_FAN_AUTO; + } + } + + if (status & TP_EC_FAN_FULLSPEED) { + mode = 0; + } else if (status & TP_EC_FAN_AUTO) { + mode = 2; + } else + mode = 1; + + return snprintf(buf, PAGE_SIZE, "%d\n", mode); +} + +static ssize_t fan_pwm1_enable_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long t; + int res, level; + + if (parse_strtoul(buf, 2, &t)) + return -EINVAL; + + switch (t) { + case 0: + level = TP_EC_FAN_FULLSPEED; + break; + case 1: + level = TPACPI_FAN_LAST_LEVEL; + break; + case 2: + level = TP_EC_FAN_AUTO; + break; + case 3: + /* reserved for software-controlled auto mode */ + return -ENOSYS; + default: + return -EINVAL; + } + + res = fan_set_level_safe(level); + if (res == -ENXIO) + return -EINVAL; + else if (res < 0) + return res; + + fan_watchdog_reset(); + + return count; +} + +static struct device_attribute dev_attr_fan_pwm1_enable = + __ATTR(pwm1_enable, S_IWUSR | S_IRUGO, + fan_pwm1_enable_show, fan_pwm1_enable_store); + +/* sysfs fan pwm1 ------------------------------------------------------ */ +static ssize_t fan_pwm1_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + u8 status; + + res = fan_get_status_safe(&status); + if (res) + return res; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) { + tp_features.fan_ctrl_status_undef = 0; + } else { + status = TP_EC_FAN_AUTO; + } + } + + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) != 0) + status = fan_control_desired_level; + + if (status > 7) + status = 7; + + return snprintf(buf, PAGE_SIZE, "%u\n", (status * 255) / 7); +} + +static ssize_t fan_pwm1_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long s; + int rc; + u8 status, newlevel; + + if (parse_strtoul(buf, 255, &s)) + return -EINVAL; + + /* scale down from 0-255 to 0-7 */ + newlevel = (s >> 5) & 0x07; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = fan_get_status(&status); + if (!rc && (status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + rc = fan_set_level(newlevel); + if (rc == -ENXIO) + rc = -EINVAL; + else if (!rc) { + fan_update_desired_level(newlevel); + fan_watchdog_reset(); + } + } + + mutex_unlock(&fan_mutex); + return (rc)? rc : count; +} + +static struct device_attribute dev_attr_fan_pwm1 = + __ATTR(pwm1, S_IWUSR | S_IRUGO, + fan_pwm1_show, fan_pwm1_store); + +/* sysfs fan fan1_input ------------------------------------------------ */ +static ssize_t fan_fan1_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + unsigned int speed; + + res = fan_get_speed(&speed); + if (res < 0) + return res; + + return snprintf(buf, PAGE_SIZE, "%u\n", speed); +} + +static struct device_attribute dev_attr_fan_fan1_input = + __ATTR(fan1_input, S_IRUGO, + fan_fan1_input_show, NULL); + +/* sysfs fan fan_watchdog (driver) ------------------------------------- */ +static ssize_t fan_fan_watchdog_show(struct device_driver *drv, + char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", fan_watchdog_maxinterval); +} + +static ssize_t fan_fan_watchdog_store(struct device_driver *drv, + const char *buf, size_t count) +{ + unsigned long t; + + if (parse_strtoul(buf, 120, &t)) + return -EINVAL; + + if (!fan_control_allowed) + return -EPERM; + + fan_watchdog_maxinterval = t; + fan_watchdog_reset(); + + return count; +} + +static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, + fan_fan_watchdog_show, fan_fan_watchdog_store); + +/* --------------------------------------------------------------------- */ +static struct attribute *fan_attributes[] = { + &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, + &dev_attr_fan_fan1_input.attr, + NULL +}; + +static const struct attribute_group fan_attr_group = { + .attrs = fan_attributes, +}; + +static int __init fan_init(struct ibm_init_struct *iibm) +{ + int rc; + + vdbg_printk(TPACPI_DBG_INIT, "initializing fan subdriver\n"); + + mutex_init(&fan_mutex); + fan_status_access_mode = TPACPI_FAN_NONE; + fan_control_access_mode = TPACPI_FAN_WR_NONE; + fan_control_commands = 0; + fan_watchdog_maxinterval = 0; + tp_features.fan_ctrl_status_undef = 0; + fan_control_desired_level = 7; + + IBM_ACPIHANDLE_INIT(fans); + IBM_ACPIHANDLE_INIT(gfan); + IBM_ACPIHANDLE_INIT(sfan); + + if (gfan_handle) { + /* 570, 600e/x, 770e, 770x */ + fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; + } else { + /* all other ThinkPads: note that even old-style + * ThinkPad ECs supports the fan control register */ + if (likely(acpi_ec_read(fan_status_offset, + &fan_control_initial_status))) { + fan_status_access_mode = TPACPI_FAN_RD_TPEC; + + /* In some ThinkPads, neither the EC nor the ACPI + * DSDT initialize the fan status, and it ends up + * being set to 0x07 when it *could* be either + * 0x07 or 0x80. + * + * Enable for TP-1Y (T43), TP-78 (R51e), + * TP-76 (R52), TP-70 (T43, R52), which are known + * to be buggy. */ + if (fan_control_initial_status == 0x07 && + ibm_thinkpad_ec_found && + ((ibm_thinkpad_ec_found[0] == '1' && + ibm_thinkpad_ec_found[1] == 'Y') || + (ibm_thinkpad_ec_found[0] == '7' && + (ibm_thinkpad_ec_found[1] == '6' || + ibm_thinkpad_ec_found[1] == '8' || + ibm_thinkpad_ec_found[1] == '0')) + )) { + printk(IBM_NOTICE + "fan_init: initial fan status is " + "unknown, assuming it is in auto " + "mode\n"); + tp_features.fan_ctrl_status_undef = 1; + } + } else { + printk(IBM_ERR + "ThinkPad ACPI EC access misbehaving, " + "fan status and control unavailable\n"); + return 1; + } + } + + if (sfan_handle) { + /* 570, 770x-JL */ + fan_control_access_mode = TPACPI_FAN_WR_ACPI_SFAN; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | TPACPI_FAN_CMD_ENABLE; + } else { + if (!gfan_handle) { + /* gfan without sfan means no fan control */ + /* all other models implement TP EC 0x2f control */ + + if (fans_handle) { + /* X31, X40, X41 */ + fan_control_access_mode = + TPACPI_FAN_WR_ACPI_FANS; + fan_control_commands |= + TPACPI_FAN_CMD_SPEED | + TPACPI_FAN_CMD_LEVEL | + TPACPI_FAN_CMD_ENABLE; + } else { + fan_control_access_mode = TPACPI_FAN_WR_TPEC; + fan_control_commands |= + TPACPI_FAN_CMD_LEVEL | + TPACPI_FAN_CMD_ENABLE; + } + } + } + + vdbg_printk(TPACPI_DBG_INIT, "fan is %s, modes %d, %d\n", + str_supported(fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE), + fan_status_access_mode, fan_control_access_mode); + + /* fan control master switch */ + if (!fan_control_allowed) { + fan_control_access_mode = TPACPI_FAN_WR_NONE; + fan_control_commands = 0; + dbg_printk(TPACPI_DBG_INIT, + "fan control features disabled by parameter\n"); + } + + /* update fan_control_desired_level */ + if (fan_status_access_mode != TPACPI_FAN_NONE) + fan_get_status_safe(NULL); + + if (fan_status_access_mode != TPACPI_FAN_NONE || + fan_control_access_mode != TPACPI_FAN_WR_NONE) { + rc = sysfs_create_group(&tpacpi_pdev->dev.kobj, + &fan_attr_group); + if (!(rc < 0)) + rc = driver_create_file(&tpacpi_pdriver.driver, + &driver_attr_fan_watchdog); + if (rc < 0) + return rc; + return 0; + } else + return 1; +} + +/* + * Call with fan_mutex held + */ +static void fan_update_desired_level(u8 status) +{ + if ((status & + (TP_EC_FAN_AUTO | TP_EC_FAN_FULLSPEED)) == 0) { + if (status > 7) + fan_control_desired_level = 7; + else + fan_control_desired_level = status; + } +} + +static int fan_get_status(u8 *status) +{ + u8 s; + + /* TODO: + * Add TPACPI_FAN_RD_ACPI_FANS ? */ + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + + if (unlikely(!acpi_evalf(gfan_handle, &s, NULL, "d"))) + return -EIO; + + if (likely(status)) + *status = s & 0x07; + + break; + + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_status_offset, &s))) + return -EIO; + + if (likely(status)) + *status = s; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static int fan_get_status_safe(u8 *status) +{ + int rc; + u8 s; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + rc = fan_get_status(&s); + if (!rc) + fan_update_desired_level(s); + mutex_unlock(&fan_mutex); + + if (status) + *status = s; + + return rc; +} + +static void fan_exit(void) +{ + vdbg_printk(TPACPI_DBG_EXIT, "cancelling any pending fan watchdog tasks\n"); + + /* FIXME: can we really do this unconditionally? */ + sysfs_remove_group(&tpacpi_pdev->dev.kobj, &fan_attr_group); + driver_remove_file(&tpacpi_pdriver.driver, &driver_attr_fan_watchdog); + + cancel_delayed_work(&fan_watchdog_task); + flush_scheduled_work(); +} + +static int fan_get_speed(unsigned int *speed) +{ + u8 hi, lo; + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi))) + return -EIO; + + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + +static void fan_watchdog_fire(struct work_struct *ignored) +{ + int rc; + + printk(IBM_NOTICE "fan watchdog: enabling fan\n"); + rc = fan_set_enable(); + if (rc < 0) { + printk(IBM_ERR "fan watchdog: error %d while enabling fan, " + "will try again later...\n", -rc); + /* reschedule for later */ + fan_watchdog_reset(); + } +} + +static void fan_watchdog_reset(void) +{ + static int fan_watchdog_active = 0; + + if (fan_control_access_mode == TPACPI_FAN_WR_NONE) + return; + + if (fan_watchdog_active) + cancel_delayed_work(&fan_watchdog_task); + + if (fan_watchdog_maxinterval > 0) { + fan_watchdog_active = 1; + if (!schedule_delayed_work(&fan_watchdog_task, + msecs_to_jiffies(fan_watchdog_maxinterval + * 1000))) { + printk(IBM_ERR "failed to schedule the fan watchdog, " + "watchdog will not trigger\n"); + } + } else + fan_watchdog_active = 0; +} + +static int fan_set_level(int level) +{ + if (!fan_control_allowed) + return -EPERM; + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_SFAN: + if (level >= 0 && level <= 7) { + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", level)) + return -EIO; + } else + return -EINVAL; + break; + + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + if ((level != TP_EC_FAN_AUTO) && + (level != TP_EC_FAN_FULLSPEED) && + ((level < 0) || (level > 7))) + return -EINVAL; + + /* safety net should the EC not support AUTO + * or FULLSPEED mode bits and just ignore them */ + if (level & TP_EC_FAN_FULLSPEED) + level |= 7; /* safety min speed 7 */ + else if (level & TP_EC_FAN_FULLSPEED) + level |= 4; /* safety min speed 4 */ + + if (!acpi_ec_write(fan_status_offset, level)) + return -EIO; + else + tp_features.fan_ctrl_status_undef = 0; + break; + + default: + return -ENXIO; + } + return 0; +} + +static int fan_set_level_safe(int level) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + if (level == TPACPI_FAN_LAST_LEVEL) + level = fan_control_desired_level; + + rc = fan_set_level(level); + if (!rc) + fan_update_desired_level(level); + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_enable(void) +{ + u8 s; + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + rc = fan_get_status(&s); + if (rc < 0) + break; + + /* Don't go out of emergency fan mode */ + if (s != 7) { + s &= 0x07; + s |= TP_EC_FAN_AUTO | 4; /* min fan speed 4 */ + } + + if (!acpi_ec_write(fan_status_offset, s)) + rc = -EIO; + else { + tp_features.fan_ctrl_status_undef = 0; + rc = 0; + } + break; + + case TPACPI_FAN_WR_ACPI_SFAN: + rc = fan_get_status(&s); + if (rc < 0) + break; + + s &= 0x07; + + /* Set fan to at least level 4 */ + s |= 4; + + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", s)) + rc= -EIO; + else + rc = 0; + break; + + default: + rc = -ENXIO; + } + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_disable(void) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = 0; + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + case TPACPI_FAN_WR_TPEC: + if (!acpi_ec_write(fan_status_offset, 0x00)) + rc = -EIO; + else { + fan_control_desired_level = 0; + tp_features.fan_ctrl_status_undef = 0; + } + break; + + case TPACPI_FAN_WR_ACPI_SFAN: + if (!acpi_evalf(sfan_handle, NULL, NULL, "vd", 0x00)) + rc = -EIO; + else + fan_control_desired_level = 0; + break; + + default: + rc = -ENXIO; + } + + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_set_speed(int speed) +{ + int rc; + + if (!fan_control_allowed) + return -EPERM; + + rc = mutex_lock_interruptible(&fan_mutex); + if (rc < 0) + return rc; + + rc = 0; + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_FANS: + if (speed >= 0 && speed <= 65535) { + if (!acpi_evalf(fans_handle, NULL, NULL, "vddd", + speed, speed, speed)) + rc = -EIO; + } else + rc = -EINVAL; + break; + + default: + rc = -ENXIO; + } + + mutex_unlock(&fan_mutex); + return rc; +} + +static int fan_read(char *p) +{ + int len = 0; + int rc; + u8 status; + unsigned int speed = 0; + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_ACPI_GFAN: + /* 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status_safe(&status)) < 0) + return rc; + + len += sprintf(p + len, "status:\t\t%s\n" + "level:\t\t%d\n", + (status != 0) ? "enabled" : "disabled", status); + break; + + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if ((rc = fan_get_status_safe(&status)) < 0) + return rc; + + if (unlikely(tp_features.fan_ctrl_status_undef)) { + if (status != fan_control_initial_status) + tp_features.fan_ctrl_status_undef = 0; + else + /* Return most likely status. In fact, it + * might be the only possible status */ + status = TP_EC_FAN_AUTO; + } + + len += sprintf(p + len, "status:\t\t%s\n", + (status != 0) ? "enabled" : "disabled"); + + if ((rc = fan_get_speed(&speed)) < 0) + return rc; + + len += sprintf(p + len, "speed:\t\t%d\n", speed); + + if (status & TP_EC_FAN_FULLSPEED) + /* Disengaged mode takes precedence */ + len += sprintf(p + len, "level:\t\tdisengaged\n"); + else if (status & TP_EC_FAN_AUTO) + len += sprintf(p + len, "level:\t\tauto\n"); + else + len += sprintf(p + len, "level:\t\t%d\n", status); + break; + + case TPACPI_FAN_NONE: + default: + len += sprintf(p + len, "status:\t\tnot supported\n"); + } + + if (fan_control_commands & TPACPI_FAN_CMD_LEVEL) { + len += sprintf(p + len, "commands:\tlevel <level>"); + + switch (fan_control_access_mode) { + case TPACPI_FAN_WR_ACPI_SFAN: + len += sprintf(p + len, " (<level> is 0-7)\n"); + break; + + default: + len += sprintf(p + len, " (<level> is 0-7, " + "auto, disengaged, full-speed)\n"); + break; + } + } + + if (fan_control_commands & TPACPI_FAN_CMD_ENABLE) + len += sprintf(p + len, "commands:\tenable, disable\n" + "commands:\twatchdog <timeout> (<timeout> is 0 (off), " + "1-120 (seconds))\n"); + + if (fan_control_commands & TPACPI_FAN_CMD_SPEED) + len += sprintf(p + len, "commands:\tspeed <speed>" + " (<speed> is 0-65535)\n"); + + return len; +} + +static int fan_write_cmd_level(const char *cmd, int *rc) +{ + int level; + + if (strlencmp(cmd, "level auto") == 0) + level = TP_EC_FAN_AUTO; + else if ((strlencmp(cmd, "level disengaged") == 0) | + (strlencmp(cmd, "level full-speed") == 0)) + level = TP_EC_FAN_FULLSPEED; + else if (sscanf(cmd, "level %d", &level) != 1) + return 0; + + if ((*rc = fan_set_level_safe(level)) == -ENXIO) + printk(IBM_ERR "level command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_enable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "enable") != 0) + return 0; + + if ((*rc = fan_set_enable()) == -ENXIO) + printk(IBM_ERR "enable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_disable(const char *cmd, int *rc) +{ + if (strlencmp(cmd, "disable") != 0) + return 0; + + if ((*rc = fan_set_disable()) == -ENXIO) + printk(IBM_ERR "disable command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_speed(const char *cmd, int *rc) +{ + int speed; + + /* TODO: + * Support speed <low> <medium> <high> ? */ + + if (sscanf(cmd, "speed %d", &speed) != 1) + return 0; + + if ((*rc = fan_set_speed(speed)) == -ENXIO) + printk(IBM_ERR "speed command accepted for unsupported " + "access mode %d", fan_control_access_mode); + + return 1; +} + +static int fan_write_cmd_watchdog(const char *cmd, int *rc) +{ + int interval; + + if (sscanf(cmd, "watchdog %d", &interval) != 1) + return 0; + + if (interval < 0 || interval > 120) + *rc = -EINVAL; + else + fan_watchdog_maxinterval = interval; + + return 1; +} + +static int fan_write(char *buf) +{ + char *cmd; + int rc = 0; + + while (!rc && (cmd = next_cmd(&buf))) { + if (!((fan_control_commands & TPACPI_FAN_CMD_LEVEL) && + fan_write_cmd_level(cmd, &rc)) && + !((fan_control_commands & TPACPI_FAN_CMD_ENABLE) && + (fan_write_cmd_enable(cmd, &rc) || + fan_write_cmd_disable(cmd, &rc) || + fan_write_cmd_watchdog(cmd, &rc))) && + !((fan_control_commands & TPACPI_FAN_CMD_SPEED) && + fan_write_cmd_speed(cmd, &rc)) + ) + rc = -EINVAL; + else if (!rc) + fan_watchdog_reset(); + } + + return rc; +} + +static struct ibm_struct fan_driver_data = { + .name = "fan", + .read = fan_read, + .write = fan_write, + .exit = fan_exit, +}; + +/**************************************************************************** + **************************************************************************** + * + * Infrastructure + * + **************************************************************************** + ****************************************************************************/ + +/* /proc support */ +static struct proc_dir_entry *proc_dir = NULL; + +/* Subdriver registry */ +static LIST_HEAD(tpacpi_all_drivers); + + +/* + * Module and infrastructure proble, init and exit handling + */ + +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +static const char * __init str_supported(int is_supported) +{ + static char text_unsupported[] __initdata = "not supported"; + + return (is_supported)? &text_unsupported[4] : &text_unsupported[0]; +} +#endif /* CONFIG_THINKPAD_ACPI_DEBUG */ + +static int __init ibm_init(struct ibm_init_struct *iibm) +{ + int ret; + struct ibm_struct *ibm = iibm->data; + struct proc_dir_entry *entry; + + BUG_ON(ibm == NULL); + + INIT_LIST_HEAD(&ibm->all_drivers); + + if (ibm->flags.experimental && !experimental) + return 0; + + dbg_printk(TPACPI_DBG_INIT, + "probing for %s\n", ibm->name); + + if (iibm->init) { + ret = iibm->init(iibm); + if (ret > 0) + return 0; /* probe failed */ + if (ret) + return ret; + + ibm->flags.init_called = 1; + } + + if (ibm->acpi) { + if (ibm->acpi->hid) { + ret = register_tpacpi_subdriver(ibm); + if (ret) + goto err_out; + } + + if (ibm->acpi->notify) { + ret = setup_acpi_notify(ibm); + if (ret == -ENODEV) { + printk(IBM_NOTICE "disabling subdriver %s\n", + ibm->name); + ret = 0; + goto err_out; + } + if (ret < 0) + goto err_out; + } + } + + dbg_printk(TPACPI_DBG_INIT, + "%s installed\n", ibm->name); + + if (ibm->read) { + entry = create_proc_entry(ibm->name, + S_IFREG | S_IRUGO | S_IWUSR, + proc_dir); + if (!entry) { + printk(IBM_ERR "unable to create proc entry %s\n", + ibm->name); + ret = -ENODEV; + goto err_out; + } + entry->owner = THIS_MODULE; + entry->data = ibm; + entry->read_proc = &dispatch_procfs_read; + if (ibm->write) + entry->write_proc = &dispatch_procfs_write; + ibm->flags.proc_created = 1; + } + + list_add_tail(&ibm->all_drivers, &tpacpi_all_drivers); + + return 0; + +err_out: + dbg_printk(TPACPI_DBG_INIT, + "%s: at error exit path with result %d\n", + ibm->name, ret); + + ibm_exit(ibm); + return (ret < 0)? ret : 0; +} + +static void ibm_exit(struct ibm_struct *ibm) +{ + dbg_printk(TPACPI_DBG_EXIT, "removing %s\n", ibm->name); + + list_del_init(&ibm->all_drivers); + + if (ibm->flags.acpi_notify_installed) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_remove_notify_handler\n", ibm->name); + BUG_ON(!ibm->acpi); + acpi_remove_notify_handler(*ibm->acpi->handle, + ibm->acpi->type, + dispatch_acpi_notify); + ibm->flags.acpi_notify_installed = 0; + ibm->flags.acpi_notify_installed = 0; + } + + if (ibm->flags.proc_created) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: remove_proc_entry\n", ibm->name); + remove_proc_entry(ibm->name, proc_dir); + ibm->flags.proc_created = 0; + } + + if (ibm->flags.acpi_driver_registered) { + dbg_printk(TPACPI_DBG_EXIT, + "%s: acpi_bus_unregister_driver\n", ibm->name); + BUG_ON(!ibm->acpi); + acpi_bus_unregister_driver(ibm->acpi->driver); + kfree(ibm->acpi->driver); + ibm->acpi->driver = NULL; + ibm->flags.acpi_driver_registered = 0; + } + + if (ibm->flags.init_called && ibm->exit) { + ibm->exit(); + ibm->flags.init_called = 0; + } + + dbg_printk(TPACPI_DBG_INIT, "finished removing %s\n", ibm->name); +} + +/* Probing */ + +static char *ibm_thinkpad_ec_found = NULL; + +static char* __init check_dmi_for_ec(void) +{ + struct dmi_device *dev = NULL; + char ec_fw_string[18]; + + /* + * ThinkPad T23 or newer, A31 or newer, R50e or newer, + * X32 or newer, all Z series; Some models must have an + * up-to-date BIOS or they will not be detected. + * + * See http://thinkwiki.org/wiki/List_of_DMI_IDs + */ + while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING, NULL, dev))) { + if (sscanf(dev->name, + "IBM ThinkPad Embedded Controller -[%17c", + ec_fw_string) == 1) { + ec_fw_string[sizeof(ec_fw_string) - 1] = 0; + ec_fw_string[strcspn(ec_fw_string, " ]")] = 0; + return kstrdup(ec_fw_string, GFP_KERNEL); + } + } + return NULL; +} + +static int __init probe_for_thinkpad(void) +{ + int is_thinkpad; + + if (acpi_disabled) + return -ENODEV; + + /* + * Non-ancient models have better DMI tagging, but very old models + * don't. + */ + is_thinkpad = dmi_name_in_vendors("ThinkPad"); + + /* ec is required because many other handles are relative to it */ + IBM_ACPIHANDLE_INIT(ec); + if (!ec_handle) { + if (is_thinkpad) + printk(IBM_ERR + "Not yet supported ThinkPad detected!\n"); + return -ENODEV; + } + + /* + * Risks a regression on very old machines, but reduces potential + * false positives a damn great deal + */ + if (!is_thinkpad) + is_thinkpad = dmi_name_in_vendors("IBM"); + + if (!is_thinkpad && !force_load) + return -ENODEV; + + return 0; +} + + +/* Module init, exit, parameters */ + +static struct ibm_init_struct ibms_init[] __initdata = { + { + .init = thinkpad_acpi_driver_init, + .data = &thinkpad_acpi_driver_data, + }, + { + .init = hotkey_init, + .data = &hotkey_driver_data, + }, + { + .init = bluetooth_init, + .data = &bluetooth_driver_data, + }, + { + .init = wan_init, + .data = &wan_driver_data, + }, + { + .init = video_init, + .data = &video_driver_data, + }, + { + .init = light_init, + .data = &light_driver_data, + }, +#ifdef CONFIG_THINKPAD_ACPI_DOCK + { + .init = dock_init, + .data = &dock_driver_data[0], + }, + { + .init = dock_init2, + .data = &dock_driver_data[1], + }, +#endif +#ifdef CONFIG_THINKPAD_ACPI_BAY + { + .init = bay_init, + .data = &bay_driver_data, + }, +#endif + { + .init = cmos_init, + .data = &cmos_driver_data, + }, + { + .init = led_init, + .data = &led_driver_data, + }, + { + .init = beep_init, + .data = &beep_driver_data, + }, + { + .init = thermal_init, + .data = &thermal_driver_data, + }, + { + .data = &ecdump_driver_data, + }, + { + .init = brightness_init, + .data = &brightness_driver_data, + }, + { + .data = &volume_driver_data, + }, + { + .init = fan_init, + .data = &fan_driver_data, + }, +}; + +static int __init set_ibm_param(const char *val, struct kernel_param *kp) +{ + unsigned int i; + struct ibm_struct *ibm; + + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { + ibm = ibms_init[i].data; + BUG_ON(ibm == NULL); + + if (strcmp(ibm->name, kp->name) == 0 && ibm->write) { + if (strlen(val) > sizeof(ibms_init[i].param) - 2) + return -ENOSPC; + strcpy(ibms_init[i].param, val); + strcat(ibms_init[i].param, ","); + return 0; + } + } + + return -EINVAL; +} + +static int experimental; +module_param(experimental, int, 0); + +static u32 dbg_level; +module_param_named(debug, dbg_level, uint, 0); + +static int force_load; +module_param(force_load, int, 0); + +static int fan_control_allowed; +module_param_named(fan_control, fan_control_allowed, int, 0); + +#define IBM_PARAM(feature) \ + module_param_call(feature, set_ibm_param, NULL, NULL, 0) + +IBM_PARAM(hotkey); +IBM_PARAM(bluetooth); +IBM_PARAM(video); +IBM_PARAM(light); +#ifdef CONFIG_THINKPAD_ACPI_DOCK +IBM_PARAM(dock); +#endif +#ifdef CONFIG_THINKPAD_ACPI_BAY +IBM_PARAM(bay); +#endif /* CONFIG_THINKPAD_ACPI_BAY */ +IBM_PARAM(cmos); +IBM_PARAM(led); +IBM_PARAM(beep); +IBM_PARAM(ecdump); +IBM_PARAM(brightness); +IBM_PARAM(volume); +IBM_PARAM(fan); + +static int __init thinkpad_acpi_module_init(void) +{ + int ret, i; + + /* Driver-level probe */ + ret = probe_for_thinkpad(); + if (ret) + return ret; + + /* Driver initialization */ + ibm_thinkpad_ec_found = check_dmi_for_ec(); + IBM_ACPIHANDLE_INIT(ecrd); + IBM_ACPIHANDLE_INIT(ecwr); + + proc_dir = proc_mkdir(IBM_PROC_DIR, acpi_root_dir); + if (!proc_dir) { + printk(IBM_ERR "unable to create proc dir " IBM_PROC_DIR); + thinkpad_acpi_module_exit(); + return -ENODEV; + } + proc_dir->owner = THIS_MODULE; + + ret = platform_driver_register(&tpacpi_pdriver); + if (ret) { + printk(IBM_ERR "unable to register platform driver\n"); + thinkpad_acpi_module_exit(); + return ret; + } + ret = tpacpi_create_driver_attributes(&tpacpi_pdriver.driver); + if (ret) { + printk(IBM_ERR "unable to create sysfs driver attributes\n"); + thinkpad_acpi_module_exit(); + return ret; + } + + + /* Device initialization */ + tpacpi_pdev = platform_device_register_simple(IBM_DRVR_NAME, -1, + NULL, 0); + if (IS_ERR(tpacpi_pdev)) { + ret = PTR_ERR(tpacpi_pdev); + tpacpi_pdev = NULL; + printk(IBM_ERR "unable to register platform device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + tpacpi_hwmon = hwmon_device_register(&tpacpi_pdev->dev); + if (IS_ERR(tpacpi_hwmon)) { + ret = PTR_ERR(tpacpi_hwmon); + tpacpi_hwmon = NULL; + printk(IBM_ERR "unable to register hwmon device\n"); + thinkpad_acpi_module_exit(); + return ret; + } + for (i = 0; i < ARRAY_SIZE(ibms_init); i++) { + ret = ibm_init(&ibms_init[i]); + if (ret >= 0 && *ibms_init[i].param) + ret = ibms_init[i].data->write(ibms_init[i].param); + if (ret < 0) { + thinkpad_acpi_module_exit(); + return ret; + } + } + + return 0; +} + +static void thinkpad_acpi_module_exit(void) +{ + struct ibm_struct *ibm, *itmp; + + list_for_each_entry_safe_reverse(ibm, itmp, + &tpacpi_all_drivers, + all_drivers) { + ibm_exit(ibm); + } + + dbg_printk(TPACPI_DBG_INIT, "finished subdriver exit path...\n"); + + if (tpacpi_hwmon) + hwmon_device_unregister(tpacpi_hwmon); + + if (tpacpi_pdev) + platform_device_unregister(tpacpi_pdev); + + tpacpi_remove_driver_attributes(&tpacpi_pdriver.driver); + platform_driver_unregister(&tpacpi_pdriver); + + if (proc_dir) + remove_proc_entry(IBM_PROC_DIR, acpi_root_dir); + + kfree(ibm_thinkpad_ec_found); +} + +module_init(thinkpad_acpi_module_init); +module_exit(thinkpad_acpi_module_exit); diff --git a/drivers/misc/thinkpad_acpi.h b/drivers/misc/thinkpad_acpi.h new file mode 100644 index 0000000..440145a --- /dev/null +++ b/drivers/misc/thinkpad_acpi.h @@ -0,0 +1,572 @@ +/* + * thinkpad_acpi.h - ThinkPad ACPI Extras + * + * + * Copyright (C) 2004-2005 Borislav Deianov <borislav@users.sf.net> + * Copyright (C) 2006-2007 Henrique de Moraes Holschuh <hmh@hmh.eng.br> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You 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 Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef __THINKPAD_ACPI_H__ +#define __THINKPAD_ACPI_H__ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/list.h> +#include <linux/mutex.h> + +#include <linux/proc_fs.h> +#include <linux/sysfs.h> +#include <linux/backlight.h> +#include <linux/fb.h> +#include <linux/platform_device.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <asm/uaccess.h> + +#include <linux/dmi.h> +#include <linux/jiffies.h> +#include <linux/workqueue.h> + +#include <acpi/acpi_drivers.h> +#include <acpi/acnamesp.h> + + +/**************************************************************************** + * Main driver + */ + +#define IBM_NAME "thinkpad" +#define IBM_DESC "ThinkPad ACPI Extras" +#define IBM_FILE "thinkpad_acpi" +#define IBM_URL "http://ibm-acpi.sf.net/" +#define IBM_MAIL "ibm-acpi-devel@lists.sourceforge.net" + +#define IBM_PROC_DIR "ibm" +#define IBM_ACPI_EVENT_PREFIX "ibm" +#define IBM_DRVR_NAME IBM_FILE + +#define IBM_LOG IBM_FILE ": " +#define IBM_ERR KERN_ERR IBM_LOG +#define IBM_NOTICE KERN_NOTICE IBM_LOG +#define IBM_INFO KERN_INFO IBM_LOG +#define IBM_DEBUG KERN_DEBUG IBM_LOG + +#define IBM_MAX_ACPI_ARGS 3 + +/* ThinkPad CMOS commands */ +#define TP_CMOS_VOLUME_DOWN 0 +#define TP_CMOS_VOLUME_UP 1 +#define TP_CMOS_VOLUME_MUTE 2 +#define TP_CMOS_BRIGHTNESS_UP 4 +#define TP_CMOS_BRIGHTNESS_DOWN 5 + +#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") +#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") +#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) + +/* Debugging */ +#define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_ALL 0xffff +#define TPACPI_DBG_INIT 0x0001 +#define TPACPI_DBG_EXIT 0x0002 +#define dbg_printk(a_dbg_level, format, arg...) \ + do { if (dbg_level & a_dbg_level) \ + printk(IBM_DEBUG "%s: " format, __func__ , ## arg); } while (0) +#ifdef CONFIG_THINKPAD_ACPI_DEBUG +#define vdbg_printk(a_dbg_level, format, arg...) \ + dbg_printk(a_dbg_level, format, ## arg) +static const char *str_supported(int is_supported); +#else +#define vdbg_printk(a_dbg_level, format, arg...) +#endif + +/* ACPI HIDs */ +#define IBM_HKEY_HID "IBM0068" +#define IBM_PCI_HID "PNP0A03" + +/* ACPI helpers */ +static int __must_check acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...); +static int __must_check acpi_ec_read(int i, u8 * p); +static int __must_check acpi_ec_write(int i, u8 v); +static int __must_check _sta(acpi_handle handle); + +/* ACPI handles */ +static acpi_handle root_handle; /* root namespace */ +static acpi_handle ec_handle; /* EC */ +static acpi_handle ecrd_handle, ecwr_handle; /* 570 EC access */ +static acpi_handle cmos_handle, hkey_handle; /* basic thinkpad handles */ + +static void drv_acpi_handle_init(char *name, + acpi_handle *handle, acpi_handle parent, + char **paths, int num_paths, char **path); +#define IBM_ACPIHANDLE_INIT(object) \ + drv_acpi_handle_init(#object, &object##_handle, *object##_parent, \ + object##_paths, ARRAY_SIZE(object##_paths), &object##_path) + +/* ThinkPad ACPI helpers */ +static int issue_thinkpad_cmos_command(int cmos_cmd); + +/* procfs support */ +static struct proc_dir_entry *proc_dir; + +/* procfs helpers */ +static int dispatch_procfs_read(char *page, char **start, off_t off, + int count, int *eof, void *data); +static int dispatch_procfs_write(struct file *file, + const char __user * userbuf, + unsigned long count, void *data); +static char *next_cmd(char **cmds); + +/* sysfs support */ +struct attribute_set { + unsigned int members, max_members; + struct attribute_group group; +}; + +static struct attribute_set *create_attr_set(unsigned int max_members, + const char* name); +#define destroy_attr_set(_set) \ + kfree(_set); +static int add_to_attr_set(struct attribute_set* s, struct attribute *attr); +static int add_many_to_attr_set(struct attribute_set* s, + struct attribute **attr, + unsigned int count); +#define register_attr_set_with_sysfs(_attr_set, _kobj) \ + sysfs_create_group(_kobj, &_attr_set->group) +static void delete_attr_set(struct attribute_set* s, struct kobject *kobj); + +static int parse_strtoul(const char *buf, unsigned long max, + unsigned long *value); + +/* Device model */ +static struct platform_device *tpacpi_pdev; +static struct class_device *tpacpi_hwmon; +static struct platform_driver tpacpi_pdriver; +static int tpacpi_create_driver_attributes(struct device_driver *drv); +static void tpacpi_remove_driver_attributes(struct device_driver *drv); + +/* Module */ +static int experimental; +static u32 dbg_level; +static int force_load; +static char *ibm_thinkpad_ec_found; + +static char* check_dmi_for_ec(void); +static int thinkpad_acpi_module_init(void); +static void thinkpad_acpi_module_exit(void); + + +/**************************************************************************** + * Subdrivers + */ + +struct ibm_struct; + +struct tp_acpi_drv_struct { + char *hid; + struct acpi_driver *driver; + + void (*notify) (struct ibm_struct *, u32); + acpi_handle *handle; + u32 type; + struct acpi_device *device; +}; + +struct ibm_struct { + char *name; + + int (*read) (char *); + int (*write) (char *); + void (*exit) (void); + + struct list_head all_drivers; + + struct tp_acpi_drv_struct *acpi; + + struct { + u8 acpi_driver_registered:1; + u8 acpi_notify_installed:1; + u8 proc_created:1; + u8 init_called:1; + u8 experimental:1; + } flags; +}; + +struct ibm_init_struct { + char param[32]; + + int (*init) (struct ibm_init_struct *); + struct ibm_struct *data; +}; + +static struct { +#ifdef CONFIG_THINKPAD_ACPI_BAY + u16 bay_status:1; + u16 bay_eject:1; + u16 bay_status2:1; + u16 bay_eject2:1; +#endif + u16 bluetooth:1; + u16 hotkey:1; + u16 hotkey_mask:1; + u16 light:1; + u16 light_status:1; + u16 wan:1; + u16 fan_ctrl_status_undef:1; +} tp_features; + +static struct list_head tpacpi_all_drivers; + +static struct ibm_init_struct ibms_init[]; +static int set_ibm_param(const char *val, struct kernel_param *kp); +static int ibm_init(struct ibm_init_struct *iibm); +static void ibm_exit(struct ibm_struct *ibm); + + +/* + * procfs master subdriver + */ +static int thinkpad_acpi_driver_init(struct ibm_init_struct *iibm); +static int thinkpad_acpi_driver_read(char *p); + + +/* + * Bay subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_BAY +static acpi_handle bay_handle, bay_ej_handle; +static acpi_handle bay2_handle, bay2_ej_handle; + +static int bay_init(struct ibm_init_struct *iibm); +static void bay_notify(struct ibm_struct *ibm, u32 event); +static int bay_read(char *p); +static int bay_write(char *buf); +#endif /* CONFIG_THINKPAD_ACPI_BAY */ + + +/* + * Beep subdriver + */ + +static acpi_handle beep_handle; + +static int beep_read(char *p); +static int beep_write(char *buf); + + +/* + * Bluetooth subdriver + */ + +#define TPACPI_BLUETH_SYSFS_GROUP "bluetooth" + +enum { + /* ACPI GBDC/SBDC bits */ + TP_ACPI_BLUETOOTH_HWPRESENT = 0x01, /* Bluetooth hw available */ + TP_ACPI_BLUETOOTH_RADIOSSW = 0x02, /* Bluetooth radio enabled */ + TP_ACPI_BLUETOOTH_UNK = 0x04, /* unknown function */ +}; + +static int bluetooth_init(struct ibm_init_struct *iibm); +static int bluetooth_get_radiosw(void); +static int bluetooth_set_radiosw(int radio_on); +static int bluetooth_read(char *p); +static int bluetooth_write(char *buf); + + +/* + * Brightness (backlight) subdriver + */ + +#define TPACPI_BACKLIGHT_DEV_NAME "thinkpad_screen" + +static struct backlight_device *ibm_backlight_device; +static int brightness_offset = 0x31; + +static int brightness_init(struct ibm_init_struct *iibm); +static void brightness_exit(void); +static int brightness_get(struct backlight_device *bd); +static int brightness_set(int value); +static int brightness_update_status(struct backlight_device *bd); +static int brightness_read(char *p); +static int brightness_write(char *buf); + + +/* + * CMOS subdriver + */ + +static int cmos_read(char *p); +static int cmos_write(char *buf); + + +/* + * Dock subdriver + */ + +#ifdef CONFIG_THINKPAD_ACPI_DOCK +static acpi_handle pci_handle; +static acpi_handle dock_handle; + +static void dock_notify(struct ibm_struct *ibm, u32 event); +static int dock_read(char *p); +static int dock_write(char *buf); +#endif /* CONFIG_THINKPAD_ACPI_DOCK */ + + +/* + * EC dump subdriver + */ + +static int ecdump_read(char *p) ; +static int ecdump_write(char *buf); + + +/* + * Fan subdriver + */ + +enum { /* Fan control constants */ + fan_status_offset = 0x2f, /* EC register 0x2f */ + fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) + * 0x84 must be read before 0x85 */ + + TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ + TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ + + TPACPI_FAN_LAST_LEVEL = 0x100, /* Use cached last-seen fan level */ +}; + +enum fan_status_access_mode { + TPACPI_FAN_NONE = 0, /* No fan status or control */ + TPACPI_FAN_RD_ACPI_GFAN, /* Use ACPI GFAN */ + TPACPI_FAN_RD_TPEC, /* Use ACPI EC regs 0x2f, 0x84-0x85 */ +}; + +enum fan_control_access_mode { + TPACPI_FAN_WR_NONE = 0, /* No fan control */ + TPACPI_FAN_WR_ACPI_SFAN, /* Use ACPI SFAN */ + TPACPI_FAN_WR_TPEC, /* Use ACPI EC reg 0x2f */ + TPACPI_FAN_WR_ACPI_FANS, /* Use ACPI FANS and EC reg 0x2f */ +}; + +enum fan_control_commands { + TPACPI_FAN_CMD_SPEED = 0x0001, /* speed command */ + TPACPI_FAN_CMD_LEVEL = 0x0002, /* level command */ + TPACPI_FAN_CMD_ENABLE = 0x0004, /* enable/disable cmd, + * and also watchdog cmd */ +}; + +static int fan_control_allowed; + +static enum fan_status_access_mode fan_status_access_mode; +static enum fan_control_access_mode fan_control_access_mode; +static enum fan_control_commands fan_control_commands; +static u8 fan_control_initial_status; +static u8 fan_control_desired_level; +static int fan_watchdog_maxinterval; + +static struct mutex fan_mutex; + +static acpi_handle fans_handle, gfan_handle, sfan_handle; + +static int fan_init(struct ibm_init_struct *iibm); +static void fan_exit(void); +static int fan_get_status(u8 *status); +static int fan_get_status_safe(u8 *status); +static int fan_get_speed(unsigned int *speed); +static void fan_update_desired_level(u8 status); +static void fan_watchdog_fire(struct work_struct *ignored); +static void fan_watchdog_reset(void); +static int fan_set_level(int level); +static int fan_set_level_safe(int level); +static int fan_set_enable(void); +static int fan_set_disable(void); +static int fan_set_speed(int speed); +static int fan_read(char *p); +static int fan_write(char *buf); +static int fan_write_cmd_level(const char *cmd, int *rc); +static int fan_write_cmd_enable(const char *cmd, int *rc); +static int fan_write_cmd_disable(const char *cmd, int *rc); +static int fan_write_cmd_speed(const char *cmd, int *rc); +static int fan_write_cmd_watchdog(const char *cmd, int *rc); + + +/* + * Hotkey subdriver + */ + +#define TPACPI_HOTKEY_SYSFS_GROUP "hotkey" + +static int hotkey_orig_status; +static int hotkey_orig_mask; + +static struct mutex hotkey_mutex; + +static int hotkey_init(struct ibm_init_struct *iibm); +static void hotkey_exit(void); +static int hotkey_get(int *status, int *mask); +static int hotkey_set(int status, int mask); +static void hotkey_notify(struct ibm_struct *ibm, u32 event); +static int hotkey_read(char *p); +static int hotkey_write(char *buf); + + +/* + * LED subdriver + */ + +enum led_access_mode { + TPACPI_LED_NONE = 0, + TPACPI_LED_570, /* 570 */ + TPACPI_LED_OLD, /* 600e/x, 770e, 770x, A21e, A2xm/p, T20-22, X20-21 */ + TPACPI_LED_NEW, /* all others */ +}; + +enum { /* For TPACPI_LED_OLD */ + TPACPI_LED_EC_HLCL = 0x0c, /* EC reg to get led to power on */ + TPACPI_LED_EC_HLBL = 0x0d, /* EC reg to blink a lit led */ + TPACPI_LED_EC_HLMS = 0x0e, /* EC reg to select led to command */ +}; + +static enum led_access_mode led_supported; +static acpi_handle led_handle; + +static int led_init(struct ibm_init_struct *iibm); +static int led_read(char *p); +static int led_write(char *buf); + +/* + * Light (thinklight) subdriver + */ + +static acpi_handle lght_handle, ledb_handle; + +static int light_init(struct ibm_init_struct *iibm); +static int light_read(char *p); +static int light_write(char *buf); + + +/* + * Thermal subdriver + */ + +enum thermal_access_mode { + TPACPI_THERMAL_NONE = 0, /* No thermal support */ + TPACPI_THERMAL_ACPI_TMP07, /* Use ACPI TMP0-7 */ + TPACPI_THERMAL_ACPI_UPDT, /* Use ACPI TMP0-7 with UPDT */ + TPACPI_THERMAL_TPEC_8, /* Use ACPI EC regs, 8 sensors */ + TPACPI_THERMAL_TPEC_16, /* Use ACPI EC regs, 16 sensors */ +}; + +enum { /* TPACPI_THERMAL_TPEC_* */ + TP_EC_THERMAL_TMP0 = 0x78, /* ACPI EC regs TMP 0..7 */ + TP_EC_THERMAL_TMP8 = 0xC0, /* ACPI EC regs TMP 8..15 */ + TP_EC_THERMAL_TMP_NA = -128, /* ACPI EC sensor not available */ +}; + +#define TPACPI_MAX_THERMAL_SENSORS 16 /* Max thermal sensors supported */ +struct ibm_thermal_sensors_struct { + s32 temp[TPACPI_MAX_THERMAL_SENSORS]; +}; + +static enum thermal_access_mode thermal_read_mode; + +static int thermal_init(struct ibm_init_struct *iibm); +static int thermal_get_sensor(int idx, s32 *value); +static int thermal_get_sensors(struct ibm_thermal_sensors_struct *s); +static int thermal_read(char *p); + + +/* + * Video subdriver + */ + +enum video_access_mode { + TPACPI_VIDEO_NONE = 0, + TPACPI_VIDEO_570, /* 570 */ + TPACPI_VIDEO_770, /* 600e/x, 770e, 770x */ + TPACPI_VIDEO_NEW, /* all others */ +}; + +enum { /* video status flags, based on VIDEO_570 */ + TP_ACPI_VIDEO_S_LCD = 0x01, /* LCD output enabled */ + TP_ACPI_VIDEO_S_CRT = 0x02, /* CRT output enabled */ + TP_ACPI_VIDEO_S_DVI = 0x08, /* DVI output enabled */ +}; + +enum { /* TPACPI_VIDEO_570 constants */ + TP_ACPI_VIDEO_570_PHSCMD = 0x87, /* unknown magic constant :( */ + TP_ACPI_VIDEO_570_PHSMASK = 0x03, /* PHS bits that map to + * video_status_flags */ + TP_ACPI_VIDEO_570_PHS2CMD = 0x8b, /* unknown magic constant :( */ + TP_ACPI_VIDEO_570_PHS2SET = 0x80, /* unknown magic constant :( */ +}; + +static enum video_access_mode video_supported; +static int video_orig_autosw; +static acpi_handle vid_handle, vid2_handle; + +static int video_init(struct ibm_init_struct *iibm); +static void video_exit(void); +static int video_outputsw_get(void); +static int video_outputsw_set(int status); +static int video_autosw_get(void); +static int video_autosw_set(int enable); +static int video_outputsw_cycle(void); +static int video_expand_toggle(void); +static int video_read(char *p); +static int video_write(char *buf); + + +/* + * Volume subdriver + */ + +static int volume_offset = 0x30; + +static int volume_read(char *p); +static int volume_write(char *buf); + + +/* + * Wan subdriver + */ + +#define TPACPI_WAN_SYSFS_GROUP "wwan" + +enum { + /* ACPI GWAN/SWAN bits */ + TP_ACPI_WANCARD_HWPRESENT = 0x01, /* Wan hw available */ + TP_ACPI_WANCARD_RADIOSSW = 0x02, /* Wan radio enabled */ + TP_ACPI_WANCARD_UNK = 0x04, /* unknown function */ +}; + +static int wan_init(struct ibm_init_struct *iibm); +static int wan_get_radiosw(void); +static int wan_set_radiosw(int radio_on); +static int wan_read(char *p); +static int wan_write(char *buf); + + +#endif /* __THINKPAD_ACPI_H */ diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c index 2ce50f3..459f4b4fe 100644 --- a/drivers/mmc/at91_mci.c +++ b/drivers/mmc/at91_mci.c @@ -64,6 +64,7 @@ #include <linux/err.h> #include <linux/dma-mapping.h> #include <linux/clk.h> +#include <linux/atmel_pdc.h> #include <linux/mmc/host.h> #include <linux/mmc/protocol.h> @@ -75,7 +76,6 @@ #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> #include <asm/arch/at91_mci.h> -#include <asm/arch/at91_pdc.h> #define DRIVER_NAME "at91_mci" @@ -211,13 +211,13 @@ static void at91mci_pre_dma_read(struct at91mci_host *host) /* Check to see if this needs filling */ if (i == 0) { - if (at91_mci_read(host, AT91_PDC_RCR) != 0) { + if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { pr_debug("Transfer active in current\n"); continue; } } else { - if (at91_mci_read(host, AT91_PDC_RNCR) != 0) { + if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { pr_debug("Transfer active in next\n"); continue; } @@ -234,12 +234,12 @@ static void at91mci_pre_dma_read(struct at91mci_host *host) pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); if (i == 0) { - at91_mci_write(host, AT91_PDC_RPR, sg->dma_address); - at91_mci_write(host, AT91_PDC_RCR, sg->length / 4); + at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); } else { - at91_mci_write(host, AT91_PDC_RNPR, sg->dma_address); - at91_mci_write(host, AT91_PDC_RNCR, sg->length / 4); + at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); } } @@ -303,7 +303,7 @@ static void at91mci_post_dma_read(struct at91mci_host *host) at91mci_pre_dma_read(host); else { at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); } pr_debug("post dma read done\n"); @@ -320,7 +320,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host) pr_debug("Handling the transmit\n"); /* Disable the transfer */ - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); /* Now wait for cmd ready */ at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); @@ -431,15 +431,15 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS); - at91_mci_write(host, AT91_PDC_RPR, 0); - at91_mci_write(host, AT91_PDC_RCR, 0); - at91_mci_write(host, AT91_PDC_RNPR, 0); - at91_mci_write(host, AT91_PDC_RNCR, 0); - at91_mci_write(host, AT91_PDC_TPR, 0); - at91_mci_write(host, AT91_PDC_TCR, 0); - at91_mci_write(host, AT91_PDC_TNPR, 0); - at91_mci_write(host, AT91_PDC_TNCR, 0); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); + at91_mci_write(host, ATMEL_PDC_RPR, 0); + at91_mci_write(host, ATMEL_PDC_RCR, 0); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + at91_mci_write(host, ATMEL_PDC_TPR, 0); + at91_mci_write(host, ATMEL_PDC_TCR, 0); + at91_mci_write(host, ATMEL_PDC_TNPR, 0); + at91_mci_write(host, ATMEL_PDC_TNCR, 0); at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); @@ -452,7 +452,7 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ /* * Disable the PDC controller */ - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; @@ -481,8 +481,8 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ pr_debug("Transmitting %d bytes\n", host->total_length); - at91_mci_write(host, AT91_PDC_TPR, host->physical_address); - at91_mci_write(host, AT91_PDC_TCR, host->total_length / 4); + at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); ier = AT91_MCI_TXBUFE; } } @@ -497,9 +497,9 @@ static unsigned int at91_mci_send_command(struct at91mci_host *host, struct mmc_ if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTEN); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); else - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTEN); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); } return ier; } diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c index b060d4b..0de5c9e 100644 --- a/drivers/mmc/imxmmc.c +++ b/drivers/mmc/imxmmc.c @@ -569,10 +569,12 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) if(host->dma_dir == DMA_FROM_DEVICE) { imxmci_busy_wait_for_status(host, &stat, - STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE, + STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE | + STATUS_TIME_OUT_READ, 50, "imxmci_cpu_driven_data read"); while((stat & (STATUS_APPL_BUFF_FF | STATUS_DATA_TRANS_DONE)) && + !(stat & STATUS_TIME_OUT_READ) && (host->data_cnt < 512)) { udelay(20); /* required for clocks < 8MHz*/ @@ -602,6 +604,12 @@ static int imxmci_cpu_driven_data(struct imxmci_host *host, unsigned int *pstat) if(host->dma_size & 0x1ff) stat &= ~STATUS_CRC_READ_ERR; + if(stat & STATUS_TIME_OUT_READ) { + dev_dbg(mmc_dev(host->mmc), "imxmci_cpu_driven_data read timeout STATUS = 0x%x\n", + stat); + trans_done = -1; + } + } else { imxmci_busy_wait_for_status(host, &stat, STATUS_APPL_BUFF_FE, @@ -709,6 +717,9 @@ static void imxmci_tasklet_fnc(unsigned long data) */ stat |= host->status_reg; + if(test_bit(IMXMCI_PEND_CPU_DATA_b, &host->pending_events)) + stat &= ~STATUS_CRC_READ_ERR; + if(test_bit(IMXMCI_PEND_WAIT_RESP_b, &host->pending_events)) { imxmci_busy_wait_for_status(host, &stat, STATUS_END_CMD_RESP | STATUS_ERR_MASK, diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5046a16..4a73e8b 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -376,10 +376,11 @@ static inline void mmc_set_ios(struct mmc_host *host) { struct mmc_ios *ios = &host->ios; - pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n", + pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u " + "width %u timing %u\n", mmc_hostname(host), ios->clock, ios->bus_mode, ios->power_mode, ios->chip_select, ios->vdd, - ios->bus_width); + ios->bus_width, ios->timing); host->ops->set_ios(host, ios); } @@ -809,6 +810,7 @@ static void mmc_power_up(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); mmc_delay(1); @@ -828,6 +830,7 @@ static void mmc_power_off(struct mmc_host *host) host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_OFF; host->ios.bus_width = MMC_BUS_WIDTH_1; + host->ios.timing = MMC_TIMING_LEGACY; mmc_set_ios(host); } @@ -1112,46 +1115,50 @@ static void mmc_process_ext_csds(struct mmc_host *host) continue; } - /* Activate highspeed support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_HS_TIMING << 16) | - (1 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + if (host->caps & MMC_CAP_MMC_HIGHSPEED) { + /* Activate highspeed support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_HS_TIMING << 16) | + (1 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to mmc v4 " - "high-speed mode.\n", - mmc_hostname(card->host)); - continue; - } + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to mmc v4 " + "high-speed mode.\n", + mmc_hostname(card->host)); + continue; + } - mmc_card_set_highspeed(card); + mmc_card_set_highspeed(card); - /* Check for host support for wide-bus modes. */ - if (!(host->caps & MMC_CAP_4_BIT_DATA)) { - continue; + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); } - /* Activate 4-bit support. */ - cmd.opcode = MMC_SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (EXT_CSD_BUS_WIDTH << 16) | - (EXT_CSD_BUS_WIDTH_4 << 8) | - EXT_CSD_CMD_SET_NORMAL; - cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; + /* Check for host support for wide-bus modes. */ + if (host->caps & MMC_CAP_4_BIT_DATA) { + /* Activate 4-bit support. */ + cmd.opcode = MMC_SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (EXT_CSD_BUS_WIDTH << 16) | + (EXT_CSD_BUS_WIDTH_4 << 8) | + EXT_CSD_CMD_SET_NORMAL; + cmd.flags = MMC_RSP_R1B | MMC_CMD_AC; - err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); - if (err != MMC_ERR_NONE) { - printk("%s: failed to switch card to " - "mmc v4 4-bit bus mode.\n", - mmc_hostname(card->host)); - continue; - } + err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); + if (err != MMC_ERR_NONE) { + printk("%s: failed to switch card to " + "mmc v4 4-bit bus mode.\n", + mmc_hostname(card->host)); + continue; + } - host->ios.bus_width = MMC_BUS_WIDTH_4; + host->ios.bus_width = MMC_BUS_WIDTH_4; + mmc_set_ios(host); + } } kfree(ext_csd); @@ -1241,6 +1248,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) unsigned char *status; struct scatterlist sg; + if (!(host->caps & MMC_CAP_SD_HIGHSPEED)) + return; + status = kmalloc(64, GFP_KERNEL); if (!status) { printk(KERN_WARNING "%s: Unable to allocate buffer for " @@ -1332,6 +1342,9 @@ static void mmc_read_switch_caps(struct mmc_host *host) } mmc_card_set_highspeed(card); + + host->ios.timing = MMC_TIMING_SD_HS; + mmc_set_ios(host); } kfree(status); diff --git a/drivers/mmc/mmc_sysfs.c b/drivers/mmc/mmc_sysfs.c index d32698b..e0e82d8 100644 --- a/drivers/mmc/mmc_sysfs.c +++ b/drivers/mmc/mmc_sysfs.c @@ -86,31 +86,26 @@ mmc_bus_uevent(struct device *dev, char **envp, int num_envp, char *buf, { struct mmc_card *card = dev_to_mmc_card(dev); char ccc[13]; - int i = 0; - -#define add_env(fmt,val) \ - ({ \ - int len, ret = -ENOMEM; \ - if (i < num_envp) { \ - envp[i++] = buf; \ - len = snprintf(buf, buf_size, fmt, val) + 1; \ - buf_size -= len; \ - buf += len; \ - if (buf_size >= 0) \ - ret = 0; \ - } \ - ret; \ - }) + int retval = 0, i = 0, length = 0; + +#define add_env(fmt,val) do { \ + retval = add_uevent_var(envp, num_envp, &i, \ + buf, buf_size, &length, \ + fmt, val); \ + if (retval) \ + return retval; \ +} while (0); for (i = 0; i < 12; i++) ccc[i] = card->csd.cmdclass & (1 << i) ? '1' : '0'; ccc[12] = '\0'; - i = 0; add_env("MMC_CCC=%s", ccc); add_env("MMC_MANFID=%06x", card->cid.manfid); add_env("MMC_NAME=%s", mmc_card_name(card)); add_env("MMC_OEMID=%04x", card->cid.oemid); +#undef add_env + envp[i] = NULL; return 0; } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 7522f76..d749f08 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -606,7 +606,6 @@ static void sdhci_finish_command(struct sdhci_host *host) static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) { int div; - u8 ctrl; u16 clk; unsigned long timeout; @@ -615,13 +614,6 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); - if (clock > 25000000) - ctrl |= SDHCI_CTRL_HISPD; - else - ctrl &= ~SDHCI_CTRL_HISPD; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); - if (clock == 0) goto out; @@ -761,10 +753,17 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) sdhci_set_power(host, ios->vdd); ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; else ctrl &= ~SDHCI_CTRL_4BITBUS; + + if (ios->timing == MMC_TIMING_SD_HS) + ctrl |= SDHCI_CTRL_HISPD; + else + ctrl &= ~SDHCI_CTRL_HISPD; + writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); mmiowb(); @@ -994,7 +993,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask = readl(host->ioaddr + SDHCI_INT_STATUS); - if (!intmask) { + if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; goto out; } @@ -1080,6 +1079,13 @@ static int sdhci_suspend (struct pci_dev *pdev, pm_message_t state) pci_save_state(pdev); pci_enable_wake(pdev, pci_choose_state(pdev, state), 0); + + for (i = 0;i < chip->num_slots;i++) { + if (!chip->hosts[i]) + continue; + free_irq(chip->hosts[i]->irq, chip->hosts[i]); + } + pci_disable_device(pdev); pci_set_power_state(pdev, pci_choose_state(pdev, state)); @@ -1108,6 +1114,11 @@ static int sdhci_resume (struct pci_dev *pdev) continue; if (chip->hosts[i]->flags & SDHCI_USE_DMA) pci_set_master(pdev); + ret = request_irq(chip->hosts[i]->irq, sdhci_irq, + IRQF_SHARED, chip->hosts[i]->slot_descr, + chip->hosts[i]); + if (ret) + return ret; sdhci_init(chip->hosts[i]); mmiowb(); ret = mmc_resume_host(chip->hosts[i]->mmc); @@ -1274,6 +1285,9 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) mmc->f_max = host->max_clk; mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK; + if (caps & SDHCI_CAN_DO_HISPD) + mmc->caps |= MMC_CAP_SD_HIGHSPEED; + mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; @@ -1282,13 +1296,6 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot) if (caps & SDHCI_CAN_VDD_180) mmc->ocr_avail |= MMC_VDD_17_18|MMC_VDD_18_19; - if ((host->max_clk > 25000000) && !(caps & SDHCI_CAN_DO_HISPD)) { - printk(KERN_ERR "%s: Controller reports > 25 MHz base clock," - " but no high speed support.\n", - host->slot_descr); - mmc->f_max = 25000000; - } - if (mmc->ocr_avail == 0) { printk(KERN_ERR "%s: Hardware doesn't report any " "support voltages.\n", host->slot_descr); diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 26f75c2..c1b47db 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,8 +1,6 @@ # $Id: Kconfig,v 1.11 2005/11/07 11:14:19 gleixner Exp $ -menu "Memory Technology Devices (MTD)" - -config MTD +menuconfig MTD tristate "Memory Technology Device (MTD) support" help Memory Technology Devices are flash, RAM and similar chips, often @@ -13,9 +11,10 @@ config MTD them. It will also allow you to select individual drivers for particular hardware and users of MTD devices. If unsure, say N. +if MTD + config MTD_DEBUG bool "Debugging" - depends on MTD help This turns on low-level debugging for the entire MTD sub-system. Normally, you should say 'N'. @@ -29,7 +28,6 @@ config MTD_DEBUG_VERBOSE config MTD_CONCAT tristate "MTD concatenating support" - depends on MTD help Support for concatenating several MTD devices into a single (virtual) one. This allows you to have -for example- a JFFS(2) @@ -38,7 +36,6 @@ config MTD_CONCAT config MTD_PARTITIONS bool "MTD partitioning support" - depends on MTD help If you have a device which needs to divide its flash chip(s) up into multiple 'partitions', each of which appears to the user as @@ -153,11 +150,9 @@ config MTD_AFS_PARTS 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. comment "User Modules And Translation Layers" - depends on MTD config MTD_CHAR tristate "Direct char device access to MTD devices" - depends on MTD help This provides a character device for each MTD device present in the system, allowing the user to read and write directly to the @@ -166,12 +161,12 @@ config MTD_CHAR config MTD_BLKDEVS tristate "Common interface to block layer for MTD 'translation layers'" - depends on MTD && BLOCK + depends on BLOCK default n config MTD_BLOCK tristate "Caching block device access to MTD devices" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- Although most flash chips have an erase size too large to be useful @@ -194,7 +189,7 @@ config MTD_BLOCK config MTD_BLOCK_RO tristate "Readonly block device access to MTD devices" - depends on MTD_BLOCK!=y && MTD && BLOCK + depends on MTD_BLOCK!=y && BLOCK select MTD_BLKDEVS help This allows you to mount read-only file systems (such as cramfs) @@ -206,7 +201,7 @@ config MTD_BLOCK_RO config FTL tristate "FTL (Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the original Flash Translation Layer which @@ -223,7 +218,7 @@ config FTL config NFTL tristate "NFTL (NAND Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the NAND Flash Translation Layer which is @@ -247,7 +242,7 @@ config NFTL_RW config INFTL tristate "INFTL (Inverse NAND Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the Inverse NAND Flash Translation @@ -265,7 +260,7 @@ config INFTL config RFD_FTL tristate "Resident Flash Disk (Flash Translation Layer) support" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS ---help--- This provides support for the flash translation layer known @@ -276,7 +271,7 @@ config RFD_FTL config SSFDC tristate "NAND SSFDC (SmartMedia) read only translation layer" - depends on MTD && BLOCK + depends on BLOCK select MTD_BLKDEVS help This enables read only access to SmartMedia formatted NAND @@ -292,5 +287,6 @@ source "drivers/mtd/nand/Kconfig" source "drivers/mtd/onenand/Kconfig" -endmenu +source "drivers/mtd/ubi/Kconfig" +endif # MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index c130e62..9205540 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -28,3 +28,5 @@ nftl-objs := nftlcore.o nftlmount.o inftl-objs := inftlcore.o inftlmount.o obj-y += chips/ maps/ devices/ nand/ onenand/ + +obj-$(CONFIG_MTD_UBI) += ubi/ diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index 72e6d73..d28e0fc 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -6,7 +6,6 @@ menu "RAM/ROM/Flash chip drivers" config MTD_CFI tristate "Detect flash chips by Common Flash Interface (CFI) probe" - depends on MTD select MTD_GEN_PROBE help The Common Flash Interface specification was developed by Intel, @@ -18,7 +17,6 @@ config MTD_CFI config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" - depends on MTD select MTD_GEN_PROBE help This option enables JEDEC-style probing of flash chips which are not @@ -213,21 +211,18 @@ config MTD_CFI_UTIL config MTD_RAM tristate "Support for RAM chips in bus mapping" - depends on MTD help This option enables basic support for RAM chips accessed through a bus mapping driver. config MTD_ROM tristate "Support for ROM chips in bus mapping" - depends on MTD help This option enables basic support for ROM chips accessed through a bus mapping driver. config MTD_ABSENT tristate "Support for absent chips in bus mapping" - depends on MTD help This option enables support for a dummy probing driver used to allocated placeholder MTD devices on systems that have socketed @@ -237,7 +232,6 @@ config MTD_ABSENT with this driver will return -ENODEV upon access. config MTD_OBSOLETE_CHIPS - depends on MTD bool "Older (theoretically obsoleted now) drivers for non-CFI chips" help This option does not enable any code directly, but will allow you to @@ -250,7 +244,7 @@ config MTD_OBSOLETE_CHIPS config MTD_AMDSTD tristate "AMD compatible flash chip support (non-CFI)" - depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN + depends on MTD_OBSOLETE_CHIPS && BROKEN help This option enables support for flash chips using AMD-compatible commands, including some which are not CFI-compatible and hence @@ -260,7 +254,7 @@ config MTD_AMDSTD config MTD_SHARP tristate "pre-CFI Sharp chip support" - depends on MTD && MTD_OBSOLETE_CHIPS + depends on MTD_OBSOLETE_CHIPS help This option enables support for flash chips using Sharp-compatible commands, including some which are not CFI-compatible and hence @@ -268,7 +262,7 @@ config MTD_SHARP config MTD_JEDEC tristate "JEDEC device support" - depends on MTD && MTD_OBSOLETE_CHIPS && BROKEN + depends on MTD_OBSOLETE_CHIPS && BROKEN help Enable older JEDEC flash interface devices for self programming flash. It is commonly used in older AMD chips. It is diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f69184a..2f19fa7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -15,6 +15,8 @@ * - optimized write buffer method * 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com> * - reworked lock/unlock/erase support for var size flash + * 21/03/2007 Rodolfo Giometti <giometti@linux.it> + * - auto unlock sectors on resume for auto locking flash on power up */ #include <linux/module.h> @@ -30,6 +32,7 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/reboot.h> +#include <linux/bitmap.h> #include <linux/mtd/xip.h> #include <linux/mtd/map.h> #include <linux/mtd/mtd.h> @@ -220,6 +223,15 @@ static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) } } +/* + * Some chips power-up with all sectors locked by default. + */ +static void fixup_use_powerup_lock(struct mtd_info *mtd, void *param) +{ + printk(KERN_INFO "Using auto-unlock on power-up/resume\n" ); + mtd->flags |= MTD_STUPID_LOCK; +} + static struct cfi_fixup cfi_fixup_table[] = { #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, @@ -232,6 +244,7 @@ static struct cfi_fixup cfi_fixup_table[] = { #endif { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, + { MANUFACTURER_INTEL, 0x891c, fixup_use_powerup_lock, NULL, }, { 0, 0, NULL, NULL } }; @@ -397,9 +410,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; - cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; - cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp; + if (cfi->cfiq->WordWriteTimeoutTyp) + cfi->chips[i].word_write_time = + 1<<cfi->cfiq->WordWriteTimeoutTyp; + else + cfi->chips[i].word_write_time = 50000; + + if (cfi->cfiq->BufWriteTimeoutTyp) + cfi->chips[i].buffer_write_time = + 1<<cfi->cfiq->BufWriteTimeoutTyp; + /* No default; if it isn't specified, we won't use it */ + + if (cfi->cfiq->BlockEraseTimeoutTyp) + cfi->chips[i].erase_time = + 1000<<cfi->cfiq->BlockEraseTimeoutTyp; + else + cfi->chips[i].erase_time = 2000000; + cfi->chips[i].ref_point_counter = 0; init_waitqueue_head(&(cfi->chips[i].wq)); } @@ -446,6 +473,7 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].lockmap = kmalloc(ernum / 8 + 1, GFP_KERNEL); } offset += (ersize * ernum); } @@ -546,13 +574,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, struct cfi_intelext_programming_regioninfo *prinfo; prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; mtd->writesize = cfi->interleave << prinfo->ProgRegShift; - MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; - MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; mtd->flags &= ~MTD_BIT_WRITEABLE; printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", map->name, mtd->writesize, - MTD_PROGREGION_CTRLMODE_VALID(mtd), - MTD_PROGREGION_CTRLMODE_INVALID(mtd)); + cfi->interleave * prinfo->ControlValid, + cfi->interleave * prinfo->ControlInvalid); } /* @@ -1813,8 +1839,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) } } -#ifdef DEBUG_LOCK_BITS -static int __xipram do_printlockstatus_oneblock(struct map_info *map, +static int __xipram do_getlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) @@ -1828,8 +1853,17 @@ static int __xipram do_printlockstatus_oneblock(struct map_info *map, chip->state = FL_JEDEC_QUERY; status = cfi_read_query(map, adr+(2*ofs_factor)); xip_enable(map, chip, 0); + return status; +} + +#ifdef DEBUG_LOCK_BITS +static int __xipram do_printlockstatus_oneblock(struct map_info *map, + struct flchip *chip, + unsigned long adr, + int len, void *thunk) +{ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, status); + adr, do_getlockstatus_oneblock(map, chip, adr, len, thunk)); return 0; } #endif @@ -2204,14 +2238,45 @@ static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, #endif +static void cfi_intelext_save_locks(struct mtd_info *mtd) +{ + struct mtd_erase_region_info *region; + int block, status, i; + unsigned long adr; + size_t len; + + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (!region->lockmap) + continue; + + for (block = 0; block < region->numblocks; block++){ + len = region->erasesize; + adr = region->offset + block * len; + + status = cfi_varsize_frob(mtd, + do_getlockstatus_oneblock, adr, len, 0); + if (status) + set_bit(block, region->lockmap); + else + clear_bit(block, region->lockmap); + } + } +} + static int cfi_intelext_suspend(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; int i; struct flchip *chip; int ret = 0; + if ((mtd->flags & MTD_STUPID_LOCK) + && extp && (extp->FeatureSupport & (1 << 5))) + cfi_intelext_save_locks(mtd); + for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; @@ -2273,10 +2338,33 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) return ret; } +static void cfi_intelext_restore_locks(struct mtd_info *mtd) +{ + struct mtd_erase_region_info *region; + int block, i; + unsigned long adr; + size_t len; + + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (!region->lockmap) + continue; + + for (block = 0; block < region->numblocks; block++) { + len = region->erasesize; + adr = region->offset + block * len; + + if (!test_bit(block, region->lockmap)) + cfi_intelext_unlock(mtd, adr, len); + } + } +} + static void cfi_intelext_resume(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; int i; struct flchip *chip; @@ -2295,6 +2383,10 @@ static void cfi_intelext_resume(struct mtd_info *mtd) spin_unlock(chip->mutex); } + + if ((mtd->flags & MTD_STUPID_LOCK) + && extp && (extp->FeatureSupport & (1 << 5))) + cfi_intelext_restore_locks(mtd); } static int cfi_intelext_reset(struct mtd_info *mtd) @@ -2335,12 +2427,19 @@ static void cfi_intelext_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + struct mtd_erase_region_info *region; + int i; cfi_intelext_reset(mtd); unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi->chips[0].priv); kfree(cfi); + for (i = 0; i < mtd->numeraseregions; i++) { + region = &mtd->eraseregions[i]; + if (region->lockmap) + kfree(region->lockmap); + } kfree(mtd->eraseregions); } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index e3acd39..1f64458 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -359,6 +359,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp; + cfi->chips[i].ref_point_counter = 0; + init_waitqueue_head(&(cfi->chips[i].wq)); } map->fldrv = &cfi_amdstd_chipdrv; diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d56849f..b344ff8 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -158,6 +158,8 @@ struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) cfi->chips[i].word_write_time = 128; cfi->chips[i].buffer_write_time = 128; cfi->chips[i].erase_time = 1024; + cfi->chips[i].ref_point_counter = 0; + init_waitqueue_head(&(cfi->chips[i].wq)); } return cfi_staa_setup(map); @@ -662,7 +664,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, * a small buffer for this. * XXX: If the buffer size is not a multiple of 2, this will break */ -#define ECCBUF_SIZE (mtd->eccsize) +#define ECCBUF_SIZE (mtd->writesize) #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) static int diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index 77303ce..ab44f2b 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -65,11 +65,12 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, return ret; } + chip->oldstate = chip->state; chip->state = xxlt->state; map_write(map, CMD(xxlt->val), adr); /* Done and happy. */ - chip->state = FL_READY; + chip->state = chip->oldstate; put_chip(map, chip, adr); spin_unlock(chip->mutex); return 0; diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 440f685..690c942 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -6,7 +6,7 @@ menu "Self-contained MTD device drivers" config MTD_PMC551 tristate "Ramix PMC551 PCI Mezzanine RAM card support" - depends on MTD && PCI + depends on PCI ---help--- This provides a MTD device driver for the Ramix PMC551 RAM PCI card from Ramix Inc. <http://www.ramix.com/products/memory/pmc551.html>. @@ -40,7 +40,7 @@ config MTD_PMC551_DEBUG config MTD_MS02NV tristate "DEC MS02-NV NVRAM module support" - depends on MTD && MACH_DECSTATION + depends on MACH_DECSTATION help This is an MTD driver for the DEC's MS02-NV (54-20948-01) battery backed-up NVRAM module. The module was originally meant as an NFS @@ -54,15 +54,23 @@ config MTD_MS02NV config MTD_DATAFLASH tristate "Support for AT45xxx DataFlash" - depends on MTD && SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER && EXPERIMENTAL help This enables access to AT45xxx DataFlash chips, using SPI. Sometimes DataFlash chips are packaged inside MMC-format cards; at this writing, the MMC stack won't handle those. +config MTD_DATAFLASH26 + tristate "AT91RM9200 DataFlash AT26xxx" + depends on MTD && ARCH_AT91RM9200 && AT91_SPI + help + This enables access to the DataFlash chip (AT26xxx) on an + AT91RM9200-based board. + If you have such a board and such a DataFlash, say 'Y'. + config MTD_M25P80 tristate "Support for M25 SPI Flash" - depends on MTD && SPI_MASTER && EXPERIMENTAL + depends on SPI_MASTER && EXPERIMENTAL help This enables access to ST M25P80 and similar SPI flash chips, used for program and data storage. Set up your spi devices @@ -70,7 +78,6 @@ config MTD_M25P80 config MTD_SLRAM tristate "Uncached system RAM" - depends on MTD help If your CPU cannot cache all of the physical memory in your machine, you can still use it for storage or swap by using this driver to @@ -78,7 +85,6 @@ config MTD_SLRAM config MTD_PHRAM tristate "Physical system RAM" - depends on MTD help This is a re-implementation of the slram driver above. @@ -88,7 +94,7 @@ config MTD_PHRAM config MTD_LART tristate "28F160xx flash driver for LART" - depends on SA1100_LART && MTD + depends on SA1100_LART help This enables the flash driver for LART. Please note that you do not need any mapping/chip driver for LART. This one does it all @@ -96,7 +102,6 @@ config MTD_LART config MTD_MTDRAM tristate "Test driver using RAM" - depends on MTD help This enables a test MTD device driver which uses vmalloc() to provide storage. You probably want to say 'N' unless you're @@ -136,7 +141,7 @@ config MTDRAM_ABS_POS config MTD_BLOCK2MTD tristate "MTD using block device" - depends on MTD && BLOCK + depends on BLOCK help This driver allows a block device to appear as an MTD. It would generally be used in the following cases: @@ -150,7 +155,6 @@ comment "Disk-On-Chip Device Drivers" config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium (DEPRECATED)" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -173,7 +177,6 @@ config MTD_DOC2000 config MTD_DOC2001 tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- @@ -195,7 +198,6 @@ config MTD_DOC2001 config MTD_DOC2001PLUS tristate "M-Systems Disk-On-Chip Millennium Plus" - depends on MTD select MTD_DOCPROBE select MTD_NAND_IDS ---help--- diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0f788d5..8ab568b 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,4 +16,5 @@ obj-$(CONFIG_MTD_MTDRAM) += mtdram.o obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o +obj-$(CONFIG_MTD_DATAFLASH26) += at91_dataflash26.o obj-$(CONFIG_MTD_M25P80) += m25p80.o diff --git a/drivers/mtd/devices/at91_dataflash26.c b/drivers/mtd/devices/at91_dataflash26.c new file mode 100644 index 0000000..64ce37f --- /dev/null +++ b/drivers/mtd/devices/at91_dataflash26.c @@ -0,0 +1,485 @@ +/* + * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) + * This is a largely modified version of at91_dataflash.c that + * supports AT26xxx dataflash chips. The original driver supports + * AT45xxx chips. + * + * Note: This driver was only tested with an AT26F004. It should be + * easy to make it work with other AT26xxx dataflash devices, though. + * + * Copyright (C) 2007 Hans J. Koch <hjk@linutronix.de> + * original Copyright (C) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> + +#include <asm/arch/at91_spi.h> + +#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ + +#define MANUFACTURER_ID_ATMEL 0x1F + +/* command codes */ + +#define AT26_OP_READ_STATUS 0x05 +#define AT26_OP_READ_DEV_ID 0x9F +#define AT26_OP_ERASE_PAGE_4K 0x20 +#define AT26_OP_READ_ARRAY_FAST 0x0B +#define AT26_OP_SEQUENTIAL_WRITE 0xAF +#define AT26_OP_WRITE_ENABLE 0x06 +#define AT26_OP_WRITE_DISABLE 0x04 +#define AT26_OP_SECTOR_PROTECT 0x36 +#define AT26_OP_SECTOR_UNPROTECT 0x39 + +/* status register bits */ + +#define AT26_STATUS_BUSY 0x01 +#define AT26_STATUS_WRITE_ENABLE 0x02 + +struct dataflash_local +{ + int spi; /* SPI chip-select number */ + unsigned int page_size; /* number of bytes per page */ +}; + + +/* Detected DataFlash devices */ +static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; +static int nr_devices = 0; + +/* Allocate a single SPI transfer descriptor. We're assuming that if multiple + SPI transfers occur at the same time, spi_access_bus() will serialize them. + If this is not valid, then either (i) each dataflash 'priv' structure + needs it's own transfer descriptor, (ii) we lock this one, or (iii) use + another mechanism. */ +static struct spi_transfer_list* spi_transfer_desc; + +/* + * Perform a SPI transfer to access the DataFlash device. + */ +static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, + char* txnext, int txnext_len, char* rxnext, int rxnext_len) +{ + struct spi_transfer_list* list = spi_transfer_desc; + + list->tx[0] = tx; list->txlen[0] = tx_len; + list->rx[0] = rx; list->rxlen[0] = rx_len; + + list->tx[1] = txnext; list->txlen[1] = txnext_len; + list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; + + list->nr_transfers = nr; + /* Note: spi_transfer() always returns 0, there are no error checks */ + return spi_transfer(list); +} + +/* + * Return the status of the DataFlash device. + */ +static unsigned char at91_dataflash26_status(void) +{ + unsigned char command[2]; + + command[0] = AT26_OP_READ_STATUS; + command[1] = 0; + + do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); + + return command[1]; +} + +/* + * Poll the DataFlash device until it is READY. + */ +static unsigned char at91_dataflash26_waitready(void) +{ + unsigned char status; + + while (1) { + status = at91_dataflash26_status(); + if (!(status & AT26_STATUS_BUSY)) + return status; + } +} + +/* + * Enable/disable write access + */ + static void at91_dataflash26_write_enable(int enable) +{ + unsigned char cmd[2]; + + DEBUG(MTD_DEBUG_LEVEL3, "write_enable: enable=%i\n", enable); + + if (enable) + cmd[0] = AT26_OP_WRITE_ENABLE; + else + cmd[0] = AT26_OP_WRITE_DISABLE; + cmd[1] = 0; + + do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); +} + +/* + * Protect/unprotect sector + */ + static void at91_dataflash26_sector_protect(loff_t addr, int protect) +{ + unsigned char cmd[4]; + + DEBUG(MTD_DEBUG_LEVEL3, "sector_protect: addr=0x%06x prot=%d\n", + addr, protect); + + if (protect) + cmd[0] = AT26_OP_SECTOR_PROTECT; + else + cmd[0] = AT26_OP_SECTOR_UNPROTECT; + cmd[1] = (addr & 0x00FF0000) >> 16; + cmd[2] = (addr & 0x0000FF00) >> 8; + cmd[3] = (addr & 0x000000FF); + + do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); +} + +/* + * Erase blocks of flash. + */ +static int at91_dataflash26_erase(struct mtd_info *mtd, + struct erase_info *instr) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned char cmd[4]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_erase: addr=0x%06x len=%i\n", + instr->addr, instr->len); + + /* Sanity checks */ + if (priv->page_size != 4096) + return -EINVAL; /* Can't handle other sizes at the moment */ + + if ( ((instr->len % mtd->erasesize) != 0) + || ((instr->len % priv->page_size) != 0) + || ((instr->addr % priv->page_size) != 0) + || ((instr->addr + instr->len) > mtd->size)) + return -EINVAL; + + spi_access_bus(priv->spi); + + while (instr->len > 0) { + at91_dataflash26_write_enable(1); + at91_dataflash26_sector_protect(instr->addr, 0); + at91_dataflash26_write_enable(1); + cmd[0] = AT26_OP_ERASE_PAGE_4K; + cmd[1] = (instr->addr & 0x00FF0000) >> 16; + cmd[2] = (instr->addr & 0x0000FF00) >> 8; + cmd[3] = (instr->addr & 0x000000FF); + + DEBUG(MTD_DEBUG_LEVEL3, "ERASE: (0x%02x) 0x%02x 0x%02x" + "0x%02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + do_spi_transfer(1, cmd, 4, cmd, 4, NULL, 0, NULL, 0); + at91_dataflash26_waitready(); + + instr->addr += priv->page_size; /* next page */ + instr->len -= priv->page_size; + } + + at91_dataflash26_write_enable(0); + spi_release_bus(priv->spi); + + /* Inform MTD subsystem that erase is complete */ + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +/* + * Read from the DataFlash device. + * from : Start offset in flash device + * len : Number of bytes to read + * retlen : Number of bytes actually read + * buf : Buffer that will receive data + */ +static int at91_dataflash26_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned char cmd[5]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_read: %lli .. %lli\n", + from, from+len); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (from + len > mtd->size) + return -EINVAL; + + cmd[0] = AT26_OP_READ_ARRAY_FAST; + cmd[1] = (from & 0x00FF0000) >> 16; + cmd[2] = (from & 0x0000FF00) >> 8; + cmd[3] = (from & 0x000000FF); + /* cmd[4] is a "Don't care" byte */ + + DEBUG(MTD_DEBUG_LEVEL3, "READ: (0x%02x) 0x%02x 0x%02x 0x%02x\n", + cmd[0], cmd[1], cmd[2], cmd[3]); + + spi_access_bus(priv->spi); + do_spi_transfer(2, cmd, 5, cmd, 5, buf, len, buf, len); + spi_release_bus(priv->spi); + + *retlen = len; + return 0; +} + +/* + * Write to the DataFlash device. + * to : Start offset in flash device + * len : Number of bytes to write + * retlen : Number of bytes actually written + * buf : Buffer containing the data + */ +static int at91_dataflash26_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned int addr, buf_index = 0; + int ret = -EIO, sector, last_sector; + unsigned char status, cmd[5]; + + DEBUG(MTD_DEBUG_LEVEL1, "dataflash_write: %lli .. %lli\n", to, to+len); + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (to + len > mtd->size) + return -EINVAL; + + spi_access_bus(priv->spi); + + addr = to; + last_sector = -1; + + while (buf_index < len) { + sector = addr / priv->page_size; + /* Write first byte if a new sector begins */ + if (sector != last_sector) { + at91_dataflash26_write_enable(1); + at91_dataflash26_sector_protect(addr, 0); + at91_dataflash26_write_enable(1); + + /* Program first byte of a new sector */ + cmd[0] = AT26_OP_SEQUENTIAL_WRITE; + cmd[1] = (addr & 0x00FF0000) >> 16; + cmd[2] = (addr & 0x0000FF00) >> 8; + cmd[3] = (addr & 0x000000FF); + cmd[4] = buf[buf_index++]; + do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); + status = at91_dataflash26_waitready(); + addr++; + /* On write errors, the chip resets the write enable + flag. This also happens after the last byte of a + sector is successfully programmed. */ + if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) + && ((addr % priv->page_size) != 0) ) { + DEBUG(MTD_DEBUG_LEVEL1, + "write error1: addr=0x%06x, " + "status=0x%02x\n", addr, status); + goto write_err; + } + (*retlen)++; + last_sector = sector; + } + + /* Write subsequent bytes in the same sector */ + cmd[0] = AT26_OP_SEQUENTIAL_WRITE; + cmd[1] = buf[buf_index++]; + do_spi_transfer(1, cmd, 2, cmd, 2, NULL, 0, NULL, 0); + status = at91_dataflash26_waitready(); + addr++; + + if ( ( !(status & AT26_STATUS_WRITE_ENABLE)) + && ((addr % priv->page_size) != 0) ) { + DEBUG(MTD_DEBUG_LEVEL1, "write error2: addr=0x%06x, " + "status=0x%02x\n", addr, status); + goto write_err; + } + + (*retlen)++; + } + + ret = 0; + at91_dataflash26_write_enable(0); +write_err: + spi_release_bus(priv->spi); + return ret; +} + +/* + * Initialize and register DataFlash device with MTD subsystem. + */ +static int __init add_dataflash(int channel, char *name, int nr_pages, + int pagesize) +{ + struct mtd_info *device; + struct dataflash_local *priv; + + if (nr_devices >= DATAFLASH_MAX_DEVICES) { + printk(KERN_ERR "at91_dataflash26: Too many devices " + "detected\n"); + return 0; + } + + device = kzalloc(sizeof(struct mtd_info) + strlen(name) + 8, + GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->name = (char *)&device[1]; + sprintf(device->name, "%s.spi%d", name, channel); + device->size = nr_pages * pagesize; + device->erasesize = pagesize; + device->owner = THIS_MODULE; + device->type = MTD_DATAFLASH; + device->flags = MTD_CAP_NORFLASH; + device->erase = at91_dataflash26_erase; + device->read = at91_dataflash26_read; + device->write = at91_dataflash26_write; + + priv = (struct dataflash_local *)kzalloc(sizeof(struct dataflash_local), + GFP_KERNEL); + if (!priv) { + kfree(device); + return -ENOMEM; + } + + priv->spi = channel; + priv->page_size = pagesize; + device->priv = priv; + + mtd_devices[nr_devices] = device; + nr_devices++; + printk(KERN_INFO "at91_dataflash26: %s detected [spi%i] (%i bytes)\n", + name, channel, device->size); + + return add_mtd_device(device); +} + +/* + * Detect and initialize DataFlash device connected to specified SPI channel. + * + */ + +struct dataflash26_types { + unsigned char id0; + unsigned char id1; + char *name; + int pagesize; + int nr_pages; +}; + +struct dataflash26_types df26_types[] = { + { + .id0 = 0x04, + .id1 = 0x00, + .name = "AT26F004", + .pagesize = 4096, + .nr_pages = 128, + }, + { + .id0 = 0x45, + .id1 = 0x01, + .name = "AT26DF081A", /* Not tested ! */ + .pagesize = 4096, + .nr_pages = 256, + }, +}; + +static int __init at91_dataflash26_detect(int channel) +{ + unsigned char status, cmd[5]; + int i; + + spi_access_bus(channel); + status = at91_dataflash26_status(); + + if (status == 0 || status == 0xff) { + printk(KERN_ERR "at91_dataflash26_detect: status error %d\n", + status); + spi_release_bus(channel); + return -ENODEV; + } + + cmd[0] = AT26_OP_READ_DEV_ID; + do_spi_transfer(1, cmd, 5, cmd, 5, NULL, 0, NULL, 0); + spi_release_bus(channel); + + if (cmd[1] != MANUFACTURER_ID_ATMEL) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(df26_types); i++) { + if ( cmd[2] == df26_types[i].id0 + && cmd[3] == df26_types[i].id1) + return add_dataflash(channel, + df26_types[i].name, + df26_types[i].nr_pages, + df26_types[i].pagesize); + } + + printk(KERN_ERR "at91_dataflash26_detect: Unsupported device " + "(0x%02x/0x%02x)\n", cmd[2], cmd[3]); + return -ENODEV; +} + +static int __init at91_dataflash26_init(void) +{ + spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), + GFP_KERNEL); + if (!spi_transfer_desc) + return -ENOMEM; + + /* DataFlash (SPI chip select 0) */ + at91_dataflash26_detect(0); + +#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD + /* DataFlash card (SPI chip select 3) */ + at91_dataflash26_detect(3); +#endif + return 0; +} + +static void __exit at91_dataflash26_exit(void) +{ + int i; + + for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { + if (mtd_devices[i]) { + del_mtd_device(mtd_devices[i]); + kfree(mtd_devices[i]->priv); + kfree(mtd_devices[i]); + } + } + nr_devices = 0; + kfree(spi_transfer_desc); +} + +module_init(at91_dataflash26_init); +module_exit(at91_dataflash26_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Hans J. Koch"); +MODULE_DESCRIPTION("DataFlash AT26xxx driver for Atmel AT91RM9200"); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index f9f2ce7..ce47544 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -40,56 +40,9 @@ struct block2mtd_dev { static LIST_HEAD(blkmtd_device_list); -#define PAGE_READAHEAD 64 -static void cache_readahead(struct address_space *mapping, int index) +static struct page* page_read(struct address_space *mapping, int index) { filler_t *filler = (filler_t*)mapping->a_ops->readpage; - int i, pagei; - unsigned ret = 0; - unsigned long end_index; - struct page *page; - LIST_HEAD(page_pool); - struct inode *inode = mapping->host; - loff_t isize = i_size_read(inode); - - if (!isize) { - INFO("iSize=0 in cache_readahead\n"); - return; - } - - end_index = ((isize - 1) >> PAGE_CACHE_SHIFT); - - read_lock_irq(&mapping->tree_lock); - for (i = 0; i < PAGE_READAHEAD; i++) { - pagei = index + i; - if (pagei > end_index) { - INFO("Overrun end of disk in cache readahead\n"); - break; - } - page = radix_tree_lookup(&mapping->page_tree, pagei); - if (page && (!i)) - break; - if (page) - continue; - read_unlock_irq(&mapping->tree_lock); - page = page_cache_alloc_cold(mapping); - read_lock_irq(&mapping->tree_lock); - if (!page) - break; - page->index = pagei; - list_add(&page->lru, &page_pool); - ret++; - } - read_unlock_irq(&mapping->tree_lock); - if (ret) - read_cache_pages(mapping, &page_pool, filler, NULL); -} - - -static struct page* page_readahead(struct address_space *mapping, int index) -{ - filler_t *filler = (filler_t*)mapping->a_ops->readpage; - cache_readahead(mapping, index); return read_cache_page(mapping, index, filler, NULL); } @@ -105,14 +58,14 @@ static int _block2mtd_erase(struct block2mtd_dev *dev, loff_t to, size_t len) u_long *max; while (pages) { - page = page_readahead(mapping, index); + page = page_read(mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) return PTR_ERR(page); - max = (u_long*)page_address(page) + PAGE_SIZE; - for (p=(u_long*)page_address(page); p<max; p++) + max = page_address(page) + PAGE_SIZE; + for (p=page_address(page); p<max; p++) if (*p != -1UL) { lock_page(page); memset(page_address(page), 0xff, PAGE_SIZE); @@ -174,8 +127,7 @@ static int block2mtd_read(struct mtd_info *mtd, loff_t from, size_t len, cpylen = len; // this page len = len - cpylen; - // Get page - page = page_readahead(dev->blkdev->bd_inode->i_mapping, index); + page = page_read(dev->blkdev->bd_inode->i_mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) @@ -213,8 +165,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf, cpylen = len; // this page len = len - cpylen; - // Get page - page = page_readahead(mapping, index); + page = page_read(mapping, index); if (!page) return -ENOMEM; if (IS_ERR(page)) @@ -308,9 +259,9 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* We might not have rootfs mounted at this point. Try to resolve the device name by other means. */ - dev_t dev = name_to_dev_t(devname); - if (dev != 0) { - bdev = open_by_devnum(dev, FMODE_WRITE | FMODE_READ); + dev_t devt = name_to_dev_t(devname); + if (devt) { + bdev = open_by_devnum(devt, FMODE_WRITE | FMODE_READ); } } #endif diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 603a795..8a0c4de 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; mtd->writesize = 512; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index fe71a12..6f368ae 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; /* FIXME: erase size is not always 8KiB */ diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index ba4db68..88ba82d 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f457315..d990d81 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -6,7 +6,6 @@ menu "Mapping drivers for chip access" config MTD_COMPLEX_MAPPINGS bool "Support non-linear mappings of flash chips" - depends on MTD help This causes the chip drivers to allow for complicated paged mappings of flash chips. @@ -69,6 +68,39 @@ config MTD_PHYSMAP_OF physically into the CPU's memory. The mapping description here is taken from OF device tree. +config MTD_PMC_MSP_EVM + tristate "CFI Flash device mapped on PMC-Sierra MSP" + depends on PMC_MSP && MTD_CFI + select MTD_PARTITIONS + help + This provides a 'mapping' driver which support the way + in which user-programmable flash chips are connected on the + PMC-Sierra MSP eval/demo boards + +choice + prompt "Maximum mappable memory avialable for flash IO" + depends on MTD_PMC_MSP_EVM + default MSP_FLASH_MAP_LIMIT_32M + +config MSP_FLASH_MAP_LIMIT_32M + bool "32M" + +endchoice + +config MSP_FLASH_MAP_LIMIT + hex + default "0x02000000" + depends on MSP_FLASH_MAP_LIMIT_32M + +config MTD_PMC_MSP_RAMROOT + tristate "Embedded RAM block device for root on PMC-Sierra MSP" + depends on PMC_MSP_EMBEDDED_ROOTFS && \ + (MTD_BLOCK || MTD_BLOCK_RO) && \ + MTD_RAM + help + This provides support for the embedded root file system + on PMC MSP devices. This memory is mapped as a MTD block device. + config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on SPARC && MTD_CFI @@ -204,7 +236,7 @@ config MTD_ESB2ROM config MTD_CK804XROM tristate "BIOS flash chip on Nvidia CK804" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on nvidia motherboards as an MTD device - with this you can reprogram your BIOS. @@ -240,13 +272,13 @@ config MTD_NETtel config MTD_ALCHEMY tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support" - depends on SOC_AU1X00 + depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI help Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards config MTD_MTX1 tristate "4G Systems MTX-1 Flash device" - depends on MIPS && MIPS_MTX1 + depends on MIPS_MTX1 && MTD_CFI help Flash memory access on 4G Systems MTX-1 Board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. @@ -384,7 +416,7 @@ config MTD_TQM834x config MTD_OCELOT tristate "Momenco Ocelot boot flash device" - depends on MIPS && MOMENCO_OCELOT + depends on MOMENCO_OCELOT help This enables access routines for the boot flash device and for the NVRAM on the Momenco Ocelot board. If you have one of these boards @@ -517,7 +549,7 @@ config MTD_OMAP_NOR # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" - depends on MTD && PCI && MTD_COMPLEX_MAPPINGS + depends on PCI && MTD_COMPLEX_MAPPINGS help Mapping for accessing flash devices on add-in cards like the Intel XScale IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode @@ -527,7 +559,7 @@ config MTD_PCI config MTD_PCMCIA tristate "PCMCIA MTD driver" - depends on MTD && PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN + depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN help Map driver for accessing PCMCIA linear flash memory cards. These cards are usually around 4-16MiB in size. This does not include @@ -591,13 +623,12 @@ config MTD_BAST_MAXSIZE config MTD_SHARP_SL bool "ROM mapped on Sharp SL Series" - depends on MTD && ARCH_PXA + depends on ARCH_PXA help This enables access to the flash chip on the Sharp SL Series of PDAs. config MTD_PLATRAM tristate "Map driver for platform device RAM (mtd-ram)" - depends on MTD select MTD_RAM help Map driver for RAM areas described via the platform device diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 071d0bf..de036c5 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -27,6 +27,8 @@ obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o +obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o +obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c index 7fc8097..84fbe0e 100644 --- a/drivers/mtd/maps/alchemy-flash.c +++ b/drivers/mtd/maps/alchemy-flash.c @@ -1,10 +1,7 @@ /* * Flash memory access on AMD Alchemy evaluation boards * - * $Id: alchemy-flash.c,v 1.2 2005/11/07 11:14:26 gleixner Exp $ - * * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com> - * */ #include <linux/init.h> @@ -18,12 +15,6 @@ #include <asm/io.h> -#ifdef DEBUG_RW -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - #ifdef CONFIG_MIPS_PB1000 #define BOARD_MAP_NAME "Pb1000 Flash" #define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 78b6711..728aed6 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 238d42e..688ef49 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; @@ -327,7 +327,7 @@ static int __init init_ck804xrom(void) pdev = NULL; for(id = ck804xrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) break; } @@ -338,7 +338,7 @@ static int __init init_ck804xrom(void) } return -ENXIO; #if 0 - return pci_module_init(&ck804xrom_driver); + return pci_register_driver(&ck804xrom_driver); #endif } diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c index b1104fe..1c3b34a 100644 --- a/drivers/mtd/maps/dilnetpc.c +++ b/drivers/mtd/maps/dilnetpc.c @@ -402,8 +402,8 @@ static int __init init_dnpc(void) ++higlvl_partition_info[i].name; } - printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", - is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); + printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%llx\n", + is_dnp ? "DNPC" : "ADNP", dnpc_map.size, (unsigned long long)dnpc_map.phys); dnpc_map.virt = ioremap_nocache(dnpc_map.phys, dnpc_map.size); diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index a9d808a..aa64a47 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -30,7 +30,7 @@ #define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ -#define BIOS_CNTL 0xDC +#define BIOS_CNTL 0xDC #define BIOS_LOCK_ENABLE 0x02 #define BIOS_WRITE_ENABLE 0x01 @@ -145,7 +145,7 @@ static void esb2rom_cleanup(struct esb2rom_window *window) } static int __devinit esb2rom_init_one(struct pci_dev *pdev, - const struct pci_device_id *ent) + const struct pci_device_id *ent) { static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; struct esb2rom_window *window = &esb2rom_window; @@ -185,7 +185,7 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, /* Find a region continuous to the end of the ROM window */ window->phys = 0; pci_read_config_word(pdev, FWH_DEC_EN1, &word); - printk(KERN_DEBUG "pci_read_config_byte : %x\n", word); + printk(KERN_DEBUG "pci_read_config_word : %x\n", word); if ((word & FWH_8MiB) == FWH_8MiB) window->phys = 0xff400000; @@ -212,6 +212,11 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, else if ((word & FWH_0_5MiB) == FWH_0_5MiB) window->phys = 0xfff80000; + if (window->phys == 0) { + printk(KERN_ERR MOD_NAME ": Rom window is closed\n"); + goto out; + } + /* reserved 0x0020 and 0x0010 */ window->phys -= 0x400000UL; window->size = (0xffffffffUL - window->phys) + 1UL; @@ -289,8 +294,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2bb3e636..2c884c4 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index ed21547..95dcab2 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -94,7 +94,9 @@ static struct mtd_info *mymtd; static int __init init_netsc520(void) { - printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); + printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)netsc520_map.size, + (unsigned long long)netsc520_map.phys); netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); if (!netsc520_map.virt) { diff --git a/drivers/mtd/maps/plat-ram.c b/drivers/mtd/maps/plat-ram.c index 2b6504e..894c0b2 100644 --- a/drivers/mtd/maps/plat-ram.c +++ b/drivers/mtd/maps/plat-ram.c @@ -169,7 +169,8 @@ static int platram_probe(struct platform_device *pdev) goto exit_free; } - dev_dbg(&pdev->dev, "got platform resource %p (0x%lx)\n", res, res->start); + dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res, + (unsigned long long)res->start); /* setup map parameters */ diff --git a/drivers/mtd/maps/pmcmsp-flash.c b/drivers/mtd/maps/pmcmsp-flash.c new file mode 100644 index 0000000..7e0377e --- /dev/null +++ b/drivers/mtd/maps/pmcmsp-flash.c @@ -0,0 +1,184 @@ +/* + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. + * + * Basically physmap.c with the addition of partitions and + * an array of mapping info to accomodate more than one flash type per board. + * + * Copyright 2005-2007 PMC-Sierra, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> + +#include <msp_prom.h> +#include <msp_regs.h> + + +static struct mtd_info **msp_flash; +static struct mtd_partition **msp_parts; +static struct map_info *msp_maps; +static int fcnt; + +#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n",__FUNCTION__,__LINE__) + +int __init init_msp_flash(void) +{ + int i, j; + int offset, coff; + char *env; + int pcnt; + char flash_name[] = "flash0"; + char part_name[] = "flash0_0"; + unsigned addr, size; + + /* If ELB is disabled by "ful-mux" mode, we can't get at flash */ + if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) && + (*ELB_1PC_EN_REG & SINGLE_PCCARD)) { + printk(KERN_NOTICE "Single PC Card mode: no flash access\n"); + return -ENXIO; + } + + /* examine the prom environment for flash devices */ + for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++) + flash_name[5] = '0' + fcnt + 1; + + if (fcnt < 1) + return -ENXIO; + + printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt); + msp_flash = (struct mtd_info **)kmalloc( + fcnt * sizeof(struct map_info *), GFP_KERNEL); + msp_parts = (struct mtd_partition **)kmalloc( + fcnt * sizeof(struct mtd_partition *), GFP_KERNEL); + msp_maps = (struct map_info *)kmalloc( + fcnt * sizeof(struct mtd_info), GFP_KERNEL); + memset(msp_maps, 0, fcnt * sizeof(struct mtd_info)); + + /* loop over the flash devices, initializing each */ + for (i = 0; i < fcnt; i++) { + /* examine the prom environment for flash partititions */ + part_name[5] = '0' + i; + part_name[7] = '0'; + for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++) + part_name[7] = '0' + pcnt + 1; + + if (pcnt == 0) { + printk(KERN_NOTICE "Skipping flash device %d " + "(no partitions defined)\n", i); + continue; + } + + msp_parts[i] = (struct mtd_partition *)kmalloc( + pcnt * sizeof(struct mtd_partition), GFP_KERNEL); + memset(msp_parts[i], 0, pcnt * sizeof(struct mtd_partition)); + + /* now initialize the devices proper */ + flash_name[5] = '0' + i; + env = prom_getenv(flash_name); + + if (sscanf(env, "%x:%x", &addr, &size) < 2) + return -ENXIO; + addr = CPHYSADDR(addr); + + printk(KERN_NOTICE + "MSP flash device \"%s\": 0x%08x at 0x%08x\n", + flash_name, size, addr); + /* This must matchs the actual size of the flash chip */ + msp_maps[i].size = size; + msp_maps[i].phys = addr; + + /* + * Platforms have a specific limit of the size of memory + * which may be mapped for flash: + */ + if (size > CONFIG_MSP_FLASH_MAP_LIMIT) + size = CONFIG_MSP_FLASH_MAP_LIMIT; + msp_maps[i].virt = ioremap(addr, size); + msp_maps[i].bankwidth = 1; + msp_maps[i].name = strncpy(kmalloc(7, GFP_KERNEL), + flash_name, 7); + + if (msp_maps[i].virt == NULL) + return -ENXIO; + + for (j = 0; j < pcnt; j++) { + part_name[5] = '0' + i; + part_name[7] = '0' + j; + + env = prom_getenv(part_name); + + if (sscanf(env, "%x:%x:%n", &offset, &size, &coff) < 2) + return -ENXIO; + + msp_parts[i][j].size = size; + msp_parts[i][j].offset = offset; + msp_parts[i][j].name = env + coff; + } + + /* now probe and add the device */ + simple_map_init(&msp_maps[i]); + msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]); + if (msp_flash[i]) { + msp_flash[i]->owner = THIS_MODULE; + add_mtd_partitions(msp_flash[i], msp_parts[i], pcnt); + } else { + printk(KERN_ERR "map probe failed for flash\n"); + return -ENXIO; + } + } + + return 0; +} + +static void __exit cleanup_msp_flash(void) +{ + int i; + + for (i = 0; i < sizeof(msp_flash) / sizeof(struct mtd_info **); i++) { + del_mtd_partitions(msp_flash[i]); + map_destroy(msp_flash[i]); + iounmap((void *)msp_maps[i].virt); + + /* free the memory */ + kfree(msp_maps[i].name); + kfree(msp_parts[i]); + } + + kfree(msp_flash); + kfree(msp_parts); + kfree(msp_maps); +} + +MODULE_AUTHOR("PMC-Sierra, Inc"); +MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards"); +MODULE_LICENSE("GPL"); + +module_init(init_msp_flash); +module_exit(cleanup_msp_flash); diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c new file mode 100644 index 0000000..18049bc --- /dev/null +++ b/drivers/mtd/maps/pmcmsp-ramroot.c @@ -0,0 +1,105 @@ +/* + * Mapping of the rootfs in a physical region of memory + * + * Copyright (C) 2005-2007 PMC-Sierra Inc. + * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/fs.h> +#include <linux/root_dev.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> + +#include <asm/io.h> + +#include <msp_prom.h> + +static struct mtd_info *rr_mtd; + +struct map_info rr_map = { + .name = "ramroot", + .bankwidth = 4, +}; + +static int __init init_rrmap(void) +{ + void *ramroot_start; + unsigned long ramroot_size; + + /* Check for supported rootfs types */ + if (get_ramroot(&ramroot_start, &ramroot_size)) { + rr_map.phys = CPHYSADDR(ramroot_start); + rr_map.size = ramroot_size; + + printk(KERN_NOTICE + "PMC embedded root device: 0x%08lx @ 0x%08lx\n", + rr_map.size, (unsigned long)rr_map.phys); + } else { + printk(KERN_ERR + "init_rrmap: no supported embedded rootfs detected!\n"); + return -ENXIO; + } + + /* Map rootfs to I/O space for block device driver */ + rr_map.virt = ioremap(rr_map.phys, rr_map.size); + if (!rr_map.virt) { + printk(KERN_ERR "Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&rr_map); + + rr_mtd = do_map_probe("map_ram", &rr_map); + if (rr_mtd) { + rr_mtd->owner = THIS_MODULE; + + add_mtd_device(rr_mtd); + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, rr_mtd->index); + + return 0; + } + + iounmap(rr_map.virt); + return -ENXIO; +} + +static void __exit cleanup_rrmap(void) +{ + del_mtd_device(rr_mtd); + map_destroy(rr_mtd); + + iounmap(rr_map.virt); + rr_map.virt = NULL; +} + +MODULE_AUTHOR("PMC-Sierra, Inc"); +MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem"); +MODULE_LICENSE("GPL"); + +module_init(init_rrmap); +module_exit(cleanup_rrmap); diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 9b50cfc..4045e37 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -237,8 +237,9 @@ static int __init init_sc520cdp(void) #endif for (i = 0; i < NUM_FLASH_BANKS; i++) { - printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", - sc520cdp_map[i].size, sc520cdp_map[i].phys); + printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)sc520cdp_map[i].size, + (unsigned long long)sc520cdp_map[i].phys); sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index 4db2055..001af7f 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -39,7 +39,7 @@ MODULE_VERSION("2.0"); static LIST_HEAD(device_list); struct uflash_dev { - char *name; /* device name */ + const char *name; /* device name */ struct map_info map; /* mtd map info */ struct mtd_info *mtd; /* mtd info */ }; @@ -80,7 +80,7 @@ int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp) up->name = of_get_property(dp, "model", NULL); if (up->name && 0 < strlen(up->name)) - up->map.name = up->name; + up->map.name = (char *)up->name; up->map.phys = res->start; diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index b879a66..524b83b 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -20,6 +20,7 @@ #include <linux/hdreg.h> #include <linux/init.h> #include <linux/mutex.h> +#include <linux/kthread.h> #include <asm/uaccess.h> static LIST_HEAD(blktrans_majors); @@ -28,9 +29,7 @@ extern struct mutex mtd_table_mutex; extern struct mtd_info *mtd_table[]; struct mtd_blkcore_priv { - struct completion thread_dead; - int exiting; - wait_queue_head_t thread_wq; + struct task_struct *thread; struct request_queue *rq; spinlock_t queue_lock; }; @@ -83,38 +82,19 @@ static int mtd_blktrans_thread(void *arg) /* we might get involved when memory gets low, so use PF_MEMALLOC */ current->flags |= PF_MEMALLOC | PF_NOFREEZE; - daemonize("%sd", tr->name); - - /* daemonize() doesn't do this for us since some kernel threads - actually want to deal with signals. We can't just call - exit_sighand() since that'll cause an oops when we finally - do exit. */ - spin_lock_irq(¤t->sighand->siglock); - sigfillset(¤t->blocked); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - spin_lock_irq(rq->queue_lock); - - while (!tr->blkcore_priv->exiting) { + while (!kthread_should_stop()) { struct request *req; struct mtd_blktrans_dev *dev; int res = 0; - DECLARE_WAITQUEUE(wait, current); req = elv_next_request(rq); if (!req) { - add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); set_current_state(TASK_INTERRUPTIBLE); - spin_unlock_irq(rq->queue_lock); - schedule(); - remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); - spin_lock_irq(rq->queue_lock); - continue; } @@ -133,13 +113,13 @@ static int mtd_blktrans_thread(void *arg) } spin_unlock_irq(rq->queue_lock); - complete_and_exit(&tr->blkcore_priv->thread_dead, 0); + return 0; } static void mtd_blktrans_request(struct request_queue *rq) { struct mtd_blktrans_ops *tr = rq->queuedata; - wake_up(&tr->blkcore_priv->thread_wq); + wake_up_process(tr->blkcore_priv->thread); } @@ -388,8 +368,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) return ret; } spin_lock_init(&tr->blkcore_priv->queue_lock); - init_completion(&tr->blkcore_priv->thread_dead); - init_waitqueue_head(&tr->blkcore_priv->thread_wq); tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); if (!tr->blkcore_priv->rq) { @@ -403,13 +381,14 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) blk_queue_hardsect_size(tr->blkcore_priv->rq, tr->blksize); tr->blkshift = ffs(tr->blksize) - 1; - ret = kernel_thread(mtd_blktrans_thread, tr, CLONE_KERNEL); - if (ret < 0) { + tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, + "%sd", tr->name); + if (IS_ERR(tr->blkcore_priv->thread)) { blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); kfree(tr->blkcore_priv); mutex_unlock(&mtd_table_mutex); - return ret; + return PTR_ERR(tr->blkcore_priv->thread); } INIT_LIST_HEAD(&tr->devs); @@ -432,9 +411,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); /* Clean up the kernel thread */ - tr->blkcore_priv->exiting = 1; - wake_up(&tr->blkcore_priv->thread_wq); - wait_for_completion(&tr->blkcore_priv->thread_dead); + kthread_stop(tr->blkcore_priv->thread); /* Remove it from the list of active majors */ list_del(&tr->list); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 61a994e..8c86b80 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, info.erasesize = mtd->erasesize; info.writesize = mtd->writesize; info.oobsize = mtd->oobsize; - info.ecctype = mtd->ecctype; - info.eccsize = mtd->eccsize; + /* The below fields are obsolete */ + info.ecctype = -1; + info.eccsize = 0; if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) return -EFAULT; break; @@ -552,7 +553,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, ops.datbuf = NULL; ops.mode = MTD_OOB_PLACE; - if (ops.ooboffs && ops.len > (mtd->oobsize - ops.ooboffs)) + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) return -EINVAL; ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 0690268..41844ea 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -727,8 +727,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.erasesize = subdev[0]->erasesize; concat->mtd.writesize = subdev[0]->writesize; concat->mtd.oobsize = subdev[0]->oobsize; - concat->mtd.ecctype = subdev[0]->ecctype; - concat->mtd.eccsize = subdev[0]->eccsize; + concat->mtd.oobavail = subdev[0]->oobavail; if (subdev[0]->writev) concat->mtd.writev = concat_writev; if (subdev[0]->read_oob) @@ -774,8 +773,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c if (concat->mtd.writesize != subdev[i]->writesize || concat->mtd.subpage_sft != subdev[i]->subpage_sft || concat->mtd.oobsize != subdev[i]->oobsize || - concat->mtd.ecctype != subdev[i]->ecctype || - concat->mtd.eccsize != subdev[i]->eccsize || !concat->mtd.read_oob != !subdev[i]->read_oob || !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index bafd2fb..1af9890 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -200,6 +200,11 @@ static int part_erase (struct mtd_info *mtd, struct erase_info *instr) return -EINVAL; instr->addr += part->offset; ret = part->master->erase(part->master, instr); + if (ret) { + if (instr->fail_addr != 0xffffffff) + instr->fail_addr -= part->offset; + instr->addr -= part->offset; + } return ret; } @@ -338,8 +343,7 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.size = parts[i].size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; - slave->mtd.ecctype = master->ecctype; - slave->mtd.eccsize = master->eccsize; + slave->mtd.oobavail = master->oobavail; slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; @@ -561,4 +565,3 @@ EXPORT_SYMBOL_GPL(deregister_mtd_parser); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); MODULE_DESCRIPTION("Generic support for partitioning of MTD devices"); - diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a..d05873b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,10 +1,7 @@ # drivers/mtd/nand/Kconfig # $Id: Kconfig,v 1.35 2005/11/07 11:14:30 gleixner Exp $ -menu "NAND Flash Device Drivers" - depends on MTD!=n - -config MTD_NAND +menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD select MTD_NAND_IDS @@ -13,9 +10,10 @@ config MTD_NAND devices. For further information see <http://www.linux-mtd.infradead.org/doc/nand.html>. +if MTD_NAND + config MTD_NAND_VERIFY_WRITE bool "Verify NAND page writes" - depends on MTD_NAND help This adds an extra check when data is written to the flash. The NAND flash device internally checks only bits transitioning @@ -25,53 +23,61 @@ config MTD_NAND_VERIFY_WRITE config MTD_NAND_ECC_SMC bool "NAND ECC Smart Media byte order" - depends on MTD_NAND default n help Software ECC according to the Smart Media Specification. The original Linux implementation had byte 0 and 1 swapped. +config MTD_NAND_MUSEUM_IDS + bool "Enable chip ids for obsolete ancient NAND devices" + depends on MTD_NAND + default n + help + Enable this option only when your board has first generation + NAND chips (page size 256 byte, erase size 4-8KiB). The IDs + of these chips were reused by later, larger chips. + config MTD_NAND_AUTCPU12 tristate "SmartMediaCard on autronix autcpu12 board" - depends on MTD_NAND && ARCH_AUTCPU12 + depends on ARCH_AUTCPU12 help This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" - depends on MTD_NAND && ARCH_EDB7312 + depends on ARCH_EDB7312 help This enables the driver for the Cirrus Logic EBD7312 evaluation board to access the onboard NAND Flash. config MTD_NAND_H1900 tristate "iPAQ H1900 flash" - depends on MTD_NAND && ARCH_PXA && MTD_PARTITIONS + depends on ARCH_PXA && MTD_PARTITIONS help This enables the driver for the iPAQ h1900 flash. config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" - depends on ARCH_P720T && MTD_NAND + depends on ARCH_P720T help If you had to ask, you don't have one. Say 'N'. config MTD_NAND_AMS_DELTA tristate "NAND Flash device on Amstrad E3" - depends on MACH_AMS_DELTA && MTD_NAND + depends on MACH_AMS_DELTA help Support for NAND flash on Amstrad E3 (Delta). config MTD_NAND_TOTO tristate "NAND Flash device on TOTO board" - depends on ARCH_OMAP && MTD_NAND && BROKEN + depends on ARCH_OMAP && BROKEN help Support for NAND flash on Texas Instruments Toto platform. config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" - depends on MACH_TS72XX && MTD_NAND + depends on MACH_TS72XX help Support for NAND flash on Technologic Systems TS-7250 platform. @@ -80,14 +86,14 @@ config MTD_NAND_IDS config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" - depends on (SOC_AU1200 || SOC_AU1550) && MTD_NAND + depends on SOC_AU1200 || SOC_AU1550 help This enables the driver for the NAND flash controller on the AMD/Alchemy 1550 SOC. config MTD_NAND_RTC_FROM4 tristate "Renesas Flash ROM 4-slot interface board (FROM_BOARD4)" - depends on MTD_NAND && SH_SOLUTION_ENGINE + depends on SH_SOLUTION_ENGINE select REED_SOLOMON select REED_SOLOMON_DEC8 select BITREVERSE @@ -97,13 +103,13 @@ config MTD_NAND_RTC_FROM4 config MTD_NAND_PPCHAMELEONEVB tristate "NAND Flash device on PPChameleonEVB board" - depends on PPCHAMELEONEVB && MTD_NAND && BROKEN + depends on PPCHAMELEONEVB && BROKEN help This enables the NAND flash driver on the PPChameleon EVB Board. config MTD_NAND_S3C2410 tristate "NAND Flash support for S3C2410/S3C2440 SoC" - depends on ARCH_S3C2410 && MTD_NAND + depends on ARCH_S3C2410 help This enables the NAND flash controller on the S3C2410 and S3C2440 SoCs @@ -126,13 +132,9 @@ config MTD_NAND_S3C2410_HWECC incorrect ECC generation, and if using these, the default of software ECC is preferable. - If you lay down a device with the hardware ECC, then you will - currently not be able to switch to software, as there is no - implementation for ECC method used by the S3C2410 - config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" - depends on MTD_NAND && 44x + depends on 44x select MTD_NAND_ECC_SMC help NDFC Nand Flash Controllers are integrated in EP44x SoCs @@ -149,7 +151,7 @@ config MTD_NAND_S3C2410_CLKSTOP config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" - depends on MTD_NAND && EXPERIMENTAL + depends on EXPERIMENTAL select REED_SOLOMON select REED_SOLOMON_DEC16 help @@ -219,7 +221,15 @@ config MTD_NAND_DISKONCHIP_BBTWRITE config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" - depends on MTD_NAND && ARCH_PXA + depends on ARCH_PXA + +config MTD_NAND_BASLER_EXCITE + tristate "Support for NAND Flash on Basler eXcite" + depends on BASLER_EXCITE + help + This enables the driver for the NAND flash device found on the + Basler eXcite Smart Camera. If built as a module, the driver + will be named "excite_nandflash.ko". config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" @@ -230,7 +240,7 @@ config MTD_NAND_CAFE config MTD_NAND_CS553X tristate "NAND support for CS5535/CS5536 (AMD Geode companion chip)" - depends on MTD_NAND && X86_32 && (X86_PC || X86_GENERICARCH) + depends on X86_32 && (X86_PC || X86_GENERICARCH) help The CS553x companion chips for the AMD Geode processor include NAND flash controllers with built-in hardware ECC @@ -243,16 +253,21 @@ config MTD_NAND_CS553X config MTD_NAND_AT91 bool "Support for NAND Flash / SmartMedia on AT91" - depends on MTD_NAND && ARCH_AT91 + depends on ARCH_AT91 help Enables support for NAND Flash / Smart Media Card interface on Atmel AT91 processors. +config MTD_NAND_CM_X270 + tristate "Support for NAND Flash on CM-X270 modules" + depends on MTD_NAND && MACH_ARMCORE + + config MTD_NAND_NANDSIM tristate "Support for NAND Flash Simulator" - depends on MTD_NAND && MTD_PARTITIONS + depends on MTD_PARTITIONS help The simulator may simulate various NAND flash chips for the MTD nand layer. -endmenu +endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f7a53f0..6872031 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o +obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o +obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o cafe_nand-objs := cafe.o cafe_ecc.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 08cb060..c328a75 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -78,8 +78,9 @@ module_param(regdebug, int, 0644); static int checkecc = 1; module_param(checkecc, int, 0644); -static int slowtiming = 0; -module_param(slowtiming, int, 0644); +static int numtimings; +static int timing[3]; +module_param_array(timing, int, &numtimings, 0644); /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ndelay(100); if (1) { - int c = 500000; + int c; uint32_t irqs; - while (c--) { + for (c = 500000; c != 0; c--) { irqs = cafe_readl(cafe, NAND_IRQ); if (irqs & doneint) break; @@ -580,31 +581,43 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.block_bad = cafe_nand_block_bad; } + if (numtimings && numtimings != 3) { + dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); + } + + if (numtimings == 3) { + cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + timing[0] = cafe_readl(cafe, NAND_TIMING1); + timing[1] = cafe_readl(cafe, NAND_TIMING2); + timing[2] = cafe_readl(cafe, NAND_TIMING3); + + if (timing[0] | timing[1] | timing[2]) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", + timing[0], timing[1], timing[2]); + } else { + dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); + timing[0] = timing[1] = timing[2] = 0xffffffff; + } + } + /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + cafe_writel(cafe, timing[0], NAND_TIMING1); + cafe_writel(cafe, timing[1], NAND_TIMING2); + cafe_writel(cafe, timing[2], NAND_TIMING3); - /* Timings from Marvell's test code (not verified or calculated by us) */ - if (!slowtiming) { - cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); - cafe_writel(cafe, 0x24121212, NAND_TIMING2); - cafe_writel(cafe, 0x11000000, NAND_TIMING3); - } else { - cafe_writel(cafe, 0xffffffff, NAND_TIMING1); - cafe_writel(cafe, 0xffffffff, NAND_TIMING2); - cafe_writel(cafe, 0xffffffff, NAND_TIMING3); - } cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; } -#if 1 + /* Disable master reset, enable NAND clock */ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; @@ -631,32 +644,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); -#endif -#if 1 - mtd->writesize=2048; - mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0x5a, 2112); - cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); - // nand_wait_ready(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - writel(0x84600070, cafe->mmio); - udelay(10); - cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); -#endif - /* Scan to find existance of the device */ + + /* Scan to find existence of the device */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; goto out_irq; @@ -760,13 +749,4 @@ module_exit(cafe_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); - -/* Correct ECC for 2048 bytes of 0xff: - 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ - -/* dwmw2's B-test board, in case of completely screwing it: -Bad eraseblock 2394 at 0x12b40000 -Bad eraseblock 2627 at 0x14860000 -Bad eraseblock 3349 at 0x1a2a0000 -*/ +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05..ea5c849 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { static unsigned short err_pos(unsigned short din) { - BUG_ON(din > 4096); + BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); return err_pos_lut[din]; } static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) diff --git a/drivers/mtd/nand/cmx270_nand.c b/drivers/mtd/nand/cmx270_nand.c new file mode 100644 index 0000000..cb663ef --- /dev/null +++ b/drivers/mtd/nand/cmx270_nand.c @@ -0,0 +1,267 @@ +/* + * linux/drivers/mtd/nand/cmx270-nand.c + * + * Copyright (C) 2006 Compulab, Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * Derived from drivers/mtd/nand/h1910.c + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * + * 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. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * CM-X270 board. + */ + +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/irq.h> + +#include <asm/arch/hardware.h> +#include <asm/arch/pxa-regs.h> + +#define GPIO_NAND_CS (11) +#define GPIO_NAND_RB (89) + +/* This macro needed to ensure in-order operation of GPIO and local + * bus. Without both asm command and dummy uncached read there're + * states when NAND access is broken. I've looked for such macro(s) in + * include/asm-arm but found nothing approptiate. + * dmac_clean_range is close, but is makes cache invalidation + * unnecessary here and it cannot be used in module + */ +#define DRAIN_WB() \ + do { \ + unsigned char dummy; \ + asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); \ + dummy=*((unsigned char*)UNCACHED_ADDR); \ + } while(0) + +/* MTD structure for CM-X270 board */ +static struct mtd_info *cmx270_nand_mtd; + +/* remaped IO address of the device */ +static void __iomem *cmx270_nand_io; + +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + [0] = { + .name = "cmx270-0", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; +#define NUM_PARTITIONS (ARRAY_SIZE(partition_info)) + +const char *part_probes[] = { "cmdlinepart", NULL }; + +static u_char cmx270_read_byte(struct mtd_info *mtd) +{ + struct nand_chip *this = mtd->priv; + + return (readl(this->IO_ADDR_R) >> 16); +} + +static void cmx270_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; i<len; i++) + writel((*buf++ << 16), this->IO_ADDR_W); +} + +static void cmx270_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; i<len; i++) + *buf++ = readl(this->IO_ADDR_R) >> 16; +} + +static int cmx270_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + int i; + struct nand_chip *this = mtd->priv; + + for (i=0; i<len; i++) + if (buf[i] != (u_char)(readl(this->IO_ADDR_R) >> 16)) + return -EFAULT; + + return 0; +} + +static inline void nand_cs_on(void) +{ + GPCR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); +} + +static void nand_cs_off(void) +{ + DRAIN_WB(); + + GPSR(GPIO_NAND_CS) = GPIO_bit(GPIO_NAND_CS); +} + +/* + * hardware specific access to control-lines + */ +static void cmx270_hwcontrol(struct mtd_info *mtd, int dat, + unsigned int ctrl) +{ + struct nand_chip* this = mtd->priv; + unsigned int nandaddr = (unsigned int)this->IO_ADDR_W; + + DRAIN_WB(); + + if (ctrl & NAND_CTRL_CHANGE) { + if ( ctrl & NAND_ALE ) + nandaddr |= (1 << 3); + else + nandaddr &= ~(1 << 3); + if ( ctrl & NAND_CLE ) + nandaddr |= (1 << 2); + else + nandaddr &= ~(1 << 2); + if ( ctrl & NAND_NCE ) + nand_cs_on(); + else + nand_cs_off(); + } + + DRAIN_WB(); + this->IO_ADDR_W = (void __iomem*)nandaddr; + if (dat != NAND_CMD_NONE) + writel((dat << 16), this->IO_ADDR_W); + + DRAIN_WB(); +} + +/* + * read device ready pin + */ +static int cmx270_device_ready(struct mtd_info *mtd) +{ + DRAIN_WB(); + + return (GPLR(GPIO_NAND_RB) & GPIO_bit(GPIO_NAND_RB)); +} + +/* + * Main initialization routine + */ +static int cmx270_init(void) +{ + struct nand_chip *this; + const char *part_type; + struct mtd_partition *mtd_parts; + int mtd_parts_nb = 0; + int ret; + + /* Allocate memory for MTD device structure and private data */ + cmx270_nand_mtd = kzalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!cmx270_nand_mtd) { + printk("Unable to allocate CM-X270 NAND MTD device structure.\n"); + return -ENOMEM; + } + + cmx270_nand_io = ioremap(PXA_CS1_PHYS, 12); + if (!cmx270_nand_io) { + printk("Unable to ioremap NAND device\n"); + ret = -EINVAL; + goto err1; + } + + /* Get pointer to private data */ + this = (struct nand_chip *)(&cmx270_nand_mtd[1]); + + /* Link the private data with the MTD structure */ + cmx270_nand_mtd->owner = THIS_MODULE; + cmx270_nand_mtd->priv = this; + + /* insert callbacks */ + this->IO_ADDR_R = cmx270_nand_io; + this->IO_ADDR_W = cmx270_nand_io; + this->cmd_ctrl = cmx270_hwcontrol; + this->dev_ready = cmx270_device_ready; + + /* 15 us command delay time */ + this->chip_delay = 20; + this->ecc.mode = NAND_ECC_SOFT; + + /* read/write functions */ + this->read_byte = cmx270_read_byte; + this->read_buf = cmx270_read_buf; + this->write_buf = cmx270_write_buf; + this->verify_buf = cmx270_verify_buf; + + /* Scan to find existence of the device */ + if (nand_scan (cmx270_nand_mtd, 1)) { + printk(KERN_NOTICE "No NAND device\n"); + ret = -ENXIO; + goto err2; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_mtd_partitions(cmx270_nand_mtd, part_probes, + &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (!mtd_parts_nb) { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + ret = add_mtd_partitions(cmx270_nand_mtd, mtd_parts, mtd_parts_nb); + if (ret) + goto err2; + + /* Return happy */ + return 0; + +err2: + iounmap(cmx270_nand_io); +err1: + kfree(cmx270_nand_mtd); + + return ret; + +} +module_init(cmx270_init); + +/* + * Clean up routine + */ +static void cmx270_cleanup(void) +{ + /* Release resources, unregister device */ + nand_release(cmx270_nand_mtd); + + iounmap(cmx270_nand_io); + + /* Free the MTD device structure */ + kfree (cmx270_nand_mtd); +} +module_exit(cmx270_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("NAND flash driver for Compulab CM-X270 Module"); diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index 12608c1..595208f 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -114,7 +114,7 @@ module_param(no_autopart, int, 0); static int show_firmware_partition = 0; module_param(show_firmware_partition, int, 0); -#ifdef MTD_NAND_DISKONCHIP_BBTWRITE +#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE static int inftl_bbt_write = 1; #else static int inftl_bbt_write = 0; diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c new file mode 100644 index 0000000..7e9afc4 --- /dev/null +++ b/drivers/mtd/nand/excite_nandflash.c @@ -0,0 +1,248 @@ +/* +* Copyright (C) 2005 - 2007 by Basler Vision Technologies AG +* Author: Thomas Koeller <thomas.koeller.qbaslerweb.com> +* Original code by Thies Moeller <thies.moeller@baslerweb.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/kernel.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/nand_ecc.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/rm9k-ocd.h> + +#include <excite_nandflash.h> + +#define EXCITE_NANDFLASH_VERSION "0.1" + +/* I/O register offsets */ +#define EXCITE_NANDFLASH_DATA_BYTE 0x00 +#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c +#define EXCITE_NANDFLASH_ADDR_BYTE 0x10 +#define EXCITE_NANDFLASH_CMD_BYTE 0x14 + +/* prefix for debug output */ +static const char module_id[] = "excite_nandflash"; + +/* + * partition definition + */ +static const struct mtd_partition partition_info[] = { + { + .name = "eXcite RootFS", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; + +static inline const struct resource * +excite_nand_get_resource(struct platform_device *d, unsigned long flags, + const char *basename) +{ + char buf[80]; + + if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf) + return NULL; + return platform_get_resource_byname(d, flags, buf); +} + +static inline void __iomem * +excite_nand_map_regs(struct platform_device *d, const char *basename) +{ + void *result = NULL; + const struct resource *const r = + excite_nand_get_resource(d, IORESOURCE_MEM, basename); + + if (r) + result = ioremap_nocache(r->start, r->end + 1 - r->start); + return result; +} + +/* controller and mtd information */ +struct excite_nand_drvdata { + struct mtd_info board_mtd; + struct nand_chip board_chip; + void __iomem *regs; + void __iomem *tgt; +}; + +/* Control function */ +static void excite_nand_control(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct excite_nand_drvdata * const d = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE; + break; + } + + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, d->tgt); +} + +/* Return 0 if flash is busy, 1 if ready */ +static int excite_nand_devready(struct mtd_info *mtd) +{ + struct excite_nand_drvdata * const drvdata = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE); +} + +/* + * Called by device layer to remove the driver. + * The binding to the mtd and all allocated + * resources are released. + */ +static int __exit excite_nand_remove(struct device *dev) +{ + struct excite_nand_drvdata * const this = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (unlikely(!this)) { + printk(KERN_ERR "%s: called %s without private data!!", + module_id, __func__); + return -EINVAL; + } + + /* first thing we need to do is release our mtd + * then go through freeing the resource used + */ + nand_release(&this->board_mtd); + + /* free the common resources */ + iounmap(this->regs); + kfree(this); + + DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id); + return 0; +} + +/* + * Called by device layer when it finds a device matching + * one our driver can handle. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices. +*/ +static int __init excite_nand_probe(struct device *dev) +{ + struct platform_device * const pdev = to_platform_device(dev); + struct excite_nand_drvdata *drvdata; /* private driver data */ + struct nand_chip *board_chip; /* private flash chip data */ + struct mtd_info *board_mtd; /* mtd info for this board */ + int scan_res; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (unlikely(!drvdata)) { + printk(KERN_ERR "%s: no memory for drvdata\n", + module_id); + return -ENOMEM; + } + + /* bind private data into driver */ + dev_set_drvdata(dev, drvdata); + + /* allocate and map the resource */ + drvdata->regs = + excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS); + + if (unlikely(!drvdata->regs)) { + printk(KERN_ERR "%s: cannot reserve register region\n", + module_id); + kfree(drvdata); + return -ENXIO; + } + + drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + + /* initialise our chip */ + board_chip = &drvdata->board_chip; + board_chip->IO_ADDR_R = board_chip->IO_ADDR_W = + drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + board_chip->cmd_ctrl = excite_nand_control; + board_chip->dev_ready = excite_nand_devready; + board_chip->chip_delay = 25; + board_chip->ecc.mode = NAND_ECC_SOFT; + + /* link chip to mtd */ + board_mtd = &drvdata->board_mtd; + board_mtd->priv = board_chip; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id); + scan_res = nand_scan(&drvdata->board_mtd, 1); + + if (likely(!scan_res)) { + DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); + add_mtd_partitions(&drvdata->board_mtd, partition_info, + sizeof partition_info / sizeof partition_info[0]); + } else { + iounmap(drvdata->regs); + kfree(drvdata); + printk(KERN_ERR "%s: device scan failed\n", module_id); + return -EIO; + } + return 0; +} + +static struct device_driver excite_nand_driver = { + .name = "excite_nand", + .bus = &platform_bus_type, + .probe = excite_nand_probe, + .remove = __exit_p(excite_nand_remove) +}; + +static int __init excite_nand_init(void) +{ + pr_info("Basler eXcite nand flash driver Version " + EXCITE_NANDFLASH_VERSION "\n"); + return driver_register(&excite_nand_driver); +} + +static void __exit excite_nand_exit(void) +{ + driver_unregister(&excite_nand_driver); +} + +module_init(excite_nand_init); +module_exit(excite_nand_exit); + +MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>"); +MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EXCITE_NANDFLASH_VERSION) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e0..04de315 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -312,7 +312,7 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) /* Select the NAND device */ chip->select_chip(mtd, chipnr); } else - page = (int)ofs; + page = (int)(ofs >> chip->page_shift); if (chip->options & NAND_BUSWIDTH_16) { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE, @@ -350,7 +350,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) int block, ret; /* Get block number */ - block = ((int)ofs) >> chip->bbt_erase_shift; + block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1); @@ -771,7 +771,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *ecc_code = chip->buffers->ecccode; int *eccpos = chip->ecc.layout->eccpos; - nand_read_page_raw(mtd, chip, buf); + chip->ecc.read_page_raw(mtd, chip, buf); for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) chip->ecc.calculate(mtd, p, &ecc_calc[i]); @@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); - if (ops->mode == MTD_OOB_RAW) - len = mtd->oobsize; - else + if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1411,7 +1426,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip, for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; - nand_write_page_raw(mtd, chip, buf); + chip->ecc.write_page_raw(mtd, chip, buf); } /** @@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - int chipnr, page, status; + int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int)to, (int)ops->ooblen); + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { + if ((ops->ooboffs + ops->ooblen) > len) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -2488,6 +2524,7 @@ int nand_scan_tail(struct mtd_info *mtd) for (i = 0; chip->ecc.layout->oobfree[i].length; i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; + mtd->oobavail = chip->ecc.layout->oobavail; /* * Set the number of read / write steps for one page depending on ECC @@ -2530,7 +2567,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 2e2cdf2..2fc674a 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -24,6 +24,8 @@ * 512 512 Byte page size */ struct nand_flash_dev nand_flash_ids[] = { + +#ifdef CONFIG_MTD_NAND_MUSEUM_IDS {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, @@ -39,6 +41,7 @@ struct nand_flash_dev nand_flash_ids[] = { {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, +#endif {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, @@ -137,6 +140,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_HYNIX, "Hynix"}, + {NAND_MFR_MICRON, "Micron"}, {0x0, "Unknown"} }; diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index c3bca95..205df0f 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -37,6 +37,8 @@ #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/delay.h> +#include <linux/list.h> +#include <linux/random.h> /* Default simulator parameters values */ #if !defined(CONFIG_NANDSIM_FIRST_ID_BYTE) || \ @@ -90,6 +92,15 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; static uint log = CONFIG_NANDSIM_LOG; static uint dbg = CONFIG_NANDSIM_DBG; +static unsigned long parts[MAX_MTD_DEVICES]; +static unsigned int parts_num; +static char *badblocks = NULL; +static char *weakblocks = NULL; +static char *weakpages = NULL; +static unsigned int bitflips = 0; +static char *gravepages = NULL; +static unsigned int rptwear = 0; +static unsigned int overridesize = 0; module_param(first_id_byte, uint, 0400); module_param(second_id_byte, uint, 0400); @@ -104,8 +115,16 @@ module_param(bus_width, uint, 0400); module_param(do_delays, uint, 0400); module_param(log, uint, 0400); module_param(dbg, uint, 0400); - -MODULE_PARM_DESC(first_id_byte, "The fist byte returned by NAND Flash 'read ID' command (manufaturer ID)"); +module_param_array(parts, ulong, &parts_num, 0400); +module_param(badblocks, charp, 0400); +module_param(weakblocks, charp, 0400); +module_param(weakpages, charp, 0400); +module_param(bitflips, uint, 0400); +module_param(gravepages, charp, 0400); +module_param(rptwear, uint, 0400); +module_param(overridesize, uint, 0400); + +MODULE_PARM_DESC(first_id_byte, "The first byte returned by NAND Flash 'read ID' command (manufacturer ID)"); MODULE_PARM_DESC(second_id_byte, "The second byte returned by NAND Flash 'read ID' command (chip ID)"); MODULE_PARM_DESC(third_id_byte, "The third byte returned by NAND Flash 'read ID' command"); MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read ID' command"); @@ -118,6 +137,23 @@ MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); MODULE_PARM_DESC(log, "Perform logging if not zero"); MODULE_PARM_DESC(dbg, "Output debug information if not zero"); +MODULE_PARM_DESC(parts, "Partition sizes (in erase blocks) separated by commas"); +/* Page and erase block positions for the following parameters are independent of any partitions */ +MODULE_PARM_DESC(badblocks, "Erase blocks that are initially marked bad, separated by commas"); +MODULE_PARM_DESC(weakblocks, "Weak erase blocks [: remaining erase cycles (defaults to 3)]" + " separated by commas e.g. 113:2 means eb 113" + " can be erased only twice before failing"); +MODULE_PARM_DESC(weakpages, "Weak pages [: maximum writes (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be written only twice before failing"); +MODULE_PARM_DESC(bitflips, "Maximum number of random bit flips per page (zero by default)"); +MODULE_PARM_DESC(gravepages, "Pages that lose data [: maximum reads (defaults to 3)]" + " separated by commas e.g. 1401:2 means page 1401" + " can be read only twice before failing"); +MODULE_PARM_DESC(rptwear, "Number of erases inbetween reporting wear, if not zero"); +MODULE_PARM_DESC(overridesize, "Specifies the NAND Flash size overriding the ID bytes. " + "The size is specified in erase blocks and as the exponent of a power of two" + " e.g. 5 means a size of 32 erase blocks"); /* The largest possible page size */ #define NS_LARGEST_PAGE_SIZE 2048 @@ -131,9 +167,11 @@ MODULE_PARM_DESC(dbg, "Output debug information if not zero"); #define NS_DBG(args...) \ do { if (dbg) printk(KERN_DEBUG NS_OUTPUT_PREFIX " debug: " args); } while(0) #define NS_WARN(args...) \ - do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warnig: " args); } while(0) + do { printk(KERN_WARNING NS_OUTPUT_PREFIX " warning: " args); } while(0) #define NS_ERR(args...) \ - do { printk(KERN_ERR NS_OUTPUT_PREFIX " errorr: " args); } while(0) + do { printk(KERN_ERR NS_OUTPUT_PREFIX " error: " args); } while(0) +#define NS_INFO(args...) \ + do { printk(KERN_INFO NS_OUTPUT_PREFIX " " args); } while(0) /* Busy-wait delay macros (microseconds, milliseconds) */ #define NS_UDELAY(us) \ @@ -238,7 +276,8 @@ union ns_mem { * The structure which describes all the internal simulator data. */ struct nandsim { - struct mtd_partition part; + struct mtd_partition partitions[MAX_MTD_DEVICES]; + unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ u_char ids[4]; /* chip's ID bytes */ @@ -338,6 +377,38 @@ static struct nandsim_operations { STATE_DATAOUT, STATE_READY}} }; +struct weak_block { + struct list_head list; + unsigned int erase_block_no; + unsigned int max_erases; + unsigned int erases_done; +}; + +static LIST_HEAD(weak_blocks); + +struct weak_page { + struct list_head list; + unsigned int page_no; + unsigned int max_writes; + unsigned int writes_done; +}; + +static LIST_HEAD(weak_pages); + +struct grave_page { + struct list_head list; + unsigned int page_no; + unsigned int max_reads; + unsigned int reads_done; +}; + +static LIST_HEAD(grave_pages); + +static unsigned long *erase_block_wear = NULL; +static unsigned int wear_eb_count = 0; +static unsigned long total_wear = 0; +static unsigned int rptwear_cnt = 0; + /* MTD structure for NAND controller */ static struct mtd_info *nsmtd; @@ -381,6 +452,13 @@ static void free_device(struct nandsim *ns) } } +static char *get_partition_name(int i) +{ + char buf[64]; + sprintf(buf, "NAND simulator partition %d", i); + return kstrdup(buf, GFP_KERNEL); +} + /* * Initialize the nandsim structure. * @@ -390,7 +468,9 @@ static int init_nandsim(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; struct nandsim *ns = (struct nandsim *)(chip->priv); - int i; + int i, ret = 0; + u_int32_t remains; + u_int32_t next_offset; if (NS_IS_INITIALIZED(ns)) { NS_ERR("init_nandsim: nandsim is already initialized\n"); @@ -448,6 +528,40 @@ static int init_nandsim(struct mtd_info *mtd) } } + /* Fill the partition_info structure */ + if (parts_num > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + ret = -EINVAL; + goto error; + } + remains = ns->geom.totsz; + next_offset = 0; + for (i = 0; i < parts_num; ++i) { + unsigned long part = parts[i]; + if (!part || part > remains / ns->geom.secsz) { + NS_ERR("bad partition size.\n"); + ret = -EINVAL; + goto error; + } + ns->partitions[i].name = get_partition_name(i); + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = part * ns->geom.secsz; + next_offset += ns->partitions[i].size; + remains -= ns->partitions[i].size; + } + ns->nbparts = parts_num; + if (remains) { + if (parts_num + 1 > ARRAY_SIZE(ns->partitions)) { + NS_ERR("too many partitions.\n"); + ret = -EINVAL; + goto error; + } + ns->partitions[i].name = get_partition_name(i); + ns->partitions[i].offset = next_offset; + ns->partitions[i].size = remains; + ns->nbparts += 1; + } + /* Detect how many ID bytes the NAND chip outputs */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { if (second_id_byte != nand_flash_ids[i].id) @@ -474,7 +588,7 @@ static int init_nandsim(struct mtd_info *mtd) printk("sector address bytes: %u\n", ns->geom.secaddrbytes); printk("options: %#x\n", ns->options); - if (alloc_device(ns) != 0) + if ((ret = alloc_device(ns)) != 0) goto error; /* Allocate / initialize the internal buffer */ @@ -482,21 +596,17 @@ static int init_nandsim(struct mtd_info *mtd) if (!ns->buf.byte) { NS_ERR("init_nandsim: unable to allocate %u bytes for the internal buffer\n", ns->geom.pgszoob); + ret = -ENOMEM; goto error; } memset(ns->buf.byte, 0xFF, ns->geom.pgszoob); - /* Fill the partition_info structure */ - ns->part.name = "NAND simulator partition"; - ns->part.offset = 0; - ns->part.size = ns->geom.totsz; - return 0; error: free_device(ns); - return -ENOMEM; + return ret; } /* @@ -510,6 +620,287 @@ static void free_nandsim(struct nandsim *ns) return; } +static int parse_badblocks(struct nandsim *ns, struct mtd_info *mtd) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + loff_t offset; + + if (!badblocks) + return 0; + w = badblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + offset = erase_block_no * ns->geom.secsz; + if (mtd->block_markbad(mtd, offset)) { + NS_ERR("invalid badblocks.\n"); + return -EINVAL; + } + if (*w == ',') + w += 1; + } while (*w); + return 0; +} + +static int parse_weakblocks(void) +{ + char *w; + int zero_ok; + unsigned int erase_block_no; + unsigned int max_erases; + struct weak_block *wb; + + if (!weakblocks) + return 0; + w = weakblocks; + do { + zero_ok = (*w == '0' ? 1 : 0); + erase_block_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !erase_block_no) { + NS_ERR("invalid weakblocks.\n"); + return -EINVAL; + } + max_erases = 3; + if (*w == ':') { + w += 1; + max_erases = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wb = kzalloc(sizeof(*wb), GFP_KERNEL); + if (!wb) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wb->erase_block_no = erase_block_no; + wb->max_erases = max_erases; + list_add(&wb->list, &weak_blocks); + } while (*w); + return 0; +} + +static int erase_error(unsigned int erase_block_no) +{ + struct weak_block *wb; + + list_for_each_entry(wb, &weak_blocks, list) + if (wb->erase_block_no == erase_block_no) { + if (wb->erases_done >= wb->max_erases) + return 1; + wb->erases_done += 1; + return 0; + } + return 0; +} + +static int parse_weakpages(void) +{ + char *w; + int zero_ok; + unsigned int page_no; + unsigned int max_writes; + struct weak_page *wp; + + if (!weakpages) + return 0; + w = weakpages; + do { + zero_ok = (*w == '0' ? 1 : 0); + page_no = simple_strtoul(w, &w, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid weakpagess.\n"); + return -EINVAL; + } + max_writes = 3; + if (*w == ':') { + w += 1; + max_writes = simple_strtoul(w, &w, 0); + } + if (*w == ',') + w += 1; + wp = kzalloc(sizeof(*wp), GFP_KERNEL); + if (!wp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + wp->page_no = page_no; + wp->max_writes = max_writes; + list_add(&wp->list, &weak_pages); + } while (*w); + return 0; +} + +static int write_error(unsigned int page_no) +{ + struct weak_page *wp; + + list_for_each_entry(wp, &weak_pages, list) + if (wp->page_no == page_no) { + if (wp->writes_done >= wp->max_writes) + return 1; + wp->writes_done += 1; + return 0; + } + return 0; +} + +static int parse_gravepages(void) +{ + char *g; + int zero_ok; + unsigned int page_no; + unsigned int max_reads; + struct grave_page *gp; + + if (!gravepages) + return 0; + g = gravepages; + do { + zero_ok = (*g == '0' ? 1 : 0); + page_no = simple_strtoul(g, &g, 0); + if (!zero_ok && !page_no) { + NS_ERR("invalid gravepagess.\n"); + return -EINVAL; + } + max_reads = 3; + if (*g == ':') { + g += 1; + max_reads = simple_strtoul(g, &g, 0); + } + if (*g == ',') + g += 1; + gp = kzalloc(sizeof(*gp), GFP_KERNEL); + if (!gp) { + NS_ERR("unable to allocate memory.\n"); + return -ENOMEM; + } + gp->page_no = page_no; + gp->max_reads = max_reads; + list_add(&gp->list, &grave_pages); + } while (*g); + return 0; +} + +static int read_error(unsigned int page_no) +{ + struct grave_page *gp; + + list_for_each_entry(gp, &grave_pages, list) + if (gp->page_no == page_no) { + if (gp->reads_done >= gp->max_reads) + return 1; + gp->reads_done += 1; + return 0; + } + return 0; +} + +static void free_lists(void) +{ + struct list_head *pos, *n; + list_for_each_safe(pos, n, &weak_blocks) { + list_del(pos); + kfree(list_entry(pos, struct weak_block, list)); + } + list_for_each_safe(pos, n, &weak_pages) { + list_del(pos); + kfree(list_entry(pos, struct weak_page, list)); + } + list_for_each_safe(pos, n, &grave_pages) { + list_del(pos); + kfree(list_entry(pos, struct grave_page, list)); + } + kfree(erase_block_wear); +} + +static int setup_wear_reporting(struct mtd_info *mtd) +{ + size_t mem; + + if (!rptwear) + return 0; + wear_eb_count = mtd->size / mtd->erasesize; + mem = wear_eb_count * sizeof(unsigned long); + if (mem / sizeof(unsigned long) != wear_eb_count) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + erase_block_wear = kzalloc(mem, GFP_KERNEL); + if (!erase_block_wear) { + NS_ERR("Too many erase blocks for wear reporting\n"); + return -ENOMEM; + } + return 0; +} + +static void update_wear(unsigned int erase_block_no) +{ + unsigned long wmin = -1, wmax = 0, avg; + unsigned long deciles[10], decile_max[10], tot = 0; + unsigned int i; + + if (!erase_block_wear) + return; + total_wear += 1; + if (total_wear == 0) + NS_ERR("Erase counter total overflow\n"); + erase_block_wear[erase_block_no] += 1; + if (erase_block_wear[erase_block_no] == 0) + NS_ERR("Erase counter overflow for erase block %u\n", erase_block_no); + rptwear_cnt += 1; + if (rptwear_cnt < rptwear) + return; + rptwear_cnt = 0; + /* Calc wear stats */ + for (i = 0; i < wear_eb_count; ++i) { + unsigned long wear = erase_block_wear[i]; + if (wear < wmin) + wmin = wear; + if (wear > wmax) + wmax = wear; + tot += wear; + } + for (i = 0; i < 9; ++i) { + deciles[i] = 0; + decile_max[i] = (wmax * (i + 1) + 5) / 10; + } + deciles[9] = 0; + decile_max[9] = wmax; + for (i = 0; i < wear_eb_count; ++i) { + int d; + unsigned long wear = erase_block_wear[i]; + for (d = 0; d < 10; ++d) + if (wear <= decile_max[d]) { + deciles[d] += 1; + break; + } + } + avg = tot / wear_eb_count; + /* Output wear report */ + NS_INFO("*** Wear Report ***\n"); + NS_INFO("Total numbers of erases: %lu\n", tot); + NS_INFO("Number of erase blocks: %u\n", wear_eb_count); + NS_INFO("Average number of erases: %lu\n", avg); + NS_INFO("Maximum number of erases: %lu\n", wmax); + NS_INFO("Minimum number of erases: %lu\n", wmin); + for (i = 0; i < 10; ++i) { + unsigned long from = (i ? decile_max[i - 1] + 1 : 0); + if (from > decile_max[i]) + continue; + NS_INFO("Number of ebs with erase counts from %lu to %lu : %lu\n", + from, + decile_max[i], + deciles[i]); + } + NS_INFO("*** End of Wear Report ***\n"); +} + /* * Returns the string representation of 'state' state. */ @@ -822,9 +1213,31 @@ static void read_page(struct nandsim *ns, int num) NS_DBG("read_page: page %d not allocated\n", ns->regs.row); memset(ns->buf.byte, 0xFF, num); } else { + unsigned int page_no = ns->regs.row; NS_DBG("read_page: page %d allocated, reading from %d\n", ns->regs.row, ns->regs.column + ns->regs.off); + if (read_error(page_no)) { + int i; + memset(ns->buf.byte, 0xFF, num); + for (i = 0; i < num; ++i) + ns->buf.byte[i] = random32(); + NS_WARN("simulating read error in page %u\n", page_no); + return; + } memcpy(ns->buf.byte, NS_PAGE_BYTE_OFF(ns), num); + if (bitflips && random32() < (1 << 22)) { + int flips = 1; + if (bitflips > 1) + flips = (random32() % (int) bitflips) + 1; + while (flips--) { + int pos = random32() % (num * 8); + ns->buf.byte[pos / 8] ^= (1 << (pos % 8)); + NS_WARN("read_page: flipping bit %d in page %d " + "reading from %d ecc: corrected=%u failed=%u\n", + pos, ns->regs.row, ns->regs.column + ns->regs.off, + nsmtd->ecc_stats.corrected, nsmtd->ecc_stats.failed); + } + } } } @@ -883,6 +1296,7 @@ static int do_state_action(struct nandsim *ns, uint32_t action) { int num; int busdiv = ns->busw == 8 ? 1 : 2; + unsigned int erase_block_no, page_no; action &= ACTION_MASK; @@ -942,14 +1356,24 @@ static int do_state_action(struct nandsim *ns, uint32_t action) 8 * (ns->geom.pgaddrbytes - ns->geom.secaddrbytes)) | ns->regs.column; ns->regs.column = 0; + erase_block_no = ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift); + NS_DBG("do_state_action: erase sector at address %#x, off = %d\n", ns->regs.row, NS_RAW_OFFSET(ns)); - NS_LOG("erase sector %d\n", ns->regs.row >> (ns->geom.secshift - ns->geom.pgshift)); + NS_LOG("erase sector %u\n", erase_block_no); erase_sector(ns); NS_MDELAY(erase_delay); + if (erase_block_wear) + update_wear(erase_block_no); + + if (erase_error(erase_block_no)) { + NS_WARN("simulating erase failure in erase block %u\n", erase_block_no); + return -1; + } + break; case ACTION_PRGPAGE: @@ -972,6 +1396,8 @@ static int do_state_action(struct nandsim *ns, uint32_t action) if (prog_page(ns, num) == -1) return -1; + page_no = ns->regs.row; + NS_DBG("do_state_action: copy %d bytes from int buf to (%#x, %#x), raw off = %d\n", num, ns->regs.row, ns->regs.column, NS_RAW_OFFSET(ns) + ns->regs.off); NS_LOG("programm page %d\n", ns->regs.row); @@ -979,6 +1405,11 @@ static int do_state_action(struct nandsim *ns, uint32_t action) NS_UDELAY(programm_delay); NS_UDELAY(output_cycle * ns->geom.pgsz / 1000 / busdiv); + if (write_error(page_no)) { + NS_WARN("simulating write failure in page %u\n", page_no); + return -1; + } + break; case ACTION_ZEROOFF: @@ -1503,7 +1934,7 @@ static int __init ns_init_module(void) { struct nand_chip *chip; struct nandsim *nand; - int retval = -ENOMEM; + int retval = -ENOMEM, i; if (bus_width != 8 && bus_width != 16) { NS_ERR("wrong bus width (%d), use only 8 or 16\n", bus_width); @@ -1533,6 +1964,8 @@ static int __init ns_init_module(void) chip->verify_buf = ns_nand_verify_buf; chip->read_word = ns_nand_read_word; chip->ecc.mode = NAND_ECC_SOFT; + /* The NAND_SKIP_BBTSCAN option is necessary for 'overridesize' */ + /* and 'badblocks' parameters to work */ chip->options |= NAND_SKIP_BBTSCAN; /* @@ -1557,6 +1990,15 @@ static int __init ns_init_module(void) nsmtd->owner = THIS_MODULE; + if ((retval = parse_weakblocks()) != 0) + goto error; + + if ((retval = parse_weakpages()) != 0) + goto error; + + if ((retval = parse_gravepages()) != 0) + goto error; + if ((retval = nand_scan(nsmtd, 1)) != 0) { NS_ERR("can't register NAND Simulator\n"); if (retval > 0) @@ -1564,23 +2006,44 @@ static int __init ns_init_module(void) goto error; } - if ((retval = init_nandsim(nsmtd)) != 0) { - NS_ERR("scan_bbt: can't initialize the nandsim structure\n"); - goto error; + if (overridesize) { + u_int32_t new_size = nsmtd->erasesize << overridesize; + if (new_size >> overridesize != nsmtd->erasesize) { + NS_ERR("overridesize is too big\n"); + goto err_exit; + } + /* N.B. This relies on nand_scan not doing anything with the size before we change it */ + nsmtd->size = new_size; + chip->chipsize = new_size; + chip->chip_shift = ffs(new_size) - 1; } - if ((retval = nand_default_bbt(nsmtd)) != 0) { - free_nandsim(nand); - goto error; - } + if ((retval = setup_wear_reporting(nsmtd)) != 0) + goto err_exit; + + if ((retval = init_nandsim(nsmtd)) != 0) + goto err_exit; - /* Register NAND as one big partition */ - add_mtd_partitions(nsmtd, &nand->part, 1); + if ((retval = parse_badblocks(nand, nsmtd)) != 0) + goto err_exit; + + if ((retval = nand_default_bbt(nsmtd)) != 0) + goto err_exit; + + /* Register NAND partitions */ + if ((retval = add_mtd_partitions(nsmtd, &nand->partitions[0], nand->nbparts)) != 0) + goto err_exit; return 0; +err_exit: + free_nandsim(nand); + nand_release(nsmtd); + for (i = 0;i < ARRAY_SIZE(nand->partitions); ++i) + kfree(nand->partitions[i].name); error: kfree(nsmtd); + free_lists(); return retval; } @@ -1593,10 +2056,14 @@ module_init(ns_init_module); static void __exit ns_cleanup_module(void) { struct nandsim *ns = (struct nandsim *)(((struct nand_chip *)nsmtd->priv)->priv); + int i; free_nandsim(ns); /* Free nandsim private resources */ - nand_release(nsmtd); /* Unregisterd drived */ + nand_release(nsmtd); /* Unregister driver */ + for (i = 0;i < ARRAY_SIZE(ns->partitions); ++i) + kfree(ns->partitions[i].name); kfree(nsmtd); /* Free other structures */ + free_lists(); } module_exit(ns_cleanup_module); @@ -1604,4 +2071,3 @@ module_exit(ns_cleanup_module); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Artem B. Bityuckiy"); MODULE_DESCRIPTION ("The NAND flash simulator"); - diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b32035..5fac4c4 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned int diff0, diff1, diff2; + unsigned int bit, byte; - pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", - read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); + pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); - if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) - return 0; + diff0 = read_ecc[0] ^ calc_ecc[0]; + diff1 = read_ecc[1] ^ calc_ecc[1]; + diff2 = read_ecc[2] ^ calc_ecc[2]; + + pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", + __func__, + read_ecc[0], read_ecc[1], read_ecc[2], + calc_ecc[0], calc_ecc[1], calc_ecc[2], + diff0, diff1, diff2); + + if (diff0 == 0 && diff1 == 0 && diff2 == 0) + return 0; /* ECC is ok */ + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = (diff2 >> 2) & 1; + bit |= (diff2 >> 3) & 2; + bit |= (diff2 >> 4) & 4; + + /* calculate the byte position of the error */ + + byte = (diff1 << 1) & 0x80; + byte |= (diff1 << 2) & 0x40; + byte |= (diff1 << 3) & 0x20; + byte |= (diff1 << 4) & 0x10; + + byte |= (diff0 >> 3) & 0x08; + byte |= (diff0 >> 2) & 0x04; + byte |= (diff0 >> 1) & 0x02; + byte |= (diff0 >> 0) & 0x01; - /* we curently have no method for correcting the error */ + byte |= (diff2 << 8) & 0x100; - return -1; + dev_dbg(info->device, "correcting error bit %d, byte %d\n", + bit, byte); + + dat[byte] ^= (1 << bit); + return 1; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + if ((diff0 & ~(1<<fls(diff0))) == 0) + return 1; + + return 0; } /* ECC functions @@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) writel(ctrl, info->regs + S3C2410_NFCONF); } +static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); +} + static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); + pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, + ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + +static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); return 0; @@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %06lx\n", __func__, ecc); return 0; } @@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, break; case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; + case TYPE_S3C2440: chip->ecc.hwctl = s3c2440_nand_enable_hwecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc; diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 373bddc..c257d39 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -2,20 +2,18 @@ # linux/drivers/mtd/onenand/Kconfig # -menu "OneNAND Flash Device Drivers" - depends on MTD != n - -config MTD_ONENAND +menuconfig MTD_ONENAND tristate "OneNAND Device Support" depends on MTD help This enables support for accessing all type of OneNAND flash devices. For further information see - <http://www.samsung.com/Products/Semiconductor/Flash/OneNAND_TM/index.htm>. + <http://www.samsung.com/Products/Semiconductor/OneNAND/index.htm> + +if MTD_ONENAND config MTD_ONENAND_VERIFY_WRITE bool "Verify OneNAND page writes" - depends on MTD_ONENAND help This adds an extra check when data is written to the flash. The OneNAND flash device internally checks only bits transitioning @@ -25,13 +23,12 @@ config MTD_ONENAND_VERIFY_WRITE config MTD_ONENAND_GENERIC tristate "OneNAND Flash device via platform device driver" - depends on MTD_ONENAND && ARM + depends on ARM help Support for OneNAND flash via platform device driver. config MTD_ONENAND_OTP bool "OneNAND OTP Support" - depends on MTD_ONENAND help One Block of the NAND Flash Array memory is reserved as a One-Time Programmable Block memory area. @@ -43,4 +40,4 @@ config MTD_ONENAND_OTP OTP block is fully-guaranteed to be a valid block. -endmenu +endif # MTD_ONENAND diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb2..000794c 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1,9 +1,14 @@ /* * linux/drivers/mtd/onenand/onenand_base.c * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * + * Credits: + * Adrian Hunter <ext-adrian.hunter@nokia.com>: + * auto-placement support, read-while load support, various fixes + * Copyright (C) Nokia Corporation, 2007 + * * 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. @@ -94,16 +99,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr) */ static int onenand_block_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device Flash Core select, NAND Flash Block Address */ - int dfs = 0; - - if (block & this->density_mask) - dfs = 1; - - return (dfs << ONENAND_DDP_SHIFT) | - (block & (this->density_mask - 1)); - } + /* Device Flash Core select, NAND Flash Block Address */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); return block; } @@ -118,17 +116,11 @@ static int onenand_block_address(struct onenand_chip *this, int block) */ static int onenand_bufferram_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device BufferRAM Select */ - int dbs = 0; - - if (block & this->density_mask) - dbs = 1; - - return (dbs << ONENAND_DDP_SHIFT); - } + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; - return 0; + return ONENAND_DDP_CHIP0; } /** @@ -317,22 +309,25 @@ static int onenand_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (ctrl & ONENAND_CTRL_ERROR) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl); + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); + printk(KERN_ERR "onenand_wait: it's locked error.\n"); return ctrl; } if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); if (ecc & ONENAND_ECC_2BIT_ALL) { mtd->ecc_stats.failed++; return ecc; } else if (ecc & ONENAND_ECC_1BIT_ALL) mtd->ecc_stats.corrected++; } + } else if (state == FL_READING) { + printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return -EIO; } return 0; @@ -587,22 +582,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage, found = 0; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + blockpage = (int) (addr >> this->page_shift); + /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } + } - /* Is there valid data? */ - if (this->bufferram[i].block == block && - this->bufferram[i].page == page && - this->bufferram[i].valid) - return 1; + if (found && ONENAND_IS_DDP(this)) { + /* Select DataRAM for DDP */ + int block = (int) (addr >> this->erase_shift); + int value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } - return 0; + return found; } /** @@ -613,31 +618,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) * * Update BufferRAM information */ -static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, +static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int valid) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + blockpage = (int) (addr >> this->page_shift); - /* Invalidate BufferRAM */ - for (i = 0; i < MAX_BUFFERRAM; i++) { - if (this->bufferram[i].block == block && - this->bufferram[i].page == page) - this->bufferram[i].valid = 0; - } + /* Invalidate another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + this->bufferram[i].blockpage = -1; /* Update BufferRAM */ i = ONENAND_CURRENT_BUFFERRAM(this); - this->bufferram[i].block = block; - this->bufferram[i].page = page; - this->bufferram[i].valid = valid; + if (valid) + this->bufferram[i].blockpage = blockpage; + else + this->bufferram[i].blockpage = -1; +} - return 0; +/** + * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information + * @param mtd MTD data structure + * @param addr start address to invalidate + * @param len length to invalidate + * + * Invalidate BufferRAM information + */ +static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, + unsigned int len) +{ + struct onenand_chip *this = mtd->priv; + int i; + loff_t end_addr = addr + len; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; + if (buf_addr >= addr && buf_addr < end_addr) + this->bufferram[i].blockpage = -1; + } } /** @@ -716,7 +739,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); + printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -724,8 +747,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - /* TODO handling oob */ - stats = mtd->ecc_stats; /* Read-while-load method */ @@ -754,9 +775,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, * Now we issued chip 1 read and pointed chip 1 * bufferam so we have to point chip 0 bufferam. */ - if (this->device_id & ONENAND_DEVICE_IS_DDP && - unlikely(from == (this->chipsize >> 1))) { - this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); boundary = 1; } else boundary = 0; @@ -770,7 +791,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, break; /* Set up for next read from bufferRAM */ if (unlikely(boundary)) - this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, mtd->writesize, len - read); @@ -801,20 +822,63 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, } /** + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, + int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + thislen; + int lastgap = 0; + unsigned int i; + uint8_t *oob_buf = this->oob_buf; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < readend && free_end > readcol) { + int st = max_t(int,free->offset,readcol); + int ed = min_t(int,free_end,readend); + int n = ed - st; + memcpy(buf, oob_buf + st, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data + * @param mode operation mode * * OneNAND read out-of-band data from the spare area */ -int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int read = 0, thislen, column; + int read = 0, thislen, column, oobsize; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -822,21 +886,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, /* Initialize return length value */ *retlen = 0; + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); + return -EINVAL; + } + /* Do not allow reads past end of device */ - if (unlikely((from + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); + if (unlikely(from >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - column = from & (mtd->oobsize - 1); - while (read < len) { cond_resched(); - thislen = mtd->oobsize - column; + thislen = oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); @@ -846,11 +922,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */ - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + if (mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, buf, column, thislen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); - goto out; + printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); + break; } read += thislen; @@ -868,7 +947,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, } } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); @@ -878,17 +956,139 @@ out: /** * onenand_read_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operation description structure + * @param mtd: MTD device structure + * @param from: offset to read from + * @param ops: oob operation description structure */ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); +} + +/** + * onenand_bbt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long timeout; + unsigned int interrupt; + unsigned int ctrl; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + } + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); + /* Initial bad block case */ + if (ctrl & ONENAND_CTRL_LOAD) + return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_FATAL_ERROR; + } + + if (interrupt & ONENAND_INT_READ) { + int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + if (ecc & ONENAND_ECC_2BIT_ALL) + return ONENAND_BBT_READ_ERROR; + } else { + printk(KERN_ERR "onenand_bbt_wait: read timeout!" + "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + return 0; +} + +/** + * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan + * @param mtd MTD device structure + * @param from offset to read from + * @param ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area for bbt scan + */ +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0; + size_t len = ops->ooblen; + u_char *buf = ops->oobbuf; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); + + /* Initialize return value */ + ops->oobretlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + while (read < len) { + cond_resched(); + + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = onenand_bbt_wait(mtd, FL_READING); + if (ret) + break; + + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + read += thislen; + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Update Page size */ + from += mtd->writesize; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + ops->oobretlen = read; + return ret; } #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE @@ -897,14 +1097,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from - * @param len number of bytes to read and compare * */ -static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { struct onenand_chip *this = mtd->priv; - char *readp = this->page_buf; - int column = to & (mtd->oobsize - 1); + char oobbuf[64]; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); @@ -913,51 +1111,60 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to if (status) return status; - this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); - - for(i = 0; i < len; i++) - if (buf[i] != 0xFF && buf[i] != readp[i]) + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); + for (i = 0; i < mtd->oobsize; i++) + if (buf[i] != 0xFF && buf[i] != oobbuf[i]) return -EBADMSG; return 0; } /** - * onenand_verify_page - [GENERIC] verify the chip contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify + * onenand_verify - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param addr offset to read from + * @param len number of bytes to read and compare * - * Check DataRAM area directly */ -static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) +static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram0, *dataram1; + void __iomem *dataram; int ret = 0; + int thislen, column; - /* In partial page write, just skip it */ - if ((addr & (mtd->writesize - 1)) != 0) - return 0; + while (len != 0) { + thislen = min_t(int, mtd->writesize, len); + column = addr & (mtd->writesize - 1); + if (column + thislen > mtd->writesize) + thislen = mtd->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); - ret = this->wait(mtd, FL_READING); - if (ret) - return ret; + onenand_update_bufferram(mtd, addr, 0); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; - onenand_update_bufferram(mtd, addr, 1); + onenand_update_bufferram(mtd, addr, 1); - /* Check, if the two dataram areas are same */ - dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->writesize; + dataram = this->base + ONENAND_DATARAM; + dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); - if (memcmp(dataram0, dataram1, mtd->writesize)) - return -EBADMSG; + if (memcmp(buf, dataram + column, thislen)) + return -EBADMSG; + + len -= thislen; + buf += thislen; + addr += thislen; + } return 0; } #else -#define onenand_verify_page(...) (0) +#define onenand_verify(...) (0) #define onenand_verify_oob(...) (0) #endif @@ -988,60 +1195,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); + printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); + printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); return -EINVAL; } column = to & (mtd->writesize - 1); - subpage = column || (len & (mtd->writesize - 1)); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* Loop until all data write */ while (written < len) { - int bytes = mtd->writesize; - int thislen = min_t(int, bytes, len - written); + int thislen = min_t(int, mtd->writesize - column, len - written); u_char *wbuf = (u_char *) buf; cond_resched(); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); /* Partial page write */ + subpage = thislen < mtd->writesize; if (subpage) { - bytes = min_t(int, bytes - column, (int) len); memset(this->page_buf, 0xff, mtd->writesize); - memcpy(this->page_buf + column, buf, bytes); + memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; - /* Even though partial write, we need page size */ - thislen = mtd->writesize; } - this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); + ret = this->wait(mtd, FL_WRITING); + /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !subpage); + onenand_update_bufferram(mtd, to, !ret && !subpage); - ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write: write filaed %d\n", ret); break; } /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) wbuf, to); + ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write: verify failed %d\n", ret); break; } @@ -1064,51 +1268,118 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } /** + * onenand_fill_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param oob_buf oob buffer + * @param buf source address + * @param column oob offset to write to + * @param thislen oob length to write + */ +static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, + const u_char *buf, int column, int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + thislen; + int lastgap = 0; + unsigned int i; + + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + free = this->ecclayout->oobfree; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) { + int free_end = free->offset + free->length; + if (free->offset < writeend && free_end > writecol) { + int st = max_t(int,free->offset,writecol); + int ed = min_t(int,free_end,writeend); + int n = ed - st; + memcpy(oob_buf + st, buf, n); + buf += n; + } else if (column == 0) + break; + } + return 0; +} + +/** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write + * @param mode operation mode * * OneNAND write out-of-band */ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) + size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int column, ret = 0; + int column, ret = 0, oobsize; int written = 0; + u_char *oobbuf; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Initialize retlen, in case of early exit */ *retlen = 0; - /* Do not allow writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); + return -EINVAL; + } + + /* For compatibility with NAND: Do not allow write past end of page */ + if (unlikely(column + len > oobsize)) { + printk(KERN_ERR "onenand_write_oob: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); + oobbuf = this->oob_buf; + /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->oobsize, len - written); + int thislen = min_t(int, oobsize, len - written); cond_resched(); - column = to & (mtd->oobsize - 1); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ - memset(this->page_buf, 0xff, mtd->oobsize); - memcpy(this->page_buf + column, buf, thislen); - this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); + memset(oobbuf, 0xff, mtd->oobsize); + if (mode == MTD_OOB_AUTO) + onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen); + else + memcpy(oobbuf + column, buf, thislen); + this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1116,26 +1387,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); - goto out; + printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); + break; } - ret = onenand_verify_oob(mtd, buf, to, thislen); + ret = onenand_verify_oob(mtd, oobbuf, to); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); - goto out; + printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); + break; } written += thislen; - if (written == len) break; - to += thislen; + to += mtd->writesize; buf += thislen; + column = 0; } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); @@ -1146,17 +1416,24 @@ out: /** * onenand_write_oob - [MTD Interface] NAND write data and/or out-of-band - * @mtd: MTD device structure - * @from: offset to read from - * @ops: oob operation description structure + * @param mtd: MTD device structure + * @param to: offset to write + * @param ops: oob operation description structure */ static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); } /** @@ -1199,19 +1476,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); + printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if (unlikely((instr->len + instr->addr) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } @@ -1238,10 +1515,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1322,7 +1601,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); } /** @@ -1353,6 +1632,7 @@ static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs) * @param mtd MTD device structure * @param ofs offset relative to mtd start * @param len number of bytes to lock or unlock + * @param cmd lock or unlock command * * Lock or unlock one or more blocks */ @@ -1491,6 +1771,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) struct onenand_chip *this = mtd->priv; if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Set start block address */ + this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); @@ -1503,13 +1785,10 @@ static int onenand_unlock_all(struct mtd_info *mtd) continue; /* Workaround for all block unlock in DDP */ - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - loff_t ofs; - size_t len; - + if (ONENAND_IS_DDP(this)) { /* 1st block on another chip */ - ofs = this->chipsize >> 1; - len = 1 << this->erase_shift; + loff_t ofs = this->chipsize >> 1; + size_t len = mtd->erasesize; onenand_unlock(mtd, ofs, len); } @@ -1617,7 +1896,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -1823,12 +2102,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, #endif /* CONFIG_MTD_ONENAND_OTP */ /** - * onenand_lock_scheme - Check and set OneNAND lock scheme + * onenand_check_features - Check and set OneNAND features * @param mtd MTD data structure * - * Check and set OneNAND lock scheme + * Check and set OneNAND features + * - lock scheme */ -static void onenand_lock_scheme(struct mtd_info *mtd) +static void onenand_check_features(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; unsigned int density, process; @@ -1854,10 +2134,11 @@ static void onenand_lock_scheme(struct mtd_info *mtd) } /** - * onenand_print_device_info - Print device ID + * onenand_print_device_info - Print device & version ID * @param device device ID + * @param version version ID * - * Print device ID + * Print device & version ID */ static void onenand_print_device_info(int device, int version) { @@ -1961,26 +2242,28 @@ static int onenand_probe(struct mtd_info *mtd) density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; this->chipsize = (16 << density) << 20; /* Set density mask. it is used for DDP */ - this->density_mask = (1 << (density + 6)); + if (ONENAND_IS_DDP(this)) + this->density_mask = (1 << (density + 6)); + else + this->density_mask = 0; /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); mtd->oobsize = mtd->writesize >> 5; - /* Pagers per block is always 64 in OneNAND */ + /* Pages per a block are always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; - this->ppb_shift = (this->erase_shift - this->page_shift); - this->page_mask = (mtd->erasesize / mtd->writesize) - 1; + this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; /* REVIST: Multichip handling */ mtd->size = this->chipsize; - /* Check OneNAND lock scheme */ - onenand_lock_scheme(mtd); + /* Check OneNAND features */ + onenand_check_features(mtd); return 0; } @@ -2021,6 +2304,7 @@ static void onenand_resume(struct mtd_info *mtd) */ int onenand_scan(struct mtd_info *mtd, int maxchips) { + int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2054,15 +2338,25 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* Allocate buffers, if necessary */ if (!this->page_buf) { - size_t len; - len = mtd->writesize + mtd->oobsize; - this->page_buf = kmalloc(len, GFP_KERNEL); + this->page_buf = kzalloc(mtd->writesize, GFP_KERNEL); if (!this->page_buf) { printk(KERN_ERR "onenand_scan(): Can't allocate page_buf\n"); return -ENOMEM; } this->options |= ONENAND_PAGEBUF_ALLOC; } + if (!this->oob_buf) { + this->oob_buf = kzalloc(mtd->oobsize, GFP_KERNEL); + if (!this->oob_buf) { + printk(KERN_ERR "onenand_scan(): Can't allocate oob_buf\n"); + if (this->options & ONENAND_PAGEBUF_ALLOC) { + this->options &= ~ONENAND_PAGEBUF_ALLOC; + kfree(this->page_buf); + } + return -ENOMEM; + } + this->options |= ONENAND_OOBBUF_ALLOC; + } this->state = FL_READY; init_waitqueue_head(&this->wq); @@ -2092,12 +2386,23 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + this->ecclayout->oobavail = 0; + for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && + this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->oobavail = this->ecclayout->oobavail; + mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_SW; mtd->erase = onenand_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -2144,11 +2449,16 @@ void onenand_release(struct mtd_info *mtd) del_mtd_device (mtd); /* Free bad block table memory, if allocated */ - if (this->bbm) + if (this->bbm) { + struct bbm_info *bbm = this->bbm; + kfree(bbm->bbt); kfree(this->bbm); - /* Buffer allocated by onenand_scan */ + } + /* Buffers allocated by onenand_scan */ if (this->options & ONENAND_PAGEBUF_ALLOC) kfree(this->page_buf); + if (this->options & ONENAND_OOBBUF_ALLOC) + kfree(this->oob_buf); } EXPORT_SYMBOL_GPL(onenand_scan); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 98f8fd1..aecdd50 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,8 +17,8 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/compatmac.h> -extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); +extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer @@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr int startblock; loff_t from; size_t readlen, ooblen; + struct mtd_oob_ops ops; printk(KERN_INFO "Scanning device for bad blocks\n"); - len = 1; + len = 2; /* We need only read few bytes from the OOB area */ scanlen = ooblen = 0; @@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr startblock = 0; from = 0; + ops.mode = MTD_OOB_PLACE; + ops.ooblen = readlen; + ops.oobbuf = buf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; + for (i = startblock; i < numblocks; ) { int ret; for (j = 0; j < len; j++) { - size_t retlen; - /* No need to read pages fully, * just read required OOB bytes */ - ret = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0]); + ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops); /* If it is a initial bad block, just ignore it */ - if (ret && !(ret & ONENAND_CTRL_LOAD)) - return ret; + if (ret == ONENAND_BBT_READ_FATAL_ERROR) + return -EIO; - if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { + if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); @@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) * marked good / bad blocks and writes the bad block table(s) to * the selected place. * - * The bad block table memory is allocated here. It must be freed - * by calling the onenand_free_bbt function. + * The bad block table memory is allocated here. It is freed + * by the onenand_release function. * */ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 035cd9b..a61351f 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master, * (NOTE: this is 'size' not 'data_length'; size is * the full size of the entry.) */ - if (swab32(buf[i].size) == master->erasesize) { + + /* RedBoot can combine the FIS directory and + config partitions into a single eraseblock; + we assume wrong-endian if either the swapped + 'size' matches the eraseblock size precisely, + or if the swapped size actually fits in an + eraseblock while the unswapped size doesn't. */ + if (swab32(buf[i].size) == master->erasesize || + (buf[i].size > master->erasesize + && swab32(buf[i].size) < master->erasesize)) { int j; + /* Update numslots based on actual FIS directory size */ + numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); for (j = 0; j < numslots; ++j) { /* A single 0xff denotes a deleted entry. @@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master, swab32s(&buf[j].desc_cksum); swab32s(&buf[j].file_cksum); } + } else if (buf[i].size < master->erasesize) { + /* Update numslots based on actual FIS directory size */ + numslots = buf[i].size / sizeof(struct fis_image_desc); } break; - } else { - /* re-calculate of real numslots */ - numslots = buf[i].size / sizeof(struct fis_image_desc); } } if (i == numslots) { diff --git a/drivers/mtd/ubi/Kconfig b/drivers/mtd/ubi/Kconfig new file mode 100644 index 0000000..b9daf15 --- /dev/null +++ b/drivers/mtd/ubi/Kconfig @@ -0,0 +1,58 @@ +# drivers/mtd/ubi/Kconfig + +menu "UBI - Unsorted block images" + depends on MTD + +config MTD_UBI + tristate "Enable UBI" + depends on MTD + select CRC32 + help + UBI is a software layer above MTD layer which admits of LVM-like + logical volumes on top of MTD devices, hides some complexities of + flash chips like wear and bad blocks and provides some other useful + capabilities. Please, consult the MTD web site for more details + (www.linux-mtd.infradead.org). + +config MTD_UBI_WL_THRESHOLD + int "UBI wear-leveling threshold" + default 4096 + range 2 65536 + depends on MTD_UBI + help + This parameter defines the maximum difference between the highest + erase counter value and the lowest erase counter value of eraseblocks + of UBI devices. When this threshold is exceeded, UBI starts performing + wear leveling by means of moving data from eraseblock with low erase + counter to eraseblocks with high erase counter. Leave the default + value if unsure. + +config MTD_UBI_BEB_RESERVE + int "Percentage of reserved eraseblocks for bad eraseblocks handling" + default 1 + range 0 25 + depends on MTD_UBI + help + If the MTD device admits of bad eraseblocks (e.g. NAND flash), UBI + reserves some amount of physical eraseblocks to handle new bad + eraseblocks. For example, if a flash physical eraseblock becomes bad, + UBI uses these reserved physical eraseblocks to relocate the bad one. + This option specifies how many physical eraseblocks will be reserved + for bad eraseblock handling (percents of total number of good flash + eraseblocks). If the underlying flash does not admit of bad + eraseblocks (e.g. NOR flash), this value is ignored and nothing is + reserved. Leave the default value if unsure. + +config MTD_UBI_GLUEBI + bool "Emulate MTD devices" + default n + depends on MTD_UBI + help + This option enables MTD devices emulation on top of UBI volumes: for + each UBI volumes an MTD device is created, and all I/O to this MTD + device is redirected to the UBI volume. This is handy to make + MTD-oriented software (like JFFS2) work on top of UBI. Do not enable + this if no legacy software will be used. + +source "drivers/mtd/ubi/Kconfig.debug" +endmenu diff --git a/drivers/mtd/ubi/Kconfig.debug b/drivers/mtd/ubi/Kconfig.debug new file mode 100644 index 0000000..1e2ee22 --- /dev/null +++ b/drivers/mtd/ubi/Kconfig.debug @@ -0,0 +1,104 @@ +comment "UBI debugging options" + depends on MTD_UBI + +config MTD_UBI_DEBUG + bool "UBI debugging" + depends on SYSFS + depends on MTD_UBI + select DEBUG_FS + select KALLSYMS_ALL + help + This option enables UBI debugging. + +config MTD_UBI_DEBUG_MSG + bool "UBI debugging messages" + depends on MTD_UBI_DEBUG + default n + help + This option enables UBI debugging messages. + +config MTD_UBI_DEBUG_PARANOID + bool "Extra self-checks" + default n + depends on MTD_UBI_DEBUG + help + This option enables extra checks in UBI code. Note this slows UBI down + significantly. + +config MTD_UBI_DEBUG_DISABLE_BGT + bool "Do not enable the UBI background thread" + depends on MTD_UBI_DEBUG + default n + help + This option switches the background thread off by default. The thread + may be also be enabled/disabled via UBI sysfs. + +config MTD_UBI_DEBUG_USERSPACE_IO + bool "Direct user-space write/erase support" + default n + depends on MTD_UBI_DEBUG + help + By default, users cannot directly write and erase individual + eraseblocks of dynamic volumes, and have to use update operation + instead. This option enables this capability - it is very useful for + debugging and testing. + +config MTD_UBI_DEBUG_EMULATE_BITFLIPS + bool "Emulate flash bit-flips" + depends on MTD_UBI_DEBUG + default n + help + This option emulates bit-flips with probability 1/50, which in turn + causes scrubbing. Useful for debugging and stressing UBI. + +config MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES + bool "Emulate flash write failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates write failures with probability 1/100. Useful for + debugging and testing how UBI handlines errors. + +config MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES + bool "Emulate flash erase failures" + depends on MTD_UBI_DEBUG + default n + help + This option emulates erase failures with probability 1/100. Useful for + debugging and testing how UBI handlines errors. + +menu "Additional UBI debugging messages" + depends on MTD_UBI_DEBUG + +config MTD_UBI_DEBUG_MSG_BLD + bool "Additional UBI initialization and build messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables detailed UBI initialization and device build + debugging messages. + +config MTD_UBI_DEBUG_MSG_EBA + bool "Eraseblock association unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI eraseblock + association unit. + +config MTD_UBI_DEBUG_MSG_WL + bool "Wear-leveling unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI wear-leveling + unit. + +config MTD_UBI_DEBUG_MSG_IO + bool "Input/output unit messages" + default n + depends on MTD_UBI_DEBUG + help + This option enables debugging messages from the UBI input/output unit. + +endmenu # UBI debugging messages diff --git a/drivers/mtd/ubi/Makefile b/drivers/mtd/ubi/Makefile new file mode 100644 index 0000000..dd834e0 --- /dev/null +++ b/drivers/mtd/ubi/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_MTD_UBI) += ubi.o + +ubi-y += vtbl.o vmt.o upd.o build.o cdev.o kapi.o eba.o io.o wl.o scan.o +ubi-y += misc.o + +ubi-$(CONFIG_MTD_UBI_DEBUG) += debug.o +ubi-$(CONFIG_MTD_UBI_GLUEBI) += gluebi.o diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c new file mode 100644 index 0000000..555d594 --- /dev/null +++ b/drivers/mtd/ubi/build.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём), + * Frank Haverkamp + */ + +/* + * This file includes UBI initialization and building of UBI devices. At the + * moment UBI devices may only be added while UBI is initialized, but dynamic + * device add/remove functionality is planned. Also, at the moment we only + * attach UBI devices by scanning, which will become a bottleneck when flashes + * reach certain large size. Then one may improve UBI and add other methods. + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/stringify.h> +#include <linux/stat.h> +#include "ubi.h" + +/* Maximum length of the 'mtd=' parameter */ +#define MTD_PARAM_LEN_MAX 64 + +/** + * struct mtd_dev_param - MTD device parameter description data structure. + * @name: MTD device name or number string + * @vid_hdr_offs: VID header offset + * @data_offs: data offset + */ +struct mtd_dev_param +{ + char name[MTD_PARAM_LEN_MAX]; + int vid_hdr_offs; + int data_offs; +}; + +/* Numbers of elements set in the @mtd_dev_param array */ +static int mtd_devs = 0; + +/* MTD devices specification parameters */ +static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; + +/* Number of UBI devices in system */ +int ubi_devices_cnt; + +/* All UBI devices in system */ +struct ubi_device *ubi_devices[UBI_MAX_DEVICES]; + +/* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ +struct class *ubi_class; + +/* "Show" method for files in '/<sysfs>/class/ubi/' */ +static ssize_t ubi_version_show(struct class *class, char *buf) +{ + return sprintf(buf, "%d\n", UBI_VERSION); +} + +/* UBI version attribute ('/<sysfs>/class/ubi/version') */ +static struct class_attribute ubi_version = + __ATTR(version, S_IRUGO, ubi_version_show, NULL); + +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* UBI device attributes (correspond to files in '/<sysfs>/class/ubi/ubiX') */ +static struct device_attribute dev_eraseblock_size = + __ATTR(eraseblock_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_avail_eraseblocks = + __ATTR(avail_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_total_eraseblocks = + __ATTR(total_eraseblocks, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_volumes_count = + __ATTR(volumes_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_ec = + __ATTR(max_ec, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_reserved_for_bad = + __ATTR(reserved_for_bad, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bad_peb_count = + __ATTR(bad_peb_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_max_vol_count = + __ATTR(max_vol_count, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_min_io_size = + __ATTR(min_io_size, S_IRUGO, dev_attribute_show, NULL); +static struct device_attribute dev_bgt_enabled = + __ATTR(bgt_enabled, S_IRUGO, dev_attribute_show, NULL); + +/* "Show" method for files in '/<sysfs>/class/ubi/ubiX/' */ +static ssize_t dev_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct ubi_device *ubi; + + ubi = container_of(dev, struct ubi_device, dev); + if (attr == &dev_eraseblock_size) + return sprintf(buf, "%d\n", ubi->leb_size); + else if (attr == &dev_avail_eraseblocks) + return sprintf(buf, "%d\n", ubi->avail_pebs); + else if (attr == &dev_total_eraseblocks) + return sprintf(buf, "%d\n", ubi->good_peb_count); + else if (attr == &dev_volumes_count) + return sprintf(buf, "%d\n", ubi->vol_count); + else if (attr == &dev_max_ec) + return sprintf(buf, "%d\n", ubi->max_ec); + else if (attr == &dev_reserved_for_bad) + return sprintf(buf, "%d\n", ubi->beb_rsvd_pebs); + else if (attr == &dev_bad_peb_count) + return sprintf(buf, "%d\n", ubi->bad_peb_count); + else if (attr == &dev_max_vol_count) + return sprintf(buf, "%d\n", ubi->vtbl_slots); + else if (attr == &dev_min_io_size) + return sprintf(buf, "%d\n", ubi->min_io_size); + else if (attr == &dev_bgt_enabled) + return sprintf(buf, "%d\n", ubi->thread_enabled); + else + BUG(); + + return 0; +} + +/* Fake "release" method for UBI devices */ +static void dev_release(struct device *dev) { } + +/** + * ubi_sysfs_init - initialize sysfs for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int ubi_sysfs_init(struct ubi_device *ubi) +{ + int err; + + ubi->dev.release = dev_release; + ubi->dev.devt = MKDEV(ubi->major, 0); + ubi->dev.class = ubi_class; + sprintf(&ubi->dev.bus_id[0], UBI_NAME_STR"%d", ubi->ubi_num); + err = device_register(&ubi->dev); + if (err) + goto out; + + err = device_create_file(&ubi->dev, &dev_eraseblock_size); + if (err) + goto out_unregister; + err = device_create_file(&ubi->dev, &dev_avail_eraseblocks); + if (err) + goto out_eraseblock_size; + err = device_create_file(&ubi->dev, &dev_total_eraseblocks); + if (err) + goto out_avail_eraseblocks; + err = device_create_file(&ubi->dev, &dev_volumes_count); + if (err) + goto out_total_eraseblocks; + err = device_create_file(&ubi->dev, &dev_max_ec); + if (err) + goto out_volumes_count; + err = device_create_file(&ubi->dev, &dev_reserved_for_bad); + if (err) + goto out_volumes_max_ec; + err = device_create_file(&ubi->dev, &dev_bad_peb_count); + if (err) + goto out_reserved_for_bad; + err = device_create_file(&ubi->dev, &dev_max_vol_count); + if (err) + goto out_bad_peb_count; + err = device_create_file(&ubi->dev, &dev_min_io_size); + if (err) + goto out_max_vol_count; + err = device_create_file(&ubi->dev, &dev_bgt_enabled); + if (err) + goto out_min_io_size; + + return 0; + +out_min_io_size: + device_remove_file(&ubi->dev, &dev_min_io_size); +out_max_vol_count: + device_remove_file(&ubi->dev, &dev_max_vol_count); +out_bad_peb_count: + device_remove_file(&ubi->dev, &dev_bad_peb_count); +out_reserved_for_bad: + device_remove_file(&ubi->dev, &dev_reserved_for_bad); +out_volumes_max_ec: + device_remove_file(&ubi->dev, &dev_max_ec); +out_volumes_count: + device_remove_file(&ubi->dev, &dev_volumes_count); +out_total_eraseblocks: + device_remove_file(&ubi->dev, &dev_total_eraseblocks); +out_avail_eraseblocks: + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); +out_eraseblock_size: + device_remove_file(&ubi->dev, &dev_eraseblock_size); +out_unregister: + device_unregister(&ubi->dev); +out: + ubi_err("failed to initialize sysfs for %s", ubi->ubi_name); + return err; +} + +/** + * ubi_sysfs_close - close sysfs for an UBI device. + * @ubi: UBI device description object + */ +static void ubi_sysfs_close(struct ubi_device *ubi) +{ + device_remove_file(&ubi->dev, &dev_bgt_enabled); + device_remove_file(&ubi->dev, &dev_min_io_size); + device_remove_file(&ubi->dev, &dev_max_vol_count); + device_remove_file(&ubi->dev, &dev_bad_peb_count); + device_remove_file(&ubi->dev, &dev_reserved_for_bad); + device_remove_file(&ubi->dev, &dev_max_ec); + device_remove_file(&ubi->dev, &dev_volumes_count); + device_remove_file(&ubi->dev, &dev_total_eraseblocks); + device_remove_file(&ubi->dev, &dev_avail_eraseblocks); + device_remove_file(&ubi->dev, &dev_eraseblock_size); + device_unregister(&ubi->dev); +} + +/** + * kill_volumes - destroy all volumes. + * @ubi: UBI device description object + */ +static void kill_volumes(struct ubi_device *ubi) +{ + int i; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) + ubi_free_volume(ubi, i); +} + +/** + * uif_init - initialize user interfaces for an UBI device. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int uif_init(struct ubi_device *ubi) +{ + int i, err; + dev_t dev; + + mutex_init(&ubi->vtbl_mutex); + spin_lock_init(&ubi->volumes_lock); + + sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); + + /* + * Major numbers for the UBI character devices are allocated + * dynamically. Major numbers of volume character devices are + * equivalent to ones of the corresponding UBI character device. Minor + * numbers of UBI character devices are 0, while minor numbers of + * volume character devices start from 1. Thus, we allocate one major + * number and ubi->vtbl_slots + 1 minor numbers. + */ + err = alloc_chrdev_region(&dev, 0, ubi->vtbl_slots + 1, ubi->ubi_name); + if (err) { + ubi_err("cannot register UBI character devices"); + return err; + } + + cdev_init(&ubi->cdev, &ubi_cdev_operations); + ubi->major = MAJOR(dev); + dbg_msg("%s major is %u", ubi->ubi_name, ubi->major); + ubi->cdev.owner = THIS_MODULE; + + dev = MKDEV(ubi->major, 0); + err = cdev_add(&ubi->cdev, dev, 1); + if (err) { + ubi_err("cannot add character device %s", ubi->ubi_name); + goto out_unreg; + } + + err = ubi_sysfs_init(ubi); + if (err) + goto out_cdev; + + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i]) { + err = ubi_add_volume(ubi, i); + if (err) + goto out_volumes; + } + + return 0; + +out_volumes: + kill_volumes(ubi); + ubi_sysfs_close(ubi); +out_cdev: + cdev_del(&ubi->cdev); +out_unreg: + unregister_chrdev_region(MKDEV(ubi->major, 0), + ubi->vtbl_slots + 1); + return err; +} + +/** + * uif_close - close user interfaces for an UBI device. + * @ubi: UBI device description object + */ +static void uif_close(struct ubi_device *ubi) +{ + kill_volumes(ubi); + ubi_sysfs_close(ubi); + cdev_del(&ubi->cdev); + unregister_chrdev_region(MKDEV(ubi->major, 0), ubi->vtbl_slots + 1); +} + +/** + * attach_by_scanning - attach an MTD device using scanning method. + * @ubi: UBI device descriptor + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, currently this is the only method to attach UBI devices. Hopefully in + * the future we'll have more scalable attaching methods and avoid full media + * scanning. But even in this case scanning will be needed as a fall-back + * attaching method if there are some on-flash table corruptions. + */ +static int attach_by_scanning(struct ubi_device *ubi) +{ + int err; + struct ubi_scan_info *si; + + si = ubi_scan(ubi); + if (IS_ERR(si)) + return PTR_ERR(si); + + ubi->bad_peb_count = si->bad_peb_count; + ubi->good_peb_count = ubi->peb_count - ubi->bad_peb_count; + ubi->max_ec = si->max_ec; + ubi->mean_ec = si->mean_ec; + + err = ubi_read_volume_table(ubi, si); + if (err) + goto out_si; + + err = ubi_wl_init_scan(ubi, si); + if (err) + goto out_vtbl; + + err = ubi_eba_init_scan(ubi, si); + if (err) + goto out_wl; + + ubi_scan_destroy_si(si); + return 0; + +out_wl: + ubi_wl_close(ubi); +out_vtbl: + kfree(ubi->vtbl); +out_si: + ubi_scan_destroy_si(si); + return err; +} + +/** + * io_init - initialize I/O unit for a given UBI device. + * @ubi: UBI device description object + * + * If @ubi->vid_hdr_offset or @ubi->leb_start is zero, default offsets are + * assumed: + * o EC header is always at offset zero - this cannot be changed; + * o VID header starts just after the EC header at the closest address + * aligned to @io->@hdrs_min_io_size; + * o data starts just after the VID header at the closest address aligned to + * @io->@min_io_size + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int io_init(struct ubi_device *ubi) +{ + if (ubi->mtd->numeraseregions != 0) { + /* + * Some flashes have several erase regions. Different regions + * may have different eraseblock size and other + * characteristics. It looks like mostly multi-region flashes + * have one "main" region and one or more small regions to + * store boot loader code or boot parameters or whatever. I + * guess we should just pick the largest region. But this is + * not implemented. + */ + ubi_err("multiple regions, not implemented"); + return -EINVAL; + } + + /* + * Note, in this implementation we support MTD devices with 0x7FFFFFFF + * physical eraseblocks maximum. + */ + + ubi->peb_size = ubi->mtd->erasesize; + ubi->peb_count = ubi->mtd->size / ubi->mtd->erasesize; + ubi->flash_size = ubi->mtd->size; + + if (ubi->mtd->block_isbad && ubi->mtd->block_markbad) + ubi->bad_allowed = 1; + + ubi->min_io_size = ubi->mtd->writesize; + ubi->hdrs_min_io_size = ubi->mtd->writesize >> ubi->mtd->subpage_sft; + + /* Make sure minimal I/O unit is power of 2 */ + if (ubi->min_io_size == 0 || + (ubi->min_io_size & (ubi->min_io_size - 1))) { + ubi_err("bad min. I/O unit"); + return -EINVAL; + } + + ubi_assert(ubi->hdrs_min_io_size > 0); + ubi_assert(ubi->hdrs_min_io_size <= ubi->min_io_size); + ubi_assert(ubi->min_io_size % ubi->hdrs_min_io_size == 0); + + /* Calculate default aligned sizes of EC and VID headers */ + ubi->ec_hdr_alsize = ALIGN(UBI_EC_HDR_SIZE, ubi->hdrs_min_io_size); + ubi->vid_hdr_alsize = ALIGN(UBI_VID_HDR_SIZE, ubi->hdrs_min_io_size); + + dbg_msg("min_io_size %d", ubi->min_io_size); + dbg_msg("hdrs_min_io_size %d", ubi->hdrs_min_io_size); + dbg_msg("ec_hdr_alsize %d", ubi->ec_hdr_alsize); + dbg_msg("vid_hdr_alsize %d", ubi->vid_hdr_alsize); + + if (ubi->vid_hdr_offset == 0) + /* Default offset */ + ubi->vid_hdr_offset = ubi->vid_hdr_aloffset = + ubi->ec_hdr_alsize; + else { + ubi->vid_hdr_aloffset = ubi->vid_hdr_offset & + ~(ubi->hdrs_min_io_size - 1); + ubi->vid_hdr_shift = ubi->vid_hdr_offset - + ubi->vid_hdr_aloffset; + } + + /* Similar for the data offset */ + if (ubi->leb_start == 0) { + ubi->leb_start = ubi->vid_hdr_offset + ubi->vid_hdr_alsize; + ubi->leb_start = ALIGN(ubi->leb_start, ubi->min_io_size); + } + + dbg_msg("vid_hdr_offset %d", ubi->vid_hdr_offset); + dbg_msg("vid_hdr_aloffset %d", ubi->vid_hdr_aloffset); + dbg_msg("vid_hdr_shift %d", ubi->vid_hdr_shift); + dbg_msg("leb_start %d", ubi->leb_start); + + /* The shift must be aligned to 32-bit boundary */ + if (ubi->vid_hdr_shift % 4) { + ubi_err("unaligned VID header shift %d", + ubi->vid_hdr_shift); + return -EINVAL; + } + + /* Check sanity */ + if (ubi->vid_hdr_offset < UBI_EC_HDR_SIZE || + ubi->leb_start < ubi->vid_hdr_offset + UBI_VID_HDR_SIZE || + ubi->leb_start > ubi->peb_size - UBI_VID_HDR_SIZE || + ubi->leb_start % ubi->min_io_size) { + ubi_err("bad VID header (%d) or data offsets (%d)", + ubi->vid_hdr_offset, ubi->leb_start); + return -EINVAL; + } + + /* + * It may happen that EC and VID headers are situated in one minimal + * I/O unit. In this case we can only accept this UBI image in + * read-only mode. + */ + if (ubi->vid_hdr_offset + UBI_VID_HDR_SIZE <= ubi->hdrs_min_io_size) { + ubi_warn("EC and VID headers are in the same minimal I/O unit, " + "switch to read-only mode"); + ubi->ro_mode = 1; + } + + ubi->leb_size = ubi->peb_size - ubi->leb_start; + + if (!(ubi->mtd->flags & MTD_WRITEABLE)) { + ubi_msg("MTD device %d is write-protected, attach in " + "read-only mode", ubi->mtd->index); + ubi->ro_mode = 1; + } + + dbg_msg("leb_size %d", ubi->leb_size); + dbg_msg("ro_mode %d", ubi->ro_mode); + + /* + * Note, ideally, we have to initialize ubi->bad_peb_count here. But + * unfortunately, MTD does not provide this information. We should loop + * over all physical eraseblocks and invoke mtd->block_is_bad() for + * each physical eraseblock. So, we skip ubi->bad_peb_count + * uninitialized and initialize it after scanning. + */ + + return 0; +} + +/** + * attach_mtd_dev - attach an MTD device. + * @mtd_dev: MTD device name or number string + * @vid_hdr_offset: VID header offset + * @data_offset: data offset + * + * This function attaches an MTD device to UBI. It first treats @mtd_dev as the + * MTD device name, and tries to open it by this name. If it is unable to open, + * it tries to convert @mtd_dev to an integer and open the MTD device by its + * number. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int attach_mtd_dev(const char *mtd_dev, int vid_hdr_offset, + int data_offset) +{ + struct ubi_device *ubi; + struct mtd_info *mtd; + int i, err; + + mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd)) { + int mtd_num; + char *endp; + + if (PTR_ERR(mtd) != -ENODEV) + return PTR_ERR(mtd); + + /* + * Probably this is not MTD device name but MTD device number - + * check this out. + */ + mtd_num = simple_strtoul(mtd_dev, &endp, 0); + if (*endp != '\0' || mtd_dev == endp) { + ubi_err("incorrect MTD device: \"%s\"", mtd_dev); + return -ENODEV; + } + + mtd = get_mtd_device(NULL, mtd_num); + if (IS_ERR(mtd)) + return PTR_ERR(mtd); + } + + /* Check if we already have the same MTD device attached */ + for (i = 0; i < ubi_devices_cnt; i++) + if (ubi_devices[i]->mtd->index == mtd->index) { + ubi_err("mtd%d is already attached to ubi%d", + mtd->index, i); + err = -EINVAL; + goto out_mtd; + } + + ubi = ubi_devices[ubi_devices_cnt] = kzalloc(sizeof(struct ubi_device), + GFP_KERNEL); + if (!ubi) { + err = -ENOMEM; + goto out_mtd; + } + + ubi->ubi_num = ubi_devices_cnt; + ubi->mtd = mtd; + + dbg_msg("attaching mtd%d to ubi%d: VID header offset %d data offset %d", + ubi->mtd->index, ubi_devices_cnt, vid_hdr_offset, data_offset); + + ubi->vid_hdr_offset = vid_hdr_offset; + ubi->leb_start = data_offset; + err = io_init(ubi); + if (err) + goto out_free; + + err = attach_by_scanning(ubi); + if (err) { + dbg_err("failed to attach by scanning, error %d", err); + goto out_free; + } + + err = uif_init(ubi); + if (err) + goto out_detach; + + ubi_devices_cnt += 1; + + ubi_msg("attached mtd%d to ubi%d", ubi->mtd->index, ubi_devices_cnt); + ubi_msg("MTD device name: \"%s\"", ubi->mtd->name); + ubi_msg("MTD device size: %llu MiB", ubi->flash_size >> 20); + ubi_msg("physical eraseblock size: %d bytes (%d KiB)", + ubi->peb_size, ubi->peb_size >> 10); + ubi_msg("logical eraseblock size: %d bytes", ubi->leb_size); + ubi_msg("number of good PEBs: %d", ubi->good_peb_count); + ubi_msg("number of bad PEBs: %d", ubi->bad_peb_count); + ubi_msg("smallest flash I/O unit: %d", ubi->min_io_size); + ubi_msg("VID header offset: %d (aligned %d)", + ubi->vid_hdr_offset, ubi->vid_hdr_aloffset); + ubi_msg("data offset: %d", ubi->leb_start); + ubi_msg("max. allowed volumes: %d", ubi->vtbl_slots); + ubi_msg("wear-leveling threshold: %d", CONFIG_MTD_UBI_WL_THRESHOLD); + ubi_msg("number of internal volumes: %d", UBI_INT_VOL_COUNT); + ubi_msg("number of user volumes: %d", + ubi->vol_count - UBI_INT_VOL_COUNT); + ubi_msg("available PEBs: %d", ubi->avail_pebs); + ubi_msg("total number of reserved PEBs: %d", ubi->rsvd_pebs); + ubi_msg("number of PEBs reserved for bad PEB handling: %d", + ubi->beb_rsvd_pebs); + ubi_msg("max/mean erase counter: %d/%d", ubi->max_ec, ubi->mean_ec); + + /* Enable the background thread */ + if (!DBG_DISABLE_BGT) { + ubi->thread_enabled = 1; + wake_up_process(ubi->bgt_thread); + } + + return 0; + +out_detach: + ubi_eba_close(ubi); + ubi_wl_close(ubi); + kfree(ubi->vtbl); +out_free: + kfree(ubi); +out_mtd: + put_mtd_device(mtd); + ubi_devices[ubi_devices_cnt] = NULL; + return err; +} + +/** + * detach_mtd_dev - detach an MTD device. + * @ubi: UBI device description object + */ +static void detach_mtd_dev(struct ubi_device *ubi) +{ + int ubi_num = ubi->ubi_num, mtd_num = ubi->mtd->index; + + dbg_msg("detaching mtd%d from ubi%d", ubi->mtd->index, ubi_num); + uif_close(ubi); + ubi_eba_close(ubi); + ubi_wl_close(ubi); + kfree(ubi->vtbl); + put_mtd_device(ubi->mtd); + kfree(ubi_devices[ubi_num]); + ubi_devices[ubi_num] = NULL; + ubi_devices_cnt -= 1; + ubi_assert(ubi_devices_cnt >= 0); + ubi_msg("mtd%d is detached from ubi%d", mtd_num, ubi_num); +} + +static int __init ubi_init(void) +{ + int err, i, k; + + /* Ensure that EC and VID headers have correct size */ + BUILD_BUG_ON(sizeof(struct ubi_ec_hdr) != 64); + BUILD_BUG_ON(sizeof(struct ubi_vid_hdr) != 64); + + if (mtd_devs > UBI_MAX_DEVICES) { + printk("UBI error: too many MTD devices, maximum is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + ubi_class = class_create(THIS_MODULE, UBI_NAME_STR); + if (IS_ERR(ubi_class)) + return PTR_ERR(ubi_class); + + err = class_create_file(ubi_class, &ubi_version); + if (err) + goto out_class; + + /* Attach MTD devices */ + for (i = 0; i < mtd_devs; i++) { + struct mtd_dev_param *p = &mtd_dev_param[i]; + + cond_resched(); + + if (!p->name) { + dbg_err("empty name"); + err = -EINVAL; + goto out_detach; + } + + err = attach_mtd_dev(p->name, p->vid_hdr_offs, p->data_offs); + if (err) + goto out_detach; + } + + return 0; + +out_detach: + for (k = 0; k < i; k++) + detach_mtd_dev(ubi_devices[k]); + class_remove_file(ubi_class, &ubi_version); +out_class: + class_destroy(ubi_class); + return err; +} +module_init(ubi_init); + +static void __exit ubi_exit(void) +{ + int i, n = ubi_devices_cnt; + + for (i = 0; i < n; i++) + detach_mtd_dev(ubi_devices[i]); + class_remove_file(ubi_class, &ubi_version); + class_destroy(ubi_class); +} +module_exit(ubi_exit); + +/** + * bytes_str_to_int - convert a string representing number of bytes to an + * integer. + * @str: the string to convert + * + * This function returns positive resulting integer in case of success and a + * negative error code in case of failure. + */ +static int __init bytes_str_to_int(const char *str) +{ + char *endp; + unsigned long result; + + result = simple_strtoul(str, &endp, 0); + if (str == endp || result < 0) { + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + switch (*endp) { + case 'G': + result *= 1024; + case 'M': + result *= 1024; + case 'K': + case 'k': + result *= 1024; + if (endp[1] == 'i' && (endp[2] == '\0' || + endp[2] == 'B' || endp[2] == 'b')) + endp += 2; + case '\0': + break; + default: + printk("UBI error: incorrect bytes count: \"%s\"\n", str); + return -EINVAL; + } + + return result; +} + +/** + * ubi_mtd_param_parse - parse the 'mtd=' UBI parameter. + * @val: the parameter value to parse + * @kp: not used + * + * This function returns zero in case of success and a negative error code in + * case of error. + */ +static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) +{ + int i, len; + struct mtd_dev_param *p; + char buf[MTD_PARAM_LEN_MAX]; + char *pbuf = &buf[0]; + char *tokens[3] = {NULL, NULL, NULL}; + + if (mtd_devs == UBI_MAX_DEVICES) { + printk("UBI error: too many parameters, max. is %d\n", + UBI_MAX_DEVICES); + return -EINVAL; + } + + len = strnlen(val, MTD_PARAM_LEN_MAX); + if (len == MTD_PARAM_LEN_MAX) { + printk("UBI error: parameter \"%s\" is too long, max. is %d\n", + val, MTD_PARAM_LEN_MAX); + return -EINVAL; + } + + if (len == 0) { + printk("UBI warning: empty 'mtd=' parameter - ignored\n"); + return 0; + } + + strcpy(buf, val); + + /* Get rid of the final newline */ + if (buf[len - 1] == '\n') + buf[len - 1] = 0; + + for (i = 0; i < 3; i++) + tokens[i] = strsep(&pbuf, ","); + + if (pbuf) { + printk("UBI error: too many arguments at \"%s\"\n", val); + return -EINVAL; + } + + if (tokens[0] == '\0') + return -EINVAL; + + p = &mtd_dev_param[mtd_devs]; + strcpy(&p->name[0], tokens[0]); + + if (tokens[1]) + p->vid_hdr_offs = bytes_str_to_int(tokens[1]); + if (tokens[2]) + p->data_offs = bytes_str_to_int(tokens[2]); + + if (p->vid_hdr_offs < 0) + return p->vid_hdr_offs; + if (p->data_offs < 0) + return p->data_offs; + + mtd_devs += 1; + return 0; +} + +module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); +MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " + "mtd=<name|num>[,<vid_hdr_offs>,<data_offs>]. " + "Multiple \"mtd\" parameters may be specified.\n" + "MTD devices may be specified by their number or name. " + "Optional \"vid_hdr_offs\" and \"data_offs\" parameters " + "specify UBI VID header position and data starting " + "position to be used by UBI.\n" + "Example: mtd=content,1984,2048 mtd=4 - attach MTD device" + "with name content using VID header offset 1984 and data " + "start 2048, and MTD device number 4 using default " + "offsets"); + +MODULE_VERSION(__stringify(UBI_VERSION)); +MODULE_DESCRIPTION("UBI - Unsorted Block Images"); +MODULE_AUTHOR("Artem Bityutskiy"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c new file mode 100644 index 0000000..6612eb7 --- /dev/null +++ b/drivers/mtd/ubi/cdev.c @@ -0,0 +1,722 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * This file includes implementation of UBI character device operations. + * + * There are two kinds of character devices in UBI: UBI character devices and + * UBI volume character devices. UBI character devices allow users to + * manipulate whole volumes: create, remove, and re-size them. Volume character + * devices provide volume I/O capabilities. + * + * Major and minor numbers are assigned dynamically to both UBI and volume + * character devices. + */ + +#include <linux/module.h> +#include <linux/stat.h> +#include <linux/ioctl.h> +#include <linux/capability.h> +#include <mtd/ubi-user.h> +#include <asm/uaccess.h> +#include <asm/div64.h> +#include "ubi.h" + +/* + * Maximum sequence numbers of UBI and volume character device IOCTLs (direct + * logical eraseblock erase is a debug-only feature). + */ +#define UBI_CDEV_IOC_MAX_SEQ 2 +#ifndef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO +#define VOL_CDEV_IOC_MAX_SEQ 1 +#else +#define VOL_CDEV_IOC_MAX_SEQ 2 +#endif + +/** + * major_to_device - get UBI device object by character device major number. + * @major: major number + * + * This function returns a pointer to the UBI device object. + */ +static struct ubi_device *major_to_device(int major) +{ + int i; + + for (i = 0; i < ubi_devices_cnt; i++) + if (ubi_devices[i] && ubi_devices[i]->major == major) + return ubi_devices[i]; + BUG(); +} + +/** + * get_exclusive - get exclusive access to an UBI volume. + * @desc: volume descriptor + * + * This function changes UBI volume open mode to "exclusive". Returns previous + * mode value (positive integer) in case of success and a negative error code + * in case of failure. + */ +static int get_exclusive(struct ubi_volume_desc *desc) +{ + int users, err; + struct ubi_volume *vol = desc->vol; + + spin_lock(&vol->ubi->volumes_lock); + users = vol->readers + vol->writers + vol->exclusive; + ubi_assert(users > 0); + if (users > 1) { + dbg_err("%d users for volume %d", users, vol->vol_id); + err = -EBUSY; + } else { + vol->readers = vol->writers = 0; + vol->exclusive = 1; + err = desc->mode; + desc->mode = UBI_EXCLUSIVE; + } + spin_unlock(&vol->ubi->volumes_lock); + + return err; +} + +/** + * revoke_exclusive - revoke exclusive mode. + * @desc: volume descriptor + * @mode: new mode to switch to + */ +static void revoke_exclusive(struct ubi_volume_desc *desc, int mode) +{ + struct ubi_volume *vol = desc->vol; + + spin_lock(&vol->ubi->volumes_lock); + ubi_assert(vol->readers == 0 && vol->writers == 0); + ubi_assert(vol->exclusive == 1 && desc->mode == UBI_EXCLUSIVE); + vol->exclusive = 0; + if (mode == UBI_READONLY) + vol->readers = 1; + else if (mode == UBI_READWRITE) + vol->writers = 1; + else + vol->exclusive = 1; + spin_unlock(&vol->ubi->volumes_lock); + + desc->mode = mode; +} + +static int vol_cdev_open(struct inode *inode, struct file *file) +{ + struct ubi_volume_desc *desc; + const struct ubi_device *ubi = major_to_device(imajor(inode)); + int vol_id = iminor(inode) - 1; + int mode; + + if (file->f_mode & FMODE_WRITE) + mode = UBI_READWRITE; + else + mode = UBI_READONLY; + + dbg_msg("open volume %d, mode %d", vol_id, mode); + + desc = ubi_open_volume(ubi->ubi_num, vol_id, mode); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + file->private_data = desc; + return 0; +} + +static int vol_cdev_release(struct inode *inode, struct file *file) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + + dbg_msg("release volume %d, mode %d", vol->vol_id, desc->mode); + + if (vol->updating) { + ubi_warn("update of volume %d not finished, volume is damaged", + vol->vol_id); + vol->updating = 0; + kfree(vol->upd_buf); + } + + ubi_close_volume(desc); + return 0; +} + +static loff_t vol_cdev_llseek(struct file *file, loff_t offset, int origin) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + loff_t new_offset; + + if (vol->updating) { + /* Update is in progress, seeking is prohibited */ + dbg_err("updating"); + return -EBUSY; + } + + switch (origin) { + case 0: /* SEEK_SET */ + new_offset = offset; + break; + case 1: /* SEEK_CUR */ + new_offset = file->f_pos + offset; + break; + case 2: /* SEEK_END */ + new_offset = vol->used_bytes + offset; + break; + default: + return -EINVAL; + } + + if (new_offset < 0 || new_offset > vol->used_bytes) { + dbg_err("bad seek %lld", new_offset); + return -EINVAL; + } + + dbg_msg("seek volume %d, offset %lld, origin %d, new offset %lld", + vol->vol_id, offset, origin, new_offset); + + file->f_pos = new_offset; + return new_offset; +} + +static ssize_t vol_cdev_read(struct file *file, __user char *buf, size_t count, + loff_t *offp) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, lnum, off, len, vol_id = desc->vol->vol_id, tbuf_size; + size_t count_save = count; + void *tbuf; + uint64_t tmp; + + dbg_msg("read %zd bytes from offset %lld of volume %d", + count, *offp, vol_id); + + if (vol->updating) { + dbg_err("updating"); + return -EBUSY; + } + if (vol->upd_marker) { + dbg_err("damaged volume, update marker is set"); + return -EBADF; + } + if (*offp == vol->used_bytes || count == 0) + return 0; + + if (vol->corrupted) + dbg_msg("read from corrupted volume %d", vol_id); + + if (*offp + count > vol->used_bytes) + count_save = count = vol->used_bytes - *offp; + + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); + tbuf = kmalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + len = count > tbuf_size ? tbuf_size : count; + + tmp = *offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + do { + cond_resched(); + + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, tbuf, off, len, 0); + if (err) + break; + + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + count -= len; + *offp += len; + + err = copy_to_user(buf, tbuf, len); + if (err) { + err = -EFAULT; + break; + } + + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } while (count); + + kfree(tbuf); + return err ? err : count_save - count; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO + +/* + * This function allows to directly write to dynamic UBI volumes, without + * issuing the volume update operation. Available only as a debugging feature. + * Very useful for testing UBI. + */ +static ssize_t vol_cdev_direct_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int lnum, off, len, tbuf_size, vol_id = vol->vol_id, err = 0; + size_t count_save = count; + char *tbuf; + uint64_t tmp; + + dbg_msg("requested: write %zd bytes to offset %lld of volume %u", + count, *offp, desc->vol->vol_id); + + if (vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + tmp = *offp; + off = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + if (off % ubi->min_io_size) { + dbg_err("unaligned position"); + return -EINVAL; + } + + if (*offp + count > vol->used_bytes) + count_save = count = vol->used_bytes - *offp; + + /* We can write only in fractions of the minimum I/O unit */ + if (count % ubi->min_io_size) { + dbg_err("unaligned write length"); + return -EINVAL; + } + + tbuf_size = vol->usable_leb_size; + if (count < tbuf_size) + tbuf_size = ALIGN(count, ubi->min_io_size); + tbuf = kmalloc(tbuf_size, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + len = count > tbuf_size ? tbuf_size : count; + + while (count) { + cond_resched(); + + if (off + len >= vol->usable_leb_size) + len = vol->usable_leb_size - off; + + err = copy_from_user(tbuf, buf, len); + if (err) { + err = -EFAULT; + break; + } + + err = ubi_eba_write_leb(ubi, vol_id, lnum, tbuf, off, len, + UBI_UNKNOWN); + if (err) + break; + + off += len; + if (off == vol->usable_leb_size) { + lnum += 1; + off -= vol->usable_leb_size; + } + + count -= len; + *offp += len; + buf += len; + len = count > tbuf_size ? tbuf_size : count; + } + + kfree(tbuf); + return err ? err : count_save - count; +} + +#else +#define vol_cdev_direct_write(file, buf, count, offp) -EPERM +#endif /* CONFIG_MTD_UBI_DEBUG_USERSPACE_IO */ + +static ssize_t vol_cdev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offp) +{ + int err = 0; + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + + if (!vol->updating) + return vol_cdev_direct_write(file, buf, count, offp); + + err = ubi_more_update_data(ubi, vol->vol_id, buf, count); + if (err < 0) { + ubi_err("cannot write %zd bytes of update data", count); + return err; + } + + if (err) { + /* + * Update is finished, @err contains number of actually written + * bytes now. + */ + count = err; + + err = ubi_check_volume(ubi, vol->vol_id); + if (err < 0) + return err; + + if (err) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol->vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + revoke_exclusive(desc, UBI_READWRITE); + } + + *offp += count; + return count; +} + +static int vol_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ubi_volume_desc *desc = file->private_data; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > VOL_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_VOL_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + switch (cmd) { + + /* Volume update command */ + case UBI_IOCVOLUP: + { + int64_t bytes, rsvd_bytes; + + if (!capable(CAP_SYS_RESOURCE)) { + err = -EPERM; + break; + } + + err = copy_from_user(&bytes, argp, sizeof(int64_t)); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY) { + err = -EROFS; + break; + } + + rsvd_bytes = vol->reserved_pebs * (ubi->leb_size-vol->data_pad); + if (bytes < 0 || bytes > rsvd_bytes) { + err = -EINVAL; + break; + } + + err = get_exclusive(desc); + if (err < 0) + break; + + err = ubi_start_update(ubi, vol->vol_id, bytes); + if (bytes == 0) + revoke_exclusive(desc, UBI_READWRITE); + + file->f_pos = 0; + break; + } + +#ifdef CONFIG_MTD_UBI_DEBUG_USERSPACE_IO + /* Logical eraseblock erasure command */ + case UBI_IOCEBER: + { + int32_t lnum; + + err = __get_user(lnum, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + if (desc->mode == UBI_READONLY) { + err = -EROFS; + break; + } + + if (lnum < 0 || lnum >= vol->reserved_pebs) { + err = -EINVAL; + break; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME) { + err = -EROFS; + break; + } + + dbg_msg("erase LEB %d:%d", vol->vol_id, lnum); + err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum); + if (err) + break; + + err = ubi_wl_flush(ubi); + break; + } +#endif + + default: + err = -ENOTTY; + break; + } + + return err; +} + +/** + * verify_mkvol_req - verify volume creation request. + * @ubi: UBI device description object + * @req: the request to check + * + * This function zero if the request is correct, and %-EINVAL if not. + */ +static int verify_mkvol_req(const struct ubi_device *ubi, + const struct ubi_mkvol_req *req) +{ + int n, err = -EINVAL; + + if (req->bytes < 0 || req->alignment < 0 || req->vol_type < 0 || + req->name_len < 0) + goto bad; + + if ((req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) && + req->vol_id != UBI_VOL_NUM_AUTO) + goto bad; + + if (req->alignment == 0) + goto bad; + + if (req->bytes == 0) + goto bad; + + if (req->vol_type != UBI_DYNAMIC_VOLUME && + req->vol_type != UBI_STATIC_VOLUME) + goto bad; + + if (req->alignment > ubi->leb_size) + goto bad; + + n = req->alignment % ubi->min_io_size; + if (req->alignment != 1 && n) + goto bad; + + if (req->name_len > UBI_VOL_NAME_MAX) { + err = -ENAMETOOLONG; + goto bad; + } + + return 0; + +bad: + dbg_err("bad volume creation request"); + ubi_dbg_dump_mkvol_req(req); + return err; +} + +/** + * verify_rsvol_req - verify volume re-size request. + * @ubi: UBI device description object + * @req: the request to check + * + * This function returns zero if the request is correct, and %-EINVAL if not. + */ +static int verify_rsvol_req(const struct ubi_device *ubi, + const struct ubi_rsvol_req *req) +{ + if (req->bytes <= 0) + return -EINVAL; + + if (req->vol_id < 0 || req->vol_id >= ubi->vtbl_slots) + return -EINVAL; + + return 0; +} + +static int ubi_cdev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct ubi_device *ubi; + struct ubi_volume_desc *desc; + void __user *argp = (void __user *)arg; + + if (_IOC_NR(cmd) > UBI_CDEV_IOC_MAX_SEQ || + _IOC_TYPE(cmd) != UBI_IOC_MAGIC) + return -ENOTTY; + + if (_IOC_DIR(cmd) && _IOC_READ) + err = !access_ok(VERIFY_WRITE, argp, _IOC_SIZE(cmd)); + else if (_IOC_DIR(cmd) && _IOC_WRITE) + err = !access_ok(VERIFY_READ, argp, _IOC_SIZE(cmd)); + if (err) + return -EFAULT; + + if (!capable(CAP_SYS_RESOURCE)) + return -EPERM; + + ubi = major_to_device(imajor(inode)); + if (IS_ERR(ubi)) + return PTR_ERR(ubi); + + switch (cmd) { + /* Create volume command */ + case UBI_IOCMKVOL: + { + struct ubi_mkvol_req req; + + dbg_msg("create volume"); + err = __copy_from_user(&req, argp, + sizeof(struct ubi_mkvol_req)); + if (err) { + err = -EFAULT; + break; + } + + err = verify_mkvol_req(ubi, &req); + if (err) + break; + + req.name[req.name_len] = '\0'; + + err = ubi_create_volume(ubi, &req); + if (err) + break; + + err = __put_user(req.vol_id, (__user int32_t *)argp); + if (err) + err = -EFAULT; + + break; + } + + /* Remove volume command */ + case UBI_IOCRMVOL: + { + int vol_id; + + dbg_msg("remove volume"); + err = __get_user(vol_id, (__user int32_t *)argp); + if (err) { + err = -EFAULT; + break; + } + + desc = ubi_open_volume(ubi->ubi_num, vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + err = ubi_remove_volume(desc); + if (err) + ubi_close_volume(desc); + + break; + } + + /* Re-size volume command */ + case UBI_IOCRSVOL: + { + int pebs; + uint64_t tmp; + struct ubi_rsvol_req req; + + dbg_msg("re-size volume"); + err = __copy_from_user(&req, argp, + sizeof(struct ubi_rsvol_req)); + if (err) { + err = -EFAULT; + break; + } + + err = verify_rsvol_req(ubi, &req); + if (err) + break; + + desc = ubi_open_volume(ubi->ubi_num, req.vol_id, UBI_EXCLUSIVE); + if (IS_ERR(desc)) { + err = PTR_ERR(desc); + break; + } + + tmp = req.bytes; + pebs = !!do_div(tmp, desc->vol->usable_leb_size); + pebs += tmp; + + err = ubi_resize_volume(desc, pebs); + ubi_close_volume(desc); + break; + } + + default: + err = -ENOTTY; + break; + } + + return err; +} + +/* UBI character device operations */ +struct file_operations ubi_cdev_operations = { + .owner = THIS_MODULE, + .ioctl = ubi_cdev_ioctl, + .llseek = no_llseek +}; + +/* UBI volume character device operations */ +struct file_operations ubi_vol_cdev_operations = { + .owner = THIS_MODULE, + .open = vol_cdev_open, + .release = vol_cdev_release, + .llseek = vol_cdev_llseek, + .read = vol_cdev_read, + .write = vol_cdev_write, + .ioctl = vol_cdev_ioctl +}; diff --git a/drivers/mtd/ubi/debug.c b/drivers/mtd/ubi/debug.c new file mode 100644 index 0000000..8636422 --- /dev/null +++ b/drivers/mtd/ubi/debug.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * Here we keep all the UBI debugging stuff which should normally be disabled + * and compiled-out, but it is extremely helpful when hunting bugs or doing big + * changes. + */ + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG + +#include "ubi.h" + +/** + * ubi_dbg_dump_ec_hdr - dump an erase counter header. + * @ec_hdr: the erase counter header to dump + */ +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr) +{ + dbg_msg("erase counter header dump:"); + dbg_msg("magic %#08x", ubi32_to_cpu(ec_hdr->magic)); + dbg_msg("version %d", (int)ec_hdr->version); + dbg_msg("ec %llu", (long long)ubi64_to_cpu(ec_hdr->ec)); + dbg_msg("vid_hdr_offset %d", ubi32_to_cpu(ec_hdr->vid_hdr_offset)); + dbg_msg("data_offset %d", ubi32_to_cpu(ec_hdr->data_offset)); + dbg_msg("hdr_crc %#08x", ubi32_to_cpu(ec_hdr->hdr_crc)); + dbg_msg("erase counter header hexdump:"); + ubi_dbg_hexdump(ec_hdr, UBI_EC_HDR_SIZE); +} + +/** + * ubi_dbg_dump_vid_hdr - dump a volume identifier header. + * @vid_hdr: the volume identifier header to dump + */ +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr) +{ + dbg_msg("volume identifier header dump:"); + dbg_msg("magic %08x", ubi32_to_cpu(vid_hdr->magic)); + dbg_msg("version %d", (int)vid_hdr->version); + dbg_msg("vol_type %d", (int)vid_hdr->vol_type); + dbg_msg("copy_flag %d", (int)vid_hdr->copy_flag); + dbg_msg("compat %d", (int)vid_hdr->compat); + dbg_msg("vol_id %d", ubi32_to_cpu(vid_hdr->vol_id)); + dbg_msg("lnum %d", ubi32_to_cpu(vid_hdr->lnum)); + dbg_msg("leb_ver %u", ubi32_to_cpu(vid_hdr->leb_ver)); + dbg_msg("data_size %d", ubi32_to_cpu(vid_hdr->data_size)); + dbg_msg("used_ebs %d", ubi32_to_cpu(vid_hdr->used_ebs)); + dbg_msg("data_pad %d", ubi32_to_cpu(vid_hdr->data_pad)); + dbg_msg("sqnum %llu", + (unsigned long long)ubi64_to_cpu(vid_hdr->sqnum)); + dbg_msg("hdr_crc %08x", ubi32_to_cpu(vid_hdr->hdr_crc)); + dbg_msg("volume identifier header hexdump:"); +} + +/** + * ubi_dbg_dump_vol_info- dump volume information. + * @vol: UBI volume description object + */ +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol) +{ + dbg_msg("volume information dump:"); + dbg_msg("vol_id %d", vol->vol_id); + dbg_msg("reserved_pebs %d", vol->reserved_pebs); + dbg_msg("alignment %d", vol->alignment); + dbg_msg("data_pad %d", vol->data_pad); + dbg_msg("vol_type %d", vol->vol_type); + dbg_msg("name_len %d", vol->name_len); + dbg_msg("usable_leb_size %d", vol->usable_leb_size); + dbg_msg("used_ebs %d", vol->used_ebs); + dbg_msg("used_bytes %lld", vol->used_bytes); + dbg_msg("last_eb_bytes %d", vol->last_eb_bytes); + dbg_msg("corrupted %d", vol->corrupted); + dbg_msg("upd_marker %d", vol->upd_marker); + + if (vol->name_len <= UBI_VOL_NAME_MAX && + strnlen(vol->name, vol->name_len + 1) == vol->name_len) { + dbg_msg("name %s", vol->name); + } else { + dbg_msg("the 1st 5 characters of the name: %c%c%c%c%c", + vol->name[0], vol->name[1], vol->name[2], + vol->name[3], vol->name[4]); + } +} + +/** + * ubi_dbg_dump_vtbl_record - dump a &struct ubi_vtbl_record object. + * @r: the object to dump + * @idx: volume table index + */ +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx) +{ + int name_len = ubi16_to_cpu(r->name_len); + + dbg_msg("volume table record %d dump:", idx); + dbg_msg("reserved_pebs %d", ubi32_to_cpu(r->reserved_pebs)); + dbg_msg("alignment %d", ubi32_to_cpu(r->alignment)); + dbg_msg("data_pad %d", ubi32_to_cpu(r->data_pad)); + dbg_msg("vol_type %d", (int)r->vol_type); + dbg_msg("upd_marker %d", (int)r->upd_marker); + dbg_msg("name_len %d", name_len); + + if (r->name[0] == '\0') { + dbg_msg("name NULL"); + return; + } + + if (name_len <= UBI_VOL_NAME_MAX && + strnlen(&r->name[0], name_len + 1) == name_len) { + dbg_msg("name %s", &r->name[0]); + } else { + dbg_msg("1st 5 characters of the name: %c%c%c%c%c", + r->name[0], r->name[1], r->name[2], r->name[3], + r->name[4]); + } + dbg_msg("crc %#08x", ubi32_to_cpu(r->crc)); +} + +/** + * ubi_dbg_dump_sv - dump a &struct ubi_scan_volume object. + * @sv: the object to dump + */ +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv) +{ + dbg_msg("volume scanning information dump:"); + dbg_msg("vol_id %d", sv->vol_id); + dbg_msg("highest_lnum %d", sv->highest_lnum); + dbg_msg("leb_count %d", sv->leb_count); + dbg_msg("compat %d", sv->compat); + dbg_msg("vol_type %d", sv->vol_type); + dbg_msg("used_ebs %d", sv->used_ebs); + dbg_msg("last_data_size %d", sv->last_data_size); + dbg_msg("data_pad %d", sv->data_pad); +} + +/** + * ubi_dbg_dump_seb - dump a &struct ubi_scan_leb object. + * @seb: the object to dump + * @type: object type: 0 - not corrupted, 1 - corrupted + */ +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type) +{ + dbg_msg("eraseblock scanning information dump:"); + dbg_msg("ec %d", seb->ec); + dbg_msg("pnum %d", seb->pnum); + if (type == 0) { + dbg_msg("lnum %d", seb->lnum); + dbg_msg("scrub %d", seb->scrub); + dbg_msg("sqnum %llu", seb->sqnum); + dbg_msg("leb_ver %u", seb->leb_ver); + } +} + +/** + * ubi_dbg_dump_mkvol_req - dump a &struct ubi_mkvol_req object. + * @req: the object to dump + */ +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req) +{ + char nm[17]; + + dbg_msg("volume creation request dump:"); + dbg_msg("vol_id %d", req->vol_id); + dbg_msg("alignment %d", req->alignment); + dbg_msg("bytes %lld", (long long)req->bytes); + dbg_msg("vol_type %d", req->vol_type); + dbg_msg("name_len %d", req->name_len); + + memcpy(nm, req->name, 16); + nm[16] = 0; + dbg_msg("the 1st 16 characters of the name: %s", nm); +} + +#define BYTES_PER_LINE 32 + +/** + * ubi_dbg_hexdump - dump a buffer. + * @ptr: the buffer to dump + * @size: buffer size which must be multiple of 4 bytes + */ +void ubi_dbg_hexdump(const void *ptr, int size) +{ + int i, k = 0, rows, columns; + const uint8_t *p = ptr; + + size = ALIGN(size, 4); + rows = size/BYTES_PER_LINE + size % BYTES_PER_LINE; + for (i = 0; i < rows; i++) { + int j; + + cond_resched(); + columns = min(size - k, BYTES_PER_LINE) / 4; + if (columns == 0) + break; + printk(KERN_DEBUG "%5d: ", i * BYTES_PER_LINE); + for (j = 0; j < columns; j++) { + int n, N; + + N = size - k > 4 ? 4 : size - k; + for (n = 0; n < N; n++) + printk("%02x", p[k++]); + printk(" "); + } + printk("\n"); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h new file mode 100644 index 0000000..f816ad9 --- /dev/null +++ b/drivers/mtd/ubi/debug.h @@ -0,0 +1,161 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +#ifndef __UBI_DEBUG_H__ +#define __UBI_DEBUG_H__ + +#ifdef CONFIG_MTD_UBI_DEBUG +#include <linux/random.h> + +#define ubi_assert(expr) BUG_ON(!(expr)) +#define dbg_err(fmt, ...) ubi_err(fmt, ##__VA_ARGS__) +#else +#define ubi_assert(expr) ({}) +#define dbg_err(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT +#define DBG_DISABLE_BGT 1 +#else +#define DBG_DISABLE_BGT 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG +/* Generic debugging message */ +#define dbg_msg(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG: %s: " fmt "\n", __FUNCTION__, ##__VA_ARGS__) + +#define ubi_dbg_dump_stack() dump_stack() + +struct ubi_ec_hdr; +struct ubi_vid_hdr; +struct ubi_volume; +struct ubi_vtbl_record; +struct ubi_scan_volume; +struct ubi_scan_leb; +struct ubi_mkvol_req; + +void ubi_dbg_print(int type, const char *func, const char *fmt, ...); +void ubi_dbg_dump_ec_hdr(const struct ubi_ec_hdr *ec_hdr); +void ubi_dbg_dump_vid_hdr(const struct ubi_vid_hdr *vid_hdr); +void ubi_dbg_dump_vol_info(const struct ubi_volume *vol); +void ubi_dbg_dump_vtbl_record(const struct ubi_vtbl_record *r, int idx); +void ubi_dbg_dump_sv(const struct ubi_scan_volume *sv); +void ubi_dbg_dump_seb(const struct ubi_scan_leb *seb, int type); +void ubi_dbg_dump_mkvol_req(const struct ubi_mkvol_req *req); +void ubi_dbg_hexdump(const void *buf, int size); + +#else + +#define dbg_msg(fmt, ...) ({}) +#define ubi_dbg_dump_stack() ({}) +#define ubi_dbg_print(func, fmt, ...) ({}) +#define ubi_dbg_dump_ec_hdr(ec_hdr) ({}) +#define ubi_dbg_dump_vid_hdr(vid_hdr) ({}) +#define ubi_dbg_dump_vol_info(vol) ({}) +#define ubi_dbg_dump_vtbl_record(r, idx) ({}) +#define ubi_dbg_dump_sv(sv) ({}) +#define ubi_dbg_dump_seb(seb, type) ({}) +#define ubi_dbg_dump_mkvol_req(req) ({}) +#define ubi_dbg_hexdump(buf, size) ({}) + +#endif /* CONFIG_MTD_UBI_DEBUG_MSG */ + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_EBA +/* Messages from the eraseblock association unit */ +#define dbg_eba(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG eba: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_eba(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_WL +/* Messages from the wear-leveling unit */ +#define dbg_wl(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG wl: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_wl(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_IO +/* Messages from the input/output unit */ +#define dbg_io(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG io: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_io(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_MSG_BLD +/* Initialization and build messages */ +#define dbg_bld(fmt, ...) \ + printk(KERN_DEBUG "UBI DBG bld: %s: " fmt "\n", __FUNCTION__, \ + ##__VA_ARGS__) +#else +#define dbg_bld(fmt, ...) ({}) +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_BITFLIPS +/** + * ubi_dbg_is_bitflip - if it is time to emulate a bit-flip. + * + * Returns non-zero if a bit-flip should be emulated, otherwise returns zero. + */ +static inline int ubi_dbg_is_bitflip(void) +{ + return !(random32() % 200); +} +#else +#define ubi_dbg_is_bitflip() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_WRITE_FAILURES +/** + * ubi_dbg_is_write_failure - if it is time to emulate a write failure. + * + * Returns non-zero if a write failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_write_failure(void) +{ + return !(random32() % 500); +} +#else +#define ubi_dbg_is_write_failure() 0 +#endif + +#ifdef CONFIG_MTD_UBI_DEBUG_EMULATE_ERASE_FAILURES +/** + * ubi_dbg_is_erase_failure - if its time to emulate an erase failure. + * + * Returns non-zero if an erase failure should be emulated, otherwise returns + * zero. + */ +static inline int ubi_dbg_is_erase_failure(void) +{ + return !(random32() % 400); +} +#else +#define ubi_dbg_is_erase_failure() 0 +#endif + +#endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c new file mode 100644 index 0000000..d847ee1 --- /dev/null +++ b/drivers/mtd/ubi/eba.c @@ -0,0 +1,1241 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * The UBI Eraseblock Association (EBA) unit. + * + * This unit is responsible for I/O to/from logical eraseblock. + * + * Although in this implementation the EBA table is fully kept and managed in + * RAM, which assumes poor scalability, it might be (partially) maintained on + * flash in future implementations. + * + * The EBA unit implements per-logical eraseblock locking. Before accessing a + * logical eraseblock it is locked for reading or writing. The per-logical + * eraseblock locking is implemented by means of the lock tree. The lock tree + * is an RB-tree which refers all the currently locked logical eraseblocks. The + * lock tree elements are &struct ltree_entry objects. They are indexed by + * (@vol_id, @lnum) pairs. + * + * EBA also maintains the global sequence counter which is incremented each + * time a logical eraseblock is mapped to a physical eraseblock and it is + * stored in the volume identifier header. This means that each VID header has + * a unique sequence number. The sequence number is only increased an we assume + * 64 bits is enough to never overflow. + */ + +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/err.h> +#include "ubi.h" + +/** + * struct ltree_entry - an entry in the lock tree. + * @rb: links RB-tree nodes + * @vol_id: volume ID of the locked logical eraseblock + * @lnum: locked logical eraseblock number + * @users: how many tasks are using this logical eraseblock or wait for it + * @mutex: read/write mutex to implement read/write access serialization to + * the (@vol_id, @lnum) logical eraseblock + * + * When a logical eraseblock is being locked - corresponding &struct ltree_entry + * object is inserted to the lock tree (@ubi->ltree). + */ +struct ltree_entry { + struct rb_node rb; + int vol_id; + int lnum; + int users; + struct rw_semaphore mutex; +}; + +/* Slab cache for lock-tree entries */ +static struct kmem_cache *ltree_slab; + +/** + * next_sqnum - get next sequence number. + * @ubi: UBI device description object + * + * This function returns next sequence number to use, which is just the current + * global sequence counter value. It also increases the global sequence + * counter. + */ +static unsigned long long next_sqnum(struct ubi_device *ubi) +{ + unsigned long long sqnum; + + spin_lock(&ubi->ltree_lock); + sqnum = ubi->global_sqnum++; + spin_unlock(&ubi->ltree_lock); + + return sqnum; +} + +/** + * ubi_get_compat - get compatibility flags of a volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function returns compatibility flags for an internal volume. User + * volumes have no compatibility flags, so %0 is returned. + */ +static int ubi_get_compat(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id == UBI_LAYOUT_VOL_ID) + return UBI_LAYOUT_VOLUME_COMPAT; + return 0; +} + +/** + * ltree_lookup - look up the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function returns a pointer to the corresponding &struct ltree_entry + * object if the logical eraseblock is locked and %NULL if it is not. + * @ubi->ltree_lock has to be locked. + */ +static struct ltree_entry *ltree_lookup(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct rb_node *p; + + p = ubi->ltree.rb_node; + while (p) { + struct ltree_entry *le; + + le = rb_entry(p, struct ltree_entry, rb); + + if (vol_id < le->vol_id) + p = p->rb_left; + else if (vol_id > le->vol_id) + p = p->rb_right; + else { + if (lnum < le->lnum) + p = p->rb_left; + else if (lnum > le->lnum) + p = p->rb_right; + else + return le; + } + } + + return NULL; +} + +/** + * ltree_add_entry - add new entry to the lock tree. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function adds new entry for logical eraseblock (@vol_id, @lnum) to the + * lock tree. If such entry is already there, its usage counter is increased. + * Returns pointer to the lock tree entry or %-ENOMEM if memory allocation + * failed. + */ +static struct ltree_entry *ltree_add_entry(struct ubi_device *ubi, int vol_id, + int lnum) +{ + struct ltree_entry *le, *le1, *le_free; + + le = kmem_cache_alloc(ltree_slab, GFP_KERNEL); + if (!le) + return ERR_PTR(-ENOMEM); + + le->vol_id = vol_id; + le->lnum = lnum; + + spin_lock(&ubi->ltree_lock); + le1 = ltree_lookup(ubi, vol_id, lnum); + + if (le1) { + /* + * This logical eraseblock is already locked. The newly + * allocated lock entry is not needed. + */ + le_free = le; + le = le1; + } else { + struct rb_node **p, *parent = NULL; + + /* + * No lock entry, add the newly allocated one to the + * @ubi->ltree RB-tree. + */ + le_free = NULL; + + p = &ubi->ltree.rb_node; + while (*p) { + parent = *p; + le1 = rb_entry(parent, struct ltree_entry, rb); + + if (vol_id < le1->vol_id) + p = &(*p)->rb_left; + else if (vol_id > le1->vol_id) + p = &(*p)->rb_right; + else { + ubi_assert(lnum != le1->lnum); + if (lnum < le1->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&le->rb, parent, p); + rb_insert_color(&le->rb, &ubi->ltree); + } + le->users += 1; + spin_unlock(&ubi->ltree_lock); + + if (le_free) + kmem_cache_free(ltree_slab, le_free); + + return le; +} + +/** + * leb_read_lock - lock logical eraseblock for reading. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for reading. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_read_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_read(&le->mutex); + return 0; +} + +/** + * leb_read_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_read_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free = 0; + struct ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } + spin_unlock(&ubi->ltree_lock); + + up_read(&le->mutex); + if (free) + kmem_cache_free(ltree_slab, le); +} + +/** + * leb_write_lock - lock logical eraseblock for writing. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function locks a logical eraseblock for writing. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int leb_write_lock(struct ubi_device *ubi, int vol_id, int lnum) +{ + struct ltree_entry *le; + + le = ltree_add_entry(ubi, vol_id, lnum); + if (IS_ERR(le)) + return PTR_ERR(le); + down_write(&le->mutex); + return 0; +} + +/** + * leb_write_unlock - unlock logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + */ +static void leb_write_unlock(struct ubi_device *ubi, int vol_id, int lnum) +{ + int free; + struct ltree_entry *le; + + spin_lock(&ubi->ltree_lock); + le = ltree_lookup(ubi, vol_id, lnum); + le->users -= 1; + ubi_assert(le->users >= 0); + if (le->users == 0) { + rb_erase(&le->rb, &ubi->ltree); + free = 1; + } else + free = 0; + spin_unlock(&ubi->ltree_lock); + + up_write(&le->mutex); + if (free) + kmem_cache_free(ltree_slab, le); +} + +/** + * ubi_eba_unmap_leb - un-map logical eraseblock. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules corresponding + * physical eraseblock for erasure. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum) +{ + int idx = vol_id2idx(ubi, vol_id), err, pnum; + struct ubi_volume *vol = ubi->volumes[idx]; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) + /* This logical eraseblock is already unmapped */ + goto out_unlock; + + dbg_eba("erase LEB %d:%d, PEB %d", vol_id, lnum, pnum); + + vol->eba_tbl[lnum] = UBI_LEB_UNMAPPED; + err = ubi_wl_put_peb(ubi, pnum, 0); + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * ubi_eba_read_leb - read data. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: buffer to store the read data + * @offset: offset from where to read + * @len: how many bytes to read + * @check: data CRC check flag + * + * If the logical eraseblock @lnum is unmapped, @buf is filled with 0xFF + * bytes. The @check flag only makes sense for static volumes and forces + * eraseblock data CRC checking. + * + * In case of success this function returns zero. In case of a static volume, + * if data CRC mismatches - %-EBADMSG is returned. %-EBADMSG may also be + * returned for any volume type if an ECC error was detected by the MTD device + * driver. Other negative error cored may be returned in case of other errors. + */ +int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int offset, int len, int check) +{ + int err, pnum, scrub = 0, idx = vol_id2idx(ubi, vol_id); + struct ubi_vid_hdr *vid_hdr; + struct ubi_volume *vol = ubi->volumes[idx]; + uint32_t crc, crc1; + + err = leb_read_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum < 0) { + /* + * The logical eraseblock is not mapped, fill the whole buffer + * with 0xFF bytes. The exception is static volumes for which + * it is an error to read unmapped logical eraseblocks. + */ + dbg_eba("read %d bytes from offset %d of LEB %d:%d (unmapped)", + len, offset, vol_id, lnum); + leb_read_unlock(ubi, vol_id, lnum); + ubi_assert(vol->vol_type != UBI_STATIC_VOLUME); + memset(buf, 0xFF, len); + return 0; + } + + dbg_eba("read %d bytes from offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + check = 0; + +retry: + if (check) { + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + err = -ENOMEM; + goto out_unlock; + } + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) { + /* + * The header is either absent or corrupted. + * The former case means there is a bug - + * switch to read-only mode just in case. + * The latter case means a real corruption - we + * may try to recover data. FIXME: but this is + * not implemented. + */ + if (err == UBI_IO_BAD_VID_HDR) { + ubi_warn("bad VID header at PEB %d, LEB" + "%d:%d", pnum, vol_id, lnum); + err = -EBADMSG; + } else + ubi_ro_mode(ubi); + } + goto out_free; + } else if (err == UBI_IO_BITFLIPS) + scrub = 1; + + ubi_assert(lnum < ubi32_to_cpu(vid_hdr->used_ebs)); + ubi_assert(len == ubi32_to_cpu(vid_hdr->data_size)); + + crc = ubi32_to_cpu(vid_hdr->data_crc); + ubi_free_vid_hdr(ubi, vid_hdr); + } + + err = ubi_io_read_data(ubi, buf, pnum, offset, len); + if (err) { + if (err == UBI_IO_BITFLIPS) { + scrub = 1; + err = 0; + } else if (err == -EBADMSG) { + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + goto out_unlock; + scrub = 1; + if (!check) { + ubi_msg("force data checking"); + check = 1; + goto retry; + } + } else + goto out_unlock; + } + + if (check) { + crc1 = crc32(UBI_CRC32_INIT, buf, len); + if (crc1 != crc) { + ubi_warn("CRC error: calculated %#08x, must be %#08x", + crc1, crc); + err = -EBADMSG; + goto out_unlock; + } + } + + if (scrub) + err = ubi_wl_scrub_peb(ubi, pnum); + + leb_read_unlock(ubi, vol_id, lnum); + return err; + +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); +out_unlock: + leb_read_unlock(ubi, vol_id, lnum); + return err; +} + +/** + * recover_peb - recover from write failure. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to recover + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data which was not written because of the write failure + * @offset: offset of the failed write + * @len: how many bytes should have been written + * + * This function is called in case of a write failure and moves all good data + * from the potentially bad physical eraseblock to a good physical eraseblock. + * This function also writes the data which was not written due to the failure. + * Returns new physical eraseblock number in case of success, and a negative + * error code in case of failure. + */ +static int recover_peb(struct ubi_device *ubi, int pnum, int vol_id, int lnum, + const void *buf, int offset, int len) +{ + int err, idx = vol_id2idx(ubi, vol_id), new_pnum, data_size, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + unsigned char *new_buf; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + return -ENOMEM; + } + +retry: + new_pnum = ubi_wl_get_peb(ubi, UBI_UNKNOWN); + if (new_pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + return new_pnum; + } + + ubi_msg("recover PEB %d, move data to PEB %d", pnum, new_pnum); + + err = ubi_io_read_vid_hdr(ubi, pnum, vid_hdr, 1); + if (err && err != UBI_IO_BITFLIPS) { + if (err > 0) + err = -EIO; + goto out_put; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + err = ubi_io_write_vid_hdr(ubi, new_pnum, vid_hdr); + if (err) + goto write_error; + + data_size = offset + len; + new_buf = kmalloc(data_size, GFP_KERNEL); + if (!new_buf) { + err = -ENOMEM; + goto out_put; + } + memset(new_buf + offset, 0xFF, len); + + /* Read everything before the area where the write failure happened */ + if (offset > 0) { + err = ubi_io_read_data(ubi, new_buf, pnum, 0, offset); + if (err && err != UBI_IO_BITFLIPS) { + kfree(new_buf); + goto out_put; + } + } + + memcpy(new_buf + offset, buf, len); + + err = ubi_io_write_data(ubi, new_buf, new_pnum, 0, data_size); + if (err) { + kfree(new_buf); + goto write_error; + } + + kfree(new_buf); + ubi_free_vid_hdr(ubi, vid_hdr); + + vol->eba_tbl[lnum] = new_pnum; + ubi_wl_put_peb(ubi, pnum, 1); + + ubi_msg("data was successfully recovered"); + return 0; + +out_put: + ubi_wl_put_peb(ubi, new_pnum, 1); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + /* + * Bad luck? This physical eraseblock is bad too? Crud. Let's try to + * get another one. + */ + ubi_warn("failed to write to PEB %d", new_pnum); + ubi_wl_put_peb(ubi, new_pnum, 1); + if (++tries > UBI_IO_RETRIES) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + ubi_msg("try again"); + goto retry; +} + +/** + * ubi_eba_write_leb - write data to dynamic volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: the data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: data type + * + * This function writes data to logical eraseblock @lnum of a dynamic volume + * @vol_id. Returns zero in case of success and a negative error code in case + * of failure. In case of error, it is possible that something was still + * written to the flash media, but may be some garbage. + */ +int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, int dtype) +{ + int idx = vol_id2idx(ubi, vol_id), err, pnum, tries = 0; + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + + if (ubi->ro_mode) + return -EROFS; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) + return err; + + pnum = vol->eba_tbl[lnum]; + if (pnum >= 0) { + dbg_eba("write %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write data to PEB %d", pnum); + if (err == -EIO && ubi->bad_allowed) + err = recover_peb(ubi, pnum, vol_id, lnum, buf, offset, len); + if (err) + ubi_ro_mode(ubi); + } + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + /* + * The logical eraseblock is not mapped. We have to get a free physical + * eraseblock and write the volume identifier header there first. + */ + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) { + leb_write_unlock(ubi, vol_id, lnum); + return -ENOMEM; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at offset %d of LEB %d:%d, PEB %d", + len, offset, vol_id, lnum, pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, offset, len); + if (err) { + ubi_warn("failed to write %d bytes at offset %d of LEB %d:%d, " + "PEB %d", len, offset, vol_id, lnum, pnum); + goto write_error; + } + + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + /* + * Fortunately, this is the first write operation to this physical + * eraseblock, so just put it and request a new one. We assume that if + * this physical eraseblock went bad, the erase code will handle that. + */ + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ubi_eba_write_leb_st - write data to static volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * @used_ebs: how many logical eraseblocks will this volume contain + * + * This function writes data to logical eraseblock @lnum of static volume + * @vol_id. The @used_ebs argument should contain total number of logical + * eraseblock in this static volume. + * + * When writing to the last logical eraseblock, the @len argument doesn't have + * to be aligned to the minimal I/O unit size. Instead, it has to be equivalent + * to the real data size, although the @buf buffer has to contain the + * alignment. In all other cases, @len has to be aligned. + * + * It is prohibited to write more then once to logical eraseblocks of static + * volumes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype, int used_ebs) +{ + int err, pnum, tries = 0, data_size = len; + int idx = vol_id2idx(ubi, vol_id); + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + if (lnum == used_ebs - 1) + /* If this is the last LEB @len may be unaligned */ + len = ALIGN(data_size, ubi->min_io_size); + else + ubi_assert(len % ubi->min_io_size == 0); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, data_size); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->used_ebs = cpu_to_ubi32(used_ebs); + vid_hdr->data_crc = cpu_to_ubi32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("write VID hdr and %d bytes at LEB %d:%d, PEB %d, used_ebs %d", + len, vol_id, lnum, pnum, used_ebs); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + ubi_assert(vol->eba_tbl[lnum] < 0); + vol->eba_tbl[lnum] = pnum; + + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/* + * ubi_eba_atomic_leb_change - change logical eraseblock atomically. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: how many bytes to write + * @dtype: data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. This function guarantees that in case of an + * unclean reboot the old contents is preserved. Returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype) +{ + int err, pnum, tries = 0, idx = vol_id2idx(ubi, vol_id); + struct ubi_volume *vol = ubi->volumes[idx]; + struct ubi_vid_hdr *vid_hdr; + uint32_t crc; + + if (ubi->ro_mode) + return -EROFS; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + vid_hdr->vol_id = cpu_to_ubi32(vol_id); + vid_hdr->lnum = cpu_to_ubi32(lnum); + vid_hdr->compat = ubi_get_compat(ubi, vol_id); + vid_hdr->data_pad = cpu_to_ubi32(vol->data_pad); + + crc = crc32(UBI_CRC32_INIT, buf, len); + vid_hdr->vol_type = UBI_VID_STATIC; + vid_hdr->data_size = cpu_to_ubi32(len); + vid_hdr->copy_flag = 1; + vid_hdr->data_crc = cpu_to_ubi32(crc); + +retry: + pnum = ubi_wl_get_peb(ubi, dtype); + if (pnum < 0) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return pnum; + } + + dbg_eba("change LEB %d:%d, PEB %d, write VID hdr to PEB %d", + vol_id, lnum, vol->eba_tbl[lnum], pnum); + + err = ubi_io_write_vid_hdr(ubi, pnum, vid_hdr); + if (err) { + ubi_warn("failed to write VID header to LEB %d:%d, PEB %d", + vol_id, lnum, pnum); + goto write_error; + } + + err = ubi_io_write_data(ubi, buf, pnum, 0, len); + if (err) { + ubi_warn("failed to write %d bytes of data to PEB %d", + len, pnum); + goto write_error; + } + + err = ubi_wl_put_peb(ubi, vol->eba_tbl[lnum], 1); + if (err) { + ubi_free_vid_hdr(ubi, vid_hdr); + leb_write_unlock(ubi, vol_id, lnum); + return err; + } + + vol->eba_tbl[lnum] = pnum; + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + +write_error: + if (err != -EIO || !ubi->bad_allowed) { + /* + * This flash device does not admit of bad eraseblocks or + * something nasty and unexpected happened. Switch to read-only + * mode just in case. + */ + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + err = ubi_wl_put_peb(ubi, pnum, 1); + if (err || ++tries > UBI_IO_RETRIES) { + ubi_ro_mode(ubi); + leb_write_unlock(ubi, vol_id, lnum); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + } + + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + ubi_msg("try another PEB"); + goto retry; +} + +/** + * ltree_entry_ctor - lock tree entries slab cache constructor. + * @obj: the lock-tree entry to construct + * @cache: the lock tree entry slab cache + * @flags: constructor flags + */ +static void ltree_entry_ctor(void *obj, struct kmem_cache *cache, + unsigned long flags) +{ + struct ltree_entry *le = obj; + + if ((flags & (SLAB_CTOR_VERIFY | SLAB_CTOR_CONSTRUCTOR)) != + SLAB_CTOR_CONSTRUCTOR) + return; + + le->users = 0; + init_rwsem(&le->mutex); +} + +/** + * ubi_eba_copy_leb - copy logical eraseblock. + * @ubi: UBI device description object + * @from: physical eraseblock number from where to copy + * @to: physical eraseblock number where to copy + * @vid_hdr: VID header of the @from physical eraseblock + * + * This function copies logical eraseblock from physical eraseblock @from to + * physical eraseblock @to. The @vid_hdr buffer may be changed by this + * function. Returns zero in case of success, %UBI_IO_BITFLIPS if the operation + * was canceled because bit-flips were detected at the target PEB, and a + * negative error code in case of failure. + */ +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr) +{ + int err, vol_id, lnum, data_size, aldata_size, pnum, idx; + struct ubi_volume *vol; + uint32_t crc; + void *buf, *buf1 = NULL; + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + + dbg_eba("copy LEB %d:%d, PEB %d to PEB %d", vol_id, lnum, from, to); + + if (vid_hdr->vol_type == UBI_VID_STATIC) { + data_size = ubi32_to_cpu(vid_hdr->data_size); + aldata_size = ALIGN(data_size, ubi->min_io_size); + } else + data_size = aldata_size = + ubi->leb_size - ubi32_to_cpu(vid_hdr->data_pad); + + buf = kmalloc(aldata_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * We do not want anybody to write to this logical eraseblock while we + * are moving it, so we lock it. + */ + err = leb_write_lock(ubi, vol_id, lnum); + if (err) { + kfree(buf); + return err; + } + + /* + * But the logical eraseblock might have been put by this time. + * Cancel if it is true. + */ + idx = vol_id2idx(ubi, vol_id); + + /* + * We may race with volume deletion/re-size, so we have to hold + * @ubi->volumes_lock. + */ + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[idx]; + if (!vol) { + dbg_eba("volume %d was removed meanwhile", vol_id); + spin_unlock(&ubi->volumes_lock); + goto out_unlock; + } + + pnum = vol->eba_tbl[lnum]; + if (pnum != from) { + dbg_eba("LEB %d:%d is no longer mapped to PEB %d, mapped to " + "PEB %d, cancel", vol_id, lnum, from, pnum); + spin_unlock(&ubi->volumes_lock); + goto out_unlock; + } + spin_unlock(&ubi->volumes_lock); + + /* OK, now the LEB is locked and we can safely start moving it */ + + dbg_eba("read %d bytes of data", aldata_size); + err = ubi_io_read_data(ubi, buf, from, 0, aldata_size); + if (err && err != UBI_IO_BITFLIPS) { + ubi_warn("error %d while reading data from PEB %d", + err, from); + goto out_unlock; + } + + /* + * Now we have got to calculate how much data we have to to copy. In + * case of a static volume it is fairly easy - the VID header contains + * the data size. In case of a dynamic volume it is more difficult - we + * have to read the contents, cut 0xFF bytes from the end and copy only + * the first part. We must do this to avoid writing 0xFF bytes as it + * may have some side-effects. And not only this. It is important not + * to include those 0xFFs to CRC because later the they may be filled + * by data. + */ + if (vid_hdr->vol_type == UBI_VID_DYNAMIC) + aldata_size = data_size = + ubi_calc_data_len(ubi, buf, data_size); + + cond_resched(); + crc = crc32(UBI_CRC32_INIT, buf, data_size); + cond_resched(); + + /* + * It may turn out to me that the whole @from physical eraseblock + * contains only 0xFF bytes. Then we have to only write the VID header + * and do not write any data. This also means we should not set + * @vid_hdr->copy_flag, @vid_hdr->data_size, and @vid_hdr->data_crc. + */ + if (data_size > 0) { + vid_hdr->copy_flag = 1; + vid_hdr->data_size = cpu_to_ubi32(data_size); + vid_hdr->data_crc = cpu_to_ubi32(crc); + } + vid_hdr->sqnum = cpu_to_ubi64(next_sqnum(ubi)); + + err = ubi_io_write_vid_hdr(ubi, to, vid_hdr); + if (err) + goto out_unlock; + + cond_resched(); + + /* Read the VID header back and check if it was written correctly */ + err = ubi_io_read_vid_hdr(ubi, to, vid_hdr, 1); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read VID header back from PEB %d", to); + goto out_unlock; + } + + if (data_size > 0) { + err = ubi_io_write_data(ubi, buf, to, 0, aldata_size); + if (err) + goto out_unlock; + + /* + * We've written the data and are going to read it back to make + * sure it was written correctly. + */ + buf1 = kmalloc(aldata_size, GFP_KERNEL); + if (!buf1) { + err = -ENOMEM; + goto out_unlock; + } + + cond_resched(); + + err = ubi_io_read_data(ubi, buf1, to, 0, aldata_size); + if (err) { + if (err != UBI_IO_BITFLIPS) + ubi_warn("cannot read data back from PEB %d", + to); + goto out_unlock; + } + + cond_resched(); + + if (memcmp(buf, buf1, aldata_size)) { + ubi_warn("read data back from PEB %d - it is different", + to); + goto out_unlock; + } + } + + ubi_assert(vol->eba_tbl[lnum] == from); + vol->eba_tbl[lnum] = to; + + leb_write_unlock(ubi, vol_id, lnum); + kfree(buf); + kfree(buf1); + + return 0; + +out_unlock: + leb_write_unlock(ubi, vol_id, lnum); + kfree(buf); + kfree(buf1); + return err; +} + +/** + * ubi_eba_init_scan - initialize the EBA unit using scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, j, err, num_volumes; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + struct ubi_scan_leb *seb; + struct rb_node *rb; + + dbg_eba("initialize EBA unit"); + + spin_lock_init(&ubi->ltree_lock); + ubi->ltree = RB_ROOT; + + if (ubi_devices_cnt == 0) { + ltree_slab = kmem_cache_create("ubi_ltree_slab", + sizeof(struct ltree_entry), 0, + 0, <ree_entry_ctor, NULL); + if (!ltree_slab) + return -ENOMEM; + } + + ubi->global_sqnum = si->max_sqnum + 1; + num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + for (i = 0; i < num_volumes; i++) { + vol = ubi->volumes[i]; + if (!vol) + continue; + + cond_resched(); + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), + GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_free; + } + + for (j = 0; j < vol->reserved_pebs; j++) + vol->eba_tbl[j] = UBI_LEB_UNMAPPED; + + sv = ubi_scan_find_sv(si, idx2vol_id(ubi, i)); + if (!sv) + continue; + + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + if (seb->lnum >= vol->reserved_pebs) + /* + * This may happen in case of an unclean reboot + * during re-size. + */ + ubi_scan_move_to_list(sv, seb, &si->erase); + vol->eba_tbl[seb->lnum] = seb->pnum; + } + } + + if (ubi->bad_allowed) { + ubi_calculate_reserved(ubi); + + if (ubi->avail_pebs < ubi->beb_rsvd_level) { + /* No enough free physical eraseblocks */ + ubi->beb_rsvd_pebs = ubi->avail_pebs; + ubi_warn("cannot reserve enough PEBs for bad PEB " + "handling, reserved %d, need %d", + ubi->beb_rsvd_pebs, ubi->beb_rsvd_level); + } else + ubi->beb_rsvd_pebs = ubi->beb_rsvd_level; + + ubi->avail_pebs -= ubi->beb_rsvd_pebs; + ubi->rsvd_pebs += ubi->beb_rsvd_pebs; + } + + dbg_eba("EBA unit is initialized"); + return 0; + +out_free: + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + if (ubi_devices_cnt == 0) + kmem_cache_destroy(ltree_slab); + return err; +} + +/** + * ubi_eba_close - close EBA unit. + * @ubi: UBI device description object + */ +void ubi_eba_close(const struct ubi_device *ubi) +{ + int i, num_volumes = ubi->vtbl_slots + UBI_INT_VOL_COUNT; + + dbg_eba("close EBA unit"); + + for (i = 0; i < num_volumes; i++) { + if (!ubi->volumes[i]) + continue; + kfree(ubi->volumes[i]->eba_tbl); + } + if (ubi_devices_cnt == 1) + kmem_cache_destroy(ltree_slab); +} diff --git a/drivers/mtd/ubi/gluebi.c b/drivers/mtd/ubi/gluebi.c new file mode 100644 index 0000000..fc9478d --- /dev/null +++ b/drivers/mtd/ubi/gluebi.c @@ -0,0 +1,323 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём), Joern Engel + */ + +/* + * This file includes implementation of fake MTD devices for each UBI volume. + * This sounds strange, but it is in fact quite useful to make MTD-oriented + * software (including all the legacy software) to work on top of UBI. + * + * Gluebi emulates MTD devices of "MTD_UBIVOLUME" type. Their minimal I/O unit + * size (mtd->writesize) is equivalent to the UBI minimal I/O unit. The + * eraseblock size is equivalent to the logical eraseblock size of the volume. + */ + +#include <asm/div64.h> +#include "ubi.h" + +/** + * gluebi_get_device - get MTD device reference. + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being opened and + * implements the MTD get_device() operation. Returns zero in case of success + * and a negative error code in case of failure. + */ +static int gluebi_get_device(struct mtd_info *mtd) +{ + struct ubi_volume *vol; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + + /* + * We do not introduce locks for gluebi reference count because the + * get_device()/put_device() calls are already serialized at MTD. + */ + if (vol->gluebi_refcount > 0) { + /* + * The MTD device is already referenced and this is just one + * more reference. MTD allows many users to open the same + * volume simultaneously and do not distinguish between + * readers/writers/exclusive openers as UBI does. So we do not + * open the UBI volume again - just increase the reference + * counter and return. + */ + vol->gluebi_refcount += 1; + return 0; + } + + /* + * This is the first reference to this UBI volume via the MTD device + * interface. Open the corresponding volume in read-write mode. + */ + vol->gluebi_desc = ubi_open_volume(vol->ubi->ubi_num, vol->vol_id, + UBI_READWRITE); + if (IS_ERR(vol->gluebi_desc)) + return PTR_ERR(vol->gluebi_desc); + vol->gluebi_refcount += 1; + return 0; +} + +/** + * gluebi_put_device - put MTD device reference. + * @mtd: the MTD device description object + * + * This function is called every time the MTD device is being put. Returns + * zero in case of success and a negative error code in case of failure. + */ +static void gluebi_put_device(struct mtd_info *mtd) +{ + struct ubi_volume *vol; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + vol->gluebi_refcount -= 1; + ubi_assert(vol->gluebi_refcount >= 0); + if (vol->gluebi_refcount == 0) + ubi_close_volume(vol->gluebi_desc); +} + +/** + * gluebi_read - read operation of emulated MTD devices. + * @mtd: MTD device description object + * @from: absolute offset from where to read + * @len: how many bytes to read + * @retlen: count of read bytes is returned here + * @buf: buffer to store the read data + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, unsigned char *buf) +{ + int err = 0, lnum, offs, total_read; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = from; + + dbg_msg("read %zd bytes from offset %lld", len, from); + + if (len < 0 || from < 0 || from + len > mtd->size) + return -EINVAL; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + offs = do_div(tmp, mtd->erasesize); + lnum = tmp; + + total_read = len; + while (total_read) { + size_t to_read = mtd->erasesize - offs; + + if (to_read > total_read) + to_read = total_read; + + err = ubi_eba_read_leb(ubi, vol->vol_id, lnum, buf, offs, + to_read, 0); + if (err) + break; + + lnum += 1; + offs = 0; + total_read -= to_read; + buf += to_read; + } + + *retlen = len - total_read; + return err; +} + +/** + * gluebi_write - write operation of emulated MTD devices. + * @mtd: MTD device description object + * @to: absolute offset where to write + * @len: how many bytes to write + * @retlen: count of written bytes is returned here + * @buf: buffer with data to write + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int gluebi_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + int err = 0, lnum, offs, total_written; + struct ubi_volume *vol; + struct ubi_device *ubi; + uint64_t tmp = to; + + dbg_msg("write %zd bytes to offset %lld", len, to); + + if (len < 0 || to < 0 || len + to > mtd->size) + return -EINVAL; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + if (ubi->ro_mode) + return -EROFS; + + offs = do_div(tmp, mtd->erasesize); + lnum = tmp; + + if (len % mtd->writesize || offs % mtd->writesize) + return -EINVAL; + + total_written = len; + while (total_written) { + size_t to_write = mtd->erasesize - offs; + + if (to_write > total_written) + to_write = total_written; + + err = ubi_eba_write_leb(ubi, vol->vol_id, lnum, buf, offs, + to_write, UBI_UNKNOWN); + if (err) + break; + + lnum += 1; + offs = 0; + total_written -= to_write; + buf += to_write; + } + + *retlen = len - total_written; + return err; +} + +/** + * gluebi_erase - erase operation of emulated MTD devices. + * @mtd: the MTD device description object + * @instr: the erase operation description + * + * This function calls the erase callback when finishes. Returns zero in case + * of success and a negative error code in case of failure. + */ +static int gluebi_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + int err, i, lnum, count; + struct ubi_volume *vol; + struct ubi_device *ubi; + + dbg_msg("erase %u bytes at offset %u", instr->len, instr->addr); + + if (instr->addr < 0 || instr->addr > mtd->size - mtd->erasesize) + return -EINVAL; + + if (instr->len < 0 || instr->addr + instr->len > mtd->size) + return -EINVAL; + + if (instr->addr % mtd->writesize || instr->len % mtd->writesize) + return -EINVAL; + + lnum = instr->addr / mtd->erasesize; + count = instr->len / mtd->erasesize; + + vol = container_of(mtd, struct ubi_volume, gluebi_mtd); + ubi = vol->ubi; + + if (ubi->ro_mode) + return -EROFS; + + for (i = 0; i < count; i++) { + err = ubi_eba_unmap_leb(ubi, vol->vol_id, lnum + i); + if (err) + goto out_err; + } + + /* + * MTD erase operations are synchronous, so we have to make sure the + * physical eraseblock is wiped out. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_err; + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + return 0; + +out_err: + instr->state = MTD_ERASE_FAILED; + instr->fail_addr = lnum * mtd->erasesize; + return err; +} + +/** + * ubi_create_gluebi - initialize gluebi for an UBI volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function is called when an UBI volume is created in order to create + * corresponding fake MTD device. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol) +{ + struct mtd_info *mtd = &vol->gluebi_mtd; + + mtd->name = kmemdup(vol->name, vol->name_len + 1, GFP_KERNEL); + if (!mtd->name) + return -ENOMEM; + + mtd->type = MTD_UBIVOLUME; + if (!ubi->ro_mode) + mtd->flags = MTD_WRITEABLE; + mtd->writesize = ubi->min_io_size; + mtd->owner = THIS_MODULE; + mtd->size = vol->usable_leb_size * vol->reserved_pebs; + mtd->erasesize = vol->usable_leb_size; + mtd->read = gluebi_read; + mtd->write = gluebi_write; + mtd->erase = gluebi_erase; + mtd->get_device = gluebi_get_device; + mtd->put_device = gluebi_put_device; + + if (add_mtd_device(mtd)) { + ubi_err("cannot not add MTD device\n"); + kfree(mtd->name); + return -ENFILE; + } + + dbg_msg("added mtd%d (\"%s\"), size %u, EB size %u", + mtd->index, mtd->name, mtd->size, mtd->erasesize); + return 0; +} + +/** + * ubi_destroy_gluebi - close gluebi for an UBI volume. + * @vol: volume description object + * + * This function is called when an UBI volume is removed in order to remove + * corresponding fake MTD device. Returns zero in case of success and a + * negative error code in case of failure. + */ +int ubi_destroy_gluebi(struct ubi_volume *vol) +{ + int err; + struct mtd_info *mtd = &vol->gluebi_mtd; + + dbg_msg("remove mtd%d", mtd->index); + err = del_mtd_device(mtd); + if (err) + return err; + kfree(mtd->name); + return 0; +} diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c new file mode 100644 index 0000000..438914d --- /dev/null +++ b/drivers/mtd/ubi/io.c @@ -0,0 +1,1259 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * UBI input/output unit. + * + * This unit provides a uniform way to work with all kinds of the underlying + * MTD devices. It also implements handy functions for reading and writing UBI + * headers. + * + * We are trying to have a paranoid mindset and not to trust to what we read + * from the flash media in order to be more secure and robust. So this unit + * validates every single header it reads from the flash media. + * + * Some words about how the eraseblock headers are stored. + * + * The erase counter header is always stored at offset zero. By default, the + * VID header is stored after the EC header at the closest aligned offset + * (i.e. aligned to the minimum I/O unit size). Data starts next to the VID + * header at the closest aligned offset. But this default layout may be + * changed. For example, for different reasons (e.g., optimization) UBI may be + * asked to put the VID header at further offset, and even at an unaligned + * offset. Of course, if the offset of the VID header is unaligned, UBI adds + * proper padding in front of it. Data offset may also be changed but it has to + * be aligned. + * + * About minimal I/O units. In general, UBI assumes flash device model where + * there is only one minimal I/O unit size. E.g., in case of NOR flash it is 1, + * in case of NAND flash it is a NAND page, etc. This is reported by MTD in the + * @ubi->mtd->writesize field. But as an exception, UBI admits of using another + * (smaller) minimal I/O unit size for EC and VID headers to make it possible + * to do different optimizations. + * + * This is extremely useful in case of NAND flashes which admit of several + * write operations to one NAND page. In this case UBI can fit EC and VID + * headers at one NAND page. Thus, UBI may use "sub-page" size as the minimal + * I/O unit for the headers (the @ubi->hdrs_min_io_size field). But it still + * reports NAND page size (@ubi->min_io_size) as a minimal I/O unit for the UBI + * users. + * + * Example: some Samsung NANDs with 2KiB pages allow 4x 512-byte writes, so + * although the minimal I/O unit is 2K, UBI uses 512 bytes for EC and VID + * headers. + * + * Q: why not just to treat sub-page as a minimal I/O unit of this flash + * device, e.g., make @ubi->min_io_size = 512 in the example above? + * + * A: because when writing a sub-page, MTD still writes a full 2K page but the + * bytes which are no relevant to the sub-page are 0xFF. So, basically, writing + * 4x512 sub-pages is 4 times slower then writing one 2KiB NAND page. Thus, we + * prefer to use sub-pages only for EV and VID headers. + * + * As it was noted above, the VID header may start at a non-aligned offset. + * For example, in case of a 2KiB page NAND flash with a 512 bytes sub-page, + * the VID header may reside at offset 1984 which is the last 64 bytes of the + * last sub-page (EC header is always at offset zero). This causes some + * difficulties when reading and writing VID headers. + * + * Suppose we have a 64-byte buffer and we read a VID header at it. We change + * the data and want to write this VID header out. As we can only write in + * 512-byte chunks, we have to allocate one more buffer and copy our VID header + * to offset 448 of this buffer. + * + * The I/O unit does the following trick in order to avoid this extra copy. + * It always allocates a @ubi->vid_hdr_alsize bytes buffer for the VID header + * and returns a pointer to offset @ubi->vid_hdr_shift of this buffer. When the + * VID header is being written out, it shifts the VID header pointer back and + * writes the whole sub-page. + */ + +#include <linux/crc32.h> +#include <linux/err.h> +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum); +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr); +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum); +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr); +static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, + int offset, int len); +#else +#define paranoid_check_not_bad(ubi, pnum) 0 +#define paranoid_check_peb_ec_hdr(ubi, pnum) 0 +#define paranoid_check_ec_hdr(ubi, pnum, ec_hdr) 0 +#define paranoid_check_peb_vid_hdr(ubi, pnum) 0 +#define paranoid_check_vid_hdr(ubi, pnum, vid_hdr) 0 +#define paranoid_check_all_ff(ubi, pnum, offset, len) 0 +#endif + +/** + * ubi_io_read - read data from a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer where to store the read data + * @pnum: physical eraseblock number to read from + * @offset: offset within the physical eraseblock from where to read + * @len: how many bytes to read + * + * This function reads data from offset @offset of physical eraseblock @pnum + * and stores the read data in the @buf buffer. The following return codes are + * possible: + * + * o %0 if all the requested data were successfully read; + * o %UBI_IO_BITFLIPS if all the requested data were successfully read, but + * correctable bit-flips were detected; this is harmless but may indicate + * that this eraseblock may become bad soon (but do not have to); + * o %-EBADMSG if the MTD subsystem reported about data data integrity + * problems, for example it can me an ECC error in case of NAND; this most + * probably means that the data is corrupted; + * o %-EIO if some I/O error occurred; + * o other negative error codes in case of other errors. + */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len) +{ + int err, retries = 0; + size_t read; + loff_t addr; + + dbg_io("read %d bytes from PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(len > 0); + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + addr = (loff_t)pnum * ubi->peb_size + offset; +retry: + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err) { + if (err == -EUCLEAN) { + /* + * -EUCLEAN is reported if there was a bit-flip which + * was corrected, so this is harmless. + */ + ubi_msg("fixable bit-flip detected at PEB %d", pnum); + ubi_assert(len == read); + return UBI_IO_BITFLIPS; + } + + if (read != len && retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while reading %d bytes from PEB %d:%d, " + "read only %zd bytes, retry", + err, len, pnum, offset, read); + yield(); + goto retry; + } + + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + ubi_dbg_dump_stack(); + } else { + ubi_assert(len == read); + + if (ubi_dbg_is_bitflip()) { + dbg_msg("bit-flip (emulated)"); + err = UBI_IO_BITFLIPS; + } + } + + return err; +} + +/** + * ubi_io_write - write data to a physical eraseblock. + * @ubi: UBI device description object + * @buf: buffer with the data to write + * @pnum: physical eraseblock number to write to + * @offset: offset within the physical eraseblock where to write + * @len: how many bytes to write + * + * This function writes @len bytes of data from buffer @buf to offset @offset + * of physical eraseblock @pnum. If all the data were successfully written, + * zero is returned. If an error occurred, this function returns a negative + * error code. If %-EIO is returned, the physical eraseblock most probably went + * bad. + * + * Note, in case of an error, it is possible that something was still written + * to the flash media, but may be some garbage. + */ +int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len) +{ + int err; + size_t written; + loff_t addr; + + dbg_io("write %d bytes to PEB %d:%d", len, pnum, offset); + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + ubi_assert(offset >= 0 && offset + len <= ubi->peb_size); + ubi_assert(offset % ubi->hdrs_min_io_size == 0); + ubi_assert(len > 0 && len % ubi->hdrs_min_io_size == 0); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + /* The below has to be compiled out if paranoid checks are disabled */ + + err = paranoid_check_not_bad(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + + /* The area we are writing to has to contain all 0xFF bytes */ + err = paranoid_check_all_ff(ubi, pnum, offset, len); + if (err) + return err > 0 ? -EINVAL : err; + + if (offset >= ubi->leb_start) { + /* + * We write to the data area of the physical eraseblock. Make + * sure it has valid EC and VID headers. + */ + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + err = paranoid_check_peb_vid_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL : err; + } + + if (ubi_dbg_is_write_failure()) { + dbg_err("cannot write %d bytes to PEB %d:%d " + "(emulated)", len, pnum, offset); + ubi_dbg_dump_stack(); + return -EIO; + } + + addr = (loff_t)pnum * ubi->peb_size + offset; + err = ubi->mtd->write(ubi->mtd, addr, len, &written, buf); + if (err) { + ubi_err("error %d while writing %d bytes to PEB %d:%d, written" + " %zd bytes", err, len, pnum, offset, written); + ubi_dbg_dump_stack(); + } else + ubi_assert(written == len); + + return err; +} + +/** + * erase_callback - MTD erasure call-back. + * @ei: MTD erase information object. + * + * Note, even though MTD erase interface is asynchronous, all the current + * implementations are synchronous anyway. + */ +static void erase_callback(struct erase_info *ei) +{ + wake_up_interruptible((wait_queue_head_t *)ei->priv); +} + +/** + * do_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to erase + * + * This function synchronously erases physical eraseblock @pnum and returns + * zero in case of success and a negative error code in case of failure. If + * %-EIO is returned, the physical eraseblock most probably went bad. + */ +static int do_sync_erase(const struct ubi_device *ubi, int pnum) +{ + int err, retries = 0; + struct erase_info ei; + wait_queue_head_t wq; + + dbg_io("erase PEB %d", pnum); + +retry: + init_waitqueue_head(&wq); + memset(&ei, 0, sizeof(struct erase_info)); + + ei.mtd = ubi->mtd; + ei.addr = pnum * ubi->peb_size; + ei.len = ubi->peb_size; + ei.callback = erase_callback; + ei.priv = (unsigned long)&wq; + + err = ubi->mtd->erase(ubi->mtd, &ei); + if (err) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error %d while erasing PEB %d, retry", + err, pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d, error %d", pnum, err); + ubi_dbg_dump_stack(); + return err; + } + + err = wait_event_interruptible(wq, ei.state == MTD_ERASE_DONE || + ei.state == MTD_ERASE_FAILED); + if (err) { + ubi_err("interrupted PEB %d erasure", pnum); + return -EINTR; + } + + if (ei.state == MTD_ERASE_FAILED) { + if (retries++ < UBI_IO_RETRIES) { + dbg_io("error while erasing PEB %d, retry", pnum); + yield(); + goto retry; + } + ubi_err("cannot erase PEB %d", pnum); + ubi_dbg_dump_stack(); + return -EIO; + } + + err = paranoid_check_all_ff(ubi, pnum, 0, ubi->peb_size); + if (err) + return err > 0 ? -EINVAL : err; + + if (ubi_dbg_is_erase_failure() && !err) { + dbg_err("cannot erase PEB %d (emulated)", pnum); + return -EIO; + } + + return 0; +} + +/** + * check_pattern - check if buffer contains only a certain byte pattern. + * @buf: buffer to check + * @patt: the pattern to check + * @size: buffer size in bytes + * + * This function returns %1 in there are only @patt bytes in @buf, and %0 if + * something else was also found. + */ +static int check_pattern(const void *buf, uint8_t patt, int size) +{ + int i; + + for (i = 0; i < size; i++) + if (((const uint8_t *)buf)[i] != patt) + return 0; + return 1; +} + +/* Patterns to write to a physical eraseblock when torturing it */ +static uint8_t patterns[] = {0xa5, 0x5a, 0x0}; + +/** + * torture_peb - test a supposedly bad physical eraseblock. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to test + * + * This function returns %-EIO if the physical eraseblock did not pass the + * test, a positive number of erase operations done if the test was + * successfully passed, and other negative error codes in case of other errors. + */ +static int torture_peb(const struct ubi_device *ubi, int pnum) +{ + void *buf; + int err, i, patt_count; + + buf = kmalloc(ubi->peb_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + patt_count = ARRAY_SIZE(patterns); + ubi_assert(patt_count > 0); + + for (i = 0; i < patt_count; i++) { + err = do_sync_erase(ubi, pnum); + if (err) + goto out; + + /* Make sure the PEB contains only 0xFF bytes */ + err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(buf, 0xFF, ubi->peb_size); + if (err == 0) { + ubi_err("erased PEB %d, but a non-0xFF byte found", + pnum); + err = -EIO; + goto out; + } + + /* Write a pattern and check it */ + memset(buf, patterns[i], ubi->peb_size); + err = ubi_io_write(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + memset(buf, ~patterns[i], ubi->peb_size); + err = ubi_io_read(ubi, buf, pnum, 0, ubi->peb_size); + if (err) + goto out; + + err = check_pattern(buf, patterns[i], ubi->peb_size); + if (err == 0) { + ubi_err("pattern %x checking failed for PEB %d", + patterns[i], pnum); + err = -EIO; + goto out; + } + } + + err = patt_count; + +out: + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) + /* + * If a bit-flip or data integrity error was detected, the test + * has not passed because it happened on a freshly erased + * physical eraseblock which means something is wrong with it. + */ + err = -EIO; + kfree(buf); + return err; +} + +/** + * ubi_io_sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to erase + * @torture: if this physical eraseblock has to be tortured + * + * This function synchronously erases physical eraseblock @pnum. If @torture + * flag is not zero, the physical eraseblock is checked by means of writing + * different patterns to it and reading them back. If the torturing is enabled, + * the physical eraseblock is erased more then once. + * + * This function returns the number of erasures made in case of success, %-EIO + * if the erasure failed or the torturing test failed, and other negative error + * codes in case of other errors. Note, %-EIO means that the physical + * eraseblock is bad. + */ +int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture) +{ + int err, ret = 0; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_not_bad(ubi, pnum); + if (err != 0) + return err > 0 ? -EINVAL : err; + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (torture) { + ret = torture_peb(ubi, pnum); + if (ret < 0) + return ret; + } + + err = do_sync_erase(ubi, pnum); + if (err) + return err; + + return ret + 1; +} + +/** + * ubi_io_is_bad - check if a physical eraseblock is bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns a positive number if the physical eraseblock is bad, + * zero if not, and a negative error code if an error occurred. + */ +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum) +{ + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->bad_allowed) { + int ret; + + ret = mtd->block_isbad(mtd, (loff_t)pnum * ubi->peb_size); + if (ret < 0) + ubi_err("error %d while checking if PEB %d is bad", + ret, pnum); + else if (ret) + dbg_io("PEB %d is bad", pnum); + return ret; + } + + return 0; +} + +/** + * ubi_io_mark_bad - mark a physical eraseblock as bad. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to mark + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + struct mtd_info *mtd = ubi->mtd; + + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + if (ubi->ro_mode) { + ubi_err("read-only mode"); + return -EROFS; + } + + if (!ubi->bad_allowed) + return 0; + + err = mtd->block_markbad(mtd, (loff_t)pnum * ubi->peb_size); + if (err) + ubi_err("cannot mark PEB %d bad, error %d", pnum, err); + return err; +} + +/** + * validate_ec_hdr - validate an erase counter header. + * @ubi: UBI device description object + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header is OK, and %1 if + * not. + */ +static int validate_ec_hdr(const struct ubi_device *ubi, + const struct ubi_ec_hdr *ec_hdr) +{ + long long ec; + int vid_hdr_offset, leb_start; + + ec = ubi64_to_cpu(ec_hdr->ec); + vid_hdr_offset = ubi32_to_cpu(ec_hdr->vid_hdr_offset); + leb_start = ubi32_to_cpu(ec_hdr->data_offset); + + if (ec_hdr->version != UBI_VERSION) { + ubi_err("node with incompatible UBI version found: " + "this UBI version is %d, image version is %d", + UBI_VERSION, (int)ec_hdr->version); + goto bad; + } + + if (vid_hdr_offset != ubi->vid_hdr_offset) { + ubi_err("bad VID header offset %d, expected %d", + vid_hdr_offset, ubi->vid_hdr_offset); + goto bad; + } + + if (leb_start != ubi->leb_start) { + ubi_err("bad data offset %d, expected %d", + leb_start, ubi->leb_start); + goto bad; + } + + if (ec < 0 || ec > UBI_MAX_ERASECOUNTER) { + ubi_err("bad erase counter %lld", ec); + goto bad; + } + + return 0; + +bad: + ubi_err("bad EC header"); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_ec_hdr - read and check an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to read from + * @ec_hdr: a &struct ubi_ec_hdr object where to store the read erase counter + * header + * @verbose: be verbose if the header is corrupted or was not found + * + * This function reads erase counter header from physical eraseblock @pnum and + * stores it in @ec_hdr. This function also checks CRC checksum of the read + * erase counter header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon (but may be not); + * o %UBI_IO_BAD_EC_HDR if the erase counter header is corrupted (a CRC error); + * o %UBI_IO_PEB_EMPTY if the physical eraseblock is empty; + * o a negative error code in case of failure. + */ +int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + + dbg_io("read EC header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean that the read data is corrupted. But we + * have a CRC check-sum and we will detect this. If the EC + * header is still OK, we just report this as there was a + * bit-flip. + */ + read_err = err; + } + + magic = ubi32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + /* + * The magic field is wrong. Let's check if we have read all + * 0xFF. If yes, this physical eraseblock is assumed to be + * empty. + * + * But if there was a read error, we do not test it for all + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and we anyway cannot treat it as empty. + */ + if (read_err != -EBADMSG && + check_pattern(ec_hdr, 0xFF, UBI_EC_HDR_SIZE)) { + /* The physical eraseblock is supposedly empty */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, 0, + ubi->peb_size); + if (err) + return err > 0 ? UBI_IO_BAD_EC_HDR : err; + + if (verbose) + ubi_warn("no EC header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_EMPTY; + } + + /* + * This is not a valid erase counter header, and these are not + * 0xFF bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_EC_HDR_MAGIC); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad EC header CRC at PEB %d, calculated %#08x," + " read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_ec_hdr(ec_hdr); + } + return UBI_IO_BAD_EC_HDR; + } + + /* And of course validate what has just been read from the media */ + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_ec_hdr - write an erase counter header. + * @ubi: UBI device description object + * @pnum: physical eraseblock to write to + * @ec_hdr: the erase counter header to write + * + * This function writes erase counter header described by @ec_hdr to physical + * eraseblock @pnum. It also fills most fields of @ec_hdr before writing, so + * the caller do not have to fill them. Callers must only fill the @ec_hdr->ec + * field. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock most probably + * went bad. + */ +int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t crc; + + dbg_io("write EC header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + ec_hdr->magic = cpu_to_ubi32(UBI_EC_HDR_MAGIC); + ec_hdr->version = UBI_VERSION; + ec_hdr->vid_hdr_offset = cpu_to_ubi32(ubi->vid_hdr_offset); + ec_hdr->data_offset = cpu_to_ubi32(ubi->leb_start); + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + ec_hdr->hdr_crc = cpu_to_ubi32(crc); + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + if (err) + return -EINVAL; + + err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); + return err; +} + +/** + * validate_vid_hdr - validate a volume identifier header. + * @ubi: UBI device description object + * @vid_hdr: the volume identifier header to check + * + * This function checks that data stored in the volume identifier header + * @vid_hdr. Returns zero if the VID header is OK and %1 if not. + */ +static int validate_vid_hdr(const struct ubi_device *ubi, + const struct ubi_vid_hdr *vid_hdr) +{ + int vol_type = vid_hdr->vol_type; + int copy_flag = vid_hdr->copy_flag; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int lnum = ubi32_to_cpu(vid_hdr->lnum); + int compat = vid_hdr->compat; + int data_size = ubi32_to_cpu(vid_hdr->data_size); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + int data_crc = ubi32_to_cpu(vid_hdr->data_crc); + int usable_leb_size = ubi->leb_size - data_pad; + + if (copy_flag != 0 && copy_flag != 1) { + dbg_err("bad copy_flag"); + goto bad; + } + + if (vol_id < 0 || lnum < 0 || data_size < 0 || used_ebs < 0 || + data_pad < 0) { + dbg_err("negative values"); + goto bad; + } + + if (vol_id >= UBI_MAX_VOLUMES && vol_id < UBI_INTERNAL_VOL_START) { + dbg_err("bad vol_id"); + goto bad; + } + + if (vol_id < UBI_INTERNAL_VOL_START && compat != 0) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_id >= UBI_INTERNAL_VOL_START && compat != UBI_COMPAT_DELETE && + compat != UBI_COMPAT_RO && compat != UBI_COMPAT_PRESERVE && + compat != UBI_COMPAT_REJECT) { + dbg_err("bad compat"); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (data_pad >= ubi->leb_size / 2) { + dbg_err("bad data_pad"); + goto bad; + } + + if (vol_type == UBI_VID_STATIC) { + /* + * Although from high-level point of view static volumes may + * contain zero bytes of data, but no VID headers can contain + * zero at these fields, because they empty volumes do not have + * mapped logical eraseblocks. + */ + if (used_ebs == 0) { + dbg_err("zero used_ebs"); + goto bad; + } + if (data_size == 0) { + dbg_err("zero data_size"); + goto bad; + } + if (lnum < used_ebs - 1) { + if (data_size != usable_leb_size) { + dbg_err("bad data_size"); + goto bad; + } + } else if (lnum == used_ebs - 1) { + if (data_size == 0) { + dbg_err("bad data_size at last LEB"); + goto bad; + } + } else { + dbg_err("too high lnum"); + goto bad; + } + } else { + if (copy_flag == 0) { + if (data_crc != 0) { + dbg_err("non-zero data CRC"); + goto bad; + } + if (data_size != 0) { + dbg_err("non-zero data_size"); + goto bad; + } + } else { + if (data_size == 0) { + dbg_err("zero data_size of copy"); + goto bad; + } + } + if (used_ebs != 0) { + dbg_err("bad used_ebs"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("bad VID header"); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * ubi_io_read_vid_hdr - read and check a volume identifier header. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to read from + * @vid_hdr: &struct ubi_vid_hdr object where to store the read volume + * identifier header + * @verbose: be verbose if the header is corrupted or wasn't found + * + * This function reads the volume identifier header from physical eraseblock + * @pnum and stores it in @vid_hdr. It also checks CRC checksum of the read + * volume identifier header. The following codes may be returned: + * + * o %0 if the CRC checksum is correct and the header was successfully read; + * o %UBI_IO_BITFLIPS if the CRC is correct, but bit-flips were detected + * and corrected by the flash driver; this is harmless but may indicate that + * this eraseblock may become bad soon; + * o %UBI_IO_BAD_VID_HRD if the volume identifier header is corrupted (a CRC + * error detected); + * o %UBI_IO_PEB_FREE if the physical eraseblock is free (i.e., there is no VID + * header there); + * o a negative error code in case of failure. + */ +int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose) +{ + int err, read_err = 0; + uint32_t crc, magic, hdr_crc; + void *p; + + dbg_io("read VID header from PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err) { + if (err != UBI_IO_BITFLIPS && err != -EBADMSG) + return err; + + /* + * We read all the data, but either a correctable bit-flip + * occurred, or MTD reported about some data integrity error, + * like an ECC error in case of NAND. The former is harmless, + * the later may mean the read data is corrupted. But we have a + * CRC check-sum and we will identify this. If the VID header is + * still OK, we just report this as there was a bit-flip. + */ + read_err = err; + } + + magic = ubi32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + /* + * If we have read all 0xFF bytes, the VID header probably does + * not exist and the physical eraseblock is assumed to be free. + * + * But if there was a read error, we do not test the data for + * 0xFFs. Even if it does contain all 0xFFs, this error + * indicates that something is still wrong with this physical + * eraseblock and it cannot be regarded as free. + */ + if (read_err != -EBADMSG && + check_pattern(vid_hdr, 0xFF, UBI_VID_HDR_SIZE)) { + /* The physical eraseblock is supposedly free */ + + /* + * The below is just a paranoid check, it has to be + * compiled out if paranoid checks are disabled. + */ + err = paranoid_check_all_ff(ubi, pnum, ubi->leb_start, + ubi->leb_size); + if (err) + return err > 0 ? UBI_IO_BAD_VID_HDR : err; + + if (verbose) + ubi_warn("no VID header found at PEB %d, " + "only 0xFF bytes", pnum); + return UBI_IO_PEB_FREE; + } + + /* + * This is not a valid VID header, and these are not 0xFF + * bytes. Report that the header is corrupted. + */ + if (verbose) { + ubi_warn("bad magic number at PEB %d: %08x instead of " + "%08x", pnum, magic, UBI_VID_HDR_MAGIC); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + + if (hdr_crc != crc) { + if (verbose) { + ubi_warn("bad CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_dbg_dump_vid_hdr(vid_hdr); + } + return UBI_IO_BAD_VID_HDR; + } + + /* Validate the VID header that we have just read */ + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("validation failed for PEB %d", pnum); + return -EINVAL; + } + + return read_err ? UBI_IO_BITFLIPS : 0; +} + +/** + * ubi_io_write_vid_hdr - write a volume identifier header. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to write to + * @vid_hdr: the volume identifier header to write + * + * This function writes the volume identifier header described by @vid_hdr to + * physical eraseblock @pnum. This function automatically fills the + * @vid_hdr->magic and the @vid_hdr->version fields, as well as calculates + * header CRC checksum and stores it at vid_hdr->hdr_crc. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If %-EIO is returned, the physical eraseblock probably went + * bad. + */ +int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t crc; + void *p; + + dbg_io("write VID header to PEB %d", pnum); + ubi_assert(pnum >= 0 && pnum < ubi->peb_count); + + err = paranoid_check_peb_ec_hdr(ubi, pnum); + if (err) + return err > 0 ? -EINVAL: err; + + vid_hdr->magic = cpu_to_ubi32(UBI_VID_HDR_MAGIC); + vid_hdr->version = UBI_VERSION; + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_VID_HDR_SIZE_CRC); + vid_hdr->hdr_crc = cpu_to_ubi32(crc); + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + if (err) + return -EINVAL; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_not_bad - ensure that a physical eraseblock is not bad. + * @ubi: UBI device description object + * @pnum: physical eraseblock number to check + * + * This function returns zero if the physical eraseblock is good, a positive + * number if it is bad and a negative error code if an error occurred. + */ +static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) +{ + int err; + + err = ubi_io_is_bad(ubi, pnum); + if (!err) + return err; + + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_stack(); + return err; +} + +/** + * paranoid_check_ec_hdr - check if an erase counter header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the erase counter header belongs to + * @ec_hdr: the erase counter header to check + * + * This function returns zero if the erase counter header contains valid + * values, and %1 if not. + */ +static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_ec_hdr *ec_hdr) +{ + int err; + uint32_t magic; + + magic = ubi32_to_cpu(ec_hdr->magic); + if (magic != UBI_EC_HDR_MAGIC) { + ubi_err("bad magic %#08x, must be %#08x", + magic, UBI_EC_HDR_MAGIC); + goto fail; + } + + err = validate_ec_hdr(ubi, ec_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return 0; + +fail: + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + return 1; +} + +/** + * paranoid_check_peb_ec_hdr - check that the erase counter header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the erase counter header is all right, %1 if + * not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read(ubi, ec_hdr, pnum, 0, UBI_EC_HDR_SIZE); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, ec_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(ec_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad CRC, calculated %#08x, read %#08x", crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_ec_hdr(ec_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); + +exit: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_vid_hdr - check that a volume identifier header is all right. + * @ubi: UBI device description object + * @pnum: physical eraseblock number the volume identifier header belongs to + * @vid_hdr: the volume identifier header to check + * + * This function returns zero if the volume identifier header is all right, and + * %1 if not. + */ +static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + int err; + uint32_t magic; + + magic = ubi32_to_cpu(vid_hdr->magic); + if (magic != UBI_VID_HDR_MAGIC) { + ubi_err("bad VID header magic %#08x at PEB %d, must be %#08x", + magic, pnum, UBI_VID_HDR_MAGIC); + goto fail; + } + + err = validate_vid_hdr(ubi, vid_hdr); + if (err) { + ubi_err("paranoid check failed for PEB %d", pnum); + goto fail; + } + + return err; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + return 1; + +} + +/** + * paranoid_check_peb_vid_hdr - check that the volume identifier header of a + * physical eraseblock is in-place and is all right. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * + * This function returns zero if the volume identifier header is all right, + * %1 if not, and a negative error code if an error occurred. + */ +static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) +{ + int err; + uint32_t crc, hdr_crc; + struct ubi_vid_hdr *vid_hdr; + void *p; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + p = (char *)vid_hdr - ubi->vid_hdr_shift; + err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset, + ubi->vid_hdr_alsize); + if (err && err != UBI_IO_BITFLIPS && err != -EBADMSG) + goto exit; + + crc = crc32(UBI_CRC32_INIT, vid_hdr, UBI_EC_HDR_SIZE_CRC); + hdr_crc = ubi32_to_cpu(vid_hdr->hdr_crc); + if (hdr_crc != crc) { + ubi_err("bad VID header CRC at PEB %d, calculated %#08x, " + "read %#08x", pnum, crc, hdr_crc); + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_stack(); + err = 1; + goto exit; + } + + err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); + +exit: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; +} + +/** + * paranoid_check_all_ff - check that a region of flash is empty. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @offset: the starting offset within the physical eraseblock to check + * @len: the length of the region to check + * + * This function returns zero if only 0xFF bytes are present at offset + * @offset of the physical eraseblock @pnum, %1 if not, and a negative error + * code if an error occurred. + */ +static int paranoid_check_all_ff(const struct ubi_device *ubi, int pnum, + int offset, int len) +{ + size_t read; + int err; + void *buf; + loff_t addr = (loff_t)pnum * ubi->peb_size + offset; + + buf = kzalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + err = ubi->mtd->read(ubi->mtd, addr, len, &read, buf); + if (err && err != -EUCLEAN) { + ubi_err("error %d while reading %d bytes from PEB %d:%d, " + "read %zd bytes", err, len, pnum, offset, read); + goto error; + } + + err = check_pattern(buf, 0xFF, len); + if (err == 0) { + ubi_err("flash region at PEB %d:%d, length %d does not " + "contain all 0xFF bytes", pnum, offset, len); + goto fail; + } + + kfree(buf); + return 0; + +fail: + ubi_err("paranoid check failed for PEB %d", pnum); + dbg_msg("hex dump of the %d-%d region", offset, offset + len); + ubi_dbg_hexdump(buf, len); + err = 1; +error: + ubi_dbg_dump_stack(); + kfree(buf); + return err; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c new file mode 100644 index 0000000..d352c45 --- /dev/null +++ b/drivers/mtd/ubi/kapi.c @@ -0,0 +1,575 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* This file mostly implements UBI kernel API functions */ + +#include <linux/module.h> +#include <linux/err.h> +#include <asm/div64.h> +#include "ubi.h" + +/** + * ubi_get_device_info - get information about UBI device. + * @ubi_num: UBI device number + * @di: the information is stored here + * + * This function returns %0 in case of success and a %-ENODEV if there is no + * such UBI device. + */ +int ubi_get_device_info(int ubi_num, struct ubi_device_info *di) +{ + const struct ubi_device *ubi; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || + !ubi_devices[ubi_num]) { + module_put(THIS_MODULE); + return -ENODEV; + } + + ubi = ubi_devices[ubi_num]; + di->ubi_num = ubi->ubi_num; + di->leb_size = ubi->leb_size; + di->min_io_size = ubi->min_io_size; + di->ro_mode = ubi->ro_mode; + di->cdev = MKDEV(ubi->major, 0); + module_put(THIS_MODULE); + return 0; +} +EXPORT_SYMBOL_GPL(ubi_get_device_info); + +/** + * ubi_get_volume_info - get information about UBI volume. + * @desc: volume descriptor + * @vi: the information is stored here + */ +void ubi_get_volume_info(struct ubi_volume_desc *desc, + struct ubi_volume_info *vi) +{ + const struct ubi_volume *vol = desc->vol; + const struct ubi_device *ubi = vol->ubi; + + vi->vol_id = vol->vol_id; + vi->ubi_num = ubi->ubi_num; + vi->size = vol->reserved_pebs; + vi->used_bytes = vol->used_bytes; + vi->vol_type = vol->vol_type; + vi->corrupted = vol->corrupted; + vi->upd_marker = vol->upd_marker; + vi->alignment = vol->alignment; + vi->usable_leb_size = vol->usable_leb_size; + vi->name_len = vol->name_len; + vi->name = vol->name; + vi->cdev = MKDEV(ubi->major, vi->vol_id + 1); +} +EXPORT_SYMBOL_GPL(ubi_get_volume_info); + +/** + * ubi_open_volume - open UBI volume. + * @ubi_num: UBI device number + * @vol_id: volume ID + * @mode: open mode + * + * The @mode parameter specifies if the volume should be opened in read-only + * mode, read-write mode, or exclusive mode. The exclusive mode guarantees that + * nobody else will be able to open this volume. UBI allows to have many volume + * readers and one writer at a time. + * + * If a static volume is being opened for the first time since boot, it will be + * checked by this function, which means it will be fully read and the CRC + * checksum of each logical eraseblock will be checked. + * + * This function returns volume descriptor in case of success and a negative + * error code in case of failure. + */ +struct ubi_volume_desc *ubi_open_volume(int ubi_num, int vol_id, int mode) +{ + int err; + struct ubi_volume_desc *desc; + struct ubi_device *ubi = ubi_devices[ubi_num]; + struct ubi_volume *vol; + + dbg_msg("open device %d volume %d, mode %d", ubi_num, vol_id, mode); + + err = -ENODEV; + if (!try_module_get(THIS_MODULE)) + return ERR_PTR(err); + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi) + goto out_put; + + err = -EINVAL; + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + goto out_put; + if (mode != UBI_READONLY && mode != UBI_READWRITE && + mode != UBI_EXCLUSIVE) + goto out_put; + + desc = kmalloc(sizeof(struct ubi_volume_desc), GFP_KERNEL); + if (!desc) { + err = -ENOMEM; + goto out_put; + } + + spin_lock(&ubi->volumes_lock); + vol = ubi->volumes[vol_id]; + if (!vol) { + err = -ENODEV; + goto out_unlock; + } + + err = -EBUSY; + switch (mode) { + case UBI_READONLY: + if (vol->exclusive) + goto out_unlock; + vol->readers += 1; + break; + + case UBI_READWRITE: + if (vol->exclusive || vol->writers > 0) + goto out_unlock; + vol->writers += 1; + break; + + case UBI_EXCLUSIVE: + if (vol->exclusive || vol->writers || vol->readers) + goto out_unlock; + vol->exclusive = 1; + break; + } + spin_unlock(&ubi->volumes_lock); + + desc->vol = vol; + desc->mode = mode; + + /* + * To prevent simultaneous checks of the same volume we use @vtbl_mutex, + * although it is not the purpose it was introduced for. + */ + mutex_lock(&ubi->vtbl_mutex); + if (!vol->checked) { + /* This is the first open - check the volume */ + err = ubi_check_volume(ubi, vol_id); + if (err < 0) { + mutex_unlock(&ubi->vtbl_mutex); + ubi_close_volume(desc); + return ERR_PTR(err); + } + if (err == 1) { + ubi_warn("volume %d on UBI device %d is corrupted", + vol_id, ubi->ubi_num); + vol->corrupted = 1; + } + vol->checked = 1; + } + mutex_unlock(&ubi->vtbl_mutex); + return desc; + +out_unlock: + spin_unlock(&ubi->volumes_lock); + kfree(desc); +out_put: + module_put(THIS_MODULE); + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(ubi_open_volume); + +/** + * ubi_open_volume_nm - open UBI volume by name. + * @ubi_num: UBI device number + * @name: volume name + * @mode: open mode + * + * This function is similar to 'ubi_open_volume()', but opens a volume by name. + */ +struct ubi_volume_desc *ubi_open_volume_nm(int ubi_num, const char *name, + int mode) +{ + int i, vol_id = -1, len; + struct ubi_volume_desc *ret; + struct ubi_device *ubi; + + dbg_msg("open volume %s, mode %d", name, mode); + + if (!name) + return ERR_PTR(-EINVAL); + + len = strnlen(name, UBI_VOL_NAME_MAX + 1); + if (len > UBI_VOL_NAME_MAX) + return ERR_PTR(-EINVAL); + + ret = ERR_PTR(-ENODEV); + if (!try_module_get(THIS_MODULE)) + return ret; + + if (ubi_num < 0 || ubi_num >= UBI_MAX_DEVICES || !ubi_devices[ubi_num]) + goto out_put; + + ubi = ubi_devices[ubi_num]; + + spin_lock(&ubi->volumes_lock); + /* Walk all volumes of this UBI device */ + for (i = 0; i < ubi->vtbl_slots; i++) { + struct ubi_volume *vol = ubi->volumes[i]; + + if (vol && len == vol->name_len && !strcmp(name, vol->name)) { + vol_id = i; + break; + } + } + spin_unlock(&ubi->volumes_lock); + + if (vol_id < 0) + goto out_put; + + ret = ubi_open_volume(ubi_num, vol_id, mode); + +out_put: + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(ubi_open_volume_nm); + +/** + * ubi_close_volume - close UBI volume. + * @desc: volume descriptor + */ +void ubi_close_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + + dbg_msg("close volume %d, mode %d", vol->vol_id, desc->mode); + + spin_lock(&vol->ubi->volumes_lock); + switch (desc->mode) { + case UBI_READONLY: + vol->readers -= 1; + break; + case UBI_READWRITE: + vol->writers -= 1; + break; + case UBI_EXCLUSIVE: + vol->exclusive = 0; + } + spin_unlock(&vol->ubi->volumes_lock); + + kfree(desc); + module_put(THIS_MODULE); +} +EXPORT_SYMBOL_GPL(ubi_close_volume); + +/** + * ubi_leb_read - read data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to read from + * @buf: buffer where to store the read data + * @offset: offset within the logical eraseblock to read from + * @len: how many bytes to read + * @check: whether UBI has to check the read data's CRC or not. + * + * This function reads data from offset @offset of logical eraseblock @lnum and + * stores the data at @buf. When reading from static volumes, @check specifies + * whether the data has to be checked or not. If yes, the whole logical + * eraseblock will be read and its CRC checksum will be checked (i.e., the CRC + * checksum is per-eraseblock). So checking may substantially slow down the + * read speed. The @check argument is ignored for dynamic volumes. + * + * In case of success, this function returns zero. In case of failure, this + * function returns a negative error code. + * + * %-EBADMSG error code is returned: + * o for both static and dynamic volumes if MTD driver has detected a data + * integrity problem (unrecoverable ECC checksum mismatch in case of NAND); + * o for static volumes in case of data CRC mismatch. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF error code. + */ +int ubi_leb_read(struct ubi_volume_desc *desc, int lnum, char *buf, int offset, + int len, int check) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_msg("read %d bytes from LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots || lnum < 0 || + lnum >= vol->used_ebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size) + return -EINVAL; + + if (vol->vol_type == UBI_STATIC_VOLUME && lnum == vol->used_ebs - 1 && + offset + len > vol->last_eb_bytes) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + if (len == 0) + return 0; + + err = ubi_eba_read_leb(ubi, vol_id, lnum, buf, offset, len, check); + if (err && err == -EBADMSG && vol->vol_type == UBI_STATIC_VOLUME) { + ubi_warn("mark volume %d as corrupted", vol_id); + vol->corrupted = 1; + } + + return err; +} +EXPORT_SYMBOL_GPL(ubi_leb_read); + +/** + * ubi_leb_write - write data. + * @desc: volume descriptor + * @lnum: logical eraseblock number to write to + * @buf: data to write + * @offset: offset within the logical eraseblock where to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function writes @len bytes of data from @buf to offset @offset of + * logical eraseblock @lnum. The @dtype argument describes expected lifetime of + * the data. + * + * This function takes care of physical eraseblock write failures. If write to + * the physical eraseblock write operation fails, the logical eraseblock is + * re-mapped to another physical eraseblock, the data is recovered, and the + * write finishes. UBI has a pool of reserved physical eraseblocks for this. + * + * If all the data were successfully written, zero is returned. If an error + * occurred and UBI has not been able to recover from it, this function returns + * a negative error code. Note, in case of an error, it is possible that + * something was still written to the flash media, but that may be some + * garbage. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_write(struct ubi_volume_desc *desc, int lnum, const void *buf, + int offset, int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("write %d bytes to LEB %d:%d:%d", len, vol_id, lnum, offset); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || offset < 0 || len < 0 || + offset + len > vol->usable_leb_size || offset % ubi->min_io_size || + len % ubi->min_io_size) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_write_leb(ubi, vol_id, lnum, buf, offset, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_write); + +/* + * ubi_leb_change - change logical eraseblock atomically. + * @desc: volume descriptor + * @lnum: logical eraseblock number to change + * @buf: data to write + * @len: how many bytes to write + * @dtype: expected data type + * + * This function changes the contents of a logical eraseblock atomically. @buf + * has to contain new logical eraseblock data, and @len - the length of the + * data, which has to be aligned. The length may be shorter then the logical + * eraseblock size, ant the logical eraseblock may be appended to more times + * later on. This function guarantees that in case of an unclean reboot the old + * contents is preserved. Returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_leb_change(struct ubi_volume_desc *desc, int lnum, const void *buf, + int len, int dtype) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("atomically write %d bytes to LEB %d:%d", len, vol_id, lnum); + + if (vol_id < 0 || vol_id >= ubi->vtbl_slots) + return -EINVAL; + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs || len < 0 || + len > vol->usable_leb_size || len % ubi->min_io_size) + return -EINVAL; + + if (dtype != UBI_LONGTERM && dtype != UBI_SHORTTERM && + dtype != UBI_UNKNOWN) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + if (len == 0) + return 0; + + return ubi_eba_atomic_leb_change(ubi, vol_id, lnum, buf, len, dtype); +} +EXPORT_SYMBOL_GPL(ubi_leb_change); + +/** + * ubi_leb_erase - erase logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and synchronously erases the + * correspondent physical eraseblock. Returns zero in case of success and a + * negative error code in case of failure. + * + * If the volume is damaged because of an interrupted update this function just + * returns immediately with %-EBADF code. + */ +int ubi_leb_erase(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int err, vol_id = vol->vol_id; + + dbg_msg("erase LEB %d:%d", vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + err = ubi_eba_unmap_leb(ubi, vol_id, lnum); + if (err) + return err; + + return ubi_wl_flush(ubi); +} +EXPORT_SYMBOL_GPL(ubi_leb_erase); + +/** + * ubi_leb_unmap - un-map logical eraseblock. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function un-maps logical eraseblock @lnum and schedules the + * corresponding physical eraseblock for erasure, so that it will eventually be + * physically erased in background. This operation is much faster then the + * erase operation. + * + * Unlike erase, the un-map operation does not guarantee that the logical + * eraseblock will contain all 0xFF bytes when UBI is initialized again. For + * example, if several logical eraseblocks are un-mapped, and an unclean reboot + * happens after this, the logical eraseblocks will not necessarily be + * un-mapped again when this MTD device is attached. They may actually be + * mapped to the same physical eraseblocks again. So, this function has to be + * used with care. + * + * In other words, when un-mapping a logical eraseblock, UBI does not store + * any information about this on the flash media, it just marks the logical + * eraseblock as "un-mapped" in RAM. If UBI is detached before the physical + * eraseblock is physically erased, it will be mapped again to the same logical + * eraseblock when the MTD device is attached again. + * + * The main and obvious use-case of this function is when the contents of a + * logical eraseblock has to be re-written. Then it is much more efficient to + * first un-map it, then write new data, rather then first erase it, then write + * new data. Note, once new data has been written to the logical eraseblock, + * UBI guarantees that the old contents has gone forever. In other words, if an + * unclean reboot happens after the logical eraseblock has been un-mapped and + * then written to, it will contain the last written data. + * + * This function returns zero in case of success and a negative error code in + * case of failure. If the volume is damaged because of an interrupted update + * this function just returns immediately with %-EBADF code. + */ +int ubi_leb_unmap(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int vol_id = vol->vol_id; + + dbg_msg("unmap LEB %d:%d", vol_id, lnum); + + if (desc->mode == UBI_READONLY || vol->vol_type == UBI_STATIC_VOLUME) + return -EROFS; + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return ubi_eba_unmap_leb(ubi, vol_id, lnum); +} +EXPORT_SYMBOL_GPL(ubi_leb_unmap); + +/** + * ubi_is_mapped - check if logical eraseblock is mapped. + * @desc: volume descriptor + * @lnum: logical eraseblock number + * + * This function checks if logical eraseblock @lnum is mapped to a physical + * eraseblock. If a logical eraseblock is un-mapped, this does not necessarily + * mean it will still be un-mapped after the UBI device is re-attached. The + * logical eraseblock may become mapped to the physical eraseblock it was last + * mapped to. + * + * This function returns %1 if the LEB is mapped, %0 if not, and a negative + * error code in case of failure. If the volume is damaged because of an + * interrupted update this function just returns immediately with %-EBADF error + * code. + */ +int ubi_is_mapped(struct ubi_volume_desc *desc, int lnum) +{ + struct ubi_volume *vol = desc->vol; + + dbg_msg("test LEB %d:%d", vol->vol_id, lnum); + + if (lnum < 0 || lnum >= vol->reserved_pebs) + return -EINVAL; + + if (vol->upd_marker) + return -EBADF; + + return vol->eba_tbl[lnum] >= 0; +} +EXPORT_SYMBOL_GPL(ubi_is_mapped); diff --git a/drivers/mtd/ubi/misc.c b/drivers/mtd/ubi/misc.c new file mode 100644 index 0000000..38d4e67 --- /dev/null +++ b/drivers/mtd/ubi/misc.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* Here we keep miscellaneous functions which are used all over the UBI code */ + +#include "ubi.h" + +/** + * calc_data_len - calculate how much real data is stored in a buffer. + * @ubi: UBI device description object + * @buf: a buffer with the contents of the physical eraseblock + * @length: the buffer length + * + * This function calculates how much "real data" is stored in @buf and returnes + * the length. Continuous 0xFF bytes at the end of the buffer are not + * considered as "real data". + */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, + int length) +{ + int i; + + ubi_assert(length % ubi->min_io_size == 0); + + for (i = length - 1; i >= 0; i--) + if (((const uint8_t *)buf)[i] != 0xFF) + break; + + /* The resulting length must be aligned to the minimum flash I/O size */ + length = ALIGN(i + 1, ubi->min_io_size); + return length; +} + +/** + * ubi_check_volume - check the contents of a static volume. + * @ubi: UBI device description object + * @vol_id: ID of the volume to check + * + * This function checks if static volume @vol_id is corrupted by fully reading + * it and checking data CRC. This function returns %0 if the volume is not + * corrupted, %1 if it is corrupted and a negative error code in case of + * failure. Dynamic volumes are not checked and zero is returned immediately. + */ +int ubi_check_volume(struct ubi_device *ubi, int vol_id) +{ + void *buf; + int err = 0, i; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type != UBI_STATIC_VOLUME) + return 0; + + buf = kmalloc(vol->usable_leb_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < vol->used_ebs; i++) { + int size; + + if (i == vol->used_ebs - 1) + size = vol->last_eb_bytes; + else + size = vol->usable_leb_size; + + err = ubi_eba_read_leb(ubi, vol_id, i, buf, 0, size, 1); + if (err) { + if (err == -EBADMSG) + err = 1; + break; + } + } + + kfree(buf); + return err; +} + +/** + * ubi_calculate_rsvd_pool - calculate how many PEBs must be reserved for bad + * eraseblock handling. + * @ubi: UBI device description object + */ +void ubi_calculate_reserved(struct ubi_device *ubi) +{ + ubi->beb_rsvd_level = ubi->good_peb_count/100; + ubi->beb_rsvd_level *= CONFIG_MTD_UBI_BEB_RESERVE; + if (ubi->beb_rsvd_level < MIN_RESEVED_PEBS) + ubi->beb_rsvd_level = MIN_RESEVED_PEBS; +} diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c new file mode 100644 index 0000000..473f320 --- /dev/null +++ b/drivers/mtd/ubi/scan.c @@ -0,0 +1,1368 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * UBI scanning unit. + * + * This unit is responsible for scanning the flash media, checking UBI + * headers and providing complete information about the UBI flash image. + * + * The scanning information is reoresented by a &struct ubi_scan_info' object. + * Information about found volumes is represented by &struct ubi_scan_volume + * objects which are kept in volume RB-tree with root at the @volumes field. + * The RB-tree is indexed by the volume ID. + * + * Found logical eraseblocks are represented by &struct ubi_scan_leb objects. + * These objects are kept in per-volume RB-trees with the root at the + * corresponding &struct ubi_scan_volume object. To put it differently, we keep + * an RB-tree of per-volume objects and each of these objects is the root of + * RB-tree of per-eraseblock objects. + * + * Corrupted physical eraseblocks are put to the @corr list, free physical + * eraseblocks are put to the @free list and the physical eraseblock to be + * erased are put to the @erase list. + */ + +#include <linux/err.h> +#include <linux/crc32.h> +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_si(const struct ubi_device *ubi, + struct ubi_scan_info *si); +#else +#define paranoid_check_si(ubi, si) 0 +#endif + +/* Temporary variables used during scanning */ +static struct ubi_ec_hdr *ech; +static struct ubi_vid_hdr *vidh; + +int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list) +{ + struct ubi_scan_leb *seb; + + if (list == &si->free) + dbg_bld("add to free: PEB %d, EC %d", pnum, ec); + else if (list == &si->erase) + dbg_bld("add to erase: PEB %d, EC %d", pnum, ec); + else if (list == &si->corr) + dbg_bld("add to corrupted: PEB %d, EC %d", pnum, ec); + else if (list == &si->alien) + dbg_bld("add to alien: PEB %d, EC %d", pnum, ec); + else + BUG(); + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->pnum = pnum; + seb->ec = ec; + list_add_tail(&seb->u.list, list); + return 0; +} + +/** + * commit_to_mean_value - commit intermediate results to the final mean erase + * counter value. + * @si: scanning information + * + * This is a helper function which calculates partial mean erase counter mean + * value and adds it to the resulting mean value. As we can work only in + * integer arithmetic and we want to calculate the mean value of erase counter + * accurately, we first sum erase counter values in @si->ec_sum variable and + * count these components in @si->ec_count. If this temporary @si->ec_sum is + * going to overflow, we calculate the partial mean value + * (@si->ec_sum/@si->ec_count) and add it to @si->mean_ec. + */ +static void commit_to_mean_value(struct ubi_scan_info *si) +{ + si->ec_sum /= si->ec_count; + if (si->ec_sum % si->ec_count >= si->ec_count / 2) + si->mean_ec += 1; + si->mean_ec += si->ec_sum; +} + +/** + * validate_vid_hdr - check that volume identifier header is correct and + * consistent. + * @vid_hdr: the volume identifier header to check + * @sv: information about the volume this logical eraseblock belongs to + * @pnum: physical eraseblock number the VID header came from + * + * This function checks that data stored in @vid_hdr is consistent. Returns + * non-zero if an inconsistency was found and zero if not. + * + * Note, UBI does sanity check of everything it reads from the flash media. + * Most of the checks are done in the I/O unit. Here we check that the + * information in the VID header is consistent to the information in other VID + * headers of the same volume. + */ +static int validate_vid_hdr(const struct ubi_vid_hdr *vid_hdr, + const struct ubi_scan_volume *sv, int pnum) +{ + int vol_type = vid_hdr->vol_type; + int vol_id = ubi32_to_cpu(vid_hdr->vol_id); + int used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + int data_pad = ubi32_to_cpu(vid_hdr->data_pad); + + if (sv->leb_count != 0) { + int sv_vol_type; + + /* + * This is not the first logical eraseblock belonging to this + * volume. Ensure that the data in its VID header is consistent + * to the data in previous logical eraseblock headers. + */ + + if (vol_id != sv->vol_id) { + dbg_err("inconsistent vol_id"); + goto bad; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) + sv_vol_type = UBI_VID_STATIC; + else + sv_vol_type = UBI_VID_DYNAMIC; + + if (vol_type != sv_vol_type) { + dbg_err("inconsistent vol_type"); + goto bad; + } + + if (used_ebs != sv->used_ebs) { + dbg_err("inconsistent used_ebs"); + goto bad; + } + + if (data_pad != sv->data_pad) { + dbg_err("inconsistent data_pad"); + goto bad; + } + } + + return 0; + +bad: + ubi_err("inconsistent VID header at PEB %d", pnum); + ubi_dbg_dump_vid_hdr(vid_hdr); + ubi_dbg_dump_sv(sv); + return -EINVAL; +} + +/** + * add_volume - add volume to the scanning information. + * @si: scanning information + * @vol_id: ID of the volume to add + * @pnum: physical eraseblock number + * @vid_hdr: volume identifier header + * + * If the volume corresponding to the @vid_hdr logical eraseblock is already + * present in the scanning information, this function does nothing. Otherwise + * it adds corresponding volume to the scanning information. Returns a pointer + * to the scanning volume object in case of success and a negative error code + * in case of failure. + */ +static struct ubi_scan_volume *add_volume(struct ubi_scan_info *si, int vol_id, + int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + struct ubi_scan_volume *sv; + struct rb_node **p = &si->volumes.rb_node, *parent = NULL; + + ubi_assert(vol_id == ubi32_to_cpu(vid_hdr->vol_id)); + + /* Walk the volume RB-tree to look if this volume is already present */ + while (*p) { + parent = *p; + sv = rb_entry(parent, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + + /* The volume is absent - add it */ + sv = kmalloc(sizeof(struct ubi_scan_volume), GFP_KERNEL); + if (!sv) + return ERR_PTR(-ENOMEM); + + sv->highest_lnum = sv->leb_count = 0; + si->max_sqnum = 0; + sv->vol_id = vol_id; + sv->root = RB_ROOT; + sv->used_ebs = ubi32_to_cpu(vid_hdr->used_ebs); + sv->data_pad = ubi32_to_cpu(vid_hdr->data_pad); + sv->compat = vid_hdr->compat; + sv->vol_type = vid_hdr->vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME + : UBI_STATIC_VOLUME; + if (vol_id > si->highest_vol_id) + si->highest_vol_id = vol_id; + + rb_link_node(&sv->rb, parent, p); + rb_insert_color(&sv->rb, &si->volumes); + si->vols_found += 1; + dbg_bld("added volume %d", vol_id); + return sv; +} + +/** + * compare_lebs - find out which logical eraseblock is newer. + * @ubi: UBI device description object + * @seb: first logical eraseblock to compare + * @pnum: physical eraseblock number of the second logical eraseblock to + * compare + * @vid_hdr: volume identifier header of the second logical eraseblock + * + * This function compares 2 copies of a LEB and informs which one is newer. In + * case of success this function returns a positive value, in case of failure, a + * negative error code is returned. The success return codes use the following + * bits: + * o bit 0 is cleared: the first PEB (described by @seb) is newer then the + * second PEB (described by @pnum and @vid_hdr); + * o bit 0 is set: the second PEB is newer; + * o bit 1 is cleared: no bit-flips were detected in the newer LEB; + * o bit 1 is set: bit-flips were detected in the newer LEB; + * o bit 2 is cleared: the older LEB is not corrupted; + * o bit 2 is set: the older LEB is corrupted. + */ +static int compare_lebs(const struct ubi_device *ubi, + const struct ubi_scan_leb *seb, int pnum, + const struct ubi_vid_hdr *vid_hdr) +{ + void *buf; + int len, err, second_is_newer, bitflips = 0, corrupted = 0; + uint32_t data_crc, crc; + struct ubi_vid_hdr *vidh = NULL; + unsigned long long sqnum2 = ubi64_to_cpu(vid_hdr->sqnum); + + if (seb->sqnum == 0 && sqnum2 == 0) { + long long abs, v1 = seb->leb_ver, v2 = ubi32_to_cpu(vid_hdr->leb_ver); + + /* + * UBI constantly increases the logical eraseblock version + * number and it can overflow. Thus, we have to bear in mind + * that versions that are close to %0xFFFFFFFF are less then + * versions that are close to %0. + * + * The UBI WL unit guarantees that the number of pending tasks + * is not greater then %0x7FFFFFFF. So, if the difference + * between any two versions is greater or equivalent to + * %0x7FFFFFFF, there was an overflow and the logical + * eraseblock with lower version is actually newer then the one + * with higher version. + * + * FIXME: but this is anyway obsolete and will be removed at + * some point. + */ + + dbg_bld("using old crappy leb_ver stuff"); + + abs = v1 - v2; + if (abs < 0) + abs = -abs; + + if (abs < 0x7FFFFFFF) + /* Non-overflow situation */ + second_is_newer = (v2 > v1); + else + second_is_newer = (v2 < v1); + } else + /* Obviously the LEB with lower sequence counter is older */ + second_is_newer = sqnum2 > seb->sqnum; + + /* + * Now we know which copy is newer. If the copy flag of the PEB with + * newer version is not set, then we just return, otherwise we have to + * check data CRC. For the second PEB we already have the VID header, + * for the first one - we'll need to re-read it from flash. + * + * FIXME: this may be optimized so that we wouldn't read twice. + */ + + if (second_is_newer) { + if (!vid_hdr->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("second PEB %d is newer, copy_flag is unset", + pnum); + return 1; + } + } else { + pnum = seb->pnum; + + vidh = ubi_zalloc_vid_hdr(ubi); + if (!vidh) + return -ENOMEM; + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err) { + if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else { + dbg_err("VID of PEB %d header is bad, but it " + "was OK earlier", pnum); + if (err > 0) + err = -EIO; + + goto out_free_vidh; + } + } + + if (!vidh->copy_flag) { + /* It is not a copy, so it is newer */ + dbg_bld("first PEB %d is newer, copy_flag is unset", + pnum); + err = bitflips << 1; + goto out_free_vidh; + } + + vid_hdr = vidh; + } + + /* Read the data of the copy and check the CRC */ + + len = ubi32_to_cpu(vid_hdr->data_size); + buf = kmalloc(len, GFP_KERNEL); + if (!buf) { + err = -ENOMEM; + goto out_free_vidh; + } + + err = ubi_io_read_data(ubi, buf, pnum, 0, len); + if (err && err != UBI_IO_BITFLIPS) + goto out_free_buf; + + data_crc = ubi32_to_cpu(vid_hdr->data_crc); + crc = crc32(UBI_CRC32_INIT, buf, len); + if (crc != data_crc) { + dbg_bld("PEB %d CRC error: calculated %#08x, must be %#08x", + pnum, crc, data_crc); + corrupted = 1; + bitflips = 0; + second_is_newer = !second_is_newer; + } else { + dbg_bld("PEB %d CRC is OK", pnum); + bitflips = !!err; + } + + kfree(buf); + ubi_free_vid_hdr(ubi, vidh); + + if (second_is_newer) + dbg_bld("second PEB %d is newer, copy_flag is set", pnum); + else + dbg_bld("first PEB %d is newer, copy_flag is set", pnum); + + return second_is_newer | (bitflips << 1) | (corrupted << 2); + +out_free_buf: + kfree(buf); +out_free_vidh: + ubi_free_vid_hdr(ubi, vidh); + ubi_assert(err < 0); + return err; +} + +/** + * ubi_scan_add_used - add information about a physical eraseblock to the + * scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * @ec: erase counter + * @vid_hdr: the volume identifier header + * @bitflips: if bit-flips were detected when this physical eraseblock was read + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips) +{ + int err, vol_id, lnum; + uint32_t leb_ver; + unsigned long long sqnum; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct rb_node **p, *parent = NULL; + + vol_id = ubi32_to_cpu(vid_hdr->vol_id); + lnum = ubi32_to_cpu(vid_hdr->lnum); + sqnum = ubi64_to_cpu(vid_hdr->sqnum); + leb_ver = ubi32_to_cpu(vid_hdr->leb_ver); + + dbg_bld("PEB %d, LEB %d:%d, EC %d, sqnum %llu, ver %u, bitflips %d", + pnum, vol_id, lnum, ec, sqnum, leb_ver, bitflips); + + sv = add_volume(si, vol_id, pnum, vid_hdr); + if (IS_ERR(sv) < 0) + return PTR_ERR(sv); + + /* + * Walk the RB-tree of logical eraseblocks of volume @vol_id to look + * if this is the first instance of this logical eraseblock or not. + */ + p = &sv->root.rb_node; + while (*p) { + int cmp_res; + + parent = *p; + seb = rb_entry(parent, struct ubi_scan_leb, u.rb); + if (lnum != seb->lnum) { + if (lnum < seb->lnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + continue; + } + + /* + * There is already a physical eraseblock describing the same + * logical eraseblock present. + */ + + dbg_bld("this LEB already exists: PEB %d, sqnum %llu, " + "LEB ver %u, EC %d", seb->pnum, seb->sqnum, + seb->leb_ver, seb->ec); + + /* + * Make sure that the logical eraseblocks have different + * versions. Otherwise the image is bad. + */ + if (seb->leb_ver == leb_ver && leb_ver != 0) { + ubi_err("two LEBs with same version %u", leb_ver); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Make sure that the logical eraseblocks have different + * sequence numbers. Otherwise the image is bad. + * + * FIXME: remove 'sqnum != 0' check when leb_ver is removed. + */ + if (seb->sqnum == sqnum && sqnum != 0) { + ubi_err("two LEBs with same sequence number %llu", + sqnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_vid_hdr(vid_hdr); + return -EINVAL; + } + + /* + * Now we have to drop the older one and preserve the newer + * one. + */ + cmp_res = compare_lebs(ubi, seb, pnum, vid_hdr); + if (cmp_res < 0) + return cmp_res; + + if (cmp_res & 1) { + /* + * This logical eraseblock is newer then the one + * found earlier. + */ + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + if (cmp_res & 4) + err = ubi_scan_add_to_list(si, seb->pnum, + seb->ec, &si->corr); + else + err = ubi_scan_add_to_list(si, seb->pnum, + seb->ec, &si->erase); + if (err) + return err; + + seb->ec = ec; + seb->pnum = pnum; + seb->scrub = ((cmp_res & 2) || bitflips); + seb->sqnum = sqnum; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum == lnum) + sv->last_data_size = + ubi32_to_cpu(vid_hdr->data_size); + + return 0; + } else { + /* + * This logical eraseblock is older then the one found + * previously. + */ + if (cmp_res & 4) + return ubi_scan_add_to_list(si, pnum, ec, + &si->corr); + else + return ubi_scan_add_to_list(si, pnum, ec, + &si->erase); + } + } + + /* + * We've met this logical eraseblock for the first time, add it to the + * scanning information. + */ + + err = validate_vid_hdr(vid_hdr, sv, pnum); + if (err) + return err; + + seb = kmalloc(sizeof(struct ubi_scan_leb), GFP_KERNEL); + if (!seb) + return -ENOMEM; + + seb->ec = ec; + seb->pnum = pnum; + seb->lnum = lnum; + seb->sqnum = sqnum; + seb->scrub = bitflips; + seb->leb_ver = leb_ver; + + if (sv->highest_lnum <= lnum) { + sv->highest_lnum = lnum; + sv->last_data_size = ubi32_to_cpu(vid_hdr->data_size); + } + + if (si->max_sqnum < sqnum) + si->max_sqnum = sqnum; + + sv->leb_count += 1; + rb_link_node(&seb->u.rb, parent, p); + rb_insert_color(&seb->u.rb, &sv->root); + return 0; +} + +/** + * ubi_scan_find_sv - find information about a particular volume in the + * scanning information. + * @si: scanning information + * @vol_id: the requested volume ID + * + * This function returns a pointer to the volume description or %NULL if there + * are no data about this volume in the scanning information. + */ +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id) +{ + struct ubi_scan_volume *sv; + struct rb_node *p = si->volumes.rb_node; + + while (p) { + sv = rb_entry(p, struct ubi_scan_volume, rb); + + if (vol_id == sv->vol_id) + return sv; + + if (vol_id > sv->vol_id) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_find_seb - find information about a particular logical + * eraseblock in the volume scanning information. + * @sv: a pointer to the volume scanning information + * @lnum: the requested logical eraseblock + * + * This function returns a pointer to the scanning logical eraseblock or %NULL + * if there are no data about it in the scanning volume information. + */ +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum) +{ + struct ubi_scan_leb *seb; + struct rb_node *p = sv->root.rb_node; + + while (p) { + seb = rb_entry(p, struct ubi_scan_leb, u.rb); + + if (lnum == seb->lnum) + return seb; + + if (lnum > seb->lnum) + p = p->rb_left; + else + p = p->rb_right; + } + + return NULL; +} + +/** + * ubi_scan_rm_volume - delete scanning information about a volume. + * @si: scanning information + * @sv: the volume scanning information to delete + */ +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv) +{ + struct rb_node *rb; + struct ubi_scan_leb *seb; + + dbg_bld("remove scanning information about volume %d", sv->vol_id); + + while ((rb = rb_first(&sv->root))) { + seb = rb_entry(rb, struct ubi_scan_leb, u.rb); + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, &si->erase); + } + + rb_erase(&sv->rb, &si->volumes); + kfree(sv); + si->vols_found -= 1; +} + +/** + * ubi_scan_erase_peb - erase a physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: physical eraseblock number to erase; + * @ec: erase counter value to write (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * + * This function erases physical eraseblock 'pnum', and writes the erase + * counter header to it. This function should only be used on UBI device + * initialization stages, when the EBA unit had not been yet initialized. This + * function returns zero in case of success and a negative error code in case + * of failure. + */ +int ubi_scan_erase_peb(const struct ubi_device *ubi, + const struct ubi_scan_info *si, int pnum, int ec) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + if ((long long)ec >= UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %d", pnum, ec); + return -EINVAL; + } + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_sync_erase(ubi, pnum, 0); + if (err < 0) + goto out_free; + + err = ubi_io_write_ec_hdr(ubi, pnum, ec_hdr); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * ubi_scan_get_free_peb - get a free physical eraseblock. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns a free physical eraseblock. It is supposed to be + * called on the UBI initialization stages when the wear-leveling unit is not + * initialized yet. This function picks a physical eraseblocks from one of the + * lists, writes the EC header if it is needed, and removes it from the list. + * + * This function returns scanning physical eraseblock information in case of + * success and an error code in case of failure. + */ +struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err = 0, i; + struct ubi_scan_leb *seb; + + if (!list_empty(&si->free)) { + seb = list_entry(si->free.next, struct ubi_scan_leb, u.list); + list_del(&seb->u.list); + dbg_bld("return free PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + + for (i = 0; i < 2; i++) { + struct list_head *head; + struct ubi_scan_leb *tmp_seb; + + if (i == 0) + head = &si->erase; + else + head = &si->corr; + + /* + * We try to erase the first physical eraseblock from the @head + * list and pick it if we succeed, or try to erase the + * next one if not. And so forth. We don't want to take care + * about bad eraseblocks here - they'll be handled later. + */ + list_for_each_entry_safe(seb, tmp_seb, head, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = ubi_scan_erase_peb(ubi, si, seb->pnum, seb->ec+1); + if (err) + continue; + + seb->ec += 1; + list_del(&seb->u.list); + dbg_bld("return PEB %d, EC %d", seb->pnum, seb->ec); + return seb; + } + } + + ubi_err("no eraseblocks found"); + return ERR_PTR(-ENOSPC); +} + +/** + * process_eb - read UBI headers, check them and add corresponding data + * to the scanning information. + * @ubi: UBI device description object + * @si: scanning information + * @pnum: the physical eraseblock number + * + * This function returns a zero if the physical eraseblock was succesfully + * handled and a negative error code in case of failure. + */ +static int process_eb(struct ubi_device *ubi, struct ubi_scan_info *si, int pnum) +{ + long long ec; + int err, bitflips = 0, vol_id, ec_corr = 0; + + dbg_bld("scan PEB %d", pnum); + + /* Skip bad physical eraseblocks */ + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) { + /* + * FIXME: this is actually duty of the I/O unit to initialize + * this, but MTD does not provide enough information. + */ + si->bad_peb_count += 1; + return 0; + } + + err = ubi_io_read_ec_hdr(ubi, pnum, ech, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_PEB_EMPTY) + return ubi_scan_add_to_list(si, pnum, UBI_SCAN_UNKNOWN_EC, + &si->erase); + else if (err == UBI_IO_BAD_EC_HDR) { + /* + * We have to also look at the VID header, possibly it is not + * corrupted. Set %bitflips flag in order to make this PEB be + * moved and EC be re-created. + */ + ec_corr = 1; + ec = UBI_SCAN_UNKNOWN_EC; + bitflips = 1; + } + + si->is_empty = 0; + + if (!ec_corr) { + /* Make sure UBI version is OK */ + if (ech->version != UBI_VERSION) { + ubi_err("this UBI version is %d, image version is %d", + UBI_VERSION, (int)ech->version); + return -EINVAL; + } + + ec = ubi64_to_cpu(ech->ec); + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. The EC headers have 64 bits + * reserved, but we anyway make use of only 31 bit + * values, as this seems to be enough for any existing + * flash. Upgrade UBI and use 64-bit erase counters + * internally. + */ + ubi_err("erase counter overflow, max is %d", + UBI_MAX_ERASECOUNTER); + ubi_dbg_dump_ec_hdr(ech); + return -EINVAL; + } + } + + /* OK, we've done with the EC header, let's look at the VID header */ + + err = ubi_io_read_vid_hdr(ubi, pnum, vidh, 0); + if (err < 0) + return err; + else if (err == UBI_IO_BITFLIPS) + bitflips = 1; + else if (err == UBI_IO_BAD_VID_HDR || + (err == UBI_IO_PEB_FREE && ec_corr)) { + /* VID header is corrupted */ + err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + goto adjust_mean_ec; + } else if (err == UBI_IO_PEB_FREE) { + /* No VID header - the physical eraseblock is free */ + err = ubi_scan_add_to_list(si, pnum, ec, &si->free); + if (err) + return err; + goto adjust_mean_ec; + } + + vol_id = ubi32_to_cpu(vidh->vol_id); + if (vol_id > UBI_MAX_VOLUMES && vol_id != UBI_LAYOUT_VOL_ID) { + int lnum = ubi32_to_cpu(vidh->lnum); + + /* Unsupported internal volume */ + switch (vidh->compat) { + case UBI_COMPAT_DELETE: + ubi_msg("\"delete\" compatible internal volume %d:%d" + " found, remove it", vol_id, lnum); + err = ubi_scan_add_to_list(si, pnum, ec, &si->corr); + if (err) + return err; + break; + + case UBI_COMPAT_RO: + ubi_msg("read-only compatible internal volume %d:%d" + " found, switch to read-only mode", + vol_id, lnum); + ubi->ro_mode = 1; + break; + + case UBI_COMPAT_PRESERVE: + ubi_msg("\"preserve\" compatible internal volume %d:%d" + " found", vol_id, lnum); + err = ubi_scan_add_to_list(si, pnum, ec, &si->alien); + if (err) + return err; + si->alien_peb_count += 1; + return 0; + + case UBI_COMPAT_REJECT: + ubi_err("incompatible internal volume %d:%d found", + vol_id, lnum); + return -EINVAL; + } + } + + /* Both UBI headers seem to be fine */ + err = ubi_scan_add_used(ubi, si, pnum, ec, vidh, bitflips); + if (err) + return err; + +adjust_mean_ec: + if (!ec_corr) { + if (si->ec_sum + ec < ec) { + commit_to_mean_value(si); + si->ec_sum = 0; + si->ec_count = 0; + } else { + si->ec_sum += ec; + si->ec_count += 1; + } + + if (ec > si->max_ec) + si->max_ec = ec; + if (ec < si->min_ec) + si->min_ec = ec; + } + + return 0; +} + +/** + * ubi_scan - scan an MTD device. + * @ubi: UBI device description object + * + * This function does full scanning of an MTD device and returns complete + * information about it. In case of failure, an error code is returned. + */ +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) +{ + int err, pnum; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb; + struct ubi_scan_info *si; + + si = kzalloc(sizeof(struct ubi_scan_info), GFP_KERNEL); + if (!si) + return ERR_PTR(-ENOMEM); + + INIT_LIST_HEAD(&si->corr); + INIT_LIST_HEAD(&si->free); + INIT_LIST_HEAD(&si->erase); + INIT_LIST_HEAD(&si->alien); + si->volumes = RB_ROOT; + si->is_empty = 1; + + err = -ENOMEM; + ech = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ech) + goto out_si; + + vidh = ubi_zalloc_vid_hdr(ubi); + if (!vidh) + goto out_ech; + + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + cond_resched(); + + dbg_msg("process PEB %d", pnum); + err = process_eb(ubi, si, pnum); + if (err < 0) + goto out_vidh; + } + + dbg_msg("scanning is finished"); + + /* Finish mean erase counter calculations */ + if (si->ec_count) + commit_to_mean_value(si); + + if (si->is_empty) + ubi_msg("empty MTD device detected"); + + /* + * In case of unknown erase counter we use the mean erase counter + * value. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->free, u.list) { + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + } + + list_for_each_entry(seb, &si->corr, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + list_for_each_entry(seb, &si->erase, u.list) + if (seb->ec == UBI_SCAN_UNKNOWN_EC) + seb->ec = si->mean_ec; + + err = paranoid_check_si(ubi, si); + if (err) { + if (err > 0) + err = -EINVAL; + goto out_vidh; + } + + ubi_free_vid_hdr(ubi, vidh); + kfree(ech); + + return si; + +out_vidh: + ubi_free_vid_hdr(ubi, vidh); +out_ech: + kfree(ech); +out_si: + ubi_scan_destroy_si(si); + return ERR_PTR(err); +} + +/** + * destroy_sv - free the scanning volume information + * @sv: scanning volume information + * + * This function destroys the volume RB-tree (@sv->root) and the scanning + * volume information. + */ +static void destroy_sv(struct ubi_scan_volume *sv) +{ + struct ubi_scan_leb *seb; + struct rb_node *this = sv->root.rb_node; + + while (this) { + if (this->rb_left) + this = this->rb_left; + else if (this->rb_right) + this = this->rb_right; + else { + seb = rb_entry(this, struct ubi_scan_leb, u.rb); + this = rb_parent(this); + if (this) { + if (this->rb_left == &seb->u.rb) + this->rb_left = NULL; + else + this->rb_right = NULL; + } + + kfree(seb); + } + } + kfree(sv); +} + +/** + * ubi_scan_destroy_si - destroy scanning information. + * @si: scanning information + */ +void ubi_scan_destroy_si(struct ubi_scan_info *si) +{ + struct ubi_scan_leb *seb, *seb_tmp; + struct ubi_scan_volume *sv; + struct rb_node *rb; + + list_for_each_entry_safe(seb, seb_tmp, &si->alien, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->erase, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->corr, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + list_for_each_entry_safe(seb, seb_tmp, &si->free, u.list) { + list_del(&seb->u.list); + kfree(seb); + } + + /* Destroy the volume RB-tree */ + rb = si->volumes.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + sv = rb_entry(rb, struct ubi_scan_volume, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &sv->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + destroy_sv(sv); + } + } + + kfree(si); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_si - check if the scanning information is correct and + * consistent. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero if the scanning information is all right, %1 if + * not and a negative error code if an error occurred. + */ +static int paranoid_check_si(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int pnum, err, vols_found = 0; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *last_seb; + uint8_t *buf; + + /* + * At first, check that scanning information is ok. + */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + int leb_count = 0; + + cond_resched(); + + vols_found += 1; + + if (si->is_empty) { + ubi_err("bad is_empty flag"); + goto bad_sv; + } + + if (sv->vol_id < 0 || sv->highest_lnum < 0 || + sv->leb_count < 0 || sv->vol_type < 0 || sv->used_ebs < 0 || + sv->data_pad < 0 || sv->last_data_size < 0) { + ubi_err("negative values"); + goto bad_sv; + } + + if (sv->vol_id >= UBI_MAX_VOLUMES && + sv->vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("bad vol_id"); + goto bad_sv; + } + + if (sv->vol_id > si->highest_vol_id) { + ubi_err("highest_vol_id is %d, but vol_id %d is there", + si->highest_vol_id, sv->vol_id); + goto out; + } + + if (sv->vol_type != UBI_DYNAMIC_VOLUME && + sv->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto bad_sv; + } + + if (sv->data_pad > ubi->leb_size / 2) { + ubi_err("bad data_pad"); + goto bad_sv; + } + + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + last_seb = seb; + leb_count += 1; + + if (seb->pnum < 0 || seb->ec < 0) { + ubi_err("negative values"); + goto bad_seb; + } + + if (seb->ec < si->min_ec) { + ubi_err("bad si->min_ec (%d), %d found", + si->min_ec, seb->ec); + goto bad_seb; + } + + if (seb->ec > si->max_ec) { + ubi_err("bad si->max_ec (%d), %d found", + si->max_ec, seb->ec); + goto bad_seb; + } + + if (seb->pnum >= ubi->peb_count) { + ubi_err("too high PEB number %d, total PEBs %d", + seb->pnum, ubi->peb_count); + goto bad_seb; + } + + if (sv->vol_type == UBI_STATIC_VOLUME) { + if (seb->lnum >= sv->used_ebs) { + ubi_err("bad lnum or used_ebs"); + goto bad_seb; + } + } else { + if (sv->used_ebs != 0) { + ubi_err("non-zero used_ebs"); + goto bad_seb; + } + } + + if (seb->lnum > sv->highest_lnum) { + ubi_err("incorrect highest_lnum or lnum"); + goto bad_seb; + } + } + + if (sv->leb_count != leb_count) { + ubi_err("bad leb_count, %d objects in the tree", + leb_count); + goto bad_sv; + } + + if (!last_seb) + continue; + + seb = last_seb; + + if (seb->lnum != sv->highest_lnum) { + ubi_err("bad highest_lnum"); + goto bad_seb; + } + } + + if (vols_found != si->vols_found) { + ubi_err("bad si->vols_found %d, should be %d", + si->vols_found, vols_found); + goto out; + } + + /* Check that scanning information is correct */ + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + last_seb = NULL; + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + int vol_type; + + cond_resched(); + + last_seb = seb; + + err = ubi_io_read_vid_hdr(ubi, seb->pnum, vidh, 1); + if (err && err != UBI_IO_BITFLIPS) { + ubi_err("VID header is not OK (%d)", err); + if (err > 0) + err = -EIO; + return err; + } + + vol_type = vidh->vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + if (sv->vol_type != vol_type) { + ubi_err("bad vol_type"); + goto bad_vid_hdr; + } + + if (seb->sqnum != ubi64_to_cpu(vidh->sqnum)) { + ubi_err("bad sqnum %llu", seb->sqnum); + goto bad_vid_hdr; + } + + if (sv->vol_id != ubi32_to_cpu(vidh->vol_id)) { + ubi_err("bad vol_id %d", sv->vol_id); + goto bad_vid_hdr; + } + + if (sv->compat != vidh->compat) { + ubi_err("bad compat %d", vidh->compat); + goto bad_vid_hdr; + } + + if (seb->lnum != ubi32_to_cpu(vidh->lnum)) { + ubi_err("bad lnum %d", seb->lnum); + goto bad_vid_hdr; + } + + if (sv->used_ebs != ubi32_to_cpu(vidh->used_ebs)) { + ubi_err("bad used_ebs %d", sv->used_ebs); + goto bad_vid_hdr; + } + + if (sv->data_pad != ubi32_to_cpu(vidh->data_pad)) { + ubi_err("bad data_pad %d", sv->data_pad); + goto bad_vid_hdr; + } + + if (seb->leb_ver != ubi32_to_cpu(vidh->leb_ver)) { + ubi_err("bad leb_ver %u", seb->leb_ver); + goto bad_vid_hdr; + } + } + + if (!last_seb) + continue; + + if (sv->highest_lnum != ubi32_to_cpu(vidh->lnum)) { + ubi_err("bad highest_lnum %d", sv->highest_lnum); + goto bad_vid_hdr; + } + + if (sv->last_data_size != ubi32_to_cpu(vidh->data_size)) { + ubi_err("bad last_data_size %d", sv->last_data_size); + goto bad_vid_hdr; + } + } + + /* + * Make sure that all the physical eraseblocks are in one of the lists + * or trees. + */ + buf = kmalloc(ubi->peb_count, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memset(buf, 1, ubi->peb_count); + for (pnum = 0; pnum < ubi->peb_count; pnum++) { + err = ubi_io_is_bad(ubi, pnum); + if (err < 0) + return err; + else if (err) + buf[pnum] = 0; + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->free, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->corr, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->erase, u.list) + buf[seb->pnum] = 0; + + list_for_each_entry(seb, &si->alien, u.list) + buf[seb->pnum] = 0; + + err = 0; + for (pnum = 0; pnum < ubi->peb_count; pnum++) + if (buf[pnum]) { + ubi_err("PEB %d is not referred", pnum); + err = 1; + } + + kfree(buf); + if (err) + goto out; + return 0; + +bad_seb: + ubi_err("bad scanning information about LEB %d", seb->lnum); + ubi_dbg_dump_seb(seb, 0); + ubi_dbg_dump_sv(sv); + goto out; + +bad_sv: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + goto out; + +bad_vid_hdr: + ubi_err("bad scanning information about volume %d", sv->vol_id); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vid_hdr(vidh); + +out: + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/scan.h b/drivers/mtd/ubi/scan.h new file mode 100644 index 0000000..3949f61 --- /dev/null +++ b/drivers/mtd/ubi/scan.h @@ -0,0 +1,167 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +#ifndef __UBI_SCAN_H__ +#define __UBI_SCAN_H__ + +/* The erase counter value for this physical eraseblock is unknown */ +#define UBI_SCAN_UNKNOWN_EC (-1) + +/** + * struct ubi_scan_leb - scanning information about a physical eraseblock. + * @ec: erase counter (%UBI_SCAN_UNKNOWN_EC if it is unknown) + * @pnum: physical eraseblock number + * @lnum: logical eraseblock number + * @scrub: if this physical eraseblock needs scrubbing + * @sqnum: sequence number + * @u: unions RB-tree or @list links + * @u.rb: link in the per-volume RB-tree of &struct ubi_scan_leb objects + * @u.list: link in one of the eraseblock lists + * @leb_ver: logical eraseblock version (obsolete) + * + * One object of this type is allocated for each physical eraseblock during + * scanning. + */ +struct ubi_scan_leb { + int ec; + int pnum; + int lnum; + int scrub; + unsigned long long sqnum; + union { + struct rb_node rb; + struct list_head list; + } u; + uint32_t leb_ver; +}; + +/** + * struct ubi_scan_volume - scanning information about a volume. + * @vol_id: volume ID + * @highest_lnum: highest logical eraseblock number in this volume + * @leb_count: number of logical eraseblocks in this volume + * @vol_type: volume type + * @used_ebs: number of used logical eraseblocks in this volume (only for + * static volumes) + * @last_data_size: amount of data in the last logical eraseblock of this + * volume (always equivalent to the usable logical eraseblock size in case of + * dynamic volumes) + * @data_pad: how many bytes at the end of logical eraseblocks of this volume + * are not used (due to volume alignment) + * @compat: compatibility flags of this volume + * @rb: link in the volume RB-tree + * @root: root of the RB-tree containing all the eraseblock belonging to this + * volume (&struct ubi_scan_leb objects) + * + * One object of this type is allocated for each volume during scanning. + */ +struct ubi_scan_volume { + int vol_id; + int highest_lnum; + int leb_count; + int vol_type; + int used_ebs; + int last_data_size; + int data_pad; + int compat; + struct rb_node rb; + struct rb_root root; +}; + +/** + * struct ubi_scan_info - UBI scanning information. + * @volumes: root of the volume RB-tree + * @corr: list of corrupted physical eraseblocks + * @free: list of free physical eraseblocks + * @erase: list of physical eraseblocks which have to be erased + * @alien: list of physical eraseblocks which should not be used by UBI (e.g., + * @bad_peb_count: count of bad physical eraseblocks + * those belonging to "preserve"-compatible internal volumes) + * @vols_found: number of volumes found during scanning + * @highest_vol_id: highest volume ID + * @alien_peb_count: count of physical eraseblocks in the @alien list + * @is_empty: flag indicating whether the MTD device is empty or not + * @min_ec: lowest erase counter value + * @max_ec: highest erase counter value + * @max_sqnum: highest sequence number value + * @mean_ec: mean erase counter value + * @ec_sum: a temporary variable used when calculating @mean_ec + * @ec_count: a temporary variable used when calculating @mean_ec + * + * This data structure contains the result of scanning and may be used by other + * UBI units to build final UBI data structures, further error-recovery and so + * on. + */ +struct ubi_scan_info { + struct rb_root volumes; + struct list_head corr; + struct list_head free; + struct list_head erase; + struct list_head alien; + int bad_peb_count; + int vols_found; + int highest_vol_id; + int alien_peb_count; + int is_empty; + int min_ec; + int max_ec; + unsigned long long max_sqnum; + int mean_ec; + int ec_sum; + int ec_count; +}; + +struct ubi_device; +struct ubi_vid_hdr; + +/* + * ubi_scan_move_to_list - move a physical eraseblock from the volume tree to a + * list. + * + * @sv: volume scanning information + * @seb: scanning eraseblock infprmation + * @list: the list to move to + */ +static inline void ubi_scan_move_to_list(struct ubi_scan_volume *sv, + struct ubi_scan_leb *seb, + struct list_head *list) +{ + rb_erase(&seb->u.rb, &sv->root); + list_add_tail(&seb->u.list, list); +} + +int ubi_scan_add_to_list(struct ubi_scan_info *si, int pnum, int ec, + struct list_head *list); +int ubi_scan_add_used(const struct ubi_device *ubi, struct ubi_scan_info *si, + int pnum, int ec, const struct ubi_vid_hdr *vid_hdr, + int bitflips); +struct ubi_scan_volume *ubi_scan_find_sv(const struct ubi_scan_info *si, + int vol_id); +struct ubi_scan_leb *ubi_scan_find_seb(const struct ubi_scan_volume *sv, + int lnum); +void ubi_scan_rm_volume(struct ubi_scan_info *si, struct ubi_scan_volume *sv); +struct ubi_scan_leb *ubi_scan_get_free_peb(const struct ubi_device *ubi, + struct ubi_scan_info *si); +int ubi_scan_erase_peb(const struct ubi_device *ubi, + const struct ubi_scan_info *si, int pnum, int ec); +struct ubi_scan_info *ubi_scan(struct ubi_device *ubi); +void ubi_scan_destroy_si(struct ubi_scan_info *si); + +#endif /* !__UBI_SCAN_H__ */ diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h new file mode 100644 index 0000000..feb647f --- /dev/null +++ b/drivers/mtd/ubi/ubi.h @@ -0,0 +1,535 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +#ifndef __UBI_UBI_H__ +#define __UBI_UBI_H__ + +#include <linux/init.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/rbtree.h> +#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/rwsem.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/cdev.h> +#include <linux/device.h> +#include <linux/string.h> +#include <linux/mtd/mtd.h> + +#include <mtd/ubi-header.h> +#include <linux/mtd/ubi.h> + +#include "scan.h" +#include "debug.h" + +/* Maximum number of supported UBI devices */ +#define UBI_MAX_DEVICES 32 + +/* UBI name used for character devices, sysfs, etc */ +#define UBI_NAME_STR "ubi" + +/* Normal UBI messages */ +#define ubi_msg(fmt, ...) printk(KERN_NOTICE "UBI: " fmt "\n", ##__VA_ARGS__) +/* UBI warning messages */ +#define ubi_warn(fmt, ...) printk(KERN_WARNING "UBI warning: %s: " fmt "\n", \ + __FUNCTION__, ##__VA_ARGS__) +/* UBI error messages */ +#define ubi_err(fmt, ...) printk(KERN_ERR "UBI error: %s: " fmt "\n", \ + __FUNCTION__, ##__VA_ARGS__) + +/* Lowest number PEBs reserved for bad PEB handling */ +#define MIN_RESEVED_PEBS 2 + +/* Background thread name pattern */ +#define UBI_BGT_NAME_PATTERN "ubi_bgt%dd" + +/* This marker in the EBA table means that the LEB is um-mapped */ +#define UBI_LEB_UNMAPPED -1 + +/* + * In case of errors, UBI tries to repeat the operation several times before + * returning error. The below constant defines how many times UBI re-tries. + */ +#define UBI_IO_RETRIES 3 + +/* + * Error codes returned by the I/O unit. + * + * UBI_IO_PEB_EMPTY: the physical eraseblock is empty, i.e. it contains only + * 0xFF bytes + * UBI_IO_PEB_FREE: the physical eraseblock is free, i.e. it contains only a + * valid erase counter header, and the rest are %0xFF bytes + * UBI_IO_BAD_EC_HDR: the erase counter header is corrupted (bad magic or CRC) + * UBI_IO_BAD_VID_HDR: the volume identifier header is corrupted (bad magic or + * CRC) + * UBI_IO_BITFLIPS: bit-flips were detected and corrected + */ +enum { + UBI_IO_PEB_EMPTY = 1, + UBI_IO_PEB_FREE, + UBI_IO_BAD_EC_HDR, + UBI_IO_BAD_VID_HDR, + UBI_IO_BITFLIPS +}; + +extern int ubi_devices_cnt; +extern struct ubi_device *ubi_devices[]; + +struct ubi_volume_desc; + +/** + * struct ubi_volume - UBI volume description data structure. + * @dev: device object to make use of the the Linux device model + * @cdev: character device object to create character device + * @ubi: reference to the UBI device description object + * @vol_id: volume ID + * @readers: number of users holding this volume in read-only mode + * @writers: number of users holding this volume in read-write mode + * @exclusive: whether somebody holds this volume in exclusive mode + * @removed: if the volume was removed + * @checked: if this static volume was checked + * + * @reserved_pebs: how many physical eraseblocks are reserved for this volume + * @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME) + * @usable_leb_size: logical eraseblock size without padding + * @used_ebs: how many logical eraseblocks in this volume contain data + * @last_eb_bytes: how many bytes are stored in the last logical eraseblock + * @used_bytes: how many bytes of data this volume contains + * @upd_marker: non-zero if the update marker is set for this volume + * @corrupted: non-zero if the volume is corrupted (static volumes only) + * @alignment: volume alignment + * @data_pad: how many bytes are not used at the end of physical eraseblocks to + * satisfy the requested alignment + * @name_len: volume name length + * @name: volume name + * + * @updating: whether the volume is being updated + * @upd_ebs: how many eraseblocks are expected to be updated + * @upd_bytes: how many bytes are expected to be received + * @upd_received: how many update bytes were already received + * @upd_buf: update buffer which is used to collect update data + * + * @eba_tbl: EBA table of this volume (LEB->PEB mapping) + * + * @gluebi_desc: gluebi UBI volume descriptor + * @gluebi_refcount: reference count of the gluebi MTD device + * @gluebi_mtd: MTD device description object of the gluebi MTD device + * + * The @corrupted field indicates that the volume's contents is corrupted. + * Since UBI protects only static volumes, this field is not relevant to + * dynamic volumes - it is user's responsibility to assure their data + * integrity. + * + * The @upd_marker flag indicates that this volume is either being updated at + * the moment or is damaged because of an unclean reboot. + */ +struct ubi_volume { + struct device dev; + struct cdev cdev; + struct ubi_device *ubi; + int vol_id; + int readers; + int writers; + int exclusive; + int removed; + int checked; + + int reserved_pebs; + int vol_type; + int usable_leb_size; + int used_ebs; + int last_eb_bytes; + long long used_bytes; + int upd_marker; + int corrupted; + int alignment; + int data_pad; + int name_len; + char name[UBI_VOL_NAME_MAX+1]; + + int updating; + int upd_ebs; + long long upd_bytes; + long long upd_received; + void *upd_buf; + + int *eba_tbl; + +#ifdef CONFIG_MTD_UBI_GLUEBI + /* Gluebi-related stuff may be compiled out */ + struct ubi_volume_desc *gluebi_desc; + int gluebi_refcount; + struct mtd_info gluebi_mtd; +#endif +}; + +/** + * struct ubi_volume_desc - descriptor of the UBI volume returned when it is + * opened. + * @vol: reference to the corresponding volume description object + * @mode: open mode (%UBI_READONLY, %UBI_READWRITE, or %UBI_EXCLUSIVE) + */ +struct ubi_volume_desc { + struct ubi_volume *vol; + int mode; +}; + +struct ubi_wl_entry; + +/** + * struct ubi_device - UBI device description structure + * @dev: class device object to use the the Linux device model + * @cdev: character device object to create character device + * @ubi_num: UBI device number + * @ubi_name: UBI device name + * @major: character device major number + * @vol_count: number of volumes in this UBI device + * @volumes: volumes of this UBI device + * @volumes_lock: protects @volumes, @rsvd_pebs, @avail_pebs, beb_rsvd_pebs, + * @beb_rsvd_level, @bad_peb_count, @good_peb_count, @vol_count, @vol->readers, + * @vol->writers, @vol->exclusive, @vol->removed, @vol->mapping and + * @vol->eba_tbl. + * + * @rsvd_pebs: count of reserved physical eraseblocks + * @avail_pebs: count of available physical eraseblocks + * @beb_rsvd_pebs: how many physical eraseblocks are reserved for bad PEB + * handling + * @beb_rsvd_level: normal level of PEBs reserved for bad PEB handling + * + * @vtbl_slots: how many slots are available in the volume table + * @vtbl_size: size of the volume table in bytes + * @vtbl: in-RAM volume table copy + * + * @max_ec: current highest erase counter value + * @mean_ec: current mean erase counter value + * + * global_sqnum: global sequence number + * @ltree_lock: protects the lock tree and @global_sqnum + * @ltree: the lock tree + * @vtbl_mutex: protects on-flash volume table + * + * @used: RB-tree of used physical eraseblocks + * @free: RB-tree of free physical eraseblocks + * @scrub: RB-tree of physical eraseblocks which need scrubbing + * @prot: protection trees + * @prot.pnum: protection tree indexed by physical eraseblock numbers + * @prot.aec: protection tree indexed by absolute erase counter value + * @wl_lock: protects the @used, @free, @prot, @lookuptbl, @abs_ec, @move_from, + * @move_to, @move_to_put @erase_pending, @wl_scheduled, and @works + * fields + * @wl_scheduled: non-zero if the wear-leveling was scheduled + * @lookuptbl: a table to quickly find a &struct ubi_wl_entry object for any + * physical eraseblock + * @abs_ec: absolute erase counter + * @move_from: physical eraseblock from where the data is being moved + * @move_to: physical eraseblock where the data is being moved to + * @move_from_put: if the "from" PEB was put + * @move_to_put: if the "to" PEB was put + * @works: list of pending works + * @works_count: count of pending works + * @bgt_thread: background thread description object + * @thread_enabled: if the background thread is enabled + * @bgt_name: background thread name + * + * @flash_size: underlying MTD device size (in bytes) + * @peb_count: count of physical eraseblocks on the MTD device + * @peb_size: physical eraseblock size + * @bad_peb_count: count of bad physical eraseblocks + * @good_peb_count: count of good physical eraseblocks + * @min_io_size: minimal input/output unit size of the underlying MTD device + * @hdrs_min_io_size: minimal I/O unit size used for VID and EC headers + * @ro_mode: if the UBI device is in read-only mode + * @leb_size: logical eraseblock size + * @leb_start: starting offset of logical eraseblocks within physical + * eraseblocks + * @ec_hdr_alsize: size of the EC header aligned to @hdrs_min_io_size + * @vid_hdr_alsize: size of the VID header aligned to @hdrs_min_io_size + * @vid_hdr_offset: starting offset of the volume identifier header (might be + * unaligned) + * @vid_hdr_aloffset: starting offset of the VID header aligned to + * @hdrs_min_io_size + * @vid_hdr_shift: contains @vid_hdr_offset - @vid_hdr_aloffset + * @bad_allowed: whether the MTD device admits of bad physical eraseblocks or + * not + * @mtd: MTD device descriptor + */ +struct ubi_device { + struct cdev cdev; + struct device dev; + int ubi_num; + char ubi_name[sizeof(UBI_NAME_STR)+5]; + int major; + int vol_count; + struct ubi_volume *volumes[UBI_MAX_VOLUMES+UBI_INT_VOL_COUNT]; + spinlock_t volumes_lock; + + int rsvd_pebs; + int avail_pebs; + int beb_rsvd_pebs; + int beb_rsvd_level; + + int vtbl_slots; + int vtbl_size; + struct ubi_vtbl_record *vtbl; + struct mutex vtbl_mutex; + + int max_ec; + int mean_ec; + + /* EBA unit's stuff */ + unsigned long long global_sqnum; + spinlock_t ltree_lock; + struct rb_root ltree; + + /* Wear-leveling unit's stuff */ + struct rb_root used; + struct rb_root free; + struct rb_root scrub; + struct { + struct rb_root pnum; + struct rb_root aec; + } prot; + spinlock_t wl_lock; + int wl_scheduled; + struct ubi_wl_entry **lookuptbl; + unsigned long long abs_ec; + struct ubi_wl_entry *move_from; + struct ubi_wl_entry *move_to; + int move_from_put; + int move_to_put; + struct list_head works; + int works_count; + struct task_struct *bgt_thread; + int thread_enabled; + char bgt_name[sizeof(UBI_BGT_NAME_PATTERN)+2]; + + /* I/O unit's stuff */ + long long flash_size; + int peb_count; + int peb_size; + int bad_peb_count; + int good_peb_count; + int min_io_size; + int hdrs_min_io_size; + int ro_mode; + int leb_size; + int leb_start; + int ec_hdr_alsize; + int vid_hdr_alsize; + int vid_hdr_offset; + int vid_hdr_aloffset; + int vid_hdr_shift; + int bad_allowed; + struct mtd_info *mtd; +}; + +extern struct file_operations ubi_cdev_operations; +extern struct file_operations ubi_vol_cdev_operations; +extern struct class *ubi_class; + +/* vtbl.c */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec); +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si); + +/* vmt.c */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req); +int ubi_remove_volume(struct ubi_volume_desc *desc); +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs); +int ubi_add_volume(struct ubi_device *ubi, int vol_id); +void ubi_free_volume(struct ubi_device *ubi, int vol_id); + +/* upd.c */ +int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes); +int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + const void __user *buf, int count); + +/* misc.c */ +int ubi_calc_data_len(const struct ubi_device *ubi, const void *buf, int length); +int ubi_check_volume(struct ubi_device *ubi, int vol_id); +void ubi_calculate_reserved(struct ubi_device *ubi); + +/* gluebi.c */ +#ifdef CONFIG_MTD_UBI_GLUEBI +int ubi_create_gluebi(struct ubi_device *ubi, struct ubi_volume *vol); +int ubi_destroy_gluebi(struct ubi_volume *vol); +#else +#define ubi_create_gluebi(ubi, vol) 0 +#define ubi_destroy_gluebi(vol) 0 +#endif + +/* eba.c */ +int ubi_eba_unmap_leb(struct ubi_device *ubi, int vol_id, int lnum); +int ubi_eba_read_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int offset, int len, int check); +int ubi_eba_write_leb(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int offset, int len, int dtype); +int ubi_eba_write_leb_st(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype, + int used_ebs); +int ubi_eba_atomic_leb_change(struct ubi_device *ubi, int vol_id, int lnum, + const void *buf, int len, int dtype); +int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to, + struct ubi_vid_hdr *vid_hdr); +int ubi_eba_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_eba_close(const struct ubi_device *ubi); + +/* wl.c */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype); +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture); +int ubi_wl_flush(struct ubi_device *ubi); +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum); +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si); +void ubi_wl_close(struct ubi_device *ubi); + +/* io.c */ +int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, + int len); +int ubi_io_write(const struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len); +int ubi_io_sync_erase(const struct ubi_device *ubi, int pnum, int torture); +int ubi_io_is_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_mark_bad(const struct ubi_device *ubi, int pnum); +int ubi_io_read_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr, int verbose); +int ubi_io_write_ec_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_ec_hdr *ec_hdr); +int ubi_io_read_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr, int verbose); +int ubi_io_write_vid_hdr(const struct ubi_device *ubi, int pnum, + struct ubi_vid_hdr *vid_hdr); + +/* + * ubi_rb_for_each_entry - walk an RB-tree. + * @rb: a pointer to type 'struct rb_node' to to use as a loop counter + * @pos: a pointer to RB-tree entry type to use as a loop counter + * @root: RB-tree's root + * @member: the name of the 'struct rb_node' within the RB-tree entry + */ +#define ubi_rb_for_each_entry(rb, pos, root, member) \ + for (rb = rb_first(root), \ + pos = (rb ? container_of(rb, typeof(*pos), member) : NULL); \ + rb; \ + rb = rb_next(rb), pos = container_of(rb, typeof(*pos), member)) + +/** + * ubi_zalloc_vid_hdr - allocate a volume identifier header object. + * @ubi: UBI device description object + * + * This function returns a pointer to the newly allocated and zero-filled + * volume identifier header object in case of success and %NULL in case of + * failure. + */ +static inline struct ubi_vid_hdr *ubi_zalloc_vid_hdr(const struct ubi_device *ubi) +{ + void *vid_hdr; + + vid_hdr = kzalloc(ubi->vid_hdr_alsize, GFP_KERNEL); + if (!vid_hdr) + return NULL; + + /* + * VID headers may be stored at un-aligned flash offsets, so we shift + * the pointer. + */ + return vid_hdr + ubi->vid_hdr_shift; +} + +/** + * ubi_free_vid_hdr - free a volume identifier header object. + * @ubi: UBI device description object + * @vid_hdr: the object to free + */ +static inline void ubi_free_vid_hdr(const struct ubi_device *ubi, + struct ubi_vid_hdr *vid_hdr) +{ + void *p = vid_hdr; + + if (!p) + return; + + kfree(p - ubi->vid_hdr_shift); +} + +/* + * This function is equivalent to 'ubi_io_read()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_read_data(const struct ubi_device *ubi, void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_read(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/* + * This function is equivalent to 'ubi_io_write()', but @offset is relative to + * the beginning of the logical eraseblock, not to the beginning of the + * physical eraseblock. + */ +static inline int ubi_io_write_data(const struct ubi_device *ubi, const void *buf, + int pnum, int offset, int len) +{ + ubi_assert(offset >= 0); + return ubi_io_write(ubi, buf, pnum, offset + ubi->leb_start, len); +} + +/** + * ubi_ro_mode - switch to read-only mode. + * @ubi: UBI device description object + */ +static inline void ubi_ro_mode(struct ubi_device *ubi) +{ + ubi->ro_mode = 1; + ubi_warn("switch to read-only mode"); +} + +/** + * vol_id2idx - get table index by volume ID. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static inline int vol_id2idx(const struct ubi_device *ubi, int vol_id) +{ + if (vol_id >= UBI_INTERNAL_VOL_START) + return vol_id - UBI_INTERNAL_VOL_START + ubi->vtbl_slots; + else + return vol_id; +} + +/** + * idx2vol_id - get volume ID by table index. + * @ubi: UBI device description object + * @idx: table index + */ +static inline int idx2vol_id(const struct ubi_device *ubi, int idx) +{ + if (idx >= ubi->vtbl_slots) + return idx - ubi->vtbl_slots + UBI_INTERNAL_VOL_START; + else + return idx; +} + +#endif /* !__UBI_UBI_H__ */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c new file mode 100644 index 0000000..8925b97 --- /dev/null +++ b/drivers/mtd/ubi/upd.c @@ -0,0 +1,348 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + * + * Jan 2007: Alexander Schmidt, hacked per-volume update. + */ + +/* + * This file contains implementation of the volume update functionality. + * + * The update operation is based on the per-volume update marker which is + * stored in the volume table. The update marker is set before the update + * starts, and removed after the update has been finished. So if the update was + * interrupted by an unclean re-boot or due to some other reasons, the update + * marker stays on the flash media and UBI finds it when it attaches the MTD + * device next time. If the update marker is set for a volume, the volume is + * treated as damaged and most I/O operations are prohibited. Only a new update + * operation is allowed. + * + * Note, in general it is possible to implement the update operation as a + * transaction with a roll-back capability. + */ + +#include <linux/err.h> +#include <asm/uaccess.h> +#include <asm/div64.h> +#include "ubi.h" + +/** + * set_update_marker - set update marker. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function sets the update marker flag for volume @vol_id. Returns zero + * in case of success and a negative error code in case of failure. + */ +static int set_update_marker(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_vtbl_record vtbl_rec; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("set update marker for volume %d", vol_id); + + if (vol->upd_marker) { + ubi_assert(ubi->vtbl[vol_id].upd_marker); + dbg_msg("already set"); + return 0; + } + + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.upd_marker = 1; + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + vol->upd_marker = 1; + return err; +} + +/** + * clear_update_marker - clear update marker. + * @ubi: UBI device description object + * @vol_id: volume ID + * @bytes: new data size in bytes + * + * This function clears the update marker for volume @vol_id, sets new volume + * data size and clears the "corrupted" flag (static volumes only). Returns + * zero in case of success and a negative error code in case of failure. + */ +static int clear_update_marker(struct ubi_device *ubi, int vol_id, long long bytes) +{ + int err; + uint64_t tmp; + struct ubi_vtbl_record vtbl_rec; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("clear update marker for volume %d", vol_id); + + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + ubi_assert(vol->upd_marker && vtbl_rec.upd_marker); + vtbl_rec.upd_marker = 0; + + if (vol->vol_type == UBI_STATIC_VOLUME) { + vol->corrupted = 0; + vol->used_bytes = tmp = bytes; + vol->last_eb_bytes = do_div(tmp, vol->usable_leb_size); + vol->used_ebs = tmp; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + vol->upd_marker = 0; + return err; +} + +/** + * ubi_start_update - start volume update. + * @ubi: UBI device description object + * @vol_id: volume ID + * @bytes: update bytes + * + * This function starts volume update operation. If @bytes is zero, the volume + * is just wiped out. Returns zero in case of success and a negative error code + * in case of failure. + */ +int ubi_start_update(struct ubi_device *ubi, int vol_id, long long bytes) +{ + int i, err; + uint64_t tmp; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("start update of volume %d, %llu bytes", vol_id, bytes); + vol->updating = 1; + + err = set_update_marker(ubi, vol_id); + if (err) + return err; + + /* Before updating - wipe out the volume */ + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, i); + if (err) + return err; + } + + if (bytes == 0) { + err = clear_update_marker(ubi, vol_id, 0); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (!err) + vol->updating = 0; + } + + vol->upd_buf = kmalloc(ubi->leb_size, GFP_KERNEL); + if (!vol->upd_buf) + return -ENOMEM; + + tmp = bytes; + vol->upd_ebs = !!do_div(tmp, vol->usable_leb_size); + vol->upd_ebs += tmp; + vol->upd_bytes = bytes; + vol->upd_received = 0; + return 0; +} + +/** + * write_leb - write update data. + * @ubi: UBI device description object + * @vol_id: volume ID + * @lnum: logical eraseblock number + * @buf: data to write + * @len: data size + * @used_ebs: how many logical eraseblocks will this volume contain (static + * volumes only) + * + * This function writes update data to corresponding logical eraseblock. In + * case of dynamic volume, this function checks if the data contains 0xFF bytes + * at the end. If yes, the 0xFF bytes are cut and not written. So if the whole + * buffer contains only 0xFF bytes, the LEB is left unmapped. + * + * The reason why we skip the trailing 0xFF bytes in case of dynamic volume is + * that we want to make sure that more data may be appended to the logical + * eraseblock in future. Indeed, writing 0xFF bytes may have side effects and + * this PEB won't be writable anymore. So if one writes the file-system image + * to the UBI volume where 0xFFs mean free space - UBI makes sure this free + * space is writable after the update. + * + * We do not do this for static volumes because they are read-only. But this + * also cannot be done because we have to store per-LEB CRC and the correct + * data length. + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int write_leb(struct ubi_device *ubi, int vol_id, int lnum, void *buf, + int len, int used_ebs) +{ + int err, l; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + l = ALIGN(len, ubi->min_io_size); + memset(buf + len, 0xFF, l - len); + + l = ubi_calc_data_len(ubi, buf, l); + if (l == 0) { + dbg_msg("all %d bytes contain 0xFF - skip", len); + return 0; + } + if (len != l) + dbg_msg("skip last %d bytes (0xFF)", len - l); + + err = ubi_eba_write_leb(ubi, vol_id, lnum, buf, 0, l, + UBI_UNKNOWN); + } else { + /* + * When writing static volume, and this is the last logical + * eraseblock, the length (@len) does not have to be aligned to + * the minimal flash I/O unit. The 'ubi_eba_write_leb_st()' + * function accepts exact (unaligned) length and stores it in + * the VID header. And it takes care of proper alignment by + * padding the buffer. Here we just make sure the padding will + * contain zeros, not random trash. + */ + memset(buf + len, 0, vol->usable_leb_size - len); + err = ubi_eba_write_leb_st(ubi, vol_id, lnum, buf, len, + UBI_UNKNOWN, used_ebs); + } + + return err; +} + +/** + * ubi_more_update_data - write more update data. + * @vol: volume description object + * @buf: write data (user-space memory buffer) + * @count: how much bytes to write + * + * This function writes more data to the volume which is being updated. It may + * be called arbitrary number of times until all of the update data arrive. + * This function returns %0 in case of success, number of bytes written during + * the last call if the whole volume update was successfully finished, and a + * negative error code in case of failure. + */ +int ubi_more_update_data(struct ubi_device *ubi, int vol_id, + const void __user *buf, int count) +{ + uint64_t tmp; + struct ubi_volume *vol = ubi->volumes[vol_id]; + int lnum, offs, err = 0, len, to_write = count; + + dbg_msg("write %d of %lld bytes, %lld already passed", + count, vol->upd_bytes, vol->upd_received); + + if (ubi->ro_mode) + return -EROFS; + + tmp = vol->upd_received; + offs = do_div(tmp, vol->usable_leb_size); + lnum = tmp; + + if (vol->upd_received + count > vol->upd_bytes) + to_write = count = vol->upd_bytes - vol->upd_received; + + /* + * When updating volumes, we accumulate whole logical eraseblock of + * data and write it at once. + */ + if (offs != 0) { + /* + * This is a write to the middle of the logical eraseblock. We + * copy the data to our update buffer and wait for more data or + * flush it if the whole eraseblock is written or the update + * is finished. + */ + + len = vol->usable_leb_size - offs; + if (len > count) + len = count; + + err = copy_from_user(vol->upd_buf + offs, buf, len); + if (err) + return -EFAULT; + + if (offs + len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + int flush_len = offs + len; + + /* + * OK, we gathered either the whole eraseblock or this + * is the last chunk, it's time to flush the buffer. + */ + ubi_assert(flush_len <= vol->usable_leb_size); + err = write_leb(ubi, vol_id, lnum, vol->upd_buf, + flush_len, vol->upd_ebs); + if (err) + return err; + } + + vol->upd_received += len; + count -= len; + buf += len; + lnum += 1; + } + + /* + * If we've got more to write, let's continue. At this point we know we + * are starting from the beginning of an eraseblock. + */ + while (count) { + if (count > vol->usable_leb_size) + len = vol->usable_leb_size; + else + len = count; + + err = copy_from_user(vol->upd_buf, buf, len); + if (err) + return -EFAULT; + + if (len == vol->usable_leb_size || + vol->upd_received + len == vol->upd_bytes) { + err = write_leb(ubi, vol_id, lnum, vol->upd_buf, len, + vol->upd_ebs); + if (err) + break; + } + + vol->upd_received += len; + count -= len; + lnum += 1; + buf += len; + } + + ubi_assert(vol->upd_received <= vol->upd_bytes); + if (vol->upd_received == vol->upd_bytes) { + /* The update is finished, clear the update marker */ + err = clear_update_marker(ubi, vol_id, vol->upd_bytes); + if (err) + return err; + err = ubi_wl_flush(ubi); + if (err == 0) { + err = to_write; + kfree(vol->upd_buf); + vol->updating = 0; + } + } + + return err; +} diff --git a/drivers/mtd/ubi/vmt.c b/drivers/mtd/ubi/vmt.c new file mode 100644 index 0000000..622d0d1 --- /dev/null +++ b/drivers/mtd/ubi/vmt.c @@ -0,0 +1,809 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * This file contains implementation of volume creation, deletion, updating and + * resizing. + */ + +#include <linux/err.h> +#include <asm/div64.h> +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_check_volumes(struct ubi_device *ubi); +#else +#define paranoid_check_volumes(ubi) +#endif + +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf); + +/* Device attributes corresponding to files in '/<sysfs>/class/ubi/ubiX_Y' */ +static struct device_attribute vol_reserved_ebs = + __ATTR(reserved_ebs, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_type = + __ATTR(type, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_name = + __ATTR(name, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_corrupted = + __ATTR(corrupted, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_alignment = + __ATTR(alignment, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_usable_eb_size = + __ATTR(usable_eb_size, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_data_bytes = + __ATTR(data_bytes, S_IRUGO, vol_attribute_show, NULL); +static struct device_attribute vol_upd_marker = + __ATTR(upd_marker, S_IRUGO, vol_attribute_show, NULL); + +/* + * "Show" method for files in '/<sysfs>/class/ubi/ubiX_Y/'. + * + * Consider a situation: + * A. process 1 opens a sysfs file related to volume Y, say + * /<sysfs>/class/ubi/ubiX_Y/reserved_ebs; + * B. process 2 removes volume Y; + * C. process 1 starts reading the /<sysfs>/class/ubi/ubiX_Y/reserved_ebs file; + * + * What we want to do in a situation like that is to return error when the file + * is read. This is done by means of the 'removed' flag and the 'vol_lock' of + * the UBI volume description object. + */ +static ssize_t vol_attribute_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + + spin_lock(&vol->ubi->volumes_lock); + if (vol->removed) { + spin_unlock(&vol->ubi->volumes_lock); + return -ENODEV; + } + if (attr == &vol_reserved_ebs) + ret = sprintf(buf, "%d\n", vol->reserved_pebs); + else if (attr == &vol_type) { + const char *tp; + tp = vol->vol_type == UBI_DYNAMIC_VOLUME ? "dynamic" : "static"; + ret = sprintf(buf, "%s\n", tp); + } else if (attr == &vol_name) + ret = sprintf(buf, "%s\n", vol->name); + else if (attr == &vol_corrupted) + ret = sprintf(buf, "%d\n", vol->corrupted); + else if (attr == &vol_alignment) + ret = sprintf(buf, "%d\n", vol->alignment); + else if (attr == &vol_usable_eb_size) { + ret = sprintf(buf, "%d\n", vol->usable_leb_size); + } else if (attr == &vol_data_bytes) + ret = sprintf(buf, "%lld\n", vol->used_bytes); + else if (attr == &vol_upd_marker) + ret = sprintf(buf, "%d\n", vol->upd_marker); + else + BUG(); + spin_unlock(&vol->ubi->volumes_lock); + return ret; +} + +/* Release method for volume devices */ +static void vol_release(struct device *dev) +{ + struct ubi_volume *vol = container_of(dev, struct ubi_volume, dev); + ubi_assert(vol->removed); + kfree(vol); +} + +/** + * volume_sysfs_init - initialize sysfs for new volume. + * @ubi: UBI device description object + * @vol: volume description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + * + * Note, this function does not free allocated resources in case of failure - + * the caller does it. This is because this would cause release() here and the + * caller would oops. + */ +static int volume_sysfs_init(struct ubi_device *ubi, struct ubi_volume *vol) +{ + int err; + + err = device_create_file(&vol->dev, &vol_reserved_ebs); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_type); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_name); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_corrupted); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_alignment); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_usable_eb_size); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_data_bytes); + if (err) + return err; + err = device_create_file(&vol->dev, &vol_upd_marker); + if (err) + return err; + return 0; +} + +/** + * volume_sysfs_close - close sysfs for a volume. + * @vol: volume description object + */ +static void volume_sysfs_close(struct ubi_volume *vol) +{ + device_remove_file(&vol->dev, &vol_upd_marker); + device_remove_file(&vol->dev, &vol_data_bytes); + device_remove_file(&vol->dev, &vol_usable_eb_size); + device_remove_file(&vol->dev, &vol_alignment); + device_remove_file(&vol->dev, &vol_corrupted); + device_remove_file(&vol->dev, &vol_name); + device_remove_file(&vol->dev, &vol_type); + device_remove_file(&vol->dev, &vol_reserved_ebs); + device_unregister(&vol->dev); +} + +/** + * ubi_create_volume - create volume. + * @ubi: UBI device description object + * @req: volume creation request + * + * This function creates volume described by @req. If @req->vol_id id + * %UBI_VOL_NUM_AUTO, this function automatically assigne ID to the new volume + * and saves it in @req->vol_id. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_create_volume(struct ubi_device *ubi, struct ubi_mkvol_req *req) +{ + int i, err, vol_id = req->vol_id; + struct ubi_volume *vol; + struct ubi_vtbl_record vtbl_rec; + uint64_t bytes; + + if (ubi->ro_mode) + return -EROFS; + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + spin_lock(&ubi->volumes_lock); + + if (vol_id == UBI_VOL_NUM_AUTO) { + /* Find unused volume ID */ + dbg_msg("search for vacant volume ID"); + for (i = 0; i < ubi->vtbl_slots; i++) + if (!ubi->volumes[i]) { + vol_id = i; + break; + } + + if (vol_id == UBI_VOL_NUM_AUTO) { + dbg_err("out of volume IDs"); + err = -ENFILE; + goto out_unlock; + } + req->vol_id = vol_id; + } + + dbg_msg("volume ID %d, %llu bytes, type %d, name %s", + vol_id, (unsigned long long)req->bytes, + (int)req->vol_type, req->name); + + /* Ensure that this volume does not exist */ + err = -EEXIST; + if (ubi->volumes[vol_id]) { + dbg_err("volume %d already exists", vol_id); + goto out_unlock; + } + + /* Ensure that the name is unique */ + for (i = 0; i < ubi->vtbl_slots; i++) + if (ubi->volumes[i] && + ubi->volumes[i]->name_len == req->name_len && + strcmp(ubi->volumes[i]->name, req->name) == 0) { + dbg_err("volume \"%s\" exists (ID %d)", req->name, i); + goto out_unlock; + } + + /* Calculate how many eraseblocks are requested */ + vol->usable_leb_size = ubi->leb_size - ubi->leb_size % req->alignment; + bytes = req->bytes; + if (do_div(bytes, vol->usable_leb_size)) + vol->reserved_pebs = 1; + vol->reserved_pebs += bytes; + + /* Reserve physical eraseblocks */ + if (vol->reserved_pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs, only %d available", ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_unlock; + } + ubi->avail_pebs -= vol->reserved_pebs; + ubi->rsvd_pebs += vol->reserved_pebs; + + vol->vol_id = vol_id; + vol->alignment = req->alignment; + vol->data_pad = ubi->leb_size % vol->alignment; + vol->vol_type = req->vol_type; + vol->name_len = req->name_len; + memcpy(vol->name, req->name, vol->name_len + 1); + vol->exclusive = 1; + vol->ubi = ubi; + ubi->volumes[vol_id] = vol; + spin_unlock(&ubi->volumes_lock); + + /* + * Finish all pending erases because there may be some LEBs belonging + * to the same volume ID. + */ + err = ubi_wl_flush(ubi); + if (err) + goto out_acc; + + vol->eba_tbl = kmalloc(vol->reserved_pebs * sizeof(int), GFP_KERNEL); + if (!vol->eba_tbl) { + err = -ENOMEM; + goto out_acc; + } + + for (i = 0; i < vol->reserved_pebs; i++) + vol->eba_tbl[i] = UBI_LEB_UNMAPPED; + + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + } else { + bytes = vol->used_bytes; + vol->last_eb_bytes = do_div(bytes, vol->usable_leb_size); + vol->used_ebs = bytes; + if (vol->last_eb_bytes) + vol->used_ebs += 1; + else + vol->last_eb_bytes = vol->usable_leb_size; + } + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol_id + 1), 1); + if (err) { + ubi_err("cannot add character device for volume %d", vol_id); + goto out_mapping; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) + goto out_sysfs; + + /* Fill volume table record */ + memset(&vtbl_rec, 0, sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_ubi32(vol->reserved_pebs); + vtbl_rec.alignment = cpu_to_ubi32(vol->alignment); + vtbl_rec.data_pad = cpu_to_ubi32(vol->data_pad); + vtbl_rec.name_len = cpu_to_ubi16(vol->name_len); + if (vol->vol_type == UBI_DYNAMIC_VOLUME) + vtbl_rec.vol_type = UBI_VID_DYNAMIC; + else + vtbl_rec.vol_type = UBI_VID_STATIC; + memcpy(vtbl_rec.name, vol->name, vol->name_len + 1); + + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_sysfs; + + spin_lock(&ubi->volumes_lock); + ubi->vol_count += 1; + vol->exclusive = 0; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + cdev_del(&vol->cdev); +out_mapping: + kfree(vol->eba_tbl); +out_acc: + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; +out_unlock: + spin_unlock(&ubi->volumes_lock); + kfree(vol); + return err; + + /* + * We are registered, so @vol is destroyed in the release function and + * we have to de-initialize differently. + */ +out_sysfs: + err = ubi_destroy_gluebi(vol); + cdev_del(&vol->cdev); + kfree(vol->eba_tbl); + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= vol->reserved_pebs; + ubi->avail_pebs += vol->reserved_pebs; + spin_unlock(&ubi->volumes_lock); + volume_sysfs_close(vol); + return err; +} + +/** + * ubi_remove_volume - remove volume. + * @desc: volume descriptor + * + * This function removes volume described by @desc. The volume has to be opened + * in "exclusive" mode. Returns zero in case of success and a negative error + * code in case of failure. + */ +int ubi_remove_volume(struct ubi_volume_desc *desc) +{ + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + int i, err, vol_id = vol->vol_id, reserved_pebs = vol->reserved_pebs; + + dbg_msg("remove UBI volume %d", vol_id); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (ubi->ro_mode) + return -EROFS; + + err = ubi_destroy_gluebi(vol); + if (err) + return err; + + err = ubi_change_vtbl_record(ubi, vol_id, NULL); + if (err) + return err; + + for (i = 0; i < vol->reserved_pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, i); + if (err) + return err; + } + + spin_lock(&ubi->volumes_lock); + vol->removed = 1; + ubi->volumes[vol_id] = NULL; + spin_unlock(&ubi->volumes_lock); + + kfree(vol->eba_tbl); + vol->eba_tbl = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); + kfree(desc); + + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= reserved_pebs; + ubi->avail_pebs += reserved_pebs; + i = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (i > 0) { + i = ubi->avail_pebs >= i ? i : ubi->avail_pebs; + ubi->avail_pebs -= i; + ubi->rsvd_pebs += i; + ubi->beb_rsvd_pebs += i; + if (i > 0) + ubi_msg("reserve more %d PEBs", i); + } + ubi->vol_count -= 1; + spin_unlock(&ubi->volumes_lock); + + paranoid_check_volumes(ubi); + module_put(THIS_MODULE); + return 0; +} + +/** + * ubi_resize_volume - re-size volume. + * @desc: volume descriptor + * @reserved_pebs: new size in physical eraseblocks + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_resize_volume(struct ubi_volume_desc *desc, int reserved_pebs) +{ + int i, err, pebs, *new_mapping; + struct ubi_volume *vol = desc->vol; + struct ubi_device *ubi = vol->ubi; + struct ubi_vtbl_record vtbl_rec; + int vol_id = vol->vol_id; + + if (ubi->ro_mode) + return -EROFS; + + dbg_msg("re-size volume %d to from %d to %d PEBs", + vol_id, vol->reserved_pebs, reserved_pebs); + ubi_assert(desc->mode == UBI_EXCLUSIVE); + ubi_assert(vol == ubi->volumes[vol_id]); + + if (vol->vol_type == UBI_STATIC_VOLUME && + reserved_pebs < vol->used_ebs) { + dbg_err("too small size %d, %d LEBs contain data", + reserved_pebs, vol->used_ebs); + return -EINVAL; + } + + /* If the size is the same, we have nothing to do */ + if (reserved_pebs == vol->reserved_pebs) + return 0; + + new_mapping = kmalloc(reserved_pebs * sizeof(int), GFP_KERNEL); + if (!new_mapping) + return -ENOMEM; + + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = UBI_LEB_UNMAPPED; + + /* Reserve physical eraseblocks */ + pebs = reserved_pebs - vol->reserved_pebs; + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + if (pebs > ubi->avail_pebs) { + dbg_err("not enough PEBs: requested %d, available %d", + pebs, ubi->avail_pebs); + spin_unlock(&ubi->volumes_lock); + err = -ENOSPC; + goto out_free; + } + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + for (i = 0; i < vol->reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + /* Change volume table record */ + memcpy(&vtbl_rec, &ubi->vtbl[vol_id], sizeof(struct ubi_vtbl_record)); + vtbl_rec.reserved_pebs = cpu_to_ubi32(reserved_pebs); + err = ubi_change_vtbl_record(ubi, vol_id, &vtbl_rec); + if (err) + goto out_acc; + + if (pebs < 0) { + for (i = 0; i < -pebs; i++) { + err = ubi_eba_unmap_leb(ubi, vol_id, reserved_pebs + i); + if (err) + goto out_acc; + } + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs += pebs; + ubi->avail_pebs -= pebs; + pebs = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs; + if (pebs > 0) { + pebs = ubi->avail_pebs >= pebs ? pebs : ubi->avail_pebs; + ubi->avail_pebs -= pebs; + ubi->rsvd_pebs += pebs; + ubi->beb_rsvd_pebs += pebs; + if (pebs > 0) + ubi_msg("reserve more %d PEBs", pebs); + } + for (i = 0; i < reserved_pebs; i++) + new_mapping[i] = vol->eba_tbl[i]; + kfree(vol->eba_tbl); + vol->eba_tbl = new_mapping; + spin_unlock(&ubi->volumes_lock); + } + + vol->reserved_pebs = reserved_pebs; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + } + + paranoid_check_volumes(ubi); + return 0; + +out_acc: + if (pebs > 0) { + spin_lock(&ubi->volumes_lock); + ubi->rsvd_pebs -= pebs; + ubi->avail_pebs += pebs; + spin_unlock(&ubi->volumes_lock); + } +out_free: + kfree(new_mapping); + return err; +} + +/** + * ubi_add_volume - add volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function adds an existin volume and initializes all its data + * structures. Returnes zero in case of success and a negative error code in + * case of failure. + */ +int ubi_add_volume(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("add volume %d", vol_id); + ubi_dbg_dump_vol_info(vol); + ubi_assert(vol); + + /* Register character device for the volume */ + cdev_init(&vol->cdev, &ubi_vol_cdev_operations); + vol->cdev.owner = THIS_MODULE; + err = cdev_add(&vol->cdev, MKDEV(ubi->major, vol->vol_id + 1), 1); + if (err) { + ubi_err("cannot add character device for volume %d", vol_id); + return err; + } + + err = ubi_create_gluebi(ubi, vol); + if (err) + goto out_cdev; + + vol->dev.release = vol_release; + vol->dev.parent = &ubi->dev; + vol->dev.devt = MKDEV(ubi->major, vol->vol_id + 1); + vol->dev.class = ubi_class; + sprintf(&vol->dev.bus_id[0], "%s_%d", ubi->ubi_name, vol->vol_id); + err = device_register(&vol->dev); + if (err) + goto out_gluebi; + + err = volume_sysfs_init(ubi, vol); + if (err) { + cdev_del(&vol->cdev); + err = ubi_destroy_gluebi(vol); + volume_sysfs_close(vol); + return err; + } + + paranoid_check_volumes(ubi); + return 0; + +out_gluebi: + err = ubi_destroy_gluebi(vol); +out_cdev: + cdev_del(&vol->cdev); + return err; +} + +/** + * ubi_free_volume - free volume. + * @ubi: UBI device description object + * @vol_id: volume ID + * + * This function frees all resources for volume @vol_id but does not remove it. + * Used only when the UBI device is detached. + */ +void ubi_free_volume(struct ubi_device *ubi, int vol_id) +{ + int err; + struct ubi_volume *vol = ubi->volumes[vol_id]; + + dbg_msg("free volume %d", vol_id); + ubi_assert(vol); + + vol->removed = 1; + err = ubi_destroy_gluebi(vol); + ubi->volumes[vol_id] = NULL; + cdev_del(&vol->cdev); + volume_sysfs_close(vol); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_volume - check volume information. + * @ubi: UBI device description object + * @vol_id: volume ID + */ +static void paranoid_check_volume(const struct ubi_device *ubi, int vol_id) +{ + int idx = vol_id2idx(ubi, vol_id); + int reserved_pebs, alignment, data_pad, vol_type, name_len, upd_marker; + const struct ubi_volume *vol = ubi->volumes[idx]; + long long n; + const char *name; + + reserved_pebs = ubi32_to_cpu(ubi->vtbl[vol_id].reserved_pebs); + + if (!vol) { + if (reserved_pebs) { + ubi_err("no volume info, but volume exists"); + goto fail; + } + return; + } + + if (vol->reserved_pebs < 0 || vol->alignment < 0 || vol->data_pad < 0 || + vol->name_len < 0) { + ubi_err("negative values"); + goto fail; + } + if (vol->alignment > ubi->leb_size || vol->alignment == 0) { + ubi_err("bad alignment"); + goto fail; + } + + n = vol->alignment % ubi->min_io_size; + if (vol->alignment != 1 && n) { + ubi_err("alignment is not multiple of min I/O unit"); + goto fail; + } + + n = ubi->leb_size % vol->alignment; + if (vol->data_pad != n) { + ubi_err("bad data_pad, has to be %lld", n); + goto fail; + } + + if (vol->vol_type != UBI_DYNAMIC_VOLUME && + vol->vol_type != UBI_STATIC_VOLUME) { + ubi_err("bad vol_type"); + goto fail; + } + + if (vol->upd_marker != 0 && vol->upd_marker != 1) { + ubi_err("bad upd_marker"); + goto fail; + } + + if (vol->upd_marker && vol->corrupted) { + dbg_err("update marker and corrupted simultaneously"); + goto fail; + } + + if (vol->reserved_pebs > ubi->good_peb_count) { + ubi_err("too large reserved_pebs"); + goto fail; + } + + n = ubi->leb_size - vol->data_pad; + if (vol->usable_leb_size != ubi->leb_size - vol->data_pad) { + ubi_err("bad usable_leb_size, has to be %lld", n); + goto fail; + } + + if (vol->name_len > UBI_VOL_NAME_MAX) { + ubi_err("too long volume name, max is %d", UBI_VOL_NAME_MAX); + goto fail; + } + + if (!vol->name) { + ubi_err("NULL volume name"); + goto fail; + } + + n = strnlen(vol->name, vol->name_len + 1); + if (n != vol->name_len) { + ubi_err("bad name_len %lld", n); + goto fail; + } + + n = vol->used_ebs * vol->usable_leb_size; + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + if (vol->corrupted != 0) { + ubi_err("corrupted dynamic volume"); + goto fail; + } + if (vol->used_ebs != vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes != vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes != n) { + ubi_err("bad used_bytes"); + goto fail; + } + } else { + if (vol->corrupted != 0 && vol->corrupted != 1) { + ubi_err("bad corrupted"); + goto fail; + } + if (vol->used_ebs < 0 || vol->used_ebs > vol->reserved_pebs) { + ubi_err("bad used_ebs"); + goto fail; + } + if (vol->last_eb_bytes < 0 || + vol->last_eb_bytes > vol->usable_leb_size) { + ubi_err("bad last_eb_bytes"); + goto fail; + } + if (vol->used_bytes < 0 || vol->used_bytes > n || + vol->used_bytes < n - vol->usable_leb_size) { + ubi_err("bad used_bytes"); + goto fail; + } + } + + alignment = ubi32_to_cpu(ubi->vtbl[vol_id].alignment); + data_pad = ubi32_to_cpu(ubi->vtbl[vol_id].data_pad); + name_len = ubi16_to_cpu(ubi->vtbl[vol_id].name_len); + upd_marker = ubi->vtbl[vol_id].upd_marker; + name = &ubi->vtbl[vol_id].name[0]; + if (ubi->vtbl[vol_id].vol_type == UBI_VID_DYNAMIC) + vol_type = UBI_DYNAMIC_VOLUME; + else + vol_type = UBI_STATIC_VOLUME; + + if (alignment != vol->alignment || data_pad != vol->data_pad || + upd_marker != vol->upd_marker || vol_type != vol->vol_type || + name_len!= vol->name_len || strncmp(name, vol->name, name_len)) { + ubi_err("volume info is different"); + goto fail; + } + + return; + +fail: + ubi_err("paranoid check failed"); + ubi_dbg_dump_vol_info(vol); + ubi_dbg_dump_vtbl_record(&ubi->vtbl[vol_id], vol_id); + BUG(); +} + +/** + * paranoid_check_volumes - check information about all volumes. + * @ubi: UBI device description object + */ +static void paranoid_check_volumes(struct ubi_device *ubi) +{ + int i; + + mutex_lock(&ubi->vtbl_mutex); + spin_lock(&ubi->volumes_lock); + for (i = 0; i < ubi->vtbl_slots; i++) + paranoid_check_volume(ubi, i); + spin_unlock(&ubi->volumes_lock); + mutex_unlock(&ubi->vtbl_mutex); +} +#endif diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c new file mode 100644 index 0000000..b6fd6bb --- /dev/null +++ b/drivers/mtd/ubi/vtbl.c @@ -0,0 +1,809 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * Copyright (c) Nokia Corporation, 2006, 2007 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Artem Bityutskiy (Битюцкий Ðртём) + */ + +/* + * This file includes volume table manipulation code. The volume table is an + * on-flash table containing volume meta-data like name, number of reserved + * physical eraseblocks, type, etc. The volume table is stored in the so-called + * "layout volume". + * + * The layout volume is an internal volume which is organized as follows. It + * consists of two logical eraseblocks - LEB 0 and LEB 1. Each logical + * eraseblock stores one volume table copy, i.e. LEB 0 and LEB 1 duplicate each + * other. This redundancy guarantees robustness to unclean reboots. The volume + * table is basically an array of volume table records. Each record contains + * full information about the volume and protected by a CRC checksum. + * + * The volume table is changed, it is first changed in RAM. Then LEB 0 is + * erased, and the updated volume table is written back to LEB 0. Then same for + * LEB 1. This scheme guarantees recoverability from unclean reboots. + * + * In this UBI implementation the on-flash volume table does not contain any + * information about how many data static volumes contain. This information may + * be found from the scanning data. + * + * But it would still be beneficial to store this information in the volume + * table. For example, suppose we have a static volume X, and all its physical + * eraseblocks became bad for some reasons. Suppose we are attaching the + * corresponding MTD device, the scanning has found no logical eraseblocks + * corresponding to the volume X. According to the volume table volume X does + * exist. So we don't know whether it is just empty or all its physical + * eraseblocks went bad. So we cannot alarm the user about this corruption. + * + * The volume table also stores so-called "update marker", which is used for + * volume updates. Before updating the volume, the update marker is set, and + * after the update operation is finished, the update marker is cleared. So if + * the update operation was interrupted (e.g. by an unclean reboot) - the + * update marker is still there and we know that the volume's contents is + * damaged. + */ + +#include <linux/crc32.h> +#include <linux/err.h> +#include <asm/div64.h> +#include "ubi.h" + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static void paranoid_vtbl_check(const struct ubi_device *ubi); +#else +#define paranoid_vtbl_check(ubi) +#endif + +/* Empty volume table record */ +static struct ubi_vtbl_record empty_vtbl_record; + +/** + * ubi_change_vtbl_record - change volume table record. + * @ubi: UBI device description object + * @idx: table index to change + * @vtbl_rec: new volume table record + * + * This function changes volume table record @idx. If @vtbl_rec is %NULL, empty + * volume table record is written. The caller does not have to calculate CRC of + * the record as it is done by this function. Returns zero in case of success + * and a negative error code in case of failure. + */ +int ubi_change_vtbl_record(struct ubi_device *ubi, int idx, + struct ubi_vtbl_record *vtbl_rec) +{ + int i, err; + uint32_t crc; + + ubi_assert(idx >= 0 && idx < ubi->vtbl_slots); + + if (!vtbl_rec) + vtbl_rec = &empty_vtbl_record; + else { + crc = crc32(UBI_CRC32_INIT, vtbl_rec, UBI_VTBL_RECORD_SIZE_CRC); + vtbl_rec->crc = cpu_to_ubi32(crc); + } + + dbg_msg("change record %d", idx); + ubi_dbg_dump_vtbl_record(vtbl_rec, idx); + + mutex_lock(&ubi->vtbl_mutex); + memcpy(&ubi->vtbl[idx], vtbl_rec, sizeof(struct ubi_vtbl_record)); + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + err = ubi_eba_unmap_leb(ubi, UBI_LAYOUT_VOL_ID, i); + if (err) { + mutex_unlock(&ubi->vtbl_mutex); + return err; + } + err = ubi_eba_write_leb(ubi, UBI_LAYOUT_VOL_ID, i, ubi->vtbl, 0, + ubi->vtbl_size, UBI_LONGTERM); + if (err) { + mutex_unlock(&ubi->vtbl_mutex); + return err; + } + } + + paranoid_vtbl_check(ubi); + mutex_unlock(&ubi->vtbl_mutex); + return ubi_wl_flush(ubi); +} + +/** + * vol_til_check - check if volume table is not corrupted and contains sensible + * data. + * + * @ubi: UBI device description object + * @vtbl: volume table + * + * This function returns zero if @vtbl is all right, %1 if CRC is incorrect, + * and %-EINVAL if it contains inconsistent data. + */ +static int vtbl_check(const struct ubi_device *ubi, + const struct ubi_vtbl_record *vtbl) +{ + int i, n, reserved_pebs, alignment, data_pad, vol_type, name_len; + int upd_marker; + uint32_t crc; + const char *name; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); + alignment = ubi32_to_cpu(vtbl[i].alignment); + data_pad = ubi32_to_cpu(vtbl[i].data_pad); + upd_marker = vtbl[i].upd_marker; + vol_type = vtbl[i].vol_type; + name_len = ubi16_to_cpu(vtbl[i].name_len); + name = &vtbl[i].name[0]; + + crc = crc32(UBI_CRC32_INIT, &vtbl[i], UBI_VTBL_RECORD_SIZE_CRC); + if (ubi32_to_cpu(vtbl[i].crc) != crc) { + ubi_err("bad CRC at record %u: %#08x, not %#08x", + i, crc, ubi32_to_cpu(vtbl[i].crc)); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return 1; + } + + if (reserved_pebs == 0) { + if (memcmp(&vtbl[i], &empty_vtbl_record, + UBI_VTBL_RECORD_SIZE)) { + dbg_err("bad empty record"); + goto bad; + } + continue; + } + + if (reserved_pebs < 0 || alignment < 0 || data_pad < 0 || + name_len < 0) { + dbg_err("negative values"); + goto bad; + } + + if (alignment > ubi->leb_size || alignment == 0) { + dbg_err("bad alignment"); + goto bad; + } + + n = alignment % ubi->min_io_size; + if (alignment != 1 && n) { + dbg_err("alignment is not multiple of min I/O unit"); + goto bad; + } + + n = ubi->leb_size % alignment; + if (data_pad != n) { + dbg_err("bad data_pad, has to be %d", n); + goto bad; + } + + if (vol_type != UBI_VID_DYNAMIC && vol_type != UBI_VID_STATIC) { + dbg_err("bad vol_type"); + goto bad; + } + + if (upd_marker != 0 && upd_marker != 1) { + dbg_err("bad upd_marker"); + goto bad; + } + + if (reserved_pebs > ubi->good_peb_count) { + dbg_err("too large reserved_pebs, good PEBs %d", + ubi->good_peb_count); + goto bad; + } + + if (name_len > UBI_VOL_NAME_MAX) { + dbg_err("too long volume name, max %d", + UBI_VOL_NAME_MAX); + goto bad; + } + + if (name[0] == '\0') { + dbg_err("NULL volume name"); + goto bad; + } + + if (name_len != strnlen(name, name_len + 1)) { + dbg_err("bad name_len"); + goto bad; + } + } + + /* Checks that all names are unique */ + for (i = 0; i < ubi->vtbl_slots - 1; i++) { + for (n = i + 1; n < ubi->vtbl_slots; n++) { + int len1 = ubi16_to_cpu(vtbl[i].name_len); + int len2 = ubi16_to_cpu(vtbl[n].name_len); + + if (len1 > 0 && len1 == len2 && + !strncmp(vtbl[i].name, vtbl[n].name, len1)) { + ubi_err("volumes %d and %d have the same name" + " \"%s\"", i, n, vtbl[i].name); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + ubi_dbg_dump_vtbl_record(&vtbl[n], n); + return -EINVAL; + } + } + } + + return 0; + +bad: + ubi_err("volume table check failed, record %d", i); + ubi_dbg_dump_vtbl_record(&vtbl[i], i); + return -EINVAL; +} + +/** + * create_vtbl - create a copy of volume table. + * @ubi: UBI device description object + * @si: scanning information + * @copy: number of the volume table copy + * @vtbl: contents of the volume table + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int create_vtbl(const struct ubi_device *ubi, struct ubi_scan_info *si, + int copy, void *vtbl) +{ + int err, tries = 0; + static struct ubi_vid_hdr *vid_hdr; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *new_seb, *old_seb = NULL; + + ubi_msg("create volume table (copy #%d)", copy + 1); + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + /* + * Check if there is a logical eraseblock which would have to contain + * this volume table copy was found during scanning. It has to be wiped + * out. + */ + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + if (sv) + old_seb = ubi_scan_find_seb(sv, copy); + +retry: + new_seb = ubi_scan_get_free_peb(ubi, si); + if (IS_ERR(new_seb)) { + err = PTR_ERR(new_seb); + goto out_free; + } + + vid_hdr->vol_type = UBI_VID_DYNAMIC; + vid_hdr->vol_id = cpu_to_ubi32(UBI_LAYOUT_VOL_ID); + vid_hdr->compat = UBI_LAYOUT_VOLUME_COMPAT; + vid_hdr->data_size = vid_hdr->used_ebs = + vid_hdr->data_pad = cpu_to_ubi32(0); + vid_hdr->lnum = cpu_to_ubi32(copy); + vid_hdr->sqnum = cpu_to_ubi64(++si->max_sqnum); + vid_hdr->leb_ver = cpu_to_ubi32(old_seb ? old_seb->leb_ver + 1: 0); + + /* The EC header is already there, write the VID header */ + err = ubi_io_write_vid_hdr(ubi, new_seb->pnum, vid_hdr); + if (err) + goto write_error; + + /* Write the layout volume contents */ + err = ubi_io_write_data(ubi, vtbl, new_seb->pnum, 0, ubi->vtbl_size); + if (err) + goto write_error; + + /* + * And add it to the scanning information. Don't delete the old + * @old_seb as it will be deleted and freed in 'ubi_scan_add_used()'. + */ + err = ubi_scan_add_used(ubi, si, new_seb->pnum, new_seb->ec, + vid_hdr, 0); + kfree(new_seb); + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +write_error: + kfree(new_seb); + /* May be this physical eraseblock went bad, try to pick another one */ + if (++tries <= 5) { + err = ubi_scan_add_to_list(si, new_seb->pnum, new_seb->ec, + &si->corr); + if (!err) + goto retry; + } +out_free: + ubi_free_vid_hdr(ubi, vid_hdr); + return err; + +} + +/** + * process_lvol - process the layout volume. + * @ubi: UBI device description object + * @si: scanning information + * @sv: layout volume scanning information + * + * This function is responsible for reading the layout volume, ensuring it is + * not corrupted, and recovering from corruptions if needed. Returns volume + * table in case of success and a negative error code in case of failure. + */ +static struct ubi_vtbl_record *process_lvol(const struct ubi_device *ubi, + struct ubi_scan_info *si, + struct ubi_scan_volume *sv) +{ + int err; + struct rb_node *rb; + struct ubi_scan_leb *seb; + struct ubi_vtbl_record *leb[UBI_LAYOUT_VOLUME_EBS] = { NULL, NULL }; + int leb_corrupted[UBI_LAYOUT_VOLUME_EBS] = {1, 1}; + + /* + * UBI goes through the following steps when it changes the layout + * volume: + * a. erase LEB 0; + * b. write new data to LEB 0; + * c. erase LEB 1; + * d. write new data to LEB 1. + * + * Before the change, both LEBs contain the same data. + * + * Due to unclean reboots, the contents of LEB 0 may be lost, but there + * should LEB 1. So it is OK if LEB 0 is corrupted while LEB 1 is not. + * Similarly, LEB 1 may be lost, but there should be LEB 0. And + * finally, unclean reboots may result in a situation when neither LEB + * 0 nor LEB 1 are corrupted, but they are different. In this case, LEB + * 0 contains more recent information. + * + * So the plan is to first check LEB 0. Then + * a. if LEB 0 is OK, it must be containing the most resent data; then + * we compare it with LEB 1, and if they are different, we copy LEB + * 0 to LEB 1; + * b. if LEB 0 is corrupted, but LEB 1 has to be OK, and we copy LEB 1 + * to LEB 0. + */ + + dbg_msg("check layout volume"); + + /* Read both LEB 0 and LEB 1 into memory */ + ubi_rb_for_each_entry(rb, seb, &sv->root, u.rb) { + leb[seb->lnum] = kzalloc(ubi->vtbl_size, GFP_KERNEL); + if (!leb[seb->lnum]) { + err = -ENOMEM; + goto out_free; + } + + err = ubi_io_read_data(ubi, leb[seb->lnum], seb->pnum, 0, + ubi->vtbl_size); + if (err == UBI_IO_BITFLIPS || err == -EBADMSG) + /* Scrub the PEB later */ + seb->scrub = 1; + else if (err) + goto out_free; + } + + err = -EINVAL; + if (leb[0]) { + leb_corrupted[0] = vtbl_check(ubi, leb[0]); + if (leb_corrupted[0] < 0) + goto out_free; + } + + if (!leb_corrupted[0]) { + /* LEB 0 is OK */ + if (leb[1]) + leb_corrupted[1] = memcmp(leb[0], leb[1], ubi->vtbl_size); + if (leb_corrupted[1]) { + ubi_warn("volume table copy #2 is corrupted"); + err = create_vtbl(ubi, si, 1, leb[0]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + } + + /* Both LEB 1 and LEB 2 are OK and consistent */ + kfree(leb[1]); + return leb[0]; + } else { + /* LEB 0 is corrupted or does not exist */ + if (leb[1]) { + leb_corrupted[1] = vtbl_check(ubi, leb[1]); + if (leb_corrupted[1] < 0) + goto out_free; + } + if (leb_corrupted[1]) { + /* Both LEB 0 and LEB 1 are corrupted */ + ubi_err("both volume tables are corrupted"); + goto out_free; + } + + ubi_warn("volume table copy #1 is corrupted"); + err = create_vtbl(ubi, si, 0, leb[1]); + if (err) + goto out_free; + ubi_msg("volume table was restored"); + + kfree(leb[0]); + return leb[1]; + } + +out_free: + kfree(leb[0]); + kfree(leb[1]); + return ERR_PTR(err); +} + +/** + * create_empty_lvol - create empty layout volume. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns volume table contents in case of success and a + * negative error code in case of failure. + */ +static struct ubi_vtbl_record *create_empty_lvol(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int i; + struct ubi_vtbl_record *vtbl; + + vtbl = kzalloc(ubi->vtbl_size, GFP_KERNEL); + if (!vtbl) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < ubi->vtbl_slots; i++) + memcpy(&vtbl[i], &empty_vtbl_record, UBI_VTBL_RECORD_SIZE); + + for (i = 0; i < UBI_LAYOUT_VOLUME_EBS; i++) { + int err; + + err = create_vtbl(ubi, si, i, vtbl); + if (err) { + kfree(vtbl); + return ERR_PTR(err); + } + } + + return vtbl; +} + +/** + * init_volumes - initialize volume information for existing volumes. + * @ubi: UBI device description object + * @si: scanning information + * @vtbl: volume table + * + * This function allocates volume description objects for existing volumes. + * Returns zero in case of success and a negative error code in case of + * failure. + */ +static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, + const struct ubi_vtbl_record *vtbl) +{ + int i, reserved_pebs = 0; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + for (i = 0; i < ubi->vtbl_slots; i++) { + cond_resched(); + + if (ubi32_to_cpu(vtbl[i].reserved_pebs) == 0) + continue; /* Empty record */ + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = ubi32_to_cpu(vtbl[i].reserved_pebs); + vol->alignment = ubi32_to_cpu(vtbl[i].alignment); + vol->data_pad = ubi32_to_cpu(vtbl[i].data_pad); + vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? + UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; + vol->name_len = ubi16_to_cpu(vtbl[i].name_len); + vol->usable_leb_size = ubi->leb_size - vol->data_pad; + memcpy(vol->name, vtbl[i].name, vol->name_len); + vol->name[vol->name_len] = '\0'; + vol->vol_id = i; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[i] = vol; + ubi->vol_count += 1; + vol->ubi = ubi; + reserved_pebs += vol->reserved_pebs; + + /* + * In case of dynamic volume UBI knows nothing about how many + * data is stored there. So assume the whole volume is used. + */ + if (vol->vol_type == UBI_DYNAMIC_VOLUME) { + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->usable_leb_size; + vol->used_bytes = vol->used_ebs * vol->usable_leb_size; + continue; + } + + /* Static volumes only */ + sv = ubi_scan_find_sv(si, i); + if (!sv) { + /* + * No eraseblocks belonging to this volume found. We + * don't actually know whether this static volume is + * completely corrupted or just contains no data. And + * we cannot know this as long as data size is not + * stored on flash. So we just assume the volume is + * empty. FIXME: this should be handled. + */ + continue; + } + + if (sv->leb_count != sv->used_ebs) { + /* + * We found a static volume which misses several + * eraseblocks. Treat it as corrupted. + */ + ubi_warn("static volume %d misses %d LEBs - corrupted", + sv->vol_id, sv->used_ebs - sv->leb_count); + vol->corrupted = 1; + continue; + } + + vol->used_ebs = sv->used_ebs; + vol->used_bytes = (vol->used_ebs - 1) * vol->usable_leb_size; + vol->used_bytes += sv->last_data_size; + vol->last_eb_bytes = sv->last_data_size; + } + + vol = kzalloc(sizeof(struct ubi_volume), GFP_KERNEL); + if (!vol) + return -ENOMEM; + + vol->reserved_pebs = UBI_LAYOUT_VOLUME_EBS; + vol->alignment = 1; + vol->vol_type = UBI_DYNAMIC_VOLUME; + vol->name_len = sizeof(UBI_LAYOUT_VOLUME_NAME) - 1; + memcpy(vol->name, UBI_LAYOUT_VOLUME_NAME, vol->name_len + 1); + vol->usable_leb_size = ubi->leb_size; + vol->used_ebs = vol->reserved_pebs; + vol->last_eb_bytes = vol->reserved_pebs; + vol->used_bytes = vol->used_ebs * (ubi->leb_size - vol->data_pad); + vol->vol_id = UBI_LAYOUT_VOL_ID; + + ubi_assert(!ubi->volumes[i]); + ubi->volumes[vol_id2idx(ubi, vol->vol_id)] = vol; + reserved_pebs += vol->reserved_pebs; + ubi->vol_count += 1; + vol->ubi = ubi; + + if (reserved_pebs > ubi->avail_pebs) + ubi_err("not enough PEBs, required %d, available %d", + reserved_pebs, ubi->avail_pebs); + ubi->rsvd_pebs += reserved_pebs; + ubi->avail_pebs -= reserved_pebs; + + return 0; +} + +/** + * check_sv - check volume scanning information. + * @vol: UBI volume description object + * @sv: volume scanning information + * + * This function returns zero if the volume scanning information is consistent + * to the data read from the volume tabla, and %-EINVAL if not. + */ +static int check_sv(const struct ubi_volume *vol, + const struct ubi_scan_volume *sv) +{ + if (sv->highest_lnum >= vol->reserved_pebs) { + dbg_err("bad highest_lnum"); + goto bad; + } + if (sv->leb_count > vol->reserved_pebs) { + dbg_err("bad leb_count"); + goto bad; + } + if (sv->vol_type != vol->vol_type) { + dbg_err("bad vol_type"); + goto bad; + } + if (sv->used_ebs > vol->reserved_pebs) { + dbg_err("bad used_ebs"); + goto bad; + } + if (sv->data_pad != vol->data_pad) { + dbg_err("bad data_pad"); + goto bad; + } + return 0; + +bad: + ubi_err("bad scanning information"); + ubi_dbg_dump_sv(sv); + ubi_dbg_dump_vol_info(vol); + return -EINVAL; +} + +/** + * check_scanning_info - check that scanning information. + * @ubi: UBI device description object + * @si: scanning information + * + * Even though we protect on-flash data by CRC checksums, we still don't trust + * the media. This function ensures that scanning information is consistent to + * the information read from the volume table. Returns zero if the scanning + * information is OK and %-EINVAL if it is not. + */ +static int check_scanning_info(const struct ubi_device *ubi, + struct ubi_scan_info *si) +{ + int err, i; + struct ubi_scan_volume *sv; + struct ubi_volume *vol; + + if (si->vols_found > UBI_INT_VOL_COUNT + ubi->vtbl_slots) { + ubi_err("scanning found %d volumes, maximum is %d + %d", + si->vols_found, UBI_INT_VOL_COUNT, ubi->vtbl_slots); + return -EINVAL; + } + + if (si->highest_vol_id >= ubi->vtbl_slots + UBI_INT_VOL_COUNT&& + si->highest_vol_id < UBI_INTERNAL_VOL_START) { + ubi_err("too large volume ID %d found by scanning", + si->highest_vol_id); + return -EINVAL; + } + + + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) { + cond_resched(); + + sv = ubi_scan_find_sv(si, i); + vol = ubi->volumes[i]; + if (!vol) { + if (sv) + ubi_scan_rm_volume(si, sv); + continue; + } + + if (vol->reserved_pebs == 0) { + ubi_assert(i < ubi->vtbl_slots); + + if (!sv) + continue; + + /* + * During scanning we found a volume which does not + * exist according to the information in the volume + * table. This must have happened due to an unclean + * reboot while the volume was being removed. Discard + * these eraseblocks. + */ + ubi_msg("finish volume %d removal", sv->vol_id); + ubi_scan_rm_volume(si, sv); + } else if (sv) { + err = check_sv(vol, sv); + if (err) + return err; + } + } + + return 0; +} + +/** + * ubi_read_volume_table - read volume table. + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function reads volume table, checks it, recover from errors if needed, + * or creates it if needed. Returns zero in case of success and a negative + * error code in case of failure. + */ +int ubi_read_volume_table(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int i, err; + struct ubi_scan_volume *sv; + + empty_vtbl_record.crc = cpu_to_ubi32(0xf116c36b); + + /* + * The number of supported volumes is limited by the eraseblock size + * and by the UBI_MAX_VOLUMES constant. + */ + ubi->vtbl_slots = ubi->leb_size / UBI_VTBL_RECORD_SIZE; + if (ubi->vtbl_slots > UBI_MAX_VOLUMES) + ubi->vtbl_slots = UBI_MAX_VOLUMES; + + ubi->vtbl_size = ubi->vtbl_slots * UBI_VTBL_RECORD_SIZE; + ubi->vtbl_size = ALIGN(ubi->vtbl_size, ubi->min_io_size); + + sv = ubi_scan_find_sv(si, UBI_LAYOUT_VOL_ID); + if (!sv) { + /* + * No logical eraseblocks belonging to the layout volume were + * found. This could mean that the flash is just empty. In + * this case we create empty layout volume. + * + * But if flash is not empty this must be a corruption or the + * MTD device just contains garbage. + */ + if (si->is_empty) { + ubi->vtbl = create_empty_lvol(ubi, si); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } else { + ubi_err("the layout volume was not found"); + return -EINVAL; + } + } else { + if (sv->leb_count > UBI_LAYOUT_VOLUME_EBS) { + /* This must not happen with proper UBI images */ + dbg_err("too many LEBs (%d) in layout volume", + sv->leb_count); + return -EINVAL; + } + + ubi->vtbl = process_lvol(ubi, si, sv); + if (IS_ERR(ubi->vtbl)) + return PTR_ERR(ubi->vtbl); + } + + ubi->avail_pebs = ubi->good_peb_count; + + /* + * The layout volume is OK, initialize the corresponding in-RAM data + * structures. + */ + err = init_volumes(ubi, si, ubi->vtbl); + if (err) + goto out_free; + + /* + * Get sure that the scanning information is consistent to the + * information stored in the volume table. + */ + err = check_scanning_info(ubi, si); + if (err) + goto out_free; + + return 0; + +out_free: + kfree(ubi->vtbl); + for (i = 0; i < ubi->vtbl_slots + UBI_INT_VOL_COUNT; i++) + if (ubi->volumes[i]) { + kfree(ubi->volumes[i]); + ubi->volumes[i] = NULL; + } + return err; +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_vtbl_check - check volume table. + * @ubi: UBI device description object + */ +static void paranoid_vtbl_check(const struct ubi_device *ubi) +{ + if (vtbl_check(ubi, ubi->vtbl)) { + ubi_err("paranoid check failed"); + BUG(); + } +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c new file mode 100644 index 0000000..9ecaf77 --- /dev/null +++ b/drivers/mtd/ubi/wl.c @@ -0,0 +1,1671 @@ +/* + * Copyright (c) International Business Machines Corp., 2006 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Authors: Artem Bityutskiy (Битюцкий Ðртём), Thomas Gleixner + */ + +/* + * UBI wear-leveling unit. + * + * This unit is responsible for wear-leveling. It works in terms of physical + * eraseblocks and erase counters and knows nothing about logical eraseblocks, + * volumes, etc. From this unit's perspective all physical eraseblocks are of + * two types - used and free. Used physical eraseblocks are those that were + * "get" by the 'ubi_wl_get_peb()' function, and free physical eraseblocks are + * those that were put by the 'ubi_wl_put_peb()' function. + * + * Physical eraseblocks returned by 'ubi_wl_get_peb()' have only erase counter + * header. The rest of the physical eraseblock contains only 0xFF bytes. + * + * When physical eraseblocks are returned to the WL unit by means of the + * 'ubi_wl_put_peb()' function, they are scheduled for erasure. The erasure is + * done asynchronously in context of the per-UBI device background thread, + * which is also managed by the WL unit. + * + * The wear-leveling is ensured by means of moving the contents of used + * physical eraseblocks with low erase counter to free physical eraseblocks + * with high erase counter. + * + * The 'ubi_wl_get_peb()' function accepts data type hints which help to pick + * an "optimal" physical eraseblock. For example, when it is known that the + * physical eraseblock will be "put" soon because it contains short-term data, + * the WL unit may pick a free physical eraseblock with low erase counter, and + * so forth. + * + * If the WL unit fails to erase a physical eraseblock, it marks it as bad. + * + * This unit is also responsible for scrubbing. If a bit-flip is detected in a + * physical eraseblock, it has to be moved. Technically this is the same as + * moving it for wear-leveling reasons. + * + * As it was said, for the UBI unit all physical eraseblocks are either "free" + * or "used". Free eraseblock are kept in the @wl->free RB-tree, while used + * eraseblocks are kept in a set of different RB-trees: @wl->used, + * @wl->prot.pnum, @wl->prot.aec, and @wl->scrub. + * + * Note, in this implementation, we keep a small in-RAM object for each physical + * eraseblock. This is surely not a scalable solution. But it appears to be good + * enough for moderately large flashes and it is simple. In future, one may + * re-work this unit and make it more scalable. + * + * At the moment this unit does not utilize the sequence number, which was + * introduced relatively recently. But it would be wise to do this because the + * sequence number of a logical eraseblock characterizes how old is it. For + * example, when we move a PEB with low erase counter, and we need to pick the + * target PEB, we pick a PEB with the highest EC if our PEB is "old" and we + * pick target PEB with an average EC if our PEB is not very "old". This is a + * room for future re-works of the WL unit. + * + * FIXME: looks too complex, should be simplified (later). + */ + +#include <linux/slab.h> +#include <linux/crc32.h> +#include <linux/freezer.h> +#include <linux/kthread.h> +#include "ubi.h" + +/* Number of physical eraseblocks reserved for wear-leveling purposes */ +#define WL_RESERVED_PEBS 1 + +/* + * How many erase cycles are short term, unknown, and long term physical + * eraseblocks protected. + */ +#define ST_PROTECTION 16 +#define U_PROTECTION 10 +#define LT_PROTECTION 4 + +/* + * Maximum difference between two erase counters. If this threshold is + * exceeded, the WL unit starts moving data from used physical eraseblocks with + * low erase counter to free physical eraseblocks with high erase counter. + */ +#define UBI_WL_THRESHOLD CONFIG_MTD_UBI_WL_THRESHOLD + +/* + * When a physical eraseblock is moved, the WL unit has to pick the target + * physical eraseblock to move to. The simplest way would be just to pick the + * one with the highest erase counter. But in certain workloads this could lead + * to an unlimited wear of one or few physical eraseblock. Indeed, imagine a + * situation when the picked physical eraseblock is constantly erased after the + * data is written to it. So, we have a constant which limits the highest erase + * counter of the free physical eraseblock to pick. Namely, the WL unit does + * not pick eraseblocks with erase counter greater then the lowest erase + * counter plus %WL_FREE_MAX_DIFF. + */ +#define WL_FREE_MAX_DIFF (2*UBI_WL_THRESHOLD) + +/* + * Maximum number of consecutive background thread failures which is enough to + * switch to read-only mode. + */ +#define WL_MAX_FAILURES 32 + +/** + * struct ubi_wl_entry - wear-leveling entry. + * @rb: link in the corresponding RB-tree + * @ec: erase counter + * @pnum: physical eraseblock number + * + * Each physical eraseblock has a corresponding &struct wl_entry object which + * may be kept in different RB-trees. + */ +struct ubi_wl_entry { + struct rb_node rb; + int ec; + int pnum; +}; + +/** + * struct ubi_wl_prot_entry - PEB protection entry. + * @rb_pnum: link in the @wl->prot.pnum RB-tree + * @rb_aec: link in the @wl->prot.aec RB-tree + * @abs_ec: the absolute erase counter value when the protection ends + * @e: the wear-leveling entry of the physical eraseblock under protection + * + * When the WL unit returns a physical eraseblock, the physical eraseblock is + * protected from being moved for some "time". For this reason, the physical + * eraseblock is not directly moved from the @wl->free tree to the @wl->used + * tree. There is one more tree in between where this physical eraseblock is + * temporarily stored (@wl->prot). + * + * All this protection stuff is needed because: + * o we don't want to move physical eraseblocks just after we have given them + * to the user; instead, we first want to let users fill them up with data; + * + * o there is a chance that the user will put the physical eraseblock very + * soon, so it makes sense not to move it for some time, but wait; this is + * especially important in case of "short term" physical eraseblocks. + * + * Physical eraseblocks stay protected only for limited time. But the "time" is + * measured in erase cycles in this case. This is implemented with help of the + * absolute erase counter (@wl->abs_ec). When it reaches certain value, the + * physical eraseblocks are moved from the protection trees (@wl->prot.*) to + * the @wl->used tree. + * + * Protected physical eraseblocks are searched by physical eraseblock number + * (when they are put) and by the absolute erase counter (to check if it is + * time to move them to the @wl->used tree). So there are actually 2 RB-trees + * storing the protected physical eraseblocks: @wl->prot.pnum and + * @wl->prot.aec. They are referred to as the "protection" trees. The + * first one is indexed by the physical eraseblock number. The second one is + * indexed by the absolute erase counter. Both trees store + * &struct ubi_wl_prot_entry objects. + * + * Each physical eraseblock has 2 main states: free and used. The former state + * corresponds to the @wl->free tree. The latter state is split up on several + * sub-states: + * o the WL movement is allowed (@wl->used tree); + * o the WL movement is temporarily prohibited (@wl->prot.pnum and + * @wl->prot.aec trees); + * o scrubbing is needed (@wl->scrub tree). + * + * Depending on the sub-state, wear-leveling entries of the used physical + * eraseblocks may be kept in one of those trees. + */ +struct ubi_wl_prot_entry { + struct rb_node rb_pnum; + struct rb_node rb_aec; + unsigned long long abs_ec; + struct ubi_wl_entry *e; +}; + +/** + * struct ubi_work - UBI work description data structure. + * @list: a link in the list of pending works + * @func: worker function + * @priv: private data of the worker function + * + * @e: physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * The @func pointer points to the worker function. If the @cancel argument is + * not zero, the worker has to free the resources and exit immediately. The + * worker has to return zero in case of success and a negative error code in + * case of failure. + */ +struct ubi_work { + struct list_head list; + int (*func)(struct ubi_device *ubi, struct ubi_work *wrk, int cancel); + /* The below fields are only relevant to erasure works */ + struct ubi_wl_entry *e; + int torture; +}; + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID +static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec); +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root); +#else +#define paranoid_check_ec(ubi, pnum, ec) 0 +#define paranoid_check_in_wl_tree(e, root) +#endif + +/* Slab cache for wear-leveling entries */ +static struct kmem_cache *wl_entries_slab; + +/** + * tree_empty - a helper function to check if an RB-tree is empty. + * @root: the root of the tree + * + * This function returns non-zero if the RB-tree is empty and zero if not. + */ +static inline int tree_empty(struct rb_root *root) +{ + return root->rb_node == NULL; +} + +/** + * wl_tree_add - add a wear-leveling entry to a WL RB-tree. + * @e: the wear-leveling entry to add + * @root: the root of the tree + * + * Note, we use (erase counter, physical eraseblock number) pairs as keys in + * the @ubi->used and @ubi->free RB-trees. + */ +static void wl_tree_add(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node **p, *parent = NULL; + + p = &root->rb_node; + while (*p) { + struct ubi_wl_entry *e1; + + parent = *p; + e1 = rb_entry(parent, struct ubi_wl_entry, rb); + + if (e->ec < e1->ec) + p = &(*p)->rb_left; + else if (e->ec > e1->ec) + p = &(*p)->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + } + + rb_link_node(&e->rb, parent, p); + rb_insert_color(&e->rb, root); +} + + +/* + * Helper functions to add and delete wear-leveling entries from different + * trees. + */ + +static void free_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->free); +} +static inline void used_tree_add(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->used); +} +static inline void scrub_tree_add(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + wl_tree_add(e, &ubi->scrub); +} +static inline void free_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->free); + rb_erase(&e->rb, &ubi->free); +} +static inline void used_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->used); + rb_erase(&e->rb, &ubi->used); +} +static inline void scrub_tree_del(struct ubi_device *ubi, + struct ubi_wl_entry *e) +{ + paranoid_check_in_wl_tree(e, &ubi->scrub); + rb_erase(&e->rb, &ubi->scrub); +} + +/** + * do_work - do one pending work. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int do_work(struct ubi_device *ubi) +{ + int err; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + + if (list_empty(&ubi->works)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + spin_unlock(&ubi->wl_lock); + + /* + * Call the worker function. Do not touch the work structure + * after this call as it will have been freed or reused by that + * time by the worker function. + */ + err = wrk->func(ubi, wrk, 0); + if (err) + ubi_err("work failed with error code %d", err); + + spin_lock(&ubi->wl_lock); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * produce_free_peb - produce a free physical eraseblock. + * @ubi: UBI device description object + * + * This function tries to make a free PEB by means of synchronous execution of + * pending works. This may be needed if, for example the background thread is + * disabled. Returns zero in case of success and a negative error code in case + * of failure. + */ +static int produce_free_peb(struct ubi_device *ubi) +{ + int err; + + spin_lock(&ubi->wl_lock); + while (tree_empty(&ubi->free)) { + spin_unlock(&ubi->wl_lock); + + dbg_wl("do one work synchronously"); + err = do_work(ubi); + if (err) + return err; + + spin_lock(&ubi->wl_lock); + } + spin_unlock(&ubi->wl_lock); + + return 0; +} + +/** + * in_wl_tree - check if wear-leveling entry is present in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns non-zero if @e is in the @root RB-tree and zero if it + * is not. + */ +static int in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) +{ + struct rb_node *p; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + + if (e->pnum == e1->pnum) { + ubi_assert(e == e1); + return 1; + } + + if (e->ec < e1->ec) + p = p->rb_left; + else if (e->ec > e1->ec) + p = p->rb_right; + else { + ubi_assert(e->pnum != e1->pnum); + if (e->pnum < e1->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + } + + return 0; +} + +/** + * prot_tree_add - add physical eraseblock to protection trees. + * @ubi: UBI device description object + * @e: the physical eraseblock to add + * @pe: protection entry object to use + * @abs_ec: absolute erase counter value when this physical eraseblock has + * to be removed from the protection trees. + * + * @wl->lock has to be locked. + */ +static void prot_tree_add(struct ubi_device *ubi, struct ubi_wl_entry *e, + struct ubi_wl_prot_entry *pe, int abs_ec) +{ + struct rb_node **p, *parent = NULL; + struct ubi_wl_prot_entry *pe1; + + pe->e = e; + pe->abs_ec = ubi->abs_ec + abs_ec; + + p = &ubi->prot.pnum.rb_node; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_pnum); + + if (e->pnum < pe1->e->pnum) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_pnum, parent, p); + rb_insert_color(&pe->rb_pnum, &ubi->prot.pnum); + + p = &ubi->prot.aec.rb_node; + parent = NULL; + while (*p) { + parent = *p; + pe1 = rb_entry(parent, struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec < pe1->abs_ec) + p = &(*p)->rb_left; + else + p = &(*p)->rb_right; + } + rb_link_node(&pe->rb_aec, parent, p); + rb_insert_color(&pe->rb_aec, &ubi->prot.aec); +} + +/** + * find_wl_entry - find wear-leveling entry closest to certain erase counter. + * @root: the RB-tree where to look for + * @max: highest possible erase counter + * + * This function looks for a wear leveling entry with erase counter closest to + * @max and less then @max. + */ +static struct ubi_wl_entry *find_wl_entry(struct rb_root *root, int max) +{ + struct rb_node *p; + struct ubi_wl_entry *e; + + e = rb_entry(rb_first(root), struct ubi_wl_entry, rb); + max += e->ec; + + p = root->rb_node; + while (p) { + struct ubi_wl_entry *e1; + + e1 = rb_entry(p, struct ubi_wl_entry, rb); + if (e1->ec >= max) + p = p->rb_left; + else { + p = p->rb_right; + e = e1; + } + } + + return e; +} + +/** + * ubi_wl_get_peb - get a physical eraseblock. + * @ubi: UBI device description object + * @dtype: type of data which will be stored in this physical eraseblock + * + * This function returns a physical eraseblock in case of success and a + * negative error code in case of failure. Might sleep. + */ +int ubi_wl_get_peb(struct ubi_device *ubi, int dtype) +{ + int err, protect, medium_ec; + struct ubi_wl_entry *e, *first, *last; + struct ubi_wl_prot_entry *pe; + + ubi_assert(dtype == UBI_LONGTERM || dtype == UBI_SHORTTERM || + dtype == UBI_UNKNOWN); + + pe = kmalloc(sizeof(struct ubi_wl_prot_entry), GFP_KERNEL); + if (!pe) + return -ENOMEM; + +retry: + spin_lock(&ubi->wl_lock); + if (tree_empty(&ubi->free)) { + if (ubi->works_count == 0) { + ubi_assert(list_empty(&ubi->works)); + ubi_err("no free eraseblocks"); + spin_unlock(&ubi->wl_lock); + kfree(pe); + return -ENOSPC; + } + spin_unlock(&ubi->wl_lock); + + err = produce_free_peb(ubi); + if (err < 0) { + kfree(pe); + return err; + } + goto retry; + } + + switch (dtype) { + case UBI_LONGTERM: + /* + * For long term data we pick a physical eraseblock + * with high erase counter. But the highest erase + * counter we can pick is bounded by the the lowest + * erase counter plus %WL_FREE_MAX_DIFF. + */ + e = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + protect = LT_PROTECTION; + break; + case UBI_UNKNOWN: + /* + * For unknown data we pick a physical eraseblock with + * medium erase counter. But we by no means can pick a + * physical eraseblock with erase counter greater or + * equivalent than the lowest erase counter plus + * %WL_FREE_MAX_DIFF. + */ + first = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + last = rb_entry(rb_last(&ubi->free), + struct ubi_wl_entry, rb); + + if (last->ec - first->ec < WL_FREE_MAX_DIFF) + e = rb_entry(ubi->free.rb_node, + struct ubi_wl_entry, rb); + else { + medium_ec = (first->ec + WL_FREE_MAX_DIFF)/2; + e = find_wl_entry(&ubi->free, medium_ec); + } + protect = U_PROTECTION; + break; + case UBI_SHORTTERM: + /* + * For short term data we pick a physical eraseblock + * with the lowest erase counter as we expect it will + * be erased soon. + */ + e = rb_entry(rb_first(&ubi->free), + struct ubi_wl_entry, rb); + protect = ST_PROTECTION; + break; + default: + protect = 0; + e = NULL; + BUG(); + } + + /* + * Move the physical eraseblock to the protection trees where it will + * be protected from being moved for some time. + */ + free_tree_del(ubi, e); + prot_tree_add(ubi, e, pe, protect); + + dbg_wl("PEB %d EC %d, protection %d", e->pnum, e->ec, protect); + spin_unlock(&ubi->wl_lock); + + return e->pnum; +} + +/** + * prot_tree_del - remove a physical eraseblock from the protection trees + * @ubi: UBI device description object + * @pnum: the physical eraseblock to remove + */ +static void prot_tree_del(struct ubi_device *ubi, int pnum) +{ + struct rb_node *p; + struct ubi_wl_prot_entry *pe = NULL; + + p = ubi->prot.pnum.rb_node; + while (p) { + + pe = rb_entry(p, struct ubi_wl_prot_entry, rb_pnum); + + if (pnum == pe->e->pnum) + break; + + if (pnum < pe->e->pnum) + p = p->rb_left; + else + p = p->rb_right; + } + + ubi_assert(pe->e->pnum == pnum); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + kfree(pe); +} + +/** + * sync_erase - synchronously erase a physical eraseblock. + * @ubi: UBI device description object + * @e: the the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, int torture) +{ + int err; + struct ubi_ec_hdr *ec_hdr; + unsigned long long ec = e->ec; + + dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); + + err = paranoid_check_ec(ubi, e->pnum, e->ec); + if (err > 0) + return -EINVAL; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_sync_erase(ubi, e->pnum, torture); + if (err < 0) + goto out_free; + + ec += err; + if (ec > UBI_MAX_ERASECOUNTER) { + /* + * Erase counter overflow. Upgrade UBI and use 64-bit + * erase counters internally. + */ + ubi_err("erase counter overflow at PEB %d, EC %llu", + e->pnum, ec); + err = -EINVAL; + goto out_free; + } + + dbg_wl("erased PEB %d, new EC %llu", e->pnum, ec); + + ec_hdr->ec = cpu_to_ubi64(ec); + + err = ubi_io_write_ec_hdr(ubi, e->pnum, ec_hdr); + if (err) + goto out_free; + + e->ec = ec; + spin_lock(&ubi->wl_lock); + if (e->ec > ubi->max_ec) + ubi->max_ec = e->ec; + spin_unlock(&ubi->wl_lock); + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * check_protection_over - check if it is time to stop protecting some + * physical eraseblocks. + * @ubi: UBI device description object + * + * This function is called after each erase operation, when the absolute erase + * counter is incremented, to check if some physical eraseblock have not to be + * protected any longer. These physical eraseblocks are moved from the + * protection trees to the used tree. + */ +static void check_protection_over(struct ubi_device *ubi) +{ + struct ubi_wl_prot_entry *pe; + + /* + * There may be several protected physical eraseblock to remove, + * process them all. + */ + while (1) { + spin_lock(&ubi->wl_lock); + if (tree_empty(&ubi->prot.aec)) { + spin_unlock(&ubi->wl_lock); + break; + } + + pe = rb_entry(rb_first(&ubi->prot.aec), + struct ubi_wl_prot_entry, rb_aec); + + if (pe->abs_ec > ubi->abs_ec) { + spin_unlock(&ubi->wl_lock); + break; + } + + dbg_wl("PEB %d protection over, abs_ec %llu, PEB abs_ec %llu", + pe->e->pnum, ubi->abs_ec, pe->abs_ec); + rb_erase(&pe->rb_aec, &ubi->prot.aec); + rb_erase(&pe->rb_pnum, &ubi->prot.pnum); + used_tree_add(ubi, pe->e); + spin_unlock(&ubi->wl_lock); + + kfree(pe); + cond_resched(); + } +} + +/** + * schedule_ubi_work - schedule a work. + * @ubi: UBI device description object + * @wrk: the work to schedule + * + * This function enqueues a work defined by @wrk to the tail of the pending + * works list. + */ +static void schedule_ubi_work(struct ubi_device *ubi, struct ubi_work *wrk) +{ + spin_lock(&ubi->wl_lock); + list_add_tail(&wrk->list, &ubi->works); + ubi_assert(ubi->works_count >= 0); + ubi->works_count += 1; + if (ubi->thread_enabled) + wake_up_process(ubi->bgt_thread); + spin_unlock(&ubi->wl_lock); +} + +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel); + +/** + * schedule_erase - schedule an erase work. + * @ubi: UBI device description object + * @e: the WL entry of the physical eraseblock to erase + * @torture: if the physical eraseblock has to be tortured + * + * This function returns zero in case of success and a %-ENOMEM in case of + * failure. + */ +static int schedule_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, + int torture) +{ + struct ubi_work *wl_wrk; + + dbg_wl("schedule erasure of PEB %d, EC %d, torture %d", + e->pnum, e->ec, torture); + + wl_wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL); + if (!wl_wrk) + return -ENOMEM; + + wl_wrk->func = &erase_worker; + wl_wrk->e = e; + wl_wrk->torture = torture; + + schedule_ubi_work(ubi, wl_wrk); + return 0; +} + +/** + * wear_leveling_worker - wear-leveling worker function. + * @ubi: UBI device description object + * @wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function copies a more worn out physical eraseblock to a less worn out + * one. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk, + int cancel) +{ + int err, put = 0; + struct ubi_wl_entry *e1, *e2; + struct ubi_vid_hdr *vid_hdr; + + kfree(wrk); + + if (cancel) + return 0; + + vid_hdr = ubi_zalloc_vid_hdr(ubi); + if (!vid_hdr) + return -ENOMEM; + + spin_lock(&ubi->wl_lock); + + /* + * Only one WL worker at a time is supported at this implementation, so + * make sure a PEB is not being moved already. + */ + if (ubi->move_to || tree_empty(&ubi->free) || + (tree_empty(&ubi->used) && tree_empty(&ubi->scrub))) { + /* + * Only one WL worker at a time is supported at this + * implementation, so if a LEB is already being moved, cancel. + * + * No free physical eraseblocks? Well, we cancel wear-leveling + * then. It will be triggered again when a free physical + * eraseblock appears. + * + * No used physical eraseblocks? They must be temporarily + * protected from being moved. They will be moved to the + * @ubi->used tree later and the wear-leveling will be + * triggered again. + */ + dbg_wl("cancel WL, a list is empty: free %d, used %d", + tree_empty(&ubi->free), tree_empty(&ubi->used)); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + } + + if (tree_empty(&ubi->scrub)) { + /* + * Now pick the least worn-out used physical eraseblock and a + * highly worn-out free physical eraseblock. If the erase + * counters differ much enough, start wear-leveling. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) { + dbg_wl("no WL needed: min used EC %d, max free EC %d", + e1->ec, e2->ec); + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + ubi_free_vid_hdr(ubi, vid_hdr); + return 0; + } + used_tree_del(ubi, e1); + dbg_wl("move PEB %d EC %d to PEB %d EC %d", + e1->pnum, e1->ec, e2->pnum, e2->ec); + } else { + e1 = rb_entry(rb_first(&ubi->scrub), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + scrub_tree_del(ubi, e1); + dbg_wl("scrub PEB %d to PEB %d", e1->pnum, e2->pnum); + } + + free_tree_del(ubi, e2); + ubi_assert(!ubi->move_from && !ubi->move_to); + ubi_assert(!ubi->move_to_put && !ubi->move_from_put); + ubi->move_from = e1; + ubi->move_to = e2; + spin_unlock(&ubi->wl_lock); + + /* + * Now we are going to copy physical eraseblock @e1->pnum to @e2->pnum. + * We so far do not know which logical eraseblock our physical + * eraseblock (@e1) belongs to. We have to read the volume identifier + * header first. + */ + + err = ubi_io_read_vid_hdr(ubi, e1->pnum, vid_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + if (err == UBI_IO_PEB_FREE) { + /* + * We are trying to move PEB without a VID header. UBI + * always write VID headers shortly after the PEB was + * given, so we have a situation when it did not have + * chance to write it down because it was preempted. + * Just re-schedule the work, so that next time it will + * likely have the VID header in place. + */ + dbg_wl("PEB %d has no VID header", e1->pnum); + err = 0; + } else { + ubi_err("error %d while reading VID header from PEB %d", + err, e1->pnum); + if (err > 0) + err = -EIO; + } + goto error; + } + + err = ubi_eba_copy_leb(ubi, e1->pnum, e2->pnum, vid_hdr); + if (err) { + if (err == UBI_IO_BITFLIPS) + err = 0; + goto error; + } + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + if (!ubi->move_to_put) + used_tree_add(ubi, e2); + else + put = 1; + ubi->move_from = ubi->move_to = NULL; + ubi->move_from_put = ubi->move_to_put = 0; + ubi->wl_scheduled = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e2->pnum); + err = schedule_erase(ubi, e2, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e2); + ubi_ro_mode(ubi); + } + } + + err = schedule_erase(ubi, e1, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e1); + ubi_ro_mode(ubi); + } + + dbg_wl("done"); + return err; + + /* + * Some error occurred. @e1 was not changed, so return it back. @e2 + * might be changed, schedule it for erasure. + */ +error: + if (err) + dbg_wl("error %d occurred, cancel operation", err); + ubi_assert(err <= 0); + + ubi_free_vid_hdr(ubi, vid_hdr); + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; + if (ubi->move_from_put) + put = 1; + else + used_tree_add(ubi, e1); + ubi->move_from = ubi->move_to = NULL; + ubi->move_from_put = ubi->move_to_put = 0; + spin_unlock(&ubi->wl_lock); + + if (put) { + /* + * Well, the target PEB was put meanwhile, schedule it for + * erasure. + */ + dbg_wl("PEB %d was put meanwhile, erase", e1->pnum); + err = schedule_erase(ubi, e1, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e1); + ubi_ro_mode(ubi); + } + } + + err = schedule_erase(ubi, e2, 0); + if (err) { + kmem_cache_free(wl_entries_slab, e2); + ubi_ro_mode(ubi); + } + + yield(); + return err; +} + +/** + * ensure_wear_leveling - schedule wear-leveling if it is needed. + * @ubi: UBI device description object + * + * This function checks if it is time to start wear-leveling and schedules it + * if yes. This function returns zero in case of success and a negative error + * code in case of failure. + */ +static int ensure_wear_leveling(struct ubi_device *ubi) +{ + int err = 0; + struct ubi_wl_entry *e1; + struct ubi_wl_entry *e2; + struct ubi_work *wrk; + + spin_lock(&ubi->wl_lock); + if (ubi->wl_scheduled) + /* Wear-leveling is already in the work queue */ + goto out_unlock; + + /* + * If the ubi->scrub tree is not empty, scrubbing is needed, and the + * the WL worker has to be scheduled anyway. + */ + if (tree_empty(&ubi->scrub)) { + if (tree_empty(&ubi->used) || tree_empty(&ubi->free)) + /* No physical eraseblocks - no deal */ + goto out_unlock; + + /* + * We schedule wear-leveling only if the difference between the + * lowest erase counter of used physical eraseblocks and a high + * erase counter of free physical eraseblocks is greater then + * %UBI_WL_THRESHOLD. + */ + e1 = rb_entry(rb_first(&ubi->used), struct ubi_wl_entry, rb); + e2 = find_wl_entry(&ubi->free, WL_FREE_MAX_DIFF); + + if (!(e2->ec - e1->ec >= UBI_WL_THRESHOLD)) + goto out_unlock; + dbg_wl("schedule wear-leveling"); + } else + dbg_wl("schedule scrubbing"); + + ubi->wl_scheduled = 1; + spin_unlock(&ubi->wl_lock); + + wrk = kmalloc(sizeof(struct ubi_work), GFP_KERNEL); + if (!wrk) { + err = -ENOMEM; + goto out_cancel; + } + + wrk->func = &wear_leveling_worker; + schedule_ubi_work(ubi, wrk); + return err; + +out_cancel: + spin_lock(&ubi->wl_lock); + ubi->wl_scheduled = 0; +out_unlock: + spin_unlock(&ubi->wl_lock); + return err; +} + +/** + * erase_worker - physical eraseblock erase worker function. + * @ubi: UBI device description object + * @wl_wrk: the work object + * @cancel: non-zero if the worker has to free memory and exit + * + * This function erases a physical eraseblock and perform torture testing if + * needed. It also takes care about marking the physical eraseblock bad if + * needed. Returns zero in case of success and a negative error code in case of + * failure. + */ +static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk, + int cancel) +{ + int err; + struct ubi_wl_entry *e = wl_wrk->e; + int pnum = e->pnum; + + if (cancel) { + dbg_wl("cancel erasure of PEB %d EC %d", pnum, e->ec); + kfree(wl_wrk); + kmem_cache_free(wl_entries_slab, e); + return 0; + } + + dbg_wl("erase PEB %d EC %d", pnum, e->ec); + + err = sync_erase(ubi, e, wl_wrk->torture); + if (!err) { + /* Fine, we've erased it successfully */ + kfree(wl_wrk); + + spin_lock(&ubi->wl_lock); + ubi->abs_ec += 1; + free_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + + /* + * One more erase operation has happened, take care about protected + * physical eraseblocks. + */ + check_protection_over(ubi); + + /* And take care about wear-leveling */ + err = ensure_wear_leveling(ubi); + return err; + } + + kfree(wl_wrk); + kmem_cache_free(wl_entries_slab, e); + + if (err != -EIO) { + /* + * If this is not %-EIO, we have no idea what to do. Scheduling + * this physical eraseblock for erasure again would cause + * errors again and again. Well, lets switch to RO mode. + */ + ubi_ro_mode(ubi); + return err; + } + + /* It is %-EIO, the PEB went bad */ + + if (!ubi->bad_allowed) { + ubi_err("bad physical eraseblock %d detected", pnum); + ubi_ro_mode(ubi); + err = -EIO; + } else { + int need; + + spin_lock(&ubi->volumes_lock); + need = ubi->beb_rsvd_level - ubi->beb_rsvd_pebs + 1; + if (need > 0) { + need = ubi->avail_pebs >= need ? need : ubi->avail_pebs; + ubi->avail_pebs -= need; + ubi->rsvd_pebs += need; + ubi->beb_rsvd_pebs += need; + if (need > 0) + ubi_msg("reserve more %d PEBs", need); + } + + if (ubi->beb_rsvd_pebs == 0) { + spin_unlock(&ubi->volumes_lock); + ubi_err("no reserved physical eraseblocks"); + ubi_ro_mode(ubi); + return -EIO; + } + + spin_unlock(&ubi->volumes_lock); + ubi_msg("mark PEB %d as bad", pnum); + + err = ubi_io_mark_bad(ubi, pnum); + if (err) { + ubi_ro_mode(ubi); + return err; + } + + spin_lock(&ubi->volumes_lock); + ubi->beb_rsvd_pebs -= 1; + ubi->bad_peb_count += 1; + ubi->good_peb_count -= 1; + ubi_calculate_reserved(ubi); + if (ubi->beb_rsvd_pebs == 0) + ubi_warn("last PEB from the reserved pool was used"); + spin_unlock(&ubi->volumes_lock); + } + + return err; +} + +/** + * ubi_wl_put_peb - return a physical eraseblock to the wear-leveling + * unit. + * @ubi: UBI device description object + * @pnum: physical eraseblock to return + * @torture: if this physical eraseblock has to be tortured + * + * This function is called to return physical eraseblock @pnum to the pool of + * free physical eraseblocks. The @torture flag has to be set if an I/O error + * occurred to this @pnum and it has to be tested. This function returns zero + * in case of success and a negative error code in case of failure. + */ +int ubi_wl_put_peb(struct ubi_device *ubi, int pnum, int torture) +{ + int err; + struct ubi_wl_entry *e; + + dbg_wl("PEB %d", pnum); + ubi_assert(pnum >= 0); + ubi_assert(pnum < ubi->peb_count); + + spin_lock(&ubi->wl_lock); + + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from) { + /* + * User is putting the physical eraseblock which was selected to + * be moved. It will be scheduled for erasure in the + * wear-leveling worker. + */ + dbg_wl("PEB %d is being moved", pnum); + ubi_assert(!ubi->move_from_put); + ubi->move_from_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else if (e == ubi->move_to) { + /* + * User is putting the physical eraseblock which was selected + * as the target the data is moved to. It may happen if the EBA + * unit already re-mapped the LEB but the WL unit did has not + * put the PEB to the "used" tree. + */ + dbg_wl("PEB %d is the target of data moving", pnum); + ubi_assert(!ubi->move_to_put); + ubi->move_to_put = 1; + spin_unlock(&ubi->wl_lock); + return 0; + } else { + if (in_wl_tree(e, &ubi->used)) + used_tree_del(ubi, e); + else if (in_wl_tree(e, &ubi->scrub)) + scrub_tree_del(ubi, e); + else + prot_tree_del(ubi, e->pnum); + } + spin_unlock(&ubi->wl_lock); + + err = schedule_erase(ubi, e, torture); + if (err) { + spin_lock(&ubi->wl_lock); + used_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + } + + return err; +} + +/** + * ubi_wl_scrub_peb - schedule a physical eraseblock for scrubbing. + * @ubi: UBI device description object + * @pnum: the physical eraseblock to schedule + * + * If a bit-flip in a physical eraseblock is detected, this physical eraseblock + * needs scrubbing. This function schedules a physical eraseblock for + * scrubbing which is done in background. This function returns zero in case of + * success and a negative error code in case of failure. + */ +int ubi_wl_scrub_peb(struct ubi_device *ubi, int pnum) +{ + struct ubi_wl_entry *e; + + ubi_msg("schedule PEB %d for scrubbing", pnum); + +retry: + spin_lock(&ubi->wl_lock); + e = ubi->lookuptbl[pnum]; + if (e == ubi->move_from || in_wl_tree(e, &ubi->scrub)) { + spin_unlock(&ubi->wl_lock); + return 0; + } + + if (e == ubi->move_to) { + /* + * This physical eraseblock was used to move data to. The data + * was moved but the PEB was not yet inserted to the proper + * tree. We should just wait a little and let the WL worker + * proceed. + */ + spin_unlock(&ubi->wl_lock); + dbg_wl("the PEB %d is not in proper tree, retry", pnum); + yield(); + goto retry; + } + + if (in_wl_tree(e, &ubi->used)) + used_tree_del(ubi, e); + else + prot_tree_del(ubi, pnum); + + scrub_tree_add(ubi, e); + spin_unlock(&ubi->wl_lock); + + /* + * Technically scrubbing is the same as wear-leveling, so it is done + * by the WL worker. + */ + return ensure_wear_leveling(ubi); +} + +/** + * ubi_wl_flush - flush all pending works. + * @ubi: UBI device description object + * + * This function returns zero in case of success and a negative error code in + * case of failure. + */ +int ubi_wl_flush(struct ubi_device *ubi) +{ + int err, pending_count; + + pending_count = ubi->works_count; + + dbg_wl("flush (%d pending works)", pending_count); + + /* + * Erase while the pending works queue is not empty, but not more then + * the number of currently pending works. + */ + while (pending_count-- > 0) { + err = do_work(ubi); + if (err) + return err; + } + + return 0; +} + +/** + * tree_destroy - destroy an RB-tree. + * @root: the root of the tree to destroy + */ +static void tree_destroy(struct rb_root *root) +{ + struct rb_node *rb; + struct ubi_wl_entry *e; + + rb = root->rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + e = rb_entry(rb, struct ubi_wl_entry, rb); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &e->rb) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(wl_entries_slab, e); + } + } +} + +/** + * ubi_thread - UBI background thread. + * @u: the UBI device description object pointer + */ +static int ubi_thread(void *u) +{ + int failures = 0; + struct ubi_device *ubi = u; + + ubi_msg("background thread \"%s\" started, PID %d", + ubi->bgt_name, current->pid); + + for (;;) { + int err; + + if (kthread_should_stop()) + goto out; + + if (try_to_freeze()) + continue; + + spin_lock(&ubi->wl_lock); + if (list_empty(&ubi->works) || ubi->ro_mode || + !ubi->thread_enabled) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&ubi->wl_lock); + schedule(); + continue; + } + spin_unlock(&ubi->wl_lock); + + err = do_work(ubi); + if (err) { + ubi_err("%s: work failed with error code %d", + ubi->bgt_name, err); + if (failures++ > WL_MAX_FAILURES) { + /* + * Too many failures, disable the thread and + * switch to read-only mode. + */ + ubi_msg("%s: %d consecutive failures", + ubi->bgt_name, WL_MAX_FAILURES); + ubi_ro_mode(ubi); + break; + } + } else + failures = 0; + + cond_resched(); + } + +out: + dbg_wl("background thread \"%s\" is killed", ubi->bgt_name); + return 0; +} + +/** + * cancel_pending - cancel all pending works. + * @ubi: UBI device description object + */ +static void cancel_pending(struct ubi_device *ubi) +{ + while (!list_empty(&ubi->works)) { + struct ubi_work *wrk; + + wrk = list_entry(ubi->works.next, struct ubi_work, list); + list_del(&wrk->list); + wrk->func(ubi, wrk, 1); + ubi->works_count -= 1; + ubi_assert(ubi->works_count >= 0); + } +} + +/** + * ubi_wl_init_scan - initialize the wear-leveling unit using scanning + * information. + * @ubi: UBI device description object + * @si: scanning information + * + * This function returns zero in case of success, and a negative error code in + * case of failure. + */ +int ubi_wl_init_scan(struct ubi_device *ubi, struct ubi_scan_info *si) +{ + int err; + struct rb_node *rb1, *rb2; + struct ubi_scan_volume *sv; + struct ubi_scan_leb *seb, *tmp; + struct ubi_wl_entry *e; + + + ubi->used = ubi->free = ubi->scrub = RB_ROOT; + ubi->prot.pnum = ubi->prot.aec = RB_ROOT; + spin_lock_init(&ubi->wl_lock); + ubi->max_ec = si->max_ec; + INIT_LIST_HEAD(&ubi->works); + + sprintf(ubi->bgt_name, UBI_BGT_NAME_PATTERN, ubi->ubi_num); + + ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); + if (IS_ERR(ubi->bgt_thread)) { + err = PTR_ERR(ubi->bgt_thread); + ubi_err("cannot spawn \"%s\", error %d", ubi->bgt_name, + err); + return err; + } + + if (ubi_devices_cnt == 0) { + wl_entries_slab = kmem_cache_create("ubi_wl_entry_slab", + sizeof(struct ubi_wl_entry), + 0, 0, NULL, NULL); + if (!wl_entries_slab) + return -ENOMEM; + } + + err = -ENOMEM; + ubi->lookuptbl = kzalloc(ubi->peb_count * sizeof(void *), GFP_KERNEL); + if (!ubi->lookuptbl) + goto out_free; + + list_for_each_entry_safe(seb, tmp, &si->erase, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(wl_entries_slab, e); + goto out_free; + } + } + + list_for_each_entry(seb, &si->free, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi_assert(e->ec >= 0); + free_tree_add(ubi, e); + ubi->lookuptbl[e->pnum] = e; + } + + list_for_each_entry(seb, &si->corr, u.list) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (schedule_erase(ubi, e, 0)) { + kmem_cache_free(wl_entries_slab, e); + goto out_free; + } + } + + ubi_rb_for_each_entry(rb1, sv, &si->volumes, rb) { + ubi_rb_for_each_entry(rb2, seb, &sv->root, u.rb) { + cond_resched(); + + e = kmem_cache_alloc(wl_entries_slab, GFP_KERNEL); + if (!e) + goto out_free; + + e->pnum = seb->pnum; + e->ec = seb->ec; + ubi->lookuptbl[e->pnum] = e; + if (!seb->scrub) { + dbg_wl("add PEB %d EC %d to the used tree", + e->pnum, e->ec); + used_tree_add(ubi, e); + } else { + dbg_wl("add PEB %d EC %d to the scrub tree", + e->pnum, e->ec); + scrub_tree_add(ubi, e); + } + } + } + + if (WL_RESERVED_PEBS > ubi->avail_pebs) { + ubi_err("no enough physical eraseblocks (%d, need %d)", + ubi->avail_pebs, WL_RESERVED_PEBS); + goto out_free; + } + ubi->avail_pebs -= WL_RESERVED_PEBS; + ubi->rsvd_pebs += WL_RESERVED_PEBS; + + /* Schedule wear-leveling if needed */ + err = ensure_wear_leveling(ubi); + if (err) + goto out_free; + + return 0; + +out_free: + cancel_pending(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + if (ubi_devices_cnt == 0) + kmem_cache_destroy(wl_entries_slab); + return err; +} + +/** + * protection_trees_destroy - destroy the protection RB-trees. + * @ubi: UBI device description object + */ +static void protection_trees_destroy(struct ubi_device *ubi) +{ + struct rb_node *rb; + struct ubi_wl_prot_entry *pe; + + rb = ubi->prot.aec.rb_node; + while (rb) { + if (rb->rb_left) + rb = rb->rb_left; + else if (rb->rb_right) + rb = rb->rb_right; + else { + pe = rb_entry(rb, struct ubi_wl_prot_entry, rb_aec); + + rb = rb_parent(rb); + if (rb) { + if (rb->rb_left == &pe->rb_aec) + rb->rb_left = NULL; + else + rb->rb_right = NULL; + } + + kmem_cache_free(wl_entries_slab, pe->e); + kfree(pe); + } + } +} + +/** + * ubi_wl_close - close the wear-leveling unit. + * @ubi: UBI device description object + */ +void ubi_wl_close(struct ubi_device *ubi) +{ + dbg_wl("disable \"%s\"", ubi->bgt_name); + if (ubi->bgt_thread) + kthread_stop(ubi->bgt_thread); + + dbg_wl("close the UBI wear-leveling unit"); + + cancel_pending(ubi); + protection_trees_destroy(ubi); + tree_destroy(&ubi->used); + tree_destroy(&ubi->free); + tree_destroy(&ubi->scrub); + kfree(ubi->lookuptbl); + if (ubi_devices_cnt == 1) + kmem_cache_destroy(wl_entries_slab); +} + +#ifdef CONFIG_MTD_UBI_DEBUG_PARANOID + +/** + * paranoid_check_ec - make sure that the erase counter of a physical eraseblock + * is correct. + * @ubi: UBI device description object + * @pnum: the physical eraseblock number to check + * @ec: the erase counter to check + * + * This function returns zero if the erase counter of physical eraseblock @pnum + * is equivalent to @ec, %1 if not, and a negative error code if an error + * occurred. + */ +static int paranoid_check_ec(const struct ubi_device *ubi, int pnum, int ec) +{ + int err; + long long read_ec; + struct ubi_ec_hdr *ec_hdr; + + ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_KERNEL); + if (!ec_hdr) + return -ENOMEM; + + err = ubi_io_read_ec_hdr(ubi, pnum, ec_hdr, 0); + if (err && err != UBI_IO_BITFLIPS) { + /* The header does not have to exist */ + err = 0; + goto out_free; + } + + read_ec = ubi64_to_cpu(ec_hdr->ec); + if (ec != read_ec) { + ubi_err("paranoid check failed for PEB %d", pnum); + ubi_err("read EC is %lld, should be %d", read_ec, ec); + ubi_dbg_dump_stack(); + err = 1; + } else + err = 0; + +out_free: + kfree(ec_hdr); + return err; +} + +/** + * paranoid_check_in_wl_tree - make sure that a wear-leveling entry is present + * in a WL RB-tree. + * @e: the wear-leveling entry to check + * @root: the root of the tree + * + * This function returns zero if @e is in the @root RB-tree and %1 if it + * is not. + */ +static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, + struct rb_root *root) +{ + if (in_wl_tree(e, root)) + return 0; + + ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", + e->pnum, e->ec, root); + ubi_dbg_dump_stack(); + return 1; +} + +#endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c index 06e3378..4bee99b 100644 --- a/drivers/net/3c501.c +++ b/drivers/net/3c501.c @@ -735,7 +735,6 @@ static void el_receive(struct net_device *dev) else { skb_reserve(skb,2); /* Force 16 byte alignment */ - skb->dev = dev; /* * The read increments through the bytes. The interrupt * handler will fix the pointer when it returns to diff --git a/drivers/net/3c505.c b/drivers/net/3c505.c index 702bfb2..e985a85 100644 --- a/drivers/net/3c505.c +++ b/drivers/net/3c505.c @@ -615,7 +615,6 @@ static void receive_packet(struct net_device *dev, int len) if (test_and_set_bit(0, (void *) &adapter->dmaing)) printk(KERN_ERR "%s: rx blocked, DMA in progress, dir %d\n", dev->name, adapter->current_dma.direction); - skb->dev = dev; adapter->current_dma.direction = 0; adapter->current_dma.length = rlen; adapter->current_dma.skb = skb; @@ -1026,7 +1025,7 @@ static int send_packet(struct net_device *dev, struct sk_buff *skb) adapter->current_dma.start_time = jiffies; if ((unsigned long)(skb->data + nlen) >= MAX_DMA_ADDRESS || nlen != skb->len) { - memcpy(adapter->dma_buffer, skb->data, nlen); + skb_copy_from_linear_data(skb, adapter->dma_buffer, nlen); memset(adapter->dma_buffer+skb->len, 0, nlen-skb->len); target = isa_virt_to_bus(adapter->dma_buffer); } diff --git a/drivers/net/3c507.c b/drivers/net/3c507.c index 54e1d5a..eed4299 100644 --- a/drivers/net/3c507.c +++ b/drivers/net/3c507.c @@ -873,7 +873,6 @@ static void el16_rx(struct net_device *dev) } skb_reserve(skb,2); - skb->dev = dev; /* 'skb->data' points to the start of sk_buff data area. */ memcpy_fromio(skb_put(skb,pkt_len), data_frame + 10, pkt_len); diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c index f791bf0..9588da3 100644 --- a/drivers/net/3c509.c +++ b/drivers/net/3c509.c @@ -83,7 +83,6 @@ static int max_interrupt_work = 10; #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/pm.h> -#include <linux/pm_legacy.h> #include <linux/skbuff.h> #include <linux/delay.h> /* for udelay() */ #include <linux/spinlock.h> @@ -1091,7 +1090,6 @@ el3_rx(struct net_device *dev) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte */ /* 'skb->data' points to the start of sk_buff data area. */ diff --git a/drivers/net/3c515.c b/drivers/net/3c515.c index c307ce6..290166d 100644 --- a/drivers/net/3c515.c +++ b/drivers/net/3c515.c @@ -1292,7 +1292,6 @@ static int corkscrew_rx(struct net_device *dev) printk("Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ insl(ioaddr + RX_FIFO, @@ -1363,7 +1362,6 @@ static int boomerang_rx(struct net_device *dev) copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 4)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ memcpy(skb_put(skb, pkt_len), diff --git a/drivers/net/3c523.c b/drivers/net/3c523.c index 17d61eb..da1a22c 100644 --- a/drivers/net/3c523.c +++ b/drivers/net/3c523.c @@ -988,7 +988,6 @@ static void elmc_rcv_int(struct net_device *dev) rbd->status = 0; skb = (struct sk_buff *) dev_alloc_skb(totlen + 2); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte alignment */ skb_put(skb,totlen); eth_copy_and_sum(skb, (char *) p->base+(unsigned long) rbd->buffer,totlen,0); @@ -1146,7 +1145,7 @@ static int elmc_send_packet(struct sk_buff *skb, struct net_device *dev) if (len != skb->len) memset((char *) p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN); - memcpy((char *) p->xmit_cbuffs[p->xmit_count], (char *) (skb->data), skb->len); + skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len); #if (NUM_XMIT_BUFFS == 1) #ifdef NO_NOPCOMMANDS diff --git a/drivers/net/3c527.c b/drivers/net/3c527.c index 6c7437e..c7b571b 100644 --- a/drivers/net/3c527.c +++ b/drivers/net/3c527.c @@ -1189,7 +1189,6 @@ static void mc32_rx_ring(struct net_device *dev) } skb->protocol=eth_type_trans(skb,dev); - skb->dev=dev; dev->last_rx = jiffies; lp->net_stats.rx_packets++; lp->net_stats.rx_bytes += length; diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index 716a472..80924f7 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -822,11 +822,17 @@ static int vortex_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); struct vortex_private *vp = netdev_priv(dev); + int err; if (dev && vp) { pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_device(pdev); + err = pci_enable_device(pdev); + if (err) { + printk(KERN_WARNING "%s: Could not enable device \n", + dev->name); + return err; + } pci_set_master(pdev); if (request_irq(dev->irq, vp->full_bus_master_rx ? &boomerang_interrupt : &vortex_interrupt, IRQF_SHARED, dev->name, dev)) { @@ -852,19 +858,7 @@ static struct eisa_device_id vortex_eisa_ids[] = { }; MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids); -static int vortex_eisa_probe(struct device *device); -static int vortex_eisa_remove(struct device *device); - -static struct eisa_driver vortex_eisa_driver = { - .id_table = vortex_eisa_ids, - .driver = { - .name = "3c59x", - .probe = vortex_eisa_probe, - .remove = vortex_eisa_remove - } -}; - -static int vortex_eisa_probe(struct device *device) +static int __init vortex_eisa_probe(struct device *device) { void __iomem *ioaddr; struct eisa_device *edev; @@ -887,7 +881,7 @@ static int vortex_eisa_probe(struct device *device) return 0; } -static int vortex_eisa_remove(struct device *device) +static int __devexit vortex_eisa_remove(struct device *device) { struct eisa_device *edev; struct net_device *dev; @@ -912,7 +906,17 @@ static int vortex_eisa_remove(struct device *device) free_netdev(dev); return 0; } -#endif + +static struct eisa_driver vortex_eisa_driver = { + .id_table = vortex_eisa_ids, + .driver = { + .name = "3c59x", + .probe = vortex_eisa_probe, + .remove = __devexit_p(vortex_eisa_remove) + } +}; + +#endif /* CONFIG_EISA */ /* returns count found (>= 0), or negative on error */ static int __init vortex_eisa_init(void) @@ -2410,7 +2414,6 @@ static int vortex_rx(struct net_device *dev) printk(KERN_DEBUG "Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ if (vp->bus_master && @@ -2487,7 +2490,6 @@ boomerang_rx(struct net_device *dev) /* Check if the packet is long enough to just accept without copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ pci_dma_sync_single_for_cpu(VORTEX_PCI(vp), dma, PKT_BUF_SZ, PCI_DMA_FROMDEVICE); /* 'skb_put()' points to the start of sk_buff data area. */ diff --git a/drivers/net/7990.c b/drivers/net/7990.c index 1b3d11e..d396f99 100644 --- a/drivers/net/7990.c +++ b/drivers/net/7990.c @@ -331,7 +331,6 @@ static int lance_rx (struct net_device *dev) return 0; } - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put (skb, len); /* make room */ eth_copy_and_sum(skb, @@ -568,7 +567,7 @@ int lance_start_xmit (struct sk_buff *skb, struct net_device *dev) if (skb->len < ETH_ZLEN) memset((char *)&ib->tx_buf[entry][0], 0, ETH_ZLEN); - memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf[entry][0], skblen); /* Now, give the packet to the lance */ ib->btx_ring [entry].tmd1_bits = (LE_T1_POK|LE_T1_OWN); diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 6f93a76..e8c9f278 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -448,8 +448,7 @@ static void cp_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) spin_lock_irqsave(&cp->lock, flags); cp->cpcmd &= ~RxVlanOn; cpw16(CpCmd, cp->cpcmd); - if (cp->vlgrp) - cp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(cp->vlgrp, vid, NULL); spin_unlock_irqrestore(&cp->lock, flags); } #endif /* CP_VLAN_TAG_USED */ @@ -574,7 +573,6 @@ rx_status_loop: } skb_reserve(new_skb, RX_OFFSET); - new_skb->dev = dev; pci_unmap_single(cp->pdev, mapping, buflen, PCI_DMA_FROMDEVICE); @@ -808,7 +806,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev) if (mss) flags |= LargeSend | ((mss & MSSMask) << MSSShift); else if (skb->ip_summed == CHECKSUM_PARTIAL) { - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) flags |= IPCS | TCPCS; else if (ip->protocol == IPPROTO_UDP) @@ -827,7 +825,7 @@ static int cp_start_xmit (struct sk_buff *skb, struct net_device *dev) u32 first_len, first_eor; dma_addr_t first_mapping; int frag, first_entry = entry; - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); /* We must give this initial chunk to the device last. * Otherwise we could race with the device. @@ -1083,7 +1081,6 @@ static int cp_refill_rx (struct cp_private *cp) if (!skb) goto err_out; - skb->dev = cp->dev; skb_reserve(skb, RX_OFFSET); mapping = pci_map_single(cp->pdev, skb->data, cp->rx_buf_sz, diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c index 35ad5cf..a844b1f 100644 --- a/drivers/net/8139too.c +++ b/drivers/net/8139too.c @@ -1109,6 +1109,8 @@ static void __devexit rtl8139_remove_one (struct pci_dev *pdev) assert (dev != NULL); + flush_scheduled_work(); + unregister_netdev (dev); __rtl8139_cleanup_dev (dev); @@ -1603,18 +1605,21 @@ static void rtl8139_thread (struct work_struct *work) struct net_device *dev = tp->mii.dev; unsigned long thr_delay = next_tick; + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + if (tp->watchdog_fired) { tp->watchdog_fired = 0; rtl8139_tx_timeout_task(work); - } else if (rtnl_trylock()) { - rtl8139_thread_iter (dev, tp, tp->mmio_addr); - rtnl_unlock (); - } else { - /* unlikely race. mitigate with fast poll. */ - thr_delay = HZ / 2; - } + } else + rtl8139_thread_iter(dev, tp, tp->mmio_addr); - schedule_delayed_work(&tp->thread, thr_delay); + if (tp->have_thread) + schedule_delayed_work(&tp->thread, thr_delay); +out_unlock: + rtnl_unlock (); } static void rtl8139_start_thread(struct rtl8139_private *tp) @@ -1626,19 +1631,11 @@ static void rtl8139_start_thread(struct rtl8139_private *tp) return; tp->have_thread = 1; + tp->watchdog_fired = 0; schedule_delayed_work(&tp->thread, next_tick); } -static void rtl8139_stop_thread(struct rtl8139_private *tp) -{ - if (tp->have_thread) { - cancel_rearming_delayed_work(&tp->thread); - tp->have_thread = 0; - } else - flush_scheduled_work(); -} - static inline void rtl8139_tx_clear (struct rtl8139_private *tp) { tp->cur_tx = 0; @@ -1696,12 +1693,11 @@ static void rtl8139_tx_timeout (struct net_device *dev) { struct rtl8139_private *tp = netdev_priv(dev); + tp->watchdog_fired = 1; if (!tp->have_thread) { - INIT_DELAYED_WORK(&tp->thread, rtl8139_tx_timeout_task); + INIT_DELAYED_WORK(&tp->thread, rtl8139_thread); schedule_delayed_work(&tp->thread, next_tick); - } else - tp->watchdog_fired = 1; - + } } static int rtl8139_start_xmit (struct sk_buff *skb, struct net_device *dev) @@ -1908,10 +1904,10 @@ static __inline__ void wrap_copy(struct sk_buff *skb, const unsigned char *ring, u32 left = RX_BUF_LEN - offset; if (size > left) { - memcpy(skb->data, ring + offset, left); - memcpy(skb->data+left, ring, size - left); + skb_copy_to_linear_data(skb, ring + offset, left); + skb_copy_to_linear_data_offset(skb, left, ring, size - left); } else - memcpy(skb->data, ring + offset, size); + skb_copy_to_linear_data(skb, ring + offset, size); } #endif @@ -2017,7 +2013,6 @@ no_early_rx: skb = dev_alloc_skb (pkt_size + 2); if (likely(skb)) { - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align the IP fields. */ #if RX_BUF_IDX == 3 wrap_copy(skb, rx_ring, ring_offset+4, pkt_size); @@ -2233,8 +2228,6 @@ static int rtl8139_close (struct net_device *dev) netif_stop_queue (dev); - rtl8139_stop_thread(tp); - if (netif_msg_ifdown(tp)) printk(KERN_DEBUG "%s: Shutting down ethercard, status was 0x%4.4x.\n", dev->name, RTL_R16 (IntrStatus)); diff --git a/drivers/net/82596.c b/drivers/net/82596.c index 640d7ca2e..3ff1155 100644 --- a/drivers/net/82596.c +++ b/drivers/net/82596.c @@ -830,7 +830,6 @@ memory_squeeze: lp->stats.rx_dropped++; } else { - skb->dev = dev; if (!rx_in_place) { /* 16 byte align the data fields */ skb_reserve(skb, 2); diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 38f41a5..dcdad21 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -430,10 +430,10 @@ config HPLANCE config LASI_82596 tristate "Lasi ethernet" - depends on NET_ETHERNET && PARISC && GSC_LASI + depends on NET_ETHERNET && GSC help - Say Y here to support the on-board Intel 82596 ethernet controller - built into Hewlett-Packard PA-RISC machines. + Say Y here to support the builtin Intel 82596 ethernet controller + found in Hewlett-Packard PA-RISC machines with 10Mbit ethernet. config MIPS_JAZZ_SONIC tristate "MIPS JAZZ onboard SONIC Ethernet support" @@ -486,8 +486,8 @@ config SGI_IOC3_ETH_HW_TX_CSUM enables offloading for checksums on transmit. If unsure, say Y. config MIPS_SIM_NET - tristate "MIPS simulator Network device (EXPERIMENTAL)" - depends on MIPS_SIM && EXPERIMENTAL + tristate "MIPS simulator Network device" + depends on NET_ETHERNET && MIPS_SIM help The MIPSNET device is a simple Ethernet network device which is emulated by the MIPS Simulator. @@ -1284,8 +1284,8 @@ config PCNET32 will be called pcnet32. config PCNET32_NAPI - bool "Use RX polling (NAPI) (EXPERIMENTAL)" - depends on PCNET32 && EXPERIMENTAL + bool "Use RX polling (NAPI)" + depends on PCNET32 help NAPI is a new driver API designed to reduce CPU and interrupt load when the driver is receiving lots of packets from the card. It is @@ -1444,7 +1444,8 @@ config CS89x0 config TC35815 tristate "TOSHIBA TC35815 Ethernet support" - depends on NET_PCI && PCI && TOSHIBA_JMR3927 + depends on NET_PCI && PCI && MIPS + select MII config DGRS tristate "Digi Intl. RightSwitch SE-X support" @@ -2125,14 +2126,16 @@ config SKY2 will be called sky2. This is recommended. config SK98LIN - tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support" + tristate "Marvell Yukon Chipset / SysKonnect SK-98xx Support (DEPRECATED)" depends on PCI ---help--- Say Y here if you have a Marvell Yukon or SysKonnect SK-98xx/SK-95xx compliant Gigabit Ethernet Adapter. - This driver supports the original Yukon chipset. A cleaner driver is - also available (skge) which seems to work better than this one. + This driver supports the original Yukon chipset. This driver is + deprecated and will be removed from the kernel in the near future, + it has been replaced by the skge driver. skge is cleaner and + seems to work better. This driver does not support the newer Yukon2 chipset. A separate driver, sky2, is provided to support Yukon2-based adapters. @@ -2243,7 +2246,7 @@ config BNX2 config SPIDER_NET tristate "Spider Gigabit Ethernet driver" - depends on PCI && PPC_IBM_CELL_BLADE + depends on PCI && (PPC_IBM_CELL_BLADE || PPC_CELLEB) select FW_LOADER help This driver supports the Gigabit Ethernet chips present on the @@ -2261,6 +2264,7 @@ config GIANFAR tristate "Gianfar Ethernet" depends on 85xx || 83xx || PPC_86xx select PHYLIB + select CRC32 help This driver supports the Gigabit TSEC on the MPC83xx, MPC85xx, and MPC86xx family of chips, and the FEC on the 8540. @@ -2270,11 +2274,12 @@ config GFAR_NAPI depends on GIANFAR config UCC_GETH - tristate "Freescale QE UCC GETH" - depends on QUICC_ENGINE && UCC_FAST + tristate "Freescale QE Gigabit Ethernet" + depends on QUICC_ENGINE + select UCC_FAST help - This driver supports the Gigabit Ethernet mode of QE UCC. - QE can be found on MPC836x CPUs. + This driver supports the Gigabit Ethernet mode of the QUICC Engine, + which is available on some Freescale SOCs. config UGETH_NAPI bool "NAPI Support" @@ -2288,14 +2293,10 @@ config UGETH_FILTERING bool "Mac address filtering support" depends on UCC_GETH -config UGETH_TX_ON_DEMOND - bool "Transmit on Demond support" +config UGETH_TX_ON_DEMAND + bool "Transmit on Demand support" depends on UCC_GETH -config UGETH_HAS_GIGA - bool - depends on UCC_GETH && PPC_MPC836x - config MV643XX_ETH tristate "MV-643XX Ethernet support" depends on MOMENCO_OCELOT_C || MOMENCO_JAGUAR_ATX || MV64360 || MOMENCO_OCELOT_3 || (PPC_MULTIPLATFORM && PPC32) @@ -2305,27 +2306,6 @@ config MV643XX_ETH chipset which is used in the Momenco Ocelot C and Jaguar ATX and Pegasos II, amongst other PPC and MIPS boards. -config MV643XX_ETH_0 - bool "MV-643XX Port 0" - depends on MV643XX_ETH - help - This enables support for Port 0 of the Marvell MV643XX Gigabit - Ethernet. - -config MV643XX_ETH_1 - bool "MV-643XX Port 1" - depends on MV643XX_ETH - help - This enables support for Port 1 of the Marvell MV643XX Gigabit - Ethernet. - -config MV643XX_ETH_2 - bool "MV-643XX Port 2" - depends on MV643XX_ETH - help - This enables support for Port 2 of the Marvell MV643XX Gigabit - Ethernet. - config QLA3XXX tristate "QLogic QLA3XXX Network Driver Support" depends on PCI @@ -2337,7 +2317,7 @@ config QLA3XXX config ATL1 tristate "Attansic L1 Gigabit Ethernet support (EXPERIMENTAL)" - depends on NET_PCI && PCI && EXPERIMENTAL + depends on PCI && EXPERIMENTAL select CRC32 select MII help @@ -2391,22 +2371,23 @@ config CHELSIO_T1_NAPI when the driver is receiving lots of packets from the card. config CHELSIO_T3 - tristate "Chelsio Communications T3 10Gb Ethernet support" - depends on PCI - help - This driver supports Chelsio T3-based gigabit and 10Gb Ethernet - adapters. + tristate "Chelsio Communications T3 10Gb Ethernet support" + depends on PCI + select FW_LOADER + help + This driver supports Chelsio T3-based gigabit and 10Gb Ethernet + adapters. - For general information about Chelsio and our products, visit - our website at <http://www.chelsio.com>. + For general information about Chelsio and our products, visit + our website at <http://www.chelsio.com>. - For customer support, please visit our customer support page at - <http://www.chelsio.com/support.htm>. + For customer support, please visit our customer support page at + <http://www.chelsio.com/support.htm>. - Please send feedback to <linux-bugs@chelsio.com>. + Please send feedback to <linux-bugs@chelsio.com>. - To compile this driver as a module, choose M here: the module - will be called cxgb3. + To compile this driver as a module, choose M here: the module + will be called cxgb3. config EHEA tristate "eHEA Ethernet support" @@ -2946,11 +2927,6 @@ endif #NETDEVICES config NETPOLL def_bool NETCONSOLE -config NETPOLL_RX - bool "Netpoll support for trapping incoming packets" - default n - depends on NETPOLL - config NETPOLL_TRAP bool "Netpoll traffic trapping" default n diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 33af833..59c0459 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -18,7 +18,7 @@ gianfar_driver-objs := gianfar.o \ gianfar_sysfs.o obj-$(CONFIG_UCC_GETH) += ucc_geth_driver.o -ucc_geth_driver-objs := ucc_geth.o ucc_geth_phy.o +ucc_geth_driver-objs := ucc_geth.o ucc_geth_mii.o # # link order important here @@ -206,7 +206,7 @@ obj-$(CONFIG_TR) += tokenring/ obj-$(CONFIG_WAN) += wan/ obj-$(CONFIG_ARCNET) += arcnet/ obj-$(CONFIG_NET_PCMCIA) += pcmcia/ -obj-$(CONFIG_NET_RADIO) += wireless/ +obj-y += wireless/ obj-$(CONFIG_NET_TULIP) += tulip/ obj-$(CONFIG_HAMRADIO) += hamradio/ obj-$(CONFIG_IRDA) += irda/ diff --git a/drivers/net/a2065.c b/drivers/net/a2065.c index d76548e..1226cbb 100644 --- a/drivers/net/a2065.c +++ b/drivers/net/a2065.c @@ -320,7 +320,6 @@ static int lance_rx (struct net_device *dev) return 0; } - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put (skb, len); /* make room */ eth_copy_and_sum(skb, @@ -599,7 +598,7 @@ static int lance_start_xmit (struct sk_buff *skb, struct net_device *dev) ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; - memcpy ((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen); /* Clear the slack of the packet, do I need this? */ if (len != skblen) diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 33c6645..7122b7b 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -2027,7 +2027,6 @@ static void ace_rx_int(struct net_device *dev, u32 rxretprd, u32 rxretcsm) */ csum = retdesc->tcp_udp_csum; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* @@ -2293,10 +2292,7 @@ static void ace_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) local_irq_save(flags); ace_mask_irq(dev); - - if (ap->vlgrp) - ap->vlgrp->vlan_devices[vid] = NULL; - + vlan_group_set_device(ap->vlgrp, vid, NULL); ace_unmask_irq(dev); local_irq_restore(flags); } diff --git a/drivers/net/amd8111e.c b/drivers/net/amd8111e.c index 9c399aa..675fe91 100644 --- a/drivers/net/amd8111e.c +++ b/drivers/net/amd8111e.c @@ -798,9 +798,7 @@ static int amd8111e_rx_poll(struct net_device *dev, int * budget) pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index], lp->rx_buff_len-2, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); - skb->dev = dev; lp->rx_skbuff[rx_index] = new_skb; - new_skb->dev = dev; lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev, new_skb->data, lp->rx_buff_len-2, @@ -926,9 +924,7 @@ static int amd8111e_rx(struct net_device *dev) pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index], lp->rx_buff_len-2, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); - skb->dev = dev; lp->rx_skbuff[rx_index] = new_skb; - new_skb->dev = dev; lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev, new_skb->data, lp->rx_buff_len-2,PCI_DMA_FROMDEVICE); @@ -1737,8 +1733,7 @@ static void amd8111e_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid { struct amd8111e_priv *lp = netdev_priv(dev); spin_lock_irq(&lp->lock); - if (lp->vlgrp) - lp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(lp->vlgrp, vid, NULL); spin_unlock_irq(&lp->lock); } #endif diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c index dba5e51..da6ffa8 100644 --- a/drivers/net/appletalk/cops.c +++ b/drivers/net/appletalk/cops.c @@ -853,9 +853,9 @@ static void cops_rx(struct net_device *dev) return; } - skb->mac.raw = skb->data; /* Point to entire packet. */ + skb_reset_mac_header(skb); /* Point to entire packet. */ skb_pull(skb,3); - skb->h.raw = skb->data; /* Point to data (Skip header). */ + skb_reset_transport_header(skb); /* Point to data (Skip header). */ /* Update the counters. */ lp->stats.rx_packets++; diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c index 2ea44ce..6a6cbd3 100644 --- a/drivers/net/appletalk/ltpc.c +++ b/drivers/net/appletalk/ltpc.c @@ -770,13 +770,13 @@ static int sendup_buffer (struct net_device *dev) skb->data[0] = dnode; skb->data[1] = snode; skb->data[2] = llaptype; - skb->mac.raw = skb->data; /* save pointer to llap header */ + skb_reset_mac_header(skb); /* save pointer to llap header */ skb_pull(skb,3); /* copy ddp(s,e)hdr + contents */ - memcpy(skb->data,(void*)ltdmabuf,len); + skb_copy_to_linear_data(skb, ltdmabuf, len); - skb->h.raw = skb->data; + skb_reset_transport_header(skb); stats->rx_packets++; stats->rx_bytes+=skb->len; @@ -917,13 +917,14 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev) int i; struct lt_sendlap cbuf; + unsigned char *hdr; cbuf.command = LT_SENDLAP; cbuf.dnode = skb->data[0]; cbuf.laptype = skb->data[2]; skb_pull(skb,3); /* skip past LLAP header */ cbuf.length = skb->len; /* this is host order */ - skb->h.raw=skb->data; + skb_reset_transport_header(skb); if(debug & DEBUG_UPPER) { printk("command "); @@ -932,11 +933,13 @@ static int ltpc_xmit(struct sk_buff *skb, struct net_device *dev) printk("\n"); } - do_write(dev,&cbuf,sizeof(cbuf),skb->h.raw,skb->len); + hdr = skb_transport_header(skb); + do_write(dev, &cbuf, sizeof(cbuf), hdr, skb->len); if(debug & DEBUG_UPPER) { printk("sent %d ddp bytes\n",skb->len); - for(i=0;i<skb->len;i++) printk("%02x ",skb->h.raw[i]); + for (i = 0; i < skb->len; i++) + printk("%02x ", hdr[i]); printk("\n"); } diff --git a/drivers/net/arcnet/arc-rawmode.c b/drivers/net/arcnet/arc-rawmode.c index e7555d4..e0a18e7 100644 --- a/drivers/net/arcnet/arc-rawmode.c +++ b/drivers/net/arcnet/arc-rawmode.c @@ -94,7 +94,7 @@ static void rx(struct net_device *dev, int bufnum, BUGMSG(D_DURING, "it's a raw packet (length=%d)\n", length); - if (length >= MinTU) + if (length > MTU) ofs = 512 - length; else ofs = 256 - length; @@ -110,7 +110,7 @@ static void rx(struct net_device *dev, int bufnum, pkt = (struct archdr *) skb->data; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); /* up to sizeof(pkt->soft) has already been copied from the card */ @@ -183,7 +183,7 @@ static int prepare_tx(struct net_device *dev, struct archdr *pkt, int length, length, XMTU); length = XMTU; } - if (length > MinTU) { + if (length >= MinTU) { hard->offset[0] = 0; hard->offset[1] = ofs = 512 - length; } else if (length > MTU) { diff --git a/drivers/net/arcnet/arcnet.c b/drivers/net/arcnet/arcnet.c index 4e91dab..681e20b 100644 --- a/drivers/net/arcnet/arcnet.c +++ b/drivers/net/arcnet/arcnet.c @@ -41,7 +41,7 @@ * <jojo@repas.de> */ -#define VERSION "arcnet: v3.93 BETA 2000/04/29 - by Avery Pennarun et al.\n" +#define VERSION "arcnet: v3.94 BETA 2007/02/08 - by Avery Pennarun et al.\n" #include <linux/module.h> #include <linux/types.h> @@ -519,9 +519,12 @@ static int arcnet_header(struct sk_buff *skb, struct net_device *dev, * real header when we do rebuild_header. */ *(uint16_t *) skb_push(skb, 2) = type; - if (skb->nh.raw - skb->mac.raw != 2) + /* + * XXX: Why not use skb->mac_len? + */ + if (skb->network_header - skb->mac_header != 2) BUGMSG(D_NORMAL, "arcnet_header: Yikes! diff (%d) is not 2!\n", - (int)(skb->nh.raw - skb->mac.raw)); + (int)(skb->network_header - skb->mac_header)); return -2; /* return error -- can't transmit yet! */ } else { @@ -554,11 +557,13 @@ static int arcnet_rebuild_header(struct sk_buff *skb) unsigned short type; uint8_t daddr=0; struct ArcProto *proto; - - if (skb->nh.raw - skb->mac.raw != 2) { + /* + * XXX: Why not use skb->mac_len? + */ + if (skb->network_header - skb->mac_header != 2) { BUGMSG(D_NORMAL, - "rebuild_header: shouldn't be here! (hdrsize=%d)\n", - (int)(skb->nh.raw - skb->mac.raw)); + "rebuild_header: shouldn't be here! (hdrsize=%d)\n", + (int)(skb->network_header - skb->mac_header)); return 0; } type = *(uint16_t *) skb_pull(skb, 2); diff --git a/drivers/net/arcnet/capmode.c b/drivers/net/arcnet/capmode.c index 6648558..cc4610d 100644 --- a/drivers/net/arcnet/capmode.c +++ b/drivers/net/arcnet/capmode.c @@ -122,10 +122,8 @@ static void rx(struct net_device *dev, int bufnum, } skb_put(skb, length + ARC_HDR_SIZE + sizeof(int)); skb->dev = dev; - - pkt = (struct archdr *) skb->data; - - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); + pkt = (struct archdr *)skb_mac_header(skb); skb_pull(skb, ARC_HDR_SIZE); /* up to sizeof(pkt->soft) has already been copied from the card */ @@ -270,13 +268,13 @@ static int ack_tx(struct net_device *dev, int acked) skb_put(ackskb, length + ARC_HDR_SIZE ); ackskb->dev = dev; - ackpkt = (struct archdr *) ackskb->data; - - ackskb->mac.raw = ackskb->data; + skb_reset_mac_header(ackskb); + ackpkt = (struct archdr *)skb_mac_header(ackskb); /* skb_pull(ackskb, ARC_HDR_SIZE); */ - memcpy(ackpkt, lp->outgoing.skb->data, ARC_HDR_SIZE+sizeof(struct arc_cap)); + skb_copy_from_linear_data(lp->outgoing.skb, ackpkt, + ARC_HDR_SIZE + sizeof(struct arc_cap)); ackpkt->soft.cap.proto=0; /* using protocol 0 for acknowledge */ ackpkt->soft.cap.mes.ack=acked; diff --git a/drivers/net/arcnet/com20020-pci.c b/drivers/net/arcnet/com20020-pci.c index 98d326b..b8c0fa6 100644 --- a/drivers/net/arcnet/com20020-pci.c +++ b/drivers/net/arcnet/com20020-pci.c @@ -155,6 +155,7 @@ static struct pci_device_id com20020pci_id_table[] = { { 0x1571, 0xa00b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT }, { 0x1571, 0xa00c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT }, { 0x1571, 0xa00d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT }, + { 0x1571, 0xa00e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_IS_5MBIT }, { 0x1571, 0xa201, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, { 0x1571, 0xa202, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, { 0x1571, 0xa203, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, @@ -163,6 +164,8 @@ static struct pci_device_id com20020pci_id_table[] = { { 0x1571, 0xa206, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, { 0x10B5, 0x9030, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, { 0x10B5, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, + { 0x14BA, 0x6000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, + { 0x10B5, 0x2200, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT }, {0,} }; diff --git a/drivers/net/arcnet/com20020.c b/drivers/net/arcnet/com20020.c index 4218075..7cf0a25 100644 --- a/drivers/net/arcnet/com20020.c +++ b/drivers/net/arcnet/com20020.c @@ -104,7 +104,7 @@ int com20020_check(struct net_device *dev) SET_SUBADR(SUB_SETUP1); outb(lp->setup, _XREG); - if (lp->card_flags & ARC_CAN_10MBIT) + if (lp->clockm != 0) { SET_SUBADR(SUB_SETUP2); outb(lp->setup2, _XREG); diff --git a/drivers/net/arcnet/rfc1051.c b/drivers/net/arcnet/rfc1051.c index 6d6c69f..2de8877 100644 --- a/drivers/net/arcnet/rfc1051.c +++ b/drivers/net/arcnet/rfc1051.c @@ -94,7 +94,7 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev) int hdr_size = ARC_HDR_SIZE + RFC1051_HDR_SIZE; /* Pull off the arcnet header. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdr_size); if (pkt->hard.dest == 0) diff --git a/drivers/net/arcnet/rfc1201.c b/drivers/net/arcnet/rfc1201.c index bee3422..460a095 100644 --- a/drivers/net/arcnet/rfc1201.c +++ b/drivers/net/arcnet/rfc1201.c @@ -96,7 +96,7 @@ static unsigned short type_trans(struct sk_buff *skb, struct net_device *dev) int hdr_size = ARC_HDR_SIZE + RFC1201_HDR_SIZE; /* Pull off the arcnet header. */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdr_size); if (pkt->hard.dest == 0) diff --git a/drivers/net/ariadne.c b/drivers/net/ariadne.c index 9dfc09b..a0e68e7 100644 --- a/drivers/net/ariadne.c +++ b/drivers/net/ariadne.c @@ -743,7 +743,6 @@ static int ariadne_rx(struct net_device *dev) } - skb->dev = dev; skb_reserve(skb,2); /* 16 byte align */ skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, (char *)priv->rx_buff[entry], pkt_len,0); diff --git a/drivers/net/arm/am79c961a.c b/drivers/net/arm/am79c961a.c index ddd12d4..8f0d7ce 100644 --- a/drivers/net/arm/am79c961a.c +++ b/drivers/net/arm/am79c961a.c @@ -526,7 +526,6 @@ am79c961_rx(struct net_device *dev, struct dev_priv *priv) skb = dev_alloc_skb(len + 2); if (skb) { - skb->dev = dev; skb_reserve(skb, 2); am_readbuffer(dev, pktaddr, skb_put(skb, len), len); diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c index 1621b8f..152fa7a 100644 --- a/drivers/net/arm/at91_ether.c +++ b/drivers/net/arm/at91_ether.c @@ -858,7 +858,6 @@ static void at91ether_rx(struct net_device *dev) skb_reserve(skb, 2); memcpy(skb_put(skb, pktlen), p_recv, pktlen); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; lp->stats.rx_bytes += pktlen; diff --git a/drivers/net/arm/ep93xx_eth.c b/drivers/net/arm/ep93xx_eth.c index dd698b0..2438c5b 100644 --- a/drivers/net/arm/ep93xx_eth.c +++ b/drivers/net/arm/ep93xx_eth.c @@ -255,7 +255,6 @@ static int ep93xx_rx(struct net_device *dev, int *budget) skb = dev_alloc_skb(length + 2); if (likely(skb != NULL)) { - skb->dev = dev; skb_reserve(skb, 2); dma_sync_single(NULL, ep->descs->rdesc[entry].buf_addr, length, DMA_FROM_DEVICE); diff --git a/drivers/net/arm/ether1.c b/drivers/net/arm/ether1.c index a292188..f075ceb 100644 --- a/drivers/net/arm/ether1.c +++ b/drivers/net/arm/ether1.c @@ -875,7 +875,6 @@ ether1_recv_done (struct net_device *dev) skb = dev_alloc_skb (length + 2); if (skb) { - skb->dev = dev; skb_reserve (skb, 2); ether1_readbuffer (dev, skb_put (skb, length), rbd.rbd_bufl, length); diff --git a/drivers/net/arm/ether3.c b/drivers/net/arm/ether3.c index 8411783..32da2eb 100644 --- a/drivers/net/arm/ether3.c +++ b/drivers/net/arm/ether3.c @@ -661,7 +661,6 @@ if (next_ptr < RX_START || next_ptr >= RX_END) { if (skb) { unsigned char *buf; - skb->dev = dev; skb_reserve(skb, 2); buf = skb_put(skb, length); ether3_readbuffer(dev, buf + 12, length - 12); diff --git a/drivers/net/at1700.c b/drivers/net/at1700.c index 56ae8ba..bed8e0eb 100644 --- a/drivers/net/at1700.c +++ b/drivers/net/at1700.c @@ -768,7 +768,6 @@ net_rx(struct net_device *dev) lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb,2); insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1); diff --git a/drivers/net/atari_bionet.c b/drivers/net/atari_bionet.c index 4e3bf6a..3d87bd2 100644 --- a/drivers/net/atari_bionet.c +++ b/drivers/net/atari_bionet.c @@ -453,7 +453,8 @@ bionet_send_packet(struct sk_buff *skb, struct net_device *dev) { stdma_lock(bionet_intr, NULL); local_irq_restore(flags); if( !STRAM_ADDR(buf+length-1) ) { - memcpy(nic_packet->buffer, skb->data, length); + skb_copy_from_linear_data(skb, nic_packet->buffer, + length); buf = (unsigned long)&((struct nic_pkt_s *)phys_nic_packet)->buffer; } @@ -544,13 +545,13 @@ bionet_poll_rx(struct net_device *dev) { break; } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 Byte align */ skb_put( skb, pkt_len ); /* make room */ /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(skb->data, nic_packet->buffer, pkt_len); + skb_copy_to_linear_data(skb, nic_packet->buffer, + pkt_len); skb->protocol = eth_type_trans( skb, dev ); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/atari_pamsnet.c b/drivers/net/atari_pamsnet.c index 3b54361..5471440 100644 --- a/drivers/net/atari_pamsnet.c +++ b/drivers/net/atari_pamsnet.c @@ -717,7 +717,8 @@ pamsnet_send_packet(struct sk_buff *skb, struct net_device *dev) { local_irq_restore(flags); if( !STRAM_ADDR(buf+length-1) ) { - memcpy(nic_packet->buffer, skb->data, length); + skb_copy_from_linear_data(skb, nic_packet->buffer, + length); buf = (unsigned long)phys_nic_packet; } @@ -792,7 +793,8 @@ pamsnet_poll_rx(struct net_device *dev) { /* 'skb->data' points to the start of sk_buff data area. */ - memcpy(skb->data, nic_packet->buffer, pkt_len); + skb_copy_to_linear_data(skb, nic_packet->buffer, + pkt_len); netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; diff --git a/drivers/net/atarilance.c b/drivers/net/atarilance.c index 7e37ac8..dfa8b9b 100644 --- a/drivers/net/atarilance.c +++ b/drivers/net/atarilance.c @@ -1047,7 +1047,6 @@ static int lance_rx( struct net_device *dev ) pkt_len ); } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 byte align */ skb_put( skb, pkt_len ); /* Make room */ lp->memcpy_f( skb->data, PKTBUF_ADDR(head), pkt_len ); diff --git a/drivers/net/atl1/atl1_hw.c b/drivers/net/atl1/atl1_hw.c index 08b2d78..69482e0 100644 --- a/drivers/net/atl1/atl1_hw.c +++ b/drivers/net/atl1/atl1_hw.c @@ -243,14 +243,8 @@ static int atl1_get_permanent_address(struct atl1_hw *hw) i += 4; } -/* - * The following 2 lines are the Attansic originals. Saving for posterity. - * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]); - * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]); - */ - *(u32 *) & eth_addr[2] = swab32(addr[0]); - *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]); - + *(u32 *) ð_addr[2] = swab32(addr[0]); + *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; @@ -281,17 +275,28 @@ static int atl1_get_permanent_address(struct atl1_hw *hw) i += 4; } -/* - * The following 2 lines are the Attansic originals. Saving for posterity. - * *(u32 *) & eth_addr[2] = LONGSWAP(addr[0]); - * *(u16 *) & eth_addr[0] = SHORTSWAP(*(u16 *) & addr[1]); - */ - *(u32 *) & eth_addr[2] = swab32(addr[0]); - *(u16 *) & eth_addr[0] = swab16(*(u16 *) & addr[1]); + *(u32 *) ð_addr[2] = swab32(addr[0]); + *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); if (is_valid_ether_addr(eth_addr)) { memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); return 0; } + + /* + * On some motherboards, the MAC address is written by the + * BIOS directly to the MAC register during POST, and is + * not stored in eeprom. If all else thus far has failed + * to fetch the permanent MAC address, try reading it directly. + */ + addr[0] = ioread32(hw->hw_addr + REG_MAC_STA_ADDR); + addr[1] = ioread16(hw->hw_addr + (REG_MAC_STA_ADDR + 4)); + *(u32 *) ð_addr[2] = swab32(addr[0]); + *(u16 *) ð_addr[0] = swab16(*(u16 *) &addr[1]); + if (is_valid_ether_addr(eth_addr)) { + memcpy(hw->perm_mac_addr, eth_addr, ETH_ALEN); + return 0; + } + return 1; } @@ -329,7 +334,6 @@ u32 atl1_hash_mc_addr(struct atl1_hw *hw, u8 *mc_addr) int i; crc32 = ether_crc_le(6, mc_addr); - crc32 = ~crc32; for (i = 0; i < 32; i++) value |= (((crc32 >> i) & 1) << (31 - i)); @@ -357,7 +361,7 @@ void atl1_hash_set(struct atl1_hw *hw, u32 hash_value) */ hash_reg = (hash_value >> 31) & 0x1; hash_bit = (hash_value >> 26) & 0x1F; - mta = ioread32((hw + REG_RX_HASH_TABLE) + (hash_reg << 2)); + mta = ioread32((hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); mta |= (1 << hash_bit); iowrite32(mta, (hw->hw_addr + REG_RX_HASH_TABLE) + (hash_reg << 2)); } diff --git a/drivers/net/atl1/atl1_main.c b/drivers/net/atl1/atl1_main.c index 6655640..4b1d4d1 100644 --- a/drivers/net/atl1/atl1_main.c +++ b/drivers/net/atl1/atl1_main.c @@ -82,8 +82,7 @@ #include "atl1.h" -#define RUN_REALTIME 0 -#define DRIVER_VERSION "2.0.6" +#define DRIVER_VERSION "2.0.7" char atl1_driver_name[] = "atl1"; static const char atl1_driver_string[] = "Attansic L1 Ethernet Network Driver"; @@ -100,7 +99,7 @@ MODULE_VERSION(DRIVER_VERSION); * atl1_pci_tbl - PCI Device ID Table */ static const struct pci_device_id atl1_pci_tbl[] = { - {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, 0x1048)}, + {PCI_DEVICE(PCI_VENDOR_ID_ATTANSIC, PCI_DEVICE_ID_ATTANSIC_L1)}, /* required last entry */ {0,} }; @@ -409,7 +408,6 @@ static void atl1_rx_checksum(struct atl1_adapter *adapter, static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) { struct atl1_rfd_ring *rfd_ring = &adapter->rfd_ring; - struct net_device *netdev = adapter->netdev; struct pci_dev *pdev = adapter->pdev; struct page *page; unsigned long offset; @@ -445,7 +443,6 @@ static u16 atl1_alloc_rx_buffers(struct atl1_adapter *adapter) * the 14 byte MAC header is removed */ skb_reserve(skb, NET_IP_ALIGN); - skb->dev = netdev; buffer_info->alloced = 1; buffer_info->skb = skb; @@ -1253,8 +1250,7 @@ static void atl1_vlan_rx_kill_vid(struct net_device *netdev, u16 vid) spin_lock_irqsave(&adapter->lock, flags); /* atl1_irq_disable(adapter); */ - if (adapter->vlgrp) - adapter->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(adapter->vlgrp, vid, NULL); /* atl1_irq_enable(adapter); */ spin_unlock_irqrestore(&adapter->lock, flags); /* We don't do Vlan filtering */ @@ -1267,7 +1263,7 @@ static void atl1_restore_vlan(struct atl1_adapter *adapter) if (adapter->vlgrp) { u16 vid; for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) { - if (!adapter->vlgrp->vlan_devices[vid]) + if (!vlan_group_get_device(adapter->vlgrp, vid)) continue; atl1_vlan_rx_add_vid(adapter->netdev, vid); } @@ -1298,19 +1294,21 @@ static int atl1_tso(struct atl1_adapter *adapter, struct sk_buff *skb, } if (skb->protocol == ntohs(ETH_P_IP)) { - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, 0, - IPPROTO_TCP, 0); - ipofst = skb->nh.raw - skb->data; + struct iphdr *iph = ip_hdr(skb); + + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); + ipofst = skb_network_offset(skb); if (ipofst != ENET_HEADER_SIZE) /* 802.3 frame */ tso->tsopl |= 1 << TSO_PARAM_ETHTYPE_SHIFT; - tso->tsopl |= (skb->nh.iph->ihl & + tso->tsopl |= (iph->ihl & CSUM_PARAM_IPHL_MASK) << CSUM_PARAM_IPHL_SHIFT; - tso->tsopl |= ((skb->h.th->doff << 2) & + tso->tsopl |= (tcp_hdrlen(skb) & TSO_PARAM_TCPHDRLEN_MASK) << TSO_PARAM_TCPHDRLEN_SHIFT; tso->tsopl |= (skb_shinfo(skb)->gso_size & TSO_PARAM_MSS_MASK) << TSO_PARAM_MSS_SHIFT; @@ -1329,8 +1327,8 @@ static int atl1_tx_csum(struct atl1_adapter *adapter, struct sk_buff *skb, u8 css, cso; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - cso = skb->h.raw - skb->data; - css = (skb->h.raw + skb->csum) - skb->data; + cso = skb_transport_offset(skb); + css = cso + skb->csum_offset; if (unlikely(cso & 0x1)) { printk(KERN_DEBUG "%s: payload offset != even number\n", atl1_driver_name); @@ -1372,8 +1370,7 @@ static void atl1_tx_map(struct atl1_adapter *adapter, if (tcp_seg) { /* TSO/GSO */ - proto_hdr_len = - ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + proto_hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); buffer_info->length = proto_hdr_len; page = virt_to_page(skb->data); offset = (unsigned long)skb->data & ~PAGE_MASK; @@ -1564,9 +1561,9 @@ static int atl1_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* mss will be nonzero if we're doing segment offload (TSO/GSO) */ mss = skb_shinfo(skb)->gso_size; if (mss) { - if (skb->protocol == ntohs(ETH_P_IP)) { - proto_hdr_len = ((skb->h.raw - skb->data) + - (skb->h.th->doff << 2)); + if (skb->protocol == htons(ETH_P_IP)) { + proto_hdr_len = (skb_transport_offset(skb) + + tcp_hdrlen(skb)); if (unlikely(proto_hdr_len > len)) { dev_kfree_skb_any(skb); return NETDEV_TX_OK; @@ -2322,6 +2319,16 @@ static void __devexit atl1_remove(struct pci_dev *pdev) return; adapter = netdev_priv(netdev); + + /* Some atl1 boards lack persistent storage for their MAC, and get it + * from the BIOS during POST. If we've been messing with the MAC + * address, we need to save the permanent one. + */ + if (memcmp(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN)) { + memcpy(adapter->hw.mac_addr, adapter->hw.perm_mac_addr, ETH_ALEN); + atl1_set_mac_addr(&adapter->hw); + } + iowrite16(0, adapter->hw.hw_addr + REG_GPHY_ENABLE); unregister_netdev(netdev); pci_iounmap(pdev, adapter->hw.hw_addr); diff --git a/drivers/net/atp.c b/drivers/net/atp.c index 2d306fc..18aba83 100644 --- a/drivers/net/atp.c +++ b/drivers/net/atp.c @@ -793,7 +793,6 @@ static void net_rx(struct net_device *dev) lp->stats.rx_dropped++; goto done; } - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port); diff --git a/drivers/net/au1000_eth.c b/drivers/net/au1000_eth.c index 69ae229..d10fb80 100644 --- a/drivers/net/au1000_eth.c +++ b/drivers/net/au1000_eth.c @@ -1125,7 +1125,7 @@ static int au1000_tx(struct sk_buff *skb, struct net_device *dev) } pDB = aup->tx_db_inuse[aup->tx_head]; - memcpy((void *)pDB->vaddr, skb->data, skb->len); + skb_copy_from_linear_data(skb, pDB->vaddr, skb->len); if (skb->len < ETH_ZLEN) { for (i=skb->len; i<ETH_ZLEN; i++) { ((char *)pDB->vaddr)[i] = 0; @@ -1205,7 +1205,6 @@ static int au1000_rx(struct net_device *dev) aup->stats.rx_dropped++; continue; } - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte IP header align */ eth_copy_and_sum(skb, (unsigned char *)pDB->vaddr, frmlen, 0); diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 5ff7882..879a2ff 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -59,7 +59,6 @@ #define B44_DEF_TX_RING_PENDING (B44_TX_RING_SIZE - 1) #define B44_TX_RING_BYTES (sizeof(struct dma_desc) * \ B44_TX_RING_SIZE) -#define B44_DMA_MASK 0x3fffffff #define TX_RING_GAP(BP) \ (B44_TX_RING_SIZE - (BP)->tx_pending) @@ -665,7 +664,7 @@ static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) /* Hardware bug work-around, the chip is unable to do PCI DMA to/from anything above 1GB :-( */ if (dma_mapping_error(mapping) || - mapping + RX_PKT_BUF_SZ > B44_DMA_MASK) { + mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { /* Sigh... */ if (!dma_mapping_error(mapping)) pci_unmap_single(bp->pdev, mapping, RX_PKT_BUF_SZ,PCI_DMA_FROMDEVICE); @@ -677,7 +676,7 @@ static int b44_alloc_rx_skb(struct b44 *bp, int src_idx, u32 dest_idx_unmasked) RX_PKT_BUF_SZ, PCI_DMA_FROMDEVICE); if (dma_mapping_error(mapping) || - mapping + RX_PKT_BUF_SZ > B44_DMA_MASK) { + mapping + RX_PKT_BUF_SZ > DMA_30BIT_MASK) { if (!dma_mapping_error(mapping)) pci_unmap_single(bp->pdev, mapping, RX_PKT_BUF_SZ,PCI_DMA_FROMDEVICE); dev_kfree_skb_any(skb); @@ -826,12 +825,11 @@ static int b44_rx(struct b44 *bp, int budget) if (copy_skb == NULL) goto drop_it_no_recycle; - copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); /* DMA sync done above, copy just the actual packet */ - memcpy(copy_skb->data, skb->data+bp->rx_offset, len); - + skb_copy_from_linear_data_offset(skb, bp->rx_offset, + copy_skb->data, len); skb = copy_skb; } skb->ip_summed = CHECKSUM_NONE; @@ -988,7 +986,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) } mapping = pci_map_single(bp->pdev, skb->data, len, PCI_DMA_TODEVICE); - if (dma_mapping_error(mapping) || mapping + len > B44_DMA_MASK) { + if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { /* Chip can't handle DMA to/from >1GB, use bounce buffer */ if (!dma_mapping_error(mapping)) pci_unmap_single(bp->pdev, mapping, len, PCI_DMA_TODEVICE); @@ -1000,7 +998,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) mapping = pci_map_single(bp->pdev, bounce_skb->data, len, PCI_DMA_TODEVICE); - if (dma_mapping_error(mapping) || mapping + len > B44_DMA_MASK) { + if (dma_mapping_error(mapping) || mapping + len > DMA_30BIT_MASK) { if (!dma_mapping_error(mapping)) pci_unmap_single(bp->pdev, mapping, len, PCI_DMA_TODEVICE); @@ -1008,7 +1006,8 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) goto err_out; } - memcpy(skb_put(bounce_skb, len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(bounce_skb, len), + skb->len); dev_kfree_skb_any(skb); skb = bounce_skb; } @@ -1227,7 +1226,7 @@ static int b44_alloc_consistent(struct b44 *bp) DMA_BIDIRECTIONAL); if (dma_mapping_error(rx_ring_dma) || - rx_ring_dma + size > B44_DMA_MASK) { + rx_ring_dma + size > DMA_30BIT_MASK) { kfree(rx_ring); goto out_err; } @@ -1254,7 +1253,7 @@ static int b44_alloc_consistent(struct b44 *bp) DMA_TO_DEVICE); if (dma_mapping_error(tx_ring_dma) || - tx_ring_dma + size > B44_DMA_MASK) { + tx_ring_dma + size > DMA_30BIT_MASK) { kfree(tx_ring); goto out_err; } @@ -1289,7 +1288,7 @@ static void b44_chip_reset(struct b44 *bp) if (ssb_is_core_up(bp)) { bw32(bp, B44_RCV_LAZY, 0); bw32(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE); - b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 100, 1); + b44_wait_bit(bp, B44_ENET_CTRL, ENET_CTRL_DISABLE, 200, 1); bw32(bp, B44_DMATX_CTRL, 0); bp->tx_prod = bp->tx_cons = 0; if (br32(bp, B44_DMARX_STAT) & DMARX_STAT_EMASK) { @@ -1710,7 +1709,7 @@ static void __b44_set_rx_mode(struct net_device *dev) bw32(bp, B44_RXCONFIG, val); } else { unsigned char zero[6] = {0, 0, 0, 0, 0, 0}; - int i = 0; + int i = 1; __b44_set_mac_addr(bp); @@ -2151,13 +2150,13 @@ static int __devinit b44_init_one(struct pci_dev *pdev, pci_set_master(pdev); - err = pci_set_dma_mask(pdev, (u64) B44_DMA_MASK); + err = pci_set_dma_mask(pdev, (u64) DMA_30BIT_MASK); if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; } - err = pci_set_consistent_dma_mask(pdev, (u64) B44_DMA_MASK); + err = pci_set_consistent_dma_mask(pdev, (u64) DMA_30BIT_MASK); if (err) { dev_err(&pdev->dev, "No usable DMA configuration, aborting.\n"); goto err_out_free_res; diff --git a/drivers/net/bmac.c b/drivers/net/bmac.c index c143304d..4612725 100644 --- a/drivers/net/bmac.c +++ b/drivers/net/bmac.c @@ -715,7 +715,6 @@ static irqreturn_t bmac_rxdma_intr(int irq, void *dev_id) if (skb != NULL) { nb -= ETHERCRC; skb_put(skb, nb); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 5a96d76..f98a220 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -54,8 +54,8 @@ #define DRV_MODULE_NAME "bnx2" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "1.5.5" -#define DRV_MODULE_RELDATE "February 1, 2007" +#define DRV_MODULE_VERSION "1.5.8" +#define DRV_MODULE_RELDATE "April 24, 2007" #define RUN_AT(x) (jiffies + (x)) @@ -1884,10 +1884,8 @@ bnx2_rx_int(struct bnx2 *bp, int budget) goto reuse_rx; /* aligned copy */ - memcpy(new_skb->data, - skb->data + bp->rx_offset - 2, - len + 2); - + skb_copy_from_linear_data_offset(skb, bp->rx_offset - 2, + new_skb->data, len + 2); skb_reserve(new_skb, 2); skb_put(new_skb, len); @@ -2033,8 +2031,8 @@ bnx2_has_work(struct bnx2 *bp) (sblk->status_tx_quick_consumer_index0 != bp->hw_tx_cons)) return 1; - if (((sblk->status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != 0) != - bp->link_up) + if ((sblk->status_attn_bits & STATUS_ATTN_BITS_LINK_STATE) != + (sblk->status_attn_bits_ack & STATUS_ATTN_BITS_LINK_STATE)) return 1; return 0; @@ -3099,20 +3097,18 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, if ((align_start = (offset32 & 3))) { offset32 &= ~3; - len32 += (4 - align_start); + len32 += align_start; + if (len32 < 4) + len32 = 4; if ((rc = bnx2_nvram_read(bp, offset32, start, 4))) return rc; } if (len32 & 3) { - if ((len32 > 4) || !align_start) { - align_end = 4 - (len32 & 3); - len32 += align_end; - if ((rc = bnx2_nvram_read(bp, offset32 + len32 - 4, - end, 4))) { - return rc; - } - } + align_end = 4 - (len32 & 3); + len32 += align_end; + if ((rc = bnx2_nvram_read(bp, offset32 + len32 - 4, end, 4))) + return rc; } if (align_start || align_end) { @@ -3187,17 +3183,17 @@ bnx2_nvram_write(struct bnx2 *bp, u32 offset, u8 *data_buf, if ((rc = bnx2_enable_nvram_write(bp)) != 0) goto nvram_write_end; - /* Erase the page */ - if ((rc = bnx2_nvram_erase_page(bp, page_start)) != 0) - goto nvram_write_end; - - /* Re-enable the write again for the actual write */ - bnx2_enable_nvram_write(bp); - /* Loop to write back the buffer data from page_start to * data_start */ i = 0; if (bp->flash_info->buffered == 0) { + /* Erase the page */ + if ((rc = bnx2_nvram_erase_page(bp, page_start)) != 0) + goto nvram_write_end; + + /* Re-enable the write again for the actual write */ + bnx2_enable_nvram_write(bp); + for (addr = page_start; addr < data_start; addr += 4, i += 4) { @@ -3423,6 +3419,9 @@ bnx2_init_chip(struct bnx2 *bp) val = REG_RD(bp, BNX2_MQ_CONFIG); val &= ~BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE; val |= BNX2_MQ_CONFIG_KNL_BYP_BLK_SIZE_256; + if (CHIP_ID(bp) == CHIP_ID_5709_A0 || CHIP_ID(bp) == CHIP_ID_5709_A1) + val |= BNX2_MQ_CONFIG_HALT_DIS; + REG_WR(bp, BNX2_MQ_CONFIG, val); val = 0x10000 + (MAX_CID_CNT * MB_KERNEL_CTX_SIZE); @@ -4467,9 +4466,7 @@ bnx2_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) struct bnx2 *bp = netdev_priv(dev); bnx2_netif_stop(bp); - - if (bp->vlgrp) - bp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(bp->vlgrp, vid, NULL); bnx2_set_rx_mode(dev); bnx2_netif_start(bp); @@ -4514,6 +4511,7 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) if ((mss = skb_shinfo(skb)->gso_size) && (skb->len > (bp->dev->mtu + ETH_HLEN))) { u32 tcp_opt_len, ip_tcp_len; + struct iphdr *iph; if (skb_header_cloned(skb) && pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) { @@ -4521,25 +4519,23 @@ bnx2_start_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_OK; } - tcp_opt_len = ((skb->h.th->doff - 5) * 4); vlan_tag_flags |= TX_BD_FLAGS_SW_LSO; tcp_opt_len = 0; - if (skb->h.th->doff > 5) { - tcp_opt_len = (skb->h.th->doff - 5) << 2; - } - ip_tcp_len = (skb->nh.iph->ihl << 2) + sizeof(struct tcphdr); - - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - - if (tcp_opt_len || (skb->nh.iph->ihl > 5)) { - vlan_tag_flags |= ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)) << 8; + if (tcp_hdr(skb)->doff > 5) + tcp_opt_len = tcp_optlen(skb); + + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); + + iph = ip_hdr(skb); + iph->check = 0; + iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, 0); + if (tcp_opt_len || (iph->ihl > 5)) { + vlan_tag_flags |= ((iph->ihl - 5) + + (tcp_opt_len >> 2)) << 8; } } else diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index ccbdf81..878eee5 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -6518,6 +6518,7 @@ struct bnx2 { #define CHIP_ID_5708_B0 0x57081000 #define CHIP_ID_5708_B1 0x57081010 #define CHIP_ID_5709_A0 0x57090000 +#define CHIP_ID_5709_A1 0x57090010 #define CHIP_BOND_ID(bp) (((bp)->chip_id) & 0xf) diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index 3fb354d..7e03f41 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -884,8 +884,8 @@ static int ad_lacpdu_send(struct port *port) } skb->dev = slave->dev; - skb->mac.raw = skb->data; - skb->nh.raw = skb->data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = PKT_TYPE_LACPDU; skb->priority = TC_PRIO_CONTROL; @@ -928,8 +928,8 @@ static int ad_marker_send(struct port *port, struct marker *marker) skb_reserve(skb, 16); skb->dev = slave->dev; - skb->mac.raw = skb->data; - skb->nh.raw = skb->data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = PKT_TYPE_LACPDU; marker_header = (struct marker_header *)skb_put(skb, length); diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 217a2ee..92c3b6f 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -104,10 +104,15 @@ struct arp_pkt { }; #pragma pack() +static inline struct arp_pkt *arp_pkt(const struct sk_buff *skb) +{ + return (struct arp_pkt *)skb_network_header(skb); +} + /* Forward declaration */ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]); -static inline u8 _simple_hash(u8 *hash_start, int hash_size) +static inline u8 _simple_hash(const u8 *hash_start, int hash_size) { int i; u8 hash = 0; @@ -613,7 +618,7 @@ static void rlb_req_update_subnet_clients(struct bonding *bond, u32 src_ip) static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bond) { struct alb_bond_info *bond_info = &(BOND_ALB_INFO(bond)); - struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw; + struct arp_pkt *arp = arp_pkt(skb); struct slave *assigned_slave; struct rlb_client_info *client_info; u32 hash_index = 0; @@ -701,7 +706,7 @@ static struct slave *rlb_choose_channel(struct sk_buff *skb, struct bonding *bon */ static struct slave *rlb_arp_xmit(struct sk_buff *skb, struct bonding *bond) { - struct arp_pkt *arp = (struct arp_pkt *)skb->nh.raw; + struct arp_pkt *arp = arp_pkt(skb); struct slave *tx_slave = NULL; if (arp->op_code == __constant_htons(ARPOP_REPLY)) { @@ -890,8 +895,8 @@ static void alb_send_learning_packets(struct slave *slave, u8 mac_addr[]) data = skb_put(skb, size); memcpy(data, &pkt, size); - skb->mac.raw = data; - skb->nh.raw = data + ETH_HLEN; + skb_reset_mac_header(skb); + skb->network_header = skb->mac_header + ETH_HLEN; skb->protocol = pkt.type; skb->priority = TC_PRIO_CONTROL; skb->dev = slave->dev; @@ -1263,10 +1268,10 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) int hash_size = 0; int do_tx_balance = 1; u32 hash_index = 0; - u8 *hash_start = NULL; + const u8 *hash_start = NULL; int res = 1; - skb->mac.raw = (unsigned char *)skb->data; + skb_reset_mac_header(skb); eth_data = eth_hdr(skb); /* make sure that the curr_active_slave and the slaves list do @@ -1280,15 +1285,18 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) } switch (ntohs(skb->protocol)) { - case ETH_P_IP: + case ETH_P_IP: { + const struct iphdr *iph = ip_hdr(skb); + if ((memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) || - (skb->nh.iph->daddr == ip_bcast) || - (skb->nh.iph->protocol == IPPROTO_IGMP)) { + (iph->daddr == ip_bcast) || + (iph->protocol == IPPROTO_IGMP)) { do_tx_balance = 0; break; } - hash_start = (char*)&(skb->nh.iph->daddr); - hash_size = sizeof(skb->nh.iph->daddr); + hash_start = (char *)&(iph->daddr); + hash_size = sizeof(iph->daddr); + } break; case ETH_P_IPV6: if (memcmp(eth_data->h_dest, mac_bcast, ETH_ALEN) == 0) { @@ -1296,8 +1304,8 @@ int bond_alb_xmit(struct sk_buff *skb, struct net_device *bond_dev) break; } - hash_start = (char*)&(skb->nh.ipv6h->daddr); - hash_size = sizeof(skb->nh.ipv6h->daddr); + hash_start = (char *)&(ipv6_hdr(skb)->daddr); + hash_size = sizeof(ipv6_hdr(skb)->daddr); break; case ETH_P_IPX: if (ipx_hdr(skb)->ipx_checksum != diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index a7c8f98..724bce5 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -60,6 +60,7 @@ #include <linux/errno.h> #include <linux/netdevice.h> #include <linux/inetdevice.h> +#include <linux/igmp.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <net/sock.h> @@ -488,9 +489,9 @@ static void bond_vlan_rx_kill_vid(struct net_device *bond_dev, uint16_t vid) /* Save and then restore vlan_dev in the grp array, * since the slave's driver might clear it. */ - vlan_dev = bond->vlgrp->vlan_devices[vid]; + vlan_dev = vlan_group_get_device(bond->vlgrp, vid); slave_dev->vlan_rx_kill_vid(slave_dev, vid); - bond->vlgrp->vlan_devices[vid] = vlan_dev; + vlan_group_set_device(bond->vlgrp, vid, vlan_dev); } } @@ -550,9 +551,9 @@ static void bond_del_vlans_from_slave(struct bonding *bond, struct net_device *s /* Save and then restore vlan_dev in the grp array, * since the slave's driver might clear it. */ - vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id]; + vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); slave_dev->vlan_rx_kill_vid(slave_dev, vlan->vlan_id); - bond->vlgrp->vlan_devices[vlan->vlan_id] = vlan_dev; + vlan_group_set_device(bond->vlgrp, vlan->vlan_id, vlan_dev); } unreg: @@ -861,6 +862,28 @@ static void bond_mc_delete(struct bonding *bond, void *addr, int alen) } } + +/* + * Retrieve the list of registered multicast addresses for the bonding + * device and retransmit an IGMP JOIN request to the current active + * slave. + */ +static void bond_resend_igmp_join_requests(struct bonding *bond) +{ + struct in_device *in_dev; + struct ip_mc_list *im; + + rcu_read_lock(); + in_dev = __in_dev_get_rcu(bond->dev); + if (in_dev) { + for (im = in_dev->mc_list; im; im = im->next) { + ip_mc_rejoin_group(im); + } + } + + rcu_read_unlock(); +} + /* * Totally destroys the mc_list in bond */ @@ -874,6 +897,7 @@ static void bond_mc_list_destroy(struct bonding *bond) kfree(dmi); dmi = bond->mc_list; } + bond->mc_list = NULL; } /* @@ -967,6 +991,7 @@ static void bond_mc_swap(struct bonding *bond, struct slave *new_active, struct for (dmi = bond->dev->mc_list; dmi; dmi = dmi->next) { dev_mc_add(new_active->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0); } + bond_resend_igmp_join_requests(bond); } } @@ -1335,13 +1360,6 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) goto err_undo_flags; } - if (slave_dev->get_stats == NULL) { - printk(KERN_NOTICE DRV_NAME - ": %s: the driver for slave device %s does not provide " - "get_stats function, network statistics will be " - "inaccurate.\n", bond_dev->name, slave_dev->name); - } - new_slave = kzalloc(sizeof(struct slave), GFP_KERNEL); if (!new_slave) { res = -ENOMEM; @@ -2397,7 +2415,7 @@ static void bond_arp_send_all(struct bonding *bond, struct slave *slave) vlan_id = 0; list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list, vlan_list) { - vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id]; + vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); if (vlan_dev == rt->u.dst.dev) { vlan_id = vlan->vlan_id; dprintk("basa: vlan match on %s %d\n", @@ -2444,7 +2462,7 @@ static void bond_send_gratuitous_arp(struct bonding *bond) } list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id]; + vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); if (vlan->vlan_ip) { bond_arp_send(slave->dev, ARPOP_REPLY, vlan->vlan_ip, vlan->vlan_ip, vlan->vlan_id); @@ -2499,7 +2517,7 @@ static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct pack (2 * sizeof(u32))))) goto out_unlock; - arp = skb->nh.arph; + arp = arp_hdr(skb); if (arp->ar_hln != dev->addr_len || skb->pkt_type == PACKET_OTHERHOST || skb->pkt_type == PACKET_LOOPBACK || @@ -3371,7 +3389,7 @@ static int bond_inetaddr_event(struct notifier_block *this, unsigned long event, list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list, vlan_list) { - vlan_dev = bond->vlgrp->vlan_devices[vlan->vlan_id]; + vlan_dev = vlan_group_get_device(bond->vlgrp, vlan->vlan_id); if (vlan_dev == event_dev) { switch (event) { case NETDEV_UP: @@ -3423,15 +3441,21 @@ void bond_register_arp(struct bonding *bond) { struct packet_type *pt = &bond->arp_mon_pt; + if (pt->type) + return; + pt->type = htons(ETH_P_ARP); - pt->dev = NULL; /*bond->dev;XXX*/ + pt->dev = bond->dev; pt->func = bond_arp_rcv; dev_add_pack(pt); } void bond_unregister_arp(struct bonding *bond) { - dev_remove_pack(&bond->arp_mon_pt); + struct packet_type *pt = &bond->arp_mon_pt; + + dev_remove_pack(pt); + pt->type = 0; } /*---------------------------- Hashing Policies -----------------------------*/ @@ -3445,7 +3469,7 @@ static int bond_xmit_hash_policy_l34(struct sk_buff *skb, struct net_device *bond_dev, int count) { struct ethhdr *data = (struct ethhdr *)skb->data; - struct iphdr *iph = skb->nh.iph; + struct iphdr *iph = ip_hdr(skb); u16 *layer4hdr = (u16 *)((u32 *)iph + iph->ihl); int layer4_xor = 0; @@ -3609,35 +3633,32 @@ static struct net_device_stats *bond_get_stats(struct net_device *bond_dev) read_lock_bh(&bond->lock); bond_for_each_slave(bond, slave, i) { - if (slave->dev->get_stats) { - sstats = slave->dev->get_stats(slave->dev); - - stats->rx_packets += sstats->rx_packets; - stats->rx_bytes += sstats->rx_bytes; - stats->rx_errors += sstats->rx_errors; - stats->rx_dropped += sstats->rx_dropped; - - stats->tx_packets += sstats->tx_packets; - stats->tx_bytes += sstats->tx_bytes; - stats->tx_errors += sstats->tx_errors; - stats->tx_dropped += sstats->tx_dropped; - - stats->multicast += sstats->multicast; - stats->collisions += sstats->collisions; - - stats->rx_length_errors += sstats->rx_length_errors; - stats->rx_over_errors += sstats->rx_over_errors; - stats->rx_crc_errors += sstats->rx_crc_errors; - stats->rx_frame_errors += sstats->rx_frame_errors; - stats->rx_fifo_errors += sstats->rx_fifo_errors; - stats->rx_missed_errors += sstats->rx_missed_errors; - - stats->tx_aborted_errors += sstats->tx_aborted_errors; - stats->tx_carrier_errors += sstats->tx_carrier_errors; - stats->tx_fifo_errors += sstats->tx_fifo_errors; - stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; - stats->tx_window_errors += sstats->tx_window_errors; - } + sstats = slave->dev->get_stats(slave->dev); + stats->rx_packets += sstats->rx_packets; + stats->rx_bytes += sstats->rx_bytes; + stats->rx_errors += sstats->rx_errors; + stats->rx_dropped += sstats->rx_dropped; + + stats->tx_packets += sstats->tx_packets; + stats->tx_bytes += sstats->tx_bytes; + stats->tx_errors += sstats->tx_errors; + stats->tx_dropped += sstats->tx_dropped; + + stats->multicast += sstats->multicast; + stats->collisions += sstats->collisions; + + stats->rx_length_errors += sstats->rx_length_errors; + stats->rx_over_errors += sstats->rx_over_errors; + stats->rx_crc_errors += sstats->rx_crc_errors; + stats->rx_frame_errors += sstats->rx_frame_errors; + stats->rx_fifo_errors += sstats->rx_fifo_errors; + stats->rx_missed_errors += sstats->rx_missed_errors; + + stats->tx_aborted_errors += sstats->tx_aborted_errors; + stats->tx_carrier_errors += sstats->tx_carrier_errors; + stats->tx_fifo_errors += sstats->tx_fifo_errors; + stats->tx_heartbeat_errors += sstats->tx_heartbeat_errors; + stats->tx_window_errors += sstats->tx_window_errors; } read_unlock_bh(&bond->lock); @@ -4011,42 +4032,6 @@ out: return 0; } -static void bond_activebackup_xmit_copy(struct sk_buff *skb, - struct bonding *bond, - struct slave *slave) -{ - struct sk_buff *skb2 = skb_copy(skb, GFP_ATOMIC); - struct ethhdr *eth_data; - u8 *hwaddr; - int res; - - if (!skb2) { - printk(KERN_ERR DRV_NAME ": Error: " - "bond_activebackup_xmit_copy(): skb_copy() failed\n"); - return; - } - - skb2->mac.raw = (unsigned char *)skb2->data; - eth_data = eth_hdr(skb2); - - /* Pick an appropriate source MAC address - * -- use slave's perm MAC addr, unless used by bond - * -- otherwise, borrow active slave's perm MAC addr - * since that will not be used - */ - hwaddr = slave->perm_hwaddr; - if (!memcmp(eth_data->h_source, hwaddr, ETH_ALEN)) - hwaddr = bond->curr_active_slave->perm_hwaddr; - - /* Set source MAC address appropriately */ - memcpy(eth_data->h_source, hwaddr, ETH_ALEN); - - res = bond_dev_queue_xmit(bond, skb2, slave->dev); - if (res) - dev_kfree_skb(skb2); - - return; -} /* * in active-backup mode, we know that bond->curr_active_slave is always valid if @@ -4067,21 +4052,6 @@ static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *bond_d if (!bond->curr_active_slave) goto out; - /* Xmit IGMP frames on all slaves to ensure rapid fail-over - for multicast traffic on snooping switches */ - if (skb->protocol == __constant_htons(ETH_P_IP) && - skb->nh.iph->protocol == IPPROTO_IGMP) { - struct slave *slave, *active_slave; - int i; - - active_slave = bond->curr_active_slave; - bond_for_each_slave_from_to(bond, slave, i, active_slave->next, - active_slave->prev) - if (IS_UP(slave->dev) && - (slave->link == BOND_LINK_UP)) - bond_activebackup_xmit_copy(skb, bond, slave); - } - res = bond_dev_queue_xmit(bond, skb, bond->curr_active_slave->dev); out: diff --git a/drivers/net/cassini.c b/drivers/net/cassini.c index c812648..4aec747 100644 --- a/drivers/net/cassini.c +++ b/drivers/net/cassini.c @@ -1995,7 +1995,6 @@ static int cas_rx_process_pkt(struct cas *cp, struct cas_rx_comp *rxc, return -1; *skbref = skb; - skb->dev = cp->dev; skb_reserve(skb, swivel); p = skb->data; @@ -2822,10 +2821,8 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring, ctrl = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u64 csum_start_off, csum_stuff_off; - - csum_start_off = (u64) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u64 csum_start_off = skb_transport_offset(skb); + const u64 csum_stuff_off = csum_start_off + skb->csum_offset; ctrl = TX_DESC_CSUM_EN | CAS_BASE(TX_DESC_CSUM_START, csum_start_off) | @@ -2849,8 +2846,8 @@ static inline int cas_xmit_tx_ringN(struct cas *cp, int ring, ctrl | TX_DESC_SOF, 0); entry = TX_DESC_NEXT(ring, entry); - memcpy(tx_tiny_buf(cp, ring, entry), skb->data + - len - tabort, tabort); + skb_copy_from_linear_data_offset(skb, len - tabort, + tx_tiny_buf(cp, ring, entry), tabort); mapping = tx_tiny_map(cp, ring, entry, tentry); cas_write_txd(cp, ring, entry, mapping, tabort, ctrl, (nr_frags == 0)); diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile index 382d23f..743ad8b 100644 --- a/drivers/net/chelsio/Makefile +++ b/drivers/net/chelsio/Makefile @@ -4,8 +4,6 @@ obj-$(CONFIG_CHELSIO_T1) += cxgb.o -cxgb-$(CONFIG_CHELSIO_T1_1G) += ixf1010.o mac.o mv88e1xxx.o vsc7326.o vsc8244.o +cxgb-$(CONFIG_CHELSIO_T1_1G) += mac.o mv88e1xxx.o vsc7326.o cxgb-objs := cxgb2.o espi.o tp.o pm3393.o sge.o subr.o \ mv88x201x.o my3126.o $(cxgb-y) - - diff --git a/drivers/net/chelsio/common.h b/drivers/net/chelsio/common.h index 787f2f2..8ba702c 100644 --- a/drivers/net/chelsio/common.h +++ b/drivers/net/chelsio/common.h @@ -322,9 +322,9 @@ struct board_info { unsigned char mdio_mdiinv; unsigned char mdio_mdc; unsigned char mdio_phybaseaddr; - struct gmac *gmac; - struct gphy *gphy; - struct mdio_ops *mdio_ops; + const struct gmac *gmac; + const struct gphy *gphy; + const struct mdio_ops *mdio_ops; const char *desc; }; diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/chelsio/cphy.h index cf91434..79d855e 100644 --- a/drivers/net/chelsio/cphy.h +++ b/drivers/net/chelsio/cphy.h @@ -100,7 +100,7 @@ struct cphy { u32 elmer_gpo; - struct cphy_ops *ops; /* PHY operations */ + const struct cphy_ops *ops; /* PHY operations */ int (*mdio_read)(adapter_t *adapter, int phy_addr, int mmd_addr, int reg_addr, unsigned int *val); int (*mdio_write)(adapter_t *adapter, int phy_addr, int mmd_addr, @@ -136,7 +136,7 @@ static inline int simple_mdio_write(struct cphy *cphy, int reg, /* Convenience initializer */ static inline void cphy_init(struct cphy *phy, adapter_t *adapter, int phy_addr, struct cphy_ops *phy_ops, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { phy->adapter = adapter; phy->addr = phy_addr; @@ -151,7 +151,7 @@ static inline void cphy_init(struct cphy *phy, adapter_t *adapter, struct gphy { /* Construct a PHY instance with the given PHY address */ struct cphy *(*create)(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops); + const struct mdio_ops *mdio_ops); /* * Reset the PHY chip. This resets the whole PHY chip, not individual @@ -160,11 +160,9 @@ struct gphy { int (*reset)(adapter_t *adapter); }; -extern struct gphy t1_my3126_ops; -extern struct gphy t1_mv88e1xxx_ops; -extern struct gphy t1_vsc8244_ops; -extern struct gphy t1_xpak_ops; -extern struct gphy t1_mv88x201x_ops; -extern struct gphy t1_dummy_phy_ops; +extern const struct gphy t1_my3126_ops; +extern const struct gphy t1_mv88e1xxx_ops; +extern const struct gphy t1_vsc8244_ops; +extern const struct gphy t1_mv88x201x_ops; #endif /* _CXGB_CPHY_H_ */ diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c index 7d0f24f..125c9b1 100644 --- a/drivers/net/chelsio/cxgb2.c +++ b/drivers/net/chelsio/cxgb2.c @@ -889,8 +889,7 @@ static void vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) struct adapter *adapter = dev->priv; spin_lock_irq(&adapter->async_lock); - if (adapter->vlan_grp) - adapter->vlan_grp->vlan_devices[vid] = NULL; + vlan_group_set_device(adapter->vlan_grp, vid, NULL); spin_unlock_irq(&adapter->async_lock); } #endif diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/chelsio/gmac.h index 006a2eb..d423374 100644 --- a/drivers/net/chelsio/gmac.h +++ b/drivers/net/chelsio/gmac.h @@ -126,7 +126,7 @@ typedef struct _cmac_instance cmac_instance; struct cmac { struct cmac_statistics stats; adapter_t *adapter; - struct cmac_ops *ops; + const struct cmac_ops *ops; cmac_instance *instance; }; @@ -136,11 +136,7 @@ struct gmac { int (*reset)(adapter_t *); }; -extern struct gmac t1_pm3393_ops; -extern struct gmac t1_chelsio_mac_ops; -extern struct gmac t1_vsc7321_ops; -extern struct gmac t1_vsc7326_ops; -extern struct gmac t1_ixf1010_ops; -extern struct gmac t1_dummy_mac_ops; +extern const struct gmac t1_pm3393_ops; +extern const struct gmac t1_vsc7326_ops; #endif /* _CXGB_GMAC_H_ */ diff --git a/drivers/net/chelsio/ixf1010.c b/drivers/net/chelsio/ixf1010.c deleted file mode 100644 index 10b2a9a..0000000 --- a/drivers/net/chelsio/ixf1010.c +++ /dev/null @@ -1,505 +0,0 @@ -/* $Date: 2005/11/12 02:13:49 $ $RCSfile: ixf1010.c,v $ $Revision: 1.36 $ */ -#include "gmac.h" -#include "elmer0.h" - -/* Update fast changing statistics every 15 seconds */ -#define STATS_TICK_SECS 15 -/* 30 minutes for full statistics update */ -#define MAJOR_UPDATE_TICKS (1800 / STATS_TICK_SECS) - -/* - * The IXF1010 can handle frames up to 16383 bytes but it's optimized for - * frames up to 9831 (0x2667) bytes, so we limit jumbo frame size to this. - * This length includes ethernet header and FCS. - */ -#define MAX_FRAME_SIZE 0x2667 - -/* MAC registers */ -enum { - /* Per-port registers */ - REG_MACADDR_LOW = 0, - REG_MACADDR_HIGH = 0x4, - REG_FDFC_TYPE = 0xC, - REG_FC_TX_TIMER_VALUE = 0x1c, - REG_IPG_RX_TIME1 = 0x28, - REG_IPG_RX_TIME2 = 0x2c, - REG_IPG_TX_TIME = 0x30, - REG_PAUSE_THRES = 0x38, - REG_MAX_FRAME_SIZE = 0x3c, - REG_RGMII_SPEED = 0x40, - REG_FC_ENABLE = 0x48, - REG_DISCARD_CTRL_FRAMES = 0x54, - REG_DIVERSE_CONFIG = 0x60, - REG_RX_FILTER = 0x64, - REG_MC_ADDR_LOW = 0x68, - REG_MC_ADDR_HIGH = 0x6c, - - REG_RX_OCTETS_OK = 0x80, - REG_RX_OCTETS_BAD = 0x84, - REG_RX_UC_PKTS = 0x88, - REG_RX_MC_PKTS = 0x8c, - REG_RX_BC_PKTS = 0x90, - REG_RX_FCS_ERR = 0xb0, - REG_RX_TAGGED = 0xb4, - REG_RX_DATA_ERR = 0xb8, - REG_RX_ALIGN_ERR = 0xbc, - REG_RX_LONG_ERR = 0xc0, - REG_RX_JABBER_ERR = 0xc4, - REG_RX_PAUSE_FRAMES = 0xc8, - REG_RX_UNKNOWN_CTRL_FRAMES = 0xcc, - REG_RX_VERY_LONG_ERR = 0xd0, - REG_RX_RUNT_ERR = 0xd4, - REG_RX_SHORT_ERR = 0xd8, - REG_RX_SYMBOL_ERR = 0xe4, - - REG_TX_OCTETS_OK = 0x100, - REG_TX_OCTETS_BAD = 0x104, - REG_TX_UC_PKTS = 0x108, - REG_TX_MC_PKTS = 0x10c, - REG_TX_BC_PKTS = 0x110, - REG_TX_EXCESSIVE_LEN_DROP = 0x14c, - REG_TX_UNDERRUN = 0x150, - REG_TX_TAGGED = 0x154, - REG_TX_PAUSE_FRAMES = 0x15C, - - /* Global registers */ - REG_PORT_ENABLE = 0x1400, - - REG_JTAG_ID = 0x1430, - - RX_FIFO_HIGH_WATERMARK_BASE = 0x1600, - RX_FIFO_LOW_WATERMARK_BASE = 0x1628, - RX_FIFO_FRAMES_REMOVED_BASE = 0x1650, - - REG_RX_ERR_DROP = 0x167c, - REG_RX_FIFO_OVERFLOW_EVENT = 0x1680, - - TX_FIFO_HIGH_WATERMARK_BASE = 0x1800, - TX_FIFO_LOW_WATERMARK_BASE = 0x1828, - TX_FIFO_XFER_THRES_BASE = 0x1850, - - REG_TX_FIFO_OVERFLOW_EVENT = 0x1878, - REG_TX_FIFO_OOS_EVENT = 0x1884, - - TX_FIFO_FRAMES_REMOVED_BASE = 0x1888, - - REG_SPI_RX_BURST = 0x1c00, - REG_SPI_RX_TRAINING = 0x1c04, - REG_SPI_RX_CALENDAR = 0x1c08, - REG_SPI_TX_SYNC = 0x1c0c -}; - -enum { /* RMON registers */ - REG_RxOctetsTotalOK = 0x80, - REG_RxOctetsBad = 0x84, - REG_RxUCPkts = 0x88, - REG_RxMCPkts = 0x8c, - REG_RxBCPkts = 0x90, - REG_RxJumboPkts = 0xac, - REG_RxFCSErrors = 0xb0, - REG_RxDataErrors = 0xb8, - REG_RxAlignErrors = 0xbc, - REG_RxLongErrors = 0xc0, - REG_RxJabberErrors = 0xc4, - REG_RxPauseMacControlCounter = 0xc8, - REG_RxVeryLongErrors = 0xd0, - REG_RxRuntErrors = 0xd4, - REG_RxShortErrors = 0xd8, - REG_RxSequenceErrors = 0xe0, - REG_RxSymbolErrors = 0xe4, - - REG_TxOctetsTotalOK = 0x100, - REG_TxOctetsBad = 0x104, - REG_TxUCPkts = 0x108, - REG_TxMCPkts = 0x10c, - REG_TxBCPkts = 0x110, - REG_TxJumboPkts = 0x12C, - REG_TxTotalCollisions = 0x134, - REG_TxExcessiveLengthDrop = 0x14c, - REG_TxUnderrun = 0x150, - REG_TxCRCErrors = 0x158, - REG_TxPauseFrames = 0x15c -}; - -enum { - DIVERSE_CONFIG_PAD_ENABLE = 0x80, - DIVERSE_CONFIG_CRC_ADD = 0x40 -}; - -#define MACREG_BASE 0 -#define MACREG(mac, mac_reg) ((mac)->instance->mac_base + (mac_reg)) - -struct _cmac_instance { - u32 mac_base; - u32 index; - u32 version; - u32 ticks; -}; - -static void disable_port(struct cmac *mac) -{ - u32 val; - - t1_tpi_read(mac->adapter, REG_PORT_ENABLE, &val); - val &= ~(1 << mac->instance->index); - t1_tpi_write(mac->adapter, REG_PORT_ENABLE, val); -} - -/* - * Read the current values of the RMON counters and add them to the cumulative - * port statistics. The HW RMON counters are cleared by this operation. - */ -static void port_stats_update(struct cmac *mac) -{ - static struct { - unsigned int reg; - unsigned int offset; - } hw_stats[] = { - -#define HW_STAT(name, stat_name) \ - { REG_##name, \ - (&((struct cmac_statistics *)NULL)->stat_name) - (u64 *)NULL } - - /* Rx stats */ - HW_STAT(RxOctetsTotalOK, RxOctetsOK), - HW_STAT(RxOctetsBad, RxOctetsBad), - HW_STAT(RxUCPkts, RxUnicastFramesOK), - HW_STAT(RxMCPkts, RxMulticastFramesOK), - HW_STAT(RxBCPkts, RxBroadcastFramesOK), - HW_STAT(RxJumboPkts, RxJumboFramesOK), - HW_STAT(RxFCSErrors, RxFCSErrors), - HW_STAT(RxAlignErrors, RxAlignErrors), - HW_STAT(RxLongErrors, RxFrameTooLongErrors), - HW_STAT(RxVeryLongErrors, RxFrameTooLongErrors), - HW_STAT(RxPauseMacControlCounter, RxPauseFrames), - HW_STAT(RxDataErrors, RxDataErrors), - HW_STAT(RxJabberErrors, RxJabberErrors), - HW_STAT(RxRuntErrors, RxRuntErrors), - HW_STAT(RxShortErrors, RxRuntErrors), - HW_STAT(RxSequenceErrors, RxSequenceErrors), - HW_STAT(RxSymbolErrors, RxSymbolErrors), - - /* Tx stats (skip collision stats as we are full-duplex only) */ - HW_STAT(TxOctetsTotalOK, TxOctetsOK), - HW_STAT(TxOctetsBad, TxOctetsBad), - HW_STAT(TxUCPkts, TxUnicastFramesOK), - HW_STAT(TxMCPkts, TxMulticastFramesOK), - HW_STAT(TxBCPkts, TxBroadcastFramesOK), - HW_STAT(TxJumboPkts, TxJumboFramesOK), - HW_STAT(TxPauseFrames, TxPauseFrames), - HW_STAT(TxExcessiveLengthDrop, TxLengthErrors), - HW_STAT(TxUnderrun, TxUnderrun), - HW_STAT(TxCRCErrors, TxFCSErrors) - }, *p = hw_stats; - u64 *stats = (u64 *) &mac->stats; - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(hw_stats); i++) { - u32 val; - - t1_tpi_read(mac->adapter, MACREG(mac, p->reg), &val); - stats[p->offset] += val; - } -} - -/* No-op interrupt operation as this MAC does not support interrupts */ -static int mac_intr_op(struct cmac *mac) -{ - return 0; -} - -/* Expect MAC address to be in network byte order. */ -static int mac_set_address(struct cmac *mac, u8 addr[6]) -{ - u32 addr_lo, addr_hi; - - addr_lo = addr[2]; - addr_lo = (addr_lo << 8) | addr[3]; - addr_lo = (addr_lo << 8) | addr[4]; - addr_lo = (addr_lo << 8) | addr[5]; - - addr_hi = addr[0]; - addr_hi = (addr_hi << 8) | addr[1]; - - t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_LOW), addr_lo); - t1_tpi_write(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), addr_hi); - return 0; -} - -static int mac_get_address(struct cmac *mac, u8 addr[6]) -{ - u32 addr_lo, addr_hi; - - t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_LOW), &addr_lo); - t1_tpi_read(mac->adapter, MACREG(mac, REG_MACADDR_HIGH), &addr_hi); - - addr[0] = (u8) (addr_hi >> 8); - addr[1] = (u8) addr_hi; - addr[2] = (u8) (addr_lo >> 24); - addr[3] = (u8) (addr_lo >> 16); - addr[4] = (u8) (addr_lo >> 8); - addr[5] = (u8) addr_lo; - return 0; -} - -/* This is intended to reset a port, not the whole MAC */ -static int mac_reset(struct cmac *mac) -{ - return 0; -} - -static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm) -{ - u32 val, new_mode; - adapter_t *adapter = mac->adapter; - u32 addr_lo, addr_hi; - u8 *addr; - - t1_tpi_read(adapter, MACREG(mac, REG_RX_FILTER), &val); - new_mode = val & ~7; - if (!t1_rx_mode_promisc(rm) && mac->instance->version > 0) - new_mode |= 1; /* only set if version > 0 due to erratum */ - if (!t1_rx_mode_promisc(rm) && !t1_rx_mode_allmulti(rm) - && t1_rx_mode_mc_cnt(rm) <= 1) - new_mode |= 2; - if (new_mode != val) - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), new_mode); - switch (t1_rx_mode_mc_cnt(rm)) { - case 0: - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), 0); - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), 0); - break; - case 1: - addr = t1_get_next_mcaddr(rm); - addr_lo = (addr[2] << 24) | (addr[3] << 16) | (addr[4] << 8) | - addr[5]; - addr_hi = (addr[0] << 8) | addr[1]; - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_LOW), addr_lo); - t1_tpi_write(adapter, MACREG(mac, REG_MC_ADDR_HIGH), addr_hi); - break; - default: - break; - } - return 0; -} - -static int mac_set_mtu(struct cmac *mac, int mtu) -{ - /* MAX_FRAME_SIZE inludes header + FCS, mtu doesn't */ - if (mtu > (MAX_FRAME_SIZE - 14 - 4)) - return -EINVAL; - t1_tpi_write(mac->adapter, MACREG(mac, REG_MAX_FRAME_SIZE), - mtu + 14 + 4); - return 0; -} - -static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, - int fc) -{ - u32 val; - - if (speed >= 0 && speed != SPEED_100 && speed != SPEED_1000) - return -1; - if (duplex >= 0 && duplex != DUPLEX_FULL) - return -1; - - if (speed >= 0) { - val = speed == SPEED_100 ? 1 : 2; - t1_tpi_write(mac->adapter, MACREG(mac, REG_RGMII_SPEED), val); - } - - t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val); - val &= ~3; - if (fc & PAUSE_RX) - val |= 1; - if (fc & PAUSE_TX) - val |= 2; - t1_tpi_write(mac->adapter, MACREG(mac, REG_FC_ENABLE), val); - return 0; -} - -static int mac_get_speed_duplex_fc(struct cmac *mac, int *speed, int *duplex, - int *fc) -{ - u32 val; - - if (duplex) - *duplex = DUPLEX_FULL; - if (speed) { - t1_tpi_read(mac->adapter, MACREG(mac, REG_RGMII_SPEED), - &val); - *speed = (val & 2) ? SPEED_1000 : SPEED_100; - } - if (fc) { - t1_tpi_read(mac->adapter, MACREG(mac, REG_FC_ENABLE), &val); - *fc = 0; - if (val & 1) - *fc |= PAUSE_RX; - if (val & 2) - *fc |= PAUSE_TX; - } - return 0; -} - -static void enable_port(struct cmac *mac) -{ - u32 val; - u32 index = mac->instance->index; - adapter_t *adapter = mac->adapter; - - t1_tpi_read(adapter, MACREG(mac, REG_DIVERSE_CONFIG), &val); - val |= DIVERSE_CONFIG_CRC_ADD | DIVERSE_CONFIG_PAD_ENABLE; - t1_tpi_write(adapter, MACREG(mac, REG_DIVERSE_CONFIG), val); - if (mac->instance->version > 0) - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 3); - else /* Don't enable unicast address filtering due to IXF1010 bug */ - t1_tpi_write(adapter, MACREG(mac, REG_RX_FILTER), 2); - - t1_tpi_read(adapter, REG_RX_ERR_DROP, &val); - val |= (1 << index); - t1_tpi_write(adapter, REG_RX_ERR_DROP, val); - - /* - * Clear the port RMON registers by adding their current values to the - * cumulatice port stats and then clearing the stats. Really. - */ - port_stats_update(mac); - memset(&mac->stats, 0, sizeof(struct cmac_statistics)); - mac->instance->ticks = 0; - - t1_tpi_read(adapter, REG_PORT_ENABLE, &val); - val |= (1 << index); - t1_tpi_write(adapter, REG_PORT_ENABLE, val); - - index <<= 2; - if (is_T2(adapter)) { - /* T204: set the Fifo water level & threshold */ - t1_tpi_write(adapter, RX_FIFO_HIGH_WATERMARK_BASE + index, 0x740); - t1_tpi_write(adapter, RX_FIFO_LOW_WATERMARK_BASE + index, 0x730); - t1_tpi_write(adapter, TX_FIFO_HIGH_WATERMARK_BASE + index, 0x600); - t1_tpi_write(adapter, TX_FIFO_LOW_WATERMARK_BASE + index, 0x1d0); - t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x1100); - } else { - /* - * Set the TX Fifo Threshold to 0x400 instead of 0x100 to work around - * Underrun problem. Intel has blessed this solution. - */ - t1_tpi_write(adapter, TX_FIFO_XFER_THRES_BASE + index, 0x400); - } -} - -/* IXF1010 ports do not have separate enables for TX and RX */ -static int mac_enable(struct cmac *mac, int which) -{ - if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX)) - enable_port(mac); - return 0; -} - -static int mac_disable(struct cmac *mac, int which) -{ - if (which & (MAC_DIRECTION_RX | MAC_DIRECTION_TX)) - disable_port(mac); - return 0; -} - -#define RMON_UPDATE(mac, name, stat_name) \ - t1_tpi_read((mac)->adapter, MACREG(mac, REG_##name), &val); \ - (mac)->stats.stat_name += val; - -/* - * This function is called periodically to accumulate the current values of the - * RMON counters into the port statistics. Since the counters are only 32 bits - * some of them can overflow in less than a minute at GigE speeds, so this - * function should be called every 30 seconds or so. - * - * To cut down on reading costs we update only the octet counters at each tick - * and do a full update at major ticks, which can be every 30 minutes or more. - */ -static const struct cmac_statistics *mac_update_statistics(struct cmac *mac, - int flag) -{ - if (flag == MAC_STATS_UPDATE_FULL || - MAJOR_UPDATE_TICKS <= mac->instance->ticks) { - port_stats_update(mac); - mac->instance->ticks = 0; - } else { - u32 val; - - RMON_UPDATE(mac, RxOctetsTotalOK, RxOctetsOK); - RMON_UPDATE(mac, TxOctetsTotalOK, TxOctetsOK); - mac->instance->ticks++; - } - return &mac->stats; -} - -static void mac_destroy(struct cmac *mac) -{ - kfree(mac); -} - -static struct cmac_ops ixf1010_ops = { - .destroy = mac_destroy, - .reset = mac_reset, - .interrupt_enable = mac_intr_op, - .interrupt_disable = mac_intr_op, - .interrupt_clear = mac_intr_op, - .enable = mac_enable, - .disable = mac_disable, - .set_mtu = mac_set_mtu, - .set_rx_mode = mac_set_rx_mode, - .set_speed_duplex_fc = mac_set_speed_duplex_fc, - .get_speed_duplex_fc = mac_get_speed_duplex_fc, - .statistics_update = mac_update_statistics, - .macaddress_get = mac_get_address, - .macaddress_set = mac_set_address, -}; - -static int ixf1010_mac_reset(adapter_t *adapter) -{ - u32 val; - - t1_tpi_read(adapter, A_ELMER0_GPO, &val); - if ((val & 1) != 0) { - val &= ~1; - t1_tpi_write(adapter, A_ELMER0_GPO, val); - udelay(2); - } - val |= 1; - t1_tpi_write(adapter, A_ELMER0_GPO, val); - udelay(2); - - t1_tpi_write(adapter, REG_PORT_ENABLE, 0); - return 0; -} - -static struct cmac *ixf1010_mac_create(adapter_t *adapter, int index) -{ - struct cmac *mac; - u32 val; - - if (index > 9) - return NULL; - - mac = kzalloc(sizeof(*mac) + sizeof(cmac_instance), GFP_KERNEL); - if (!mac) - return NULL; - - mac->ops = &ixf1010_ops; - mac->instance = (cmac_instance *)(mac + 1); - - mac->instance->mac_base = MACREG_BASE + (index * 0x200); - mac->instance->index = index; - mac->adapter = adapter; - mac->instance->ticks = 0; - - t1_tpi_read(adapter, REG_JTAG_ID, &val); - mac->instance->version = val >> 28; - return mac; -} - -struct gmac t1_ixf1010_ops = { - STATS_TICK_SECS, - ixf1010_mac_create, - ixf1010_mac_reset -}; diff --git a/drivers/net/chelsio/mac.c b/drivers/net/chelsio/mac.c index 6af39dc7..1d97282 100644 --- a/drivers/net/chelsio/mac.c +++ b/drivers/net/chelsio/mac.c @@ -363,6 +363,6 @@ static struct cmac *mac_create(adapter_t *adapter, int index) return mac; } -struct gmac t1_chelsio_mac_ops = { +const struct gmac t1_chelsio_mac_ops = { .create = mac_create }; diff --git a/drivers/net/chelsio/mv88e1xxx.c b/drivers/net/chelsio/mv88e1xxx.c index 5867e3b..0632be0 100644 --- a/drivers/net/chelsio/mv88e1xxx.c +++ b/drivers/net/chelsio/mv88e1xxx.c @@ -354,7 +354,7 @@ static struct cphy_ops mv88e1xxx_ops = { }; static struct cphy *mv88e1xxx_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); @@ -390,7 +390,7 @@ static int mv88e1xxx_phy_reset(adapter_t* adapter) return 0; } -struct gphy t1_mv88e1xxx_ops = { - mv88e1xxx_phy_create, - mv88e1xxx_phy_reset +const struct gphy t1_mv88e1xxx_ops = { + .create = mv88e1xxx_phy_create, + .reset = mv88e1xxx_phy_reset }; diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/chelsio/mv88x201x.c index c8e8948..cd85604 100644 --- a/drivers/net/chelsio/mv88x201x.c +++ b/drivers/net/chelsio/mv88x201x.c @@ -208,7 +208,7 @@ static struct cphy_ops mv88x201x_ops = { }; static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) + const struct mdio_ops *mdio_ops) { u32 val; struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); @@ -252,7 +252,7 @@ static int mv88x201x_phy_reset(adapter_t *adapter) return 0; } -struct gphy t1_mv88x201x_ops = { - mv88x201x_phy_create, - mv88x201x_phy_reset +const struct gphy t1_mv88x201x_ops = { + .create = mv88x201x_phy_create, + .reset = mv88x201x_phy_reset }; diff --git a/drivers/net/chelsio/my3126.c b/drivers/net/chelsio/my3126.c index 87dde3e..040acd2 100644 --- a/drivers/net/chelsio/my3126.c +++ b/drivers/net/chelsio/my3126.c @@ -166,7 +166,7 @@ static struct cphy_ops my3126_ops = { }; static struct cphy *my3126_phy_create(adapter_t *adapter, - int phy_addr, struct mdio_ops *mdio_ops) + int phy_addr, const struct mdio_ops *mdio_ops) { struct cphy *cphy = kzalloc(sizeof (*cphy), GFP_KERNEL); @@ -201,7 +201,7 @@ static int my3126_phy_reset(adapter_t * adapter) return 0; } -struct gphy t1_my3126_ops = { - my3126_phy_create, - my3126_phy_reset +const struct gphy t1_my3126_ops = { + .create = my3126_phy_create, + .reset = my3126_phy_reset }; diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c index 69129ed..678778a 100644 --- a/drivers/net/chelsio/pm3393.c +++ b/drivers/net/chelsio/pm3393.c @@ -807,8 +807,8 @@ static int pm3393_mac_reset(adapter_t * adapter) return successful_reset ? 0 : 1; } -struct gmac t1_pm3393_ops = { - STATS_TICK_SECS, - pm3393_mac_create, - pm3393_mac_reset +const struct gmac t1_pm3393_ops = { + .stats_update_period = STATS_TICK_SECS, + .create = pm3393_mac_create, + .reset = pm3393_mac_reset, }; diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c index 89a68270..e4f874a 100644 --- a/drivers/net/chelsio/sge.c +++ b/drivers/net/chelsio/sge.c @@ -1062,7 +1062,7 @@ static inline struct sk_buff *get_packet(struct pci_dev *pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), PCI_DMA_FROMDEVICE); - memcpy(skb->data, ce->skb->data, len); + skb_copy_from_linear_data(ce->skb, skb->data, len); pci_dma_sync_single_for_device(pdev, pci_unmap_addr(ce, dma_addr), pci_unmap_len(ce, dma_len), @@ -1379,12 +1379,11 @@ static void sge_rx(struct sge *sge, struct freelQ *fl, unsigned int len) } __skb_pull(skb, sizeof(*p)); - skb->dev = adapter->port[p->iff].dev; skb->dev->last_rx = jiffies; st = per_cpu_ptr(sge->port_stats[p->iff], smp_processor_id()); st->rx_packets++; - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, adapter->port[p->iff].dev); if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff && skb->protocol == htons(ETH_P_IP) && (skb->data[9] == IPPROTO_TCP || skb->data[9] == IPPROTO_UDP)) { @@ -1696,6 +1695,7 @@ irqreturn_t t1_interrupt(int irq, void *cookie) { int work_done; struct adapter *adapter = cookie; + struct respQ *Q = &adapter->sge->respQ; spin_lock(&adapter->async_lock); @@ -1865,14 +1865,14 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) ++st->tx_tso; - eth_type = skb->nh.raw - skb->data == ETH_HLEN ? + eth_type = skb_network_offset(skb) == ETH_HLEN ? CPL_ETH_II : CPL_ETH_II_VLAN; hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr)); hdr->opcode = CPL_TX_PKT_LSO; hdr->ip_csum_dis = hdr->l4_csum_dis = 0; - hdr->ip_hdr_words = skb->nh.iph->ihl; - hdr->tcp_hdr_words = skb->h.th->doff; + hdr->ip_hdr_words = ip_hdr(skb)->ihl; + hdr->tcp_hdr_words = tcp_hdr(skb)->doff; hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type, skb_shinfo(skb)->gso_size)); hdr->len = htonl(skb->len - sizeof(*hdr)); @@ -1912,7 +1912,7 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) if (!(adapter->flags & UDP_CSUM_CAPABLE) && skb->ip_summed == CHECKSUM_PARTIAL && - skb->nh.iph->protocol == IPPROTO_UDP) { + ip_hdr(skb)->protocol == IPPROTO_UDP) { if (unlikely(skb_checksum_help(skb))) { pr_debug("%s: unable to do udp checksum\n", dev->name); dev_kfree_skb_any(skb); @@ -1925,7 +1925,7 @@ int t1_start_xmit(struct sk_buff *skb, struct net_device *dev) */ if ((unlikely(!adapter->sge->espibug_skb[dev->if_port]))) { if (skb->protocol == htons(ETH_P_ARP) && - skb->nh.arph->ar_op == htons(ARPOP_REQUEST)) { + arp_hdr(skb)->ar_op == htons(ARPOP_REQUEST)) { adapter->sge->espibug_skb[dev->if_port] = skb; /* We want to re-use this skb later. We * simply bump the reference count and it @@ -2095,10 +2095,14 @@ static void espibug_workaround_t204(unsigned long data) 0x0, 0x7, 0x43, 0x0, 0x0, 0x0 }; - memcpy(skb->data + sizeof(struct cpl_tx_pkt), - ch_mac_addr, ETH_ALEN); - memcpy(skb->data + skb->len - 10, - ch_mac_addr, ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + sizeof(struct cpl_tx_pkt), + ch_mac_addr, + ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + skb->len - 10, + ch_mac_addr, + ETH_ALEN); skb->cb[0] = 0xff; } @@ -2125,10 +2129,14 @@ static void espibug_workaround(unsigned long data) if (!skb->cb[0]) { u8 ch_mac_addr[ETH_ALEN] = {0x0, 0x7, 0x43, 0x0, 0x0, 0x0}; - memcpy(skb->data + sizeof(struct cpl_tx_pkt), - ch_mac_addr, ETH_ALEN); - memcpy(skb->data + skb->len - 10, ch_mac_addr, - ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + sizeof(struct cpl_tx_pkt), + ch_mac_addr, + ETH_ALEN); + skb_copy_to_linear_data_offset(skb, + skb->len - 10, + ch_mac_addr, + ETH_ALEN); skb->cb[0] = 0xff; } diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c index c2522cd..7de9a61 100644 --- a/drivers/net/chelsio/subr.c +++ b/drivers/net/chelsio/subr.c @@ -321,10 +321,10 @@ static int mi1_mdio_write(adapter_t *adapter, int phy_addr, int mmd_addr, } #if defined(CONFIG_CHELSIO_T1_1G) || defined(CONFIG_CHELSIO_T1_COUGAR) -static struct mdio_ops mi1_mdio_ops = { - mi1_mdio_init, - mi1_mdio_read, - mi1_mdio_write +static const struct mdio_ops mi1_mdio_ops = { + .init = mi1_mdio_init, + .read = mi1_mdio_read, + .write = mi1_mdio_write }; #endif @@ -377,10 +377,10 @@ static int mi1_mdio_ext_write(adapter_t *adapter, int phy_addr, int mmd_addr, return 0; } -static struct mdio_ops mi1_mdio_ext_ops = { - mi1_mdio_init, - mi1_mdio_ext_read, - mi1_mdio_ext_write +static const struct mdio_ops mi1_mdio_ext_ops = { + .init = mi1_mdio_init, + .read = mi1_mdio_ext_read, + .write = mi1_mdio_ext_write }; enum { @@ -392,63 +392,136 @@ enum { CH_BRD_N204_4CU, }; -static struct board_info t1_board[] = { - -{ CHBT_BOARD_CHT110, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T1, - CHBT_MAC_PM3393, CHBT_PHY_MY3126, - 125000000/*clk-core*/, 150000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/, - 1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops, - &t1_my3126_ops, &mi1_mdio_ext_ops, - "Chelsio T110 1x10GBase-CX4 TOE" }, - -{ CHBT_BOARD_N110, 1/*ports#*/, - SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T1, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio N110 1x10GBaseX NIC" }, - -{ CHBT_BOARD_N210, 1/*ports#*/, - SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio N210 1x10GBaseX NIC" }, - -{ CHBT_BOARD_CHT210, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_88X2010, - 125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops, - &t1_mv88x201x_ops, &mi1_mdio_ext_ops, - "Chelsio T210 1x10GBaseX TOE" }, - -{ CHBT_BOARD_CHT210, 1/*ports#*/, - SUPPORTED_10000baseT_Full /*caps*/, CHBT_TERM_T2, - CHBT_MAC_PM3393, CHBT_PHY_MY3126, - 125000000/*clk-core*/, 133000000/*clk-mc3*/, 125000000/*clk-mc4*/, - 1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 1/*mdien*/, - 1/*mdiinv*/, 1/*mdc*/, 1/*phybaseaddr*/, &t1_pm3393_ops, - &t1_my3126_ops, &mi1_mdio_ext_ops, - "Chelsio T210 1x10GBase-CX4 TOE" }, +static const struct board_info t1_board[] = { + { + .board = CHBT_BOARD_CHT110, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T1, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_MY3126, + .clock_core = 125000000, + .clock_mc3 = 150000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 1, + .mdio_mdiinv = 1, + .mdio_mdc = 1, + .mdio_phybaseaddr = 1, + .gmac = &t1_pm3393_ops, + .gphy = &t1_my3126_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T110 1x10GBase-CX4 TOE", + }, + + { + .board = CHBT_BOARD_N110, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, + .chip_term = CHBT_TERM_T1, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio N110 1x10GBaseX NIC", + }, + + { + .board = CHBT_BOARD_N210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio N210 1x10GBaseX NIC", + }, + + { + .board = CHBT_BOARD_CHT210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_88X2010, + .clock_core = 125000000, + .clock_mc3 = 133000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 1, + .mdio_phybaseaddr = 0, + .gmac = &t1_pm3393_ops, + .gphy = &t1_mv88x201x_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T210 1x10GBaseX TOE", + }, + + { + .board = CHBT_BOARD_CHT210, + .port_number = 1, + .caps = SUPPORTED_10000baseT_Full, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_PM3393, + .chip_phy = CHBT_PHY_MY3126, + .clock_core = 125000000, + .clock_mc3 = 133000000, + .clock_mc4 = 125000000, + .espi_nports = 1, + .clock_elmer0 = 44, + .mdio_mdien = 1, + .mdio_mdiinv = 1, + .mdio_mdc = 1, + .mdio_phybaseaddr = 1, + .gmac = &t1_pm3393_ops, + .gphy = &t1_my3126_ops, + .mdio_ops = &mi1_mdio_ext_ops, + .desc = "Chelsio T210 1x10GBase-CX4 TOE", + }, #ifdef CONFIG_CHELSIO_T1_1G -{ CHBT_BOARD_CHN204, 4/*ports#*/, - SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | - SUPPORTED_100baseT_Full | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | - SUPPORTED_PAUSE | SUPPORTED_TP /*caps*/, CHBT_TERM_T2, CHBT_MAC_VSC7321, CHBT_PHY_88E1111, - 100000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/, - 4/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/, - 0/*mdiinv*/, 1/*mdc*/, 4/*phybaseaddr*/, &t1_vsc7326_ops, - &t1_mv88e1xxx_ops, &mi1_mdio_ops, - "Chelsio N204 4x100/1000BaseT NIC" }, + { + .board = CHBT_BOARD_CHN204, + .port_number = 4, + .caps = SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full + | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full + | SUPPORTED_1000baseT_Full | SUPPORTED_Autoneg | + SUPPORTED_PAUSE | SUPPORTED_TP, + .chip_term = CHBT_TERM_T2, + .chip_mac = CHBT_MAC_VSC7321, + .chip_phy = CHBT_PHY_88E1111, + .clock_core = 100000000, + .espi_nports = 4, + .clock_elmer0 = 44, + .mdio_mdien = 0, + .mdio_mdiinv = 0, + .mdio_mdc = 0, + .mdio_phybaseaddr = 4, + .gmac = &t1_vsc7326_ops, + .gphy = &t1_mv88e1xxx_ops, + .mdio_ops = &mi1_mdio_ops, + .desc = "Chelsio N204 4x100/1000BaseT NIC", + }, #endif }; diff --git a/drivers/net/chelsio/vsc7326.c b/drivers/net/chelsio/vsc7326.c index 534ffa0..99b51f6 100644 --- a/drivers/net/chelsio/vsc7326.c +++ b/drivers/net/chelsio/vsc7326.c @@ -723,7 +723,7 @@ static int vsc7326_mac_reset(adapter_t *adapter) return 0; } -struct gmac t1_vsc7326_ops = { +const struct gmac t1_vsc7326_ops = { .stats_update_period = STATS_TICK_SECS, .create = vsc7326_mac_create, .reset = vsc7326_mac_reset, diff --git a/drivers/net/chelsio/vsc8244.c b/drivers/net/chelsio/vsc8244.c deleted file mode 100644 index 251d485..0000000 --- a/drivers/net/chelsio/vsc8244.c +++ /dev/null @@ -1,367 +0,0 @@ -/* - * This file is part of the Chelsio T2 Ethernet driver. - * - * Copyright (C) 2005 Chelsio Communications. All rights reserved. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE file included in this - * release for licensing terms and conditions. - */ - -#include "common.h" -#include "cphy.h" -#include "elmer0.h" - -#ifndef ADVERTISE_PAUSE_CAP -# define ADVERTISE_PAUSE_CAP 0x400 -#endif -#ifndef ADVERTISE_PAUSE_ASYM -# define ADVERTISE_PAUSE_ASYM 0x800 -#endif - -/* Gigabit MII registers */ -#ifndef MII_CTRL1000 -# define MII_CTRL1000 9 -#endif - -#ifndef ADVERTISE_1000FULL -# define ADVERTISE_1000FULL 0x200 -# define ADVERTISE_1000HALF 0x100 -#endif - -/* VSC8244 PHY specific registers. */ -enum { - VSC8244_INTR_ENABLE = 25, - VSC8244_INTR_STATUS = 26, - VSC8244_AUX_CTRL_STAT = 28, -}; - -enum { - VSC_INTR_RX_ERR = 1 << 0, - VSC_INTR_MS_ERR = 1 << 1, /* master/slave resolution error */ - VSC_INTR_CABLE = 1 << 2, /* cable impairment */ - VSC_INTR_FALSE_CARR = 1 << 3, /* false carrier */ - VSC_INTR_MEDIA_CHG = 1 << 4, /* AMS media change */ - VSC_INTR_RX_FIFO = 1 << 5, /* Rx FIFO over/underflow */ - VSC_INTR_TX_FIFO = 1 << 6, /* Tx FIFO over/underflow */ - VSC_INTR_DESCRAMBL = 1 << 7, /* descrambler lock-lost */ - VSC_INTR_SYMBOL_ERR = 1 << 8, /* symbol error */ - VSC_INTR_NEG_DONE = 1 << 10, /* autoneg done */ - VSC_INTR_NEG_ERR = 1 << 11, /* autoneg error */ - VSC_INTR_LINK_CHG = 1 << 13, /* link change */ - VSC_INTR_ENABLE = 1 << 15, /* interrupt enable */ -}; - -#define CFG_CHG_INTR_MASK (VSC_INTR_LINK_CHG | VSC_INTR_NEG_ERR | \ - VSC_INTR_NEG_DONE) -#define INTR_MASK (CFG_CHG_INTR_MASK | VSC_INTR_TX_FIFO | VSC_INTR_RX_FIFO | \ - VSC_INTR_ENABLE) - -/* PHY specific auxiliary control & status register fields */ -#define S_ACSR_ACTIPHY_TMR 0 -#define M_ACSR_ACTIPHY_TMR 0x3 -#define V_ACSR_ACTIPHY_TMR(x) ((x) << S_ACSR_ACTIPHY_TMR) - -#define S_ACSR_SPEED 3 -#define M_ACSR_SPEED 0x3 -#define G_ACSR_SPEED(x) (((x) >> S_ACSR_SPEED) & M_ACSR_SPEED) - -#define S_ACSR_DUPLEX 5 -#define F_ACSR_DUPLEX (1 << S_ACSR_DUPLEX) - -#define S_ACSR_ACTIPHY 6 -#define F_ACSR_ACTIPHY (1 << S_ACSR_ACTIPHY) - -/* - * Reset the PHY. This PHY completes reset immediately so we never wait. - */ -static int vsc8244_reset(struct cphy *cphy, int wait) -{ - int err; - unsigned int ctl; - - err = simple_mdio_read(cphy, MII_BMCR, &ctl); - if (err) - return err; - - ctl &= ~BMCR_PDOWN; - ctl |= BMCR_RESET; - return simple_mdio_write(cphy, MII_BMCR, ctl); -} - -static int vsc8244_intr_enable(struct cphy *cphy) -{ - simple_mdio_write(cphy, VSC8244_INTR_ENABLE, INTR_MASK); - - /* Enable interrupts through Elmer */ - if (t1_is_asic(cphy->adapter)) { - u32 elmer; - - t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); - elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); - } - - return 0; -} - -static int vsc8244_intr_disable(struct cphy *cphy) -{ - simple_mdio_write(cphy, VSC8244_INTR_ENABLE, 0); - - if (t1_is_asic(cphy->adapter)) { - u32 elmer; - - t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer); - elmer &= ~ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer &= ~(ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4); - t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer); - } - - return 0; -} - -static int vsc8244_intr_clear(struct cphy *cphy) -{ - u32 val; - u32 elmer; - - /* Clear PHY interrupts by reading the register. */ - simple_mdio_read(cphy, VSC8244_INTR_ENABLE, &val); - - if (t1_is_asic(cphy->adapter)) { - t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer); - elmer |= ELMER0_GP_BIT1; - if (is_T2(cphy->adapter)) - elmer |= ELMER0_GP_BIT2|ELMER0_GP_BIT3|ELMER0_GP_BIT4; - t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer); - } - - return 0; -} - -/* - * Force the PHY speed and duplex. This also disables auto-negotiation, except - * for 1Gb/s, where auto-negotiation is mandatory. - */ -static int vsc8244_set_speed_duplex(struct cphy *phy, int speed, int duplex) -{ - int err; - unsigned int ctl; - - err = simple_mdio_read(phy, MII_BMCR, &ctl); - if (err) - return err; - - if (speed >= 0) { - ctl &= ~(BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); - if (speed == SPEED_100) - ctl |= BMCR_SPEED100; - else if (speed == SPEED_1000) - ctl |= BMCR_SPEED1000; - } - if (duplex >= 0) { - ctl &= ~(BMCR_FULLDPLX | BMCR_ANENABLE); - if (duplex == DUPLEX_FULL) - ctl |= BMCR_FULLDPLX; - } - if (ctl & BMCR_SPEED1000) /* auto-negotiation required for 1Gb/s */ - ctl |= BMCR_ANENABLE; - return simple_mdio_write(phy, MII_BMCR, ctl); -} - -int t1_mdio_set_bits(struct cphy *phy, int mmd, int reg, unsigned int bits) -{ - int ret; - unsigned int val; - - ret = mdio_read(phy, mmd, reg, &val); - if (!ret) - ret = mdio_write(phy, mmd, reg, val | bits); - return ret; -} - -static int vsc8244_autoneg_enable(struct cphy *cphy) -{ - return t1_mdio_set_bits(cphy, 0, MII_BMCR, - BMCR_ANENABLE | BMCR_ANRESTART); -} - -static int vsc8244_autoneg_restart(struct cphy *cphy) -{ - return t1_mdio_set_bits(cphy, 0, MII_BMCR, BMCR_ANRESTART); -} - -static int vsc8244_advertise(struct cphy *phy, unsigned int advertise_map) -{ - int err; - unsigned int val = 0; - - err = simple_mdio_read(phy, MII_CTRL1000, &val); - if (err) - return err; - - val &= ~(ADVERTISE_1000HALF | ADVERTISE_1000FULL); - if (advertise_map & ADVERTISED_1000baseT_Half) - val |= ADVERTISE_1000HALF; - if (advertise_map & ADVERTISED_1000baseT_Full) - val |= ADVERTISE_1000FULL; - - err = simple_mdio_write(phy, MII_CTRL1000, val); - if (err) - return err; - - val = 1; - if (advertise_map & ADVERTISED_10baseT_Half) - val |= ADVERTISE_10HALF; - if (advertise_map & ADVERTISED_10baseT_Full) - val |= ADVERTISE_10FULL; - if (advertise_map & ADVERTISED_100baseT_Half) - val |= ADVERTISE_100HALF; - if (advertise_map & ADVERTISED_100baseT_Full) - val |= ADVERTISE_100FULL; - if (advertise_map & ADVERTISED_PAUSE) - val |= ADVERTISE_PAUSE_CAP; - if (advertise_map & ADVERTISED_ASYM_PAUSE) - val |= ADVERTISE_PAUSE_ASYM; - return simple_mdio_write(phy, MII_ADVERTISE, val); -} - -static int vsc8244_get_link_status(struct cphy *cphy, int *link_ok, - int *speed, int *duplex, int *fc) -{ - unsigned int bmcr, status, lpa, adv; - int err, sp = -1, dplx = -1, pause = 0; - - err = simple_mdio_read(cphy, MII_BMCR, &bmcr); - if (!err) - err = simple_mdio_read(cphy, MII_BMSR, &status); - if (err) - return err; - - if (link_ok) { - /* - * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it - * once more to get the current link state. - */ - if (!(status & BMSR_LSTATUS)) - err = simple_mdio_read(cphy, MII_BMSR, &status); - if (err) - return err; - *link_ok = (status & BMSR_LSTATUS) != 0; - } - if (!(bmcr & BMCR_ANENABLE)) { - dplx = (bmcr & BMCR_FULLDPLX) ? DUPLEX_FULL : DUPLEX_HALF; - if (bmcr & BMCR_SPEED1000) - sp = SPEED_1000; - else if (bmcr & BMCR_SPEED100) - sp = SPEED_100; - else - sp = SPEED_10; - } else if (status & BMSR_ANEGCOMPLETE) { - err = simple_mdio_read(cphy, VSC8244_AUX_CTRL_STAT, &status); - if (err) - return err; - - dplx = (status & F_ACSR_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; - sp = G_ACSR_SPEED(status); - if (sp == 0) - sp = SPEED_10; - else if (sp == 1) - sp = SPEED_100; - else - sp = SPEED_1000; - - if (fc && dplx == DUPLEX_FULL) { - err = simple_mdio_read(cphy, MII_LPA, &lpa); - if (!err) - err = simple_mdio_read(cphy, MII_ADVERTISE, - &adv); - if (err) - return err; - - if (lpa & adv & ADVERTISE_PAUSE_CAP) - pause = PAUSE_RX | PAUSE_TX; - else if ((lpa & ADVERTISE_PAUSE_CAP) && - (lpa & ADVERTISE_PAUSE_ASYM) && - (adv & ADVERTISE_PAUSE_ASYM)) - pause = PAUSE_TX; - else if ((lpa & ADVERTISE_PAUSE_ASYM) && - (adv & ADVERTISE_PAUSE_CAP)) - pause = PAUSE_RX; - } - } - if (speed) - *speed = sp; - if (duplex) - *duplex = dplx; - if (fc) - *fc = pause; - return 0; -} - -static int vsc8244_intr_handler(struct cphy *cphy) -{ - unsigned int cause; - int err, cphy_cause = 0; - - err = simple_mdio_read(cphy, VSC8244_INTR_STATUS, &cause); - if (err) - return err; - - cause &= INTR_MASK; - if (cause & CFG_CHG_INTR_MASK) - cphy_cause |= cphy_cause_link_change; - if (cause & (VSC_INTR_RX_FIFO | VSC_INTR_TX_FIFO)) - cphy_cause |= cphy_cause_fifo_error; - return cphy_cause; -} - -static void vsc8244_destroy(struct cphy *cphy) -{ - kfree(cphy); -} - -static struct cphy_ops vsc8244_ops = { - .destroy = vsc8244_destroy, - .reset = vsc8244_reset, - .interrupt_enable = vsc8244_intr_enable, - .interrupt_disable = vsc8244_intr_disable, - .interrupt_clear = vsc8244_intr_clear, - .interrupt_handler = vsc8244_intr_handler, - .autoneg_enable = vsc8244_autoneg_enable, - .autoneg_restart = vsc8244_autoneg_restart, - .advertise = vsc8244_advertise, - .set_speed_duplex = vsc8244_set_speed_duplex, - .get_link_status = vsc8244_get_link_status -}; - -static struct cphy* vsc8244_phy_create(adapter_t *adapter, int phy_addr, - struct mdio_ops *mdio_ops) -{ - struct cphy *cphy = kzalloc(sizeof(*cphy), GFP_KERNEL); - - if (!cphy) - return NULL; - - cphy_init(cphy, adapter, phy_addr, &vsc8244_ops, mdio_ops); - - return cphy; -} - - -static int vsc8244_phy_reset(adapter_t* adapter) -{ - return 0; -} - -struct gphy t1_vsc8244_ops = { - vsc8244_phy_create, - vsc8244_phy_reset -}; - - diff --git a/drivers/net/chelsio/vsc8244_reg.h b/drivers/net/chelsio/vsc8244_reg.h deleted file mode 100644 index d3c1829..0000000 --- a/drivers/net/chelsio/vsc8244_reg.h +++ /dev/null @@ -1,172 +0,0 @@ -/* $Date: 2005/11/23 16:28:53 $ $RCSfile: vsc8244_reg.h,v $ $Revision: 1.1 $ */ -#ifndef CHELSIO_MV8E1XXX_H -#define CHELSIO_MV8E1XXX_H - -#ifndef BMCR_SPEED1000 -# define BMCR_SPEED1000 0x40 -#endif - -#ifndef ADVERTISE_PAUSE -# define ADVERTISE_PAUSE 0x400 -#endif -#ifndef ADVERTISE_PAUSE_ASYM -# define ADVERTISE_PAUSE_ASYM 0x800 -#endif - -/* Gigabit MII registers */ -#define MII_GBMR 1 /* 1000Base-T mode register */ -#define MII_GBCR 9 /* 1000Base-T control register */ -#define MII_GBSR 10 /* 1000Base-T status register */ - -/* 1000Base-T control register fields */ -#define GBCR_ADV_1000HALF 0x100 -#define GBCR_ADV_1000FULL 0x200 -#define GBCR_PREFER_MASTER 0x400 -#define GBCR_MANUAL_AS_MASTER 0x800 -#define GBCR_MANUAL_CONFIG_ENABLE 0x1000 - -/* 1000Base-T status register fields */ -#define GBSR_LP_1000HALF 0x400 -#define GBSR_LP_1000FULL 0x800 -#define GBSR_REMOTE_OK 0x1000 -#define GBSR_LOCAL_OK 0x2000 -#define GBSR_LOCAL_MASTER 0x4000 -#define GBSR_MASTER_FAULT 0x8000 - -/* Vitesse PHY interrupt status bits. */ -#if 0 -#define VSC8244_INTR_JABBER 0x0001 -#define VSC8244_INTR_POLARITY_CHNG 0x0002 -#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010 -#define VSC8244_INTR_DOWNSHIFT 0x0020 -#define VSC8244_INTR_MDI_XOVER_CHNG 0x0040 -#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080 -#define VSC8244_INTR_FALSE_CARRIER 0x0100 -#define VSC8244_INTR_SYMBOL_ERROR 0x0200 -#define VSC8244_INTR_LINK_CHNG 0x0400 -#define VSC8244_INTR_AUTONEG_DONE 0x0800 -#define VSC8244_INTR_PAGE_RECV 0x1000 -#define VSC8244_INTR_DUPLEX_CHNG 0x2000 -#define VSC8244_INTR_SPEED_CHNG 0x4000 -#define VSC8244_INTR_AUTONEG_ERR 0x8000 -#else -//#define VSC8244_INTR_JABBER 0x0001 -//#define VSC8244_INTR_POLARITY_CHNG 0x0002 -//#define VSC8244_INTR_BIT2 0x0004 -//#define VSC8244_INTR_BIT3 0x0008 -#define VSC8244_INTR_RX_ERR 0x0001 -#define VSC8244_INTR_MASTER_SLAVE 0x0002 -#define VSC8244_INTR_CABLE_IMPAIRED 0x0004 -#define VSC8244_INTR_FALSE_CARRIER 0x0008 -//#define VSC8244_INTR_ENG_DETECT_CHNG 0x0010 -//#define VSC8244_INTR_DOWNSHIFT 0x0020 -//#define VSC8244_INTR_MDI_XOVER_CHNG 0x0040 -//#define VSC8244_INTR_FIFO_OVER_UNDER 0x0080 -#define VSC8244_INTR_BIT4 0x0010 -#define VSC8244_INTR_FIFO_RX 0x0020 -#define VSC8244_INTR_FIFO_OVER_UNDER 0x0040 -#define VSC8244_INTR_LOCK_LOST 0x0080 -//#define VSC8244_INTR_FALSE_CARRIER 0x0100 -//#define VSC8244_INTR_SYMBOL_ERROR 0x0200 -//#define VSC8244_INTR_LINK_CHNG 0x0400 -//#define VSC8244_INTR_AUTONEG_DONE 0x0800 -#define VSC8244_INTR_SYMBOL_ERROR 0x0100 -#define VSC8244_INTR_ENG_DETECT_CHNG 0x0200 -#define VSC8244_INTR_AUTONEG_DONE 0x0400 -#define VSC8244_INTR_AUTONEG_ERR 0x0800 -//#define VSC8244_INTR_PAGE_RECV 0x1000 -//#define VSC8244_INTR_DUPLEX_CHNG 0x2000 -//#define VSC8244_INTR_SPEED_CHNG 0x4000 -//#define VSC8244_INTR_AUTONEG_ERR 0x8000 -#define VSC8244_INTR_DUPLEX_CHNG 0x1000 -#define VSC8244_INTR_LINK_CHNG 0x2000 -#define VSC8244_INTR_SPEED_CHNG 0x4000 -#define VSC8244_INTR_STATUS 0x8000 -#endif - - -/* Vitesse PHY specific registers. */ -#define VSC8244_SPECIFIC_CNTRL_REGISTER 16 -#define VSC8244_SPECIFIC_STATUS_REGISTER 0x1c -#define VSC8244_INTERRUPT_ENABLE_REGISTER 0x19 -#define VSC8244_INTERRUPT_STATUS_REGISTER 0x1a -#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_REGISTER 20 -#define VSC8244_RECV_ERR_CNTR_REGISTER 21 -#define VSC8244_RES_REGISTER 22 -#define VSC8244_GLOBAL_STATUS_REGISTER 23 -#define VSC8244_LED_CONTROL_REGISTER 24 -#define VSC8244_MANUAL_LED_OVERRIDE_REGISTER 25 -#define VSC8244_EXT_PHY_SPECIFIC_CNTRL_2_REGISTER 26 -#define VSC8244_EXT_PHY_SPECIFIC_STATUS_REGISTER 27 -#define VSC8244_VIRTUAL_CABLE_TESTER_REGISTER 28 -#define VSC8244_EXTENDED_ADDR_REGISTER 29 -#define VSC8244_EXTENDED_REGISTER 30 - -/* PHY specific control register fields */ -#define S_PSCR_MDI_XOVER_MODE 5 -#define M_PSCR_MDI_XOVER_MODE 0x3 -#define V_PSCR_MDI_XOVER_MODE(x) ((x) << S_PSCR_MDI_XOVER_MODE) -#define G_PSCR_MDI_XOVER_MODE(x) (((x) >> S_PSCR_MDI_XOVER_MODE) & M_PSCR_MDI_XOVER_MODE) - -/* Extended PHY specific control register fields */ -#define S_DOWNSHIFT_ENABLE 8 -#define V_DOWNSHIFT_ENABLE (1 << S_DOWNSHIFT_ENABLE) - -#define S_DOWNSHIFT_CNT 9 -#define M_DOWNSHIFT_CNT 0x7 -#define V_DOWNSHIFT_CNT(x) ((x) << S_DOWNSHIFT_CNT) -#define G_DOWNSHIFT_CNT(x) (((x) >> S_DOWNSHIFT_CNT) & M_DOWNSHIFT_CNT) - -/* PHY specific status register fields */ -#define S_PSSR_JABBER 0 -#define V_PSSR_JABBER (1 << S_PSSR_JABBER) - -#define S_PSSR_POLARITY 1 -#define V_PSSR_POLARITY (1 << S_PSSR_POLARITY) - -#define S_PSSR_RX_PAUSE 2 -#define V_PSSR_RX_PAUSE (1 << S_PSSR_RX_PAUSE) - -#define S_PSSR_TX_PAUSE 3 -#define V_PSSR_TX_PAUSE (1 << S_PSSR_TX_PAUSE) - -#define S_PSSR_ENERGY_DETECT 4 -#define V_PSSR_ENERGY_DETECT (1 << S_PSSR_ENERGY_DETECT) - -#define S_PSSR_DOWNSHIFT_STATUS 5 -#define V_PSSR_DOWNSHIFT_STATUS (1 << S_PSSR_DOWNSHIFT_STATUS) - -#define S_PSSR_MDI 6 -#define V_PSSR_MDI (1 << S_PSSR_MDI) - -#define S_PSSR_CABLE_LEN 7 -#define M_PSSR_CABLE_LEN 0x7 -#define V_PSSR_CABLE_LEN(x) ((x) << S_PSSR_CABLE_LEN) -#define G_PSSR_CABLE_LEN(x) (((x) >> S_PSSR_CABLE_LEN) & M_PSSR_CABLE_LEN) - -//#define S_PSSR_LINK 10 -//#define S_PSSR_LINK 13 -#define S_PSSR_LINK 2 -#define V_PSSR_LINK (1 << S_PSSR_LINK) - -//#define S_PSSR_STATUS_RESOLVED 11 -//#define S_PSSR_STATUS_RESOLVED 10 -#define S_PSSR_STATUS_RESOLVED 15 -#define V_PSSR_STATUS_RESOLVED (1 << S_PSSR_STATUS_RESOLVED) - -#define S_PSSR_PAGE_RECEIVED 12 -#define V_PSSR_PAGE_RECEIVED (1 << S_PSSR_PAGE_RECEIVED) - -//#define S_PSSR_DUPLEX 13 -//#define S_PSSR_DUPLEX 12 -#define S_PSSR_DUPLEX 5 -#define V_PSSR_DUPLEX (1 << S_PSSR_DUPLEX) - -//#define S_PSSR_SPEED 14 -//#define S_PSSR_SPEED 14 -#define S_PSSR_SPEED 3 -#define M_PSSR_SPEED 0x3 -#define V_PSSR_SPEED(x) ((x) << S_PSSR_SPEED) -#define G_PSSR_SPEED(x) (((x) >> S_PSSR_SPEED) & M_PSSR_SPEED) - -#endif diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c index 8eb5712..5bdf5ca 100644 --- a/drivers/net/cris/eth_v10.c +++ b/drivers/net/cris/eth_v10.c @@ -1348,7 +1348,8 @@ e100_rx(struct net_device *dev) #ifdef ETHDEBUG printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n", - skb->head, skb->data, skb->tail, skb->end); + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb)); printk("copying packet to 0x%x.\n", skb_data_ptr); #endif @@ -1375,7 +1376,6 @@ e100_rx(struct net_device *dev) myNextRxDesc->descr.buf = L1_CACHE_ALIGN(virt_to_phys(myNextRxDesc->skb->data)); } - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); /* Send the packet to the upper layers */ diff --git a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c index 4612f71..9774bb1 100644 --- a/drivers/net/cs89x0.c +++ b/drivers/net/cs89x0.c @@ -1004,7 +1004,6 @@ skip_this_frame: return; } skb_reserve(skb, 2); /* longword align L3 header */ - skb->dev = dev; if (bp + length > lp->end_dma_buff) { int semi_cnt = lp->end_dma_buff - bp; @@ -1702,7 +1701,6 @@ net_rx(struct net_device *dev) return; } skb_reserve(skb, 2); /* longword align L3 header */ - skb->dev = dev; readwords(ioaddr, RX_FRAME_PORT, skb_put(skb, length), length >> 1); if (length & 1) diff --git a/drivers/net/cxgb3/adapter.h b/drivers/net/cxgb3/adapter.h index 5c97a64..80c3d8f 100644 --- a/drivers/net/cxgb3/adapter.h +++ b/drivers/net/cxgb3/adapter.h @@ -74,6 +74,11 @@ enum { /* adapter flags */ struct rx_desc; struct rx_sw_desc; +struct sge_fl_page { + struct skb_frag_struct frag; + unsigned char *va; +}; + struct sge_fl { /* SGE per free-buffer list state */ unsigned int buf_size; /* size of each Rx buffer */ unsigned int credits; /* # of available Rx buffers */ @@ -81,11 +86,13 @@ struct sge_fl { /* SGE per free-buffer list state */ unsigned int cidx; /* consumer index */ unsigned int pidx; /* producer index */ unsigned int gen; /* free list generation */ + unsigned int cntxt_id; /* SGE context id for the free list */ + struct sge_fl_page page; struct rx_desc *desc; /* address of HW Rx descriptor ring */ struct rx_sw_desc *sdesc; /* address of SW Rx descriptor ring */ dma_addr_t phys_addr; /* physical address of HW ring start */ - unsigned int cntxt_id; /* SGE context id for the free list */ unsigned long empty; /* # of times queue ran out of buffers */ + unsigned long alloc_failed; /* # of times buffer allocation failed */ }; /* @@ -121,6 +128,8 @@ struct sge_rspq { /* state for an SGE response queue */ unsigned long empty; /* # of times queue ran out of credits */ unsigned long nomem; /* # of responses deferred due to no mem */ unsigned long unhandled_irqs; /* # of spurious intrs */ + unsigned long starved; + unsigned long restarted; }; struct tx_desc; diff --git a/drivers/net/cxgb3/common.h b/drivers/net/cxgb3/common.h index e23deeb..8d13796 100644 --- a/drivers/net/cxgb3/common.h +++ b/drivers/net/cxgb3/common.h @@ -112,8 +112,7 @@ enum { }; enum { - SUPPORTED_OFFLOAD = 1 << 24, - SUPPORTED_IRQ = 1 << 25 + SUPPORTED_IRQ = 1 << 24 }; enum { /* adapter interrupt-maintained statistics */ @@ -260,6 +259,10 @@ struct mac_stats { unsigned long serdes_signal_loss; unsigned long xaui_pcs_ctc_err; unsigned long xaui_pcs_align_change; + + unsigned long num_toggled; /* # times toggled TxEn due to stuck TX */ + unsigned long num_resets; /* # times reset due to stuck TX */ + }; struct tp_mib_stats { @@ -354,6 +357,9 @@ enum { MC5_MODE_72_BIT = 2 }; +/* MC5 min active region size */ +enum { MC5_MIN_TIDS = 16 }; + struct vpd_params { unsigned int cclk; unsigned int mclk; @@ -398,6 +404,13 @@ struct adapter_params { unsigned int stats_update_period; /* MAC stats accumulation period */ unsigned int linkpoll_period; /* link poll period in 0.1s */ unsigned int rev; /* chip revision */ + unsigned int offload; +}; + +enum { /* chip revisions */ + T3_REV_A = 0, + T3_REV_B = 2, + T3_REV_B2 = 3, }; struct trace_params { @@ -465,6 +478,13 @@ struct cmac { struct adapter *adapter; unsigned int offset; unsigned int nucast; /* # of address filters for unicast MACs */ + unsigned int tx_tcnt; + unsigned int tx_xcnt; + u64 tx_mcnt; + unsigned int rx_xcnt; + u64 rx_mcnt; + unsigned int toggle_cnt; + unsigned int txen; struct mac_stats stats; }; @@ -588,7 +608,7 @@ static inline int is_10G(const struct adapter *adap) static inline int is_offload(const struct adapter *adap) { - return adapter_info(adap)->caps & SUPPORTED_OFFLOAD; + return adap->params.offload; } static inline unsigned int core_ticks_per_usec(const struct adapter *adap) @@ -666,6 +686,7 @@ int t3_mac_set_address(struct cmac *mac, unsigned int idx, u8 addr[6]); int t3_mac_set_num_ucast(struct cmac *mac, int n); const struct mac_stats *t3_mac_update_stats(struct cmac *mac); int t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc); +int t3b2_mac_watchdog_task(struct cmac *mac); void t3_mc5_prep(struct adapter *adapter, struct mc5 *mc5, int mode); int t3_mc5_init(struct mc5 *mc5, unsigned int nservers, unsigned int nfilters, diff --git a/drivers/net/cxgb3/cxgb3_defs.h b/drivers/net/cxgb3/cxgb3_defs.h index 16e0049..483a594 100644 --- a/drivers/net/cxgb3/cxgb3_defs.h +++ b/drivers/net/cxgb3/cxgb3_defs.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -68,7 +67,10 @@ static inline union listen_entry *stid2entry(const struct tid_info *t, static inline struct t3c_tid_entry *lookup_tid(const struct tid_info *t, unsigned int tid) { - return tid < t->ntids ? &(t->tid_tab[tid]) : NULL; + struct t3c_tid_entry *t3c_tid = tid < t->ntids ? + &(t->tid_tab[tid]) : NULL; + + return (t3c_tid && t3c_tid->client) ? t3c_tid : NULL; } /* diff --git a/drivers/net/cxgb3/cxgb3_ioctl.h b/drivers/net/cxgb3/cxgb3_ioctl.h index a942818..0a82fcd 100644 --- a/drivers/net/cxgb3/cxgb3_ioctl.h +++ b/drivers/net/cxgb3/cxgb3_ioctl.h @@ -36,28 +36,17 @@ * Ioctl commands specific to this driver. */ enum { - CHELSIO_SETREG = 1024, - CHELSIO_GETREG, - CHELSIO_SETTPI, - CHELSIO_GETTPI, - CHELSIO_GETMTUTAB, - CHELSIO_SETMTUTAB, - CHELSIO_GETMTU, - CHELSIO_SET_PM, - CHELSIO_GET_PM, - CHELSIO_GET_TCAM, - CHELSIO_SET_TCAM, - CHELSIO_GET_TCB, - CHELSIO_GET_MEM, - CHELSIO_LOAD_FW, - CHELSIO_GET_PROTO, - CHELSIO_SET_PROTO, - CHELSIO_SET_TRACE_FILTER, - CHELSIO_SET_QSET_PARAMS, - CHELSIO_GET_QSET_PARAMS, - CHELSIO_SET_QSET_NUM, - CHELSIO_GET_QSET_NUM, - CHELSIO_SET_PKTSCHED, + CHELSIO_GETMTUTAB = 1029, + CHELSIO_SETMTUTAB = 1030, + CHELSIO_SET_PM = 1032, + CHELSIO_GET_PM = 1033, + CHELSIO_GET_MEM = 1038, + CHELSIO_LOAD_FW = 1041, + CHELSIO_SET_TRACE_FILTER = 1044, + CHELSIO_SET_QSET_PARAMS = 1045, + CHELSIO_GET_QSET_PARAMS = 1046, + CHELSIO_SET_QSET_NUM = 1047, + CHELSIO_GET_QSET_NUM = 1048, }; struct ch_reg { diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 43583ed65..67b4b21 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -42,6 +42,7 @@ #include <linux/workqueue.h> #include <linux/proc_fs.h> #include <linux/rtnetlink.h> +#include <linux/firmware.h> #include <asm/uaccess.h> #include "common.h" @@ -184,16 +185,24 @@ void t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, int speed, int duplex, int pause) { struct net_device *dev = adapter->port[port_id]; + struct port_info *pi = netdev_priv(dev); + struct cmac *mac = &pi->mac; /* Skip changes from disabled ports. */ if (!netif_running(dev)) return; if (link_stat != netif_carrier_ok(dev)) { - if (link_stat) + if (link_stat) { + t3_mac_enable(mac, MAC_DIRECTION_RX); netif_carrier_on(dev); - else + } else { netif_carrier_off(dev); + pi->phy.ops->power_down(&pi->phy, 1); + t3_mac_disable(mac, MAC_DIRECTION_RX); + t3_link_start(&pi->phy, mac, &pi->link_config); + } + link_report(dev); } } @@ -406,7 +415,7 @@ static void quiesce_rx(struct adapter *adap) static int setup_sge_qsets(struct adapter *adap) { int i, j, err, irq_idx = 0, qset_idx = 0, dummy_dev_idx = 0; - unsigned int ntxq = is_offload(adap) ? SGE_TXQ_PER_SET : 1; + unsigned int ntxq = SGE_TXQ_PER_SET; if (adap->params.rev > 0 && !(adap->flags & USING_MSI)) irq_idx = -1; @@ -434,27 +443,25 @@ static int setup_sge_qsets(struct adapter *adap) static ssize_t attr_show(struct device *d, struct device_attribute *attr, char *buf, - ssize_t(*format) (struct adapter *, char *)) + ssize_t(*format) (struct net_device *, char *)) { ssize_t len; - struct adapter *adap = to_net_dev(d)->priv; /* Synchronize with ioctls that may shut down the device */ rtnl_lock(); - len = (*format) (adap, buf); + len = (*format) (to_net_dev(d), buf); rtnl_unlock(); return len; } static ssize_t attr_store(struct device *d, struct device_attribute *attr, const char *buf, size_t len, - ssize_t(*set) (struct adapter *, unsigned int), + ssize_t(*set) (struct net_device *, unsigned int), unsigned int min_val, unsigned int max_val) { char *endp; ssize_t ret; unsigned int val; - struct adapter *adap = to_net_dev(d)->priv; if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -464,7 +471,7 @@ static ssize_t attr_store(struct device *d, struct device_attribute *attr, return -EINVAL; rtnl_lock(); - ret = (*set) (adap, val); + ret = (*set) (to_net_dev(d), val); if (!ret) ret = len; rtnl_unlock(); @@ -472,8 +479,9 @@ static ssize_t attr_store(struct device *d, struct device_attribute *attr, } #define CXGB3_SHOW(name, val_expr) \ -static ssize_t format_##name(struct adapter *adap, char *buf) \ +static ssize_t format_##name(struct net_device *dev, char *buf) \ { \ + struct adapter *adap = dev->priv; \ return sprintf(buf, "%u\n", val_expr); \ } \ static ssize_t show_##name(struct device *d, struct device_attribute *attr, \ @@ -482,13 +490,17 @@ static ssize_t show_##name(struct device *d, struct device_attribute *attr, \ return attr_show(d, attr, buf, format_##name); \ } -static ssize_t set_nfilters(struct adapter *adap, unsigned int val) +static ssize_t set_nfilters(struct net_device *dev, unsigned int val) { + struct adapter *adap = dev->priv; + int min_tids = is_offload(adap) ? MC5_MIN_TIDS : 0; + if (adap->flags & FULL_INIT_DONE) return -EBUSY; if (val && adap->params.rev == 0) return -EINVAL; - if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers) + if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers - + min_tids) return -EINVAL; adap->params.mc5.nfilters = val; return 0; @@ -500,11 +512,14 @@ static ssize_t store_nfilters(struct device *d, struct device_attribute *attr, return attr_store(d, attr, buf, len, set_nfilters, 0, ~0); } -static ssize_t set_nservers(struct adapter *adap, unsigned int val) +static ssize_t set_nservers(struct net_device *dev, unsigned int val) { + struct adapter *adap = dev->priv; + if (adap->flags & FULL_INIT_DONE) return -EBUSY; - if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters) + if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters - + MC5_MIN_TIDS) return -EINVAL; adap->params.mc5.nservers = val; return 0; @@ -704,6 +719,28 @@ static void bind_qsets(struct adapter *adap) } } +#define FW_FNAME "t3fw-%d.%d.%d.bin" + +static int upgrade_fw(struct adapter *adap) +{ + int ret; + char buf[64]; + const struct firmware *fw; + struct device *dev = &adap->pdev->dev; + + snprintf(buf, sizeof(buf), FW_FNAME, FW_VERSION_MAJOR, + FW_VERSION_MINOR, FW_VERSION_MICRO); + ret = request_firmware(&fw, buf, dev); + if (ret < 0) { + dev_err(dev, "could not upgrade firmware: unable to load %s\n", + buf); + return ret; + } + ret = t3_load_fw(adap, fw->data, fw->size); + release_firmware(fw); + return ret; +} + /** * cxgb_up - enable the adapter * @adapter: adapter being enabled @@ -720,6 +757,8 @@ static int cxgb_up(struct adapter *adap) if (!(adap->flags & FULL_INIT_DONE)) { err = t3_check_fw_version(adap); + if (err == -EINVAL) + err = upgrade_fw(adap); if (err) goto out; @@ -731,6 +770,8 @@ static int cxgb_up(struct adapter *adap) if (err) goto out; + t3_write_reg(adap, A_ULPRX_TDDP_PSZ, V_HPZ0(PAGE_SHIFT - 12)); + err = setup_sge_qsets(adap); if (err) goto out; @@ -891,7 +932,7 @@ static int cxgb_open(struct net_device *dev) return err; set_bit(pi->port_id, &adapter->open_device_map); - if (!ofld_disable) { + if (is_offload(adapter) && !ofld_disable) { err = offload_open(dev); if (err) printk(KERN_WARNING @@ -1028,7 +1069,11 @@ static char stats_strings[][ETH_GSTRING_LEN] = { "VLANinsertions ", "TxCsumOffload ", "RxCsumGood ", - "RxDrops " + "RxDrops ", + + "CheckTXEnToggled ", + "CheckResets ", + }; static int get_stats_count(struct net_device *dev) @@ -1142,6 +1187,9 @@ static void get_stats(struct net_device *dev, struct ethtool_stats *stats, *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); *data++ = s->rx_cong_drops; + + *data++ = s->num_toggled; + *data++ = s->num_resets; } static inline void reg_block_dump(struct adapter *ap, void *buf, @@ -1359,23 +1407,27 @@ static int set_rx_csum(struct net_device *dev, u32 data) static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) { - struct adapter *adapter = dev->priv; + const struct adapter *adapter = dev->priv; + const struct port_info *pi = netdev_priv(dev); + const struct qset_params *q = &adapter->params.sge.qset[pi->first_qset]; e->rx_max_pending = MAX_RX_BUFFERS; e->rx_mini_max_pending = 0; e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS; e->tx_max_pending = MAX_TXQ_ENTRIES; - e->rx_pending = adapter->params.sge.qset[0].fl_size; - e->rx_mini_pending = adapter->params.sge.qset[0].rspq_size; - e->rx_jumbo_pending = adapter->params.sge.qset[0].jumbo_size; - e->tx_pending = adapter->params.sge.qset[0].txq_size[0]; + e->rx_pending = q->fl_size; + e->rx_mini_pending = q->rspq_size; + e->rx_jumbo_pending = q->jumbo_size; + e->tx_pending = q->txq_size[0]; } static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) { int i; + struct qset_params *q; struct adapter *adapter = dev->priv; + const struct port_info *pi = netdev_priv(dev); if (e->rx_pending > MAX_RX_BUFFERS || e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS || @@ -1390,9 +1442,8 @@ static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) if (adapter->flags & FULL_INIT_DONE) return -EBUSY; - for (i = 0; i < SGE_QSETS; ++i) { - struct qset_params *q = &adapter->params.sge.qset[i]; - + q = &adapter->params.sge.qset[pi->first_qset]; + for (i = 0; i < pi->nqsets; ++i, ++q) { q->rspq_size = e->rx_mini_pending; q->fl_size = e->rx_pending; q->jumbo_size = e->rx_jumbo_pending; @@ -1549,32 +1600,6 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) return -EFAULT; switch (cmd) { - case CHELSIO_SETREG:{ - struct ch_reg edata; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - if ((edata.addr & 3) != 0 - || edata.addr >= adapter->mmio_len) - return -EINVAL; - writel(edata.val, adapter->regs + edata.addr); - break; - } - case CHELSIO_GETREG:{ - struct ch_reg edata; - - if (copy_from_user(&edata, useraddr, sizeof(edata))) - return -EFAULT; - if ((edata.addr & 3) != 0 - || edata.addr >= adapter->mmio_len) - return -EINVAL; - edata.val = readl(adapter->regs + edata.addr); - if (copy_to_user(useraddr, &edata, sizeof(edata))) - return -EFAULT; - break; - } case CHELSIO_SET_QSET_PARAMS:{ int i; struct qset_params *q; @@ -1838,10 +1863,10 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) return -EINVAL; /* - * Version scheme: - * bits 0..9: chip version - * bits 10..15: chip revision - */ + * Version scheme: + * bits 0..9: chip version + * bits 10..15: chip revision + */ t.version = 3 | (adapter->params.rev << 10); if (copy_to_user(useraddr, &t, sizeof(t))) return -EFAULT; @@ -1890,20 +1915,6 @@ static int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) t.trace_rx); break; } - case CHELSIO_SET_PKTSCHED:{ - struct ch_pktsched_params p; - - if (!capable(CAP_NET_ADMIN)) - return -EPERM; - if (!adapter->open_device_map) - return -EAGAIN; /* uP and SGE must be running */ - if (copy_from_user(&p, useraddr, sizeof(p))) - return -EFAULT; - send_pktsched_cmd(adapter, p.sched, p.idx, p.min, p.max, - p.binding); - break; - - } default: return -EOPNOTSUPP; } @@ -2104,6 +2115,42 @@ static void check_link_status(struct adapter *adapter) } } +static void check_t3b2_mac(struct adapter *adapter) +{ + int i; + + if (!rtnl_trylock()) /* synchronize with ifdown */ + return; + + for_each_port(adapter, i) { + struct net_device *dev = adapter->port[i]; + struct port_info *p = netdev_priv(dev); + int status; + + if (!netif_running(dev)) + continue; + + status = 0; + if (netif_running(dev) && netif_carrier_ok(dev)) + status = t3b2_mac_watchdog_task(&p->mac); + if (status == 1) + p->mac.stats.num_toggled++; + else if (status == 2) { + struct cmac *mac = &p->mac; + + t3_mac_set_mtu(mac, dev->mtu); + t3_mac_set_address(mac, 0, dev->dev_addr); + cxgb_set_rxmode(dev); + t3_link_start(&p->phy, mac, &p->link_config); + t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); + t3_port_intr_enable(adapter, p->port_id); + p->mac.stats.num_resets++; + } + } + rtnl_unlock(); +} + + static void t3_adap_check_task(struct work_struct *work) { struct adapter *adapter = container_of(work, struct adapter, @@ -2124,6 +2171,9 @@ static void t3_adap_check_task(struct work_struct *work) adapter->check_task_cnt = 0; } + if (p->rev == T3_REV_B2) + check_t3b2_mac(adapter); + /* Schedule the next check update if any port is active. */ spin_lock(&adapter->work_lock); if (adapter->open_device_map & PORT_MASK) @@ -2232,9 +2282,9 @@ static void __devinit print_port_info(struct adapter *adap, if (!test_bit(i, &adap->registered_device_map)) continue; - printk(KERN_INFO "%s: %s %s RNIC (rev %d) %s%s\n", + printk(KERN_INFO "%s: %s %s %sNIC (rev %d) %s%s\n", dev->name, ai->desc, pi->port_type->desc, - adap->params.rev, buf, + is_offload(adap) ? "R" : "", adap->params.rev, buf, (adap->flags & USING_MSIX) ? " MSI-X" : (adap->flags & USING_MSI) ? " MSI" : ""); if (adap->name == dev->name && adap->params.vpd.mclk) diff --git a/drivers/net/cxgb3/cxgb3_offload.c b/drivers/net/cxgb3/cxgb3_offload.c index c6b7266..ebcf35e 100644 --- a/drivers/net/cxgb3/cxgb3_offload.c +++ b/drivers/net/cxgb3/cxgb3_offload.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU @@ -161,14 +160,16 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter, int i; for_each_port(adapter, i) { - const struct vlan_group *grp; + struct vlan_group *grp; struct net_device *dev = adapter->port[i]; const struct port_info *p = netdev_priv(dev); if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) { if (vlan && vlan != VLAN_VID_MASK) { grp = p->vlan_grp; - dev = grp ? grp->vlan_devices[vlan] : NULL; + dev = NULL; + if (grp) + dev = vlan_group_get_device(grp, vlan); } else while (dev->master) dev = dev->master; @@ -507,6 +508,7 @@ void cxgb3_queue_tid_release(struct t3cdev *tdev, unsigned int tid) spin_lock_bh(&td->tid_release_lock); p->ctx = (void *)td->tid_release_list; + p->client = NULL; td->tid_release_list = p; if (!p->ctx) schedule_work(&td->tid_release_task); @@ -552,7 +554,9 @@ int cxgb3_alloc_atid(struct t3cdev *tdev, struct cxgb3_client *client, struct tid_info *t = &(T3C_DATA(tdev))->tid_maps; spin_lock_bh(&t->atid_lock); - if (t->afree) { + if (t->afree && + t->atids_in_use + atomic_read(&t->tids_in_use) + MC5_MIN_TIDS <= + t->ntids) { union active_open_entry *p = t->afree; atid = (p - t->atid_tab) + t->atid_base; @@ -620,7 +624,8 @@ static int do_act_open_rpl(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid); - if (t3c_tid->ctx && t3c_tid->client && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client && + t3c_tid->client->handlers && t3c_tid->client->handlers[CPL_ACT_OPEN_RPL]) { return t3c_tid->client->handlers[CPL_ACT_OPEN_RPL] (dev, skb, t3c_tid-> @@ -639,7 +644,7 @@ static int do_stid_rpl(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_stid(&(T3C_DATA(dev))->tid_maps, stid); - if (t3c_tid->ctx && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers && t3c_tid->client->handlers[p->opcode]) { return t3c_tid->client->handlers[p->opcode] (dev, skb, t3c_tid->ctx); @@ -657,7 +662,7 @@ static int do_hwtid_rpl(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); - if (t3c_tid->ctx && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers && t3c_tid->client->handlers[p->opcode]) { return t3c_tid->client->handlers[p->opcode] (dev, skb, t3c_tid->ctx); @@ -686,6 +691,28 @@ static int do_cr(struct t3cdev *dev, struct sk_buff *skb) } } +/* + * Returns an sk_buff for a reply CPL message of size len. If the input + * sk_buff has no other users it is trimmed and reused, otherwise a new buffer + * is allocated. The input skb must be of size at least len. Note that this + * operation does not destroy the original skb data even if it decides to reuse + * the buffer. + */ +static struct sk_buff *cxgb3_get_cpl_reply_skb(struct sk_buff *skb, size_t len, + int gfp) +{ + if (likely(!skb_cloned(skb))) { + BUG_ON(skb->len < len); + __skb_trim(skb, len); + skb_get(skb); + } else { + skb = alloc_skb(len, gfp); + if (skb) + __skb_put(skb, len); + } + return skb; +} + static int do_abort_req_rss(struct t3cdev *dev, struct sk_buff *skb) { union opcode_tid *p = cplhdr(skb); @@ -693,30 +720,39 @@ static int do_abort_req_rss(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); - if (t3c_tid->ctx && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers && t3c_tid->client->handlers[p->opcode]) { return t3c_tid->client->handlers[p->opcode] (dev, skb, t3c_tid->ctx); } else { struct cpl_abort_req_rss *req = cplhdr(skb); struct cpl_abort_rpl *rpl; + struct sk_buff *reply_skb; + unsigned int tid = GET_TID(req); + u8 cmd = req->status; + + if (req->status == CPL_ERR_RTX_NEG_ADVICE || + req->status == CPL_ERR_PERSIST_NEG_ADVICE) + goto out; - struct sk_buff *skb = - alloc_skb(sizeof(struct cpl_abort_rpl), GFP_ATOMIC); - if (!skb) { + reply_skb = cxgb3_get_cpl_reply_skb(skb, + sizeof(struct + cpl_abort_rpl), + GFP_ATOMIC); + + if (!reply_skb) { printk("do_abort_req_rss: couldn't get skb!\n"); goto out; } - skb->priority = CPL_PRIORITY_DATA; - __skb_put(skb, sizeof(struct cpl_abort_rpl)); - rpl = cplhdr(skb); + reply_skb->priority = CPL_PRIORITY_DATA; + __skb_put(reply_skb, sizeof(struct cpl_abort_rpl)); + rpl = cplhdr(reply_skb); rpl->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_OFLD_HOST_ABORT_CON_RPL)); - rpl->wr.wr_lo = htonl(V_WR_TID(GET_TID(req))); - OPCODE_TID(rpl) = - htonl(MK_OPCODE_TID(CPL_ABORT_RPL, GET_TID(req))); - rpl->cmd = req->status; - cxgb3_ofld_send(dev, skb); + rpl->wr.wr_lo = htonl(V_WR_TID(tid)); + OPCODE_TID(rpl) = htonl(MK_OPCODE_TID(CPL_ABORT_RPL, tid)); + rpl->cmd = cmd; + cxgb3_ofld_send(dev, reply_skb); out: return CPL_RET_BUF_DONE; } @@ -729,7 +765,7 @@ static int do_act_establish(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_atid(&(T3C_DATA(dev))->tid_maps, atid); - if (t3c_tid->ctx && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers && t3c_tid->client->handlers[CPL_ACT_ESTABLISH]) { return t3c_tid->client->handlers[CPL_ACT_ESTABLISH] (dev, skb, t3c_tid->ctx); @@ -740,17 +776,6 @@ static int do_act_establish(struct t3cdev *dev, struct sk_buff *skb) } } -static int do_set_tcb_rpl(struct t3cdev *dev, struct sk_buff *skb) -{ - struct cpl_set_tcb_rpl *rpl = cplhdr(skb); - - if (rpl->status != CPL_ERR_NONE) - printk(KERN_ERR - "Unexpected SET_TCB_RPL status %u for tid %u\n", - rpl->status, GET_TID(rpl)); - return CPL_RET_BUF_DONE; -} - static int do_trace(struct t3cdev *dev, struct sk_buff *skb) { struct cpl_trace_pkt *p = cplhdr(skb); @@ -758,7 +783,7 @@ static int do_trace(struct t3cdev *dev, struct sk_buff *skb) skb->protocol = htons(0xffff); skb->dev = dev->lldev; skb_pull(skb, sizeof(*p)); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_receive_skb(skb); return 0; } @@ -770,7 +795,7 @@ static int do_term(struct t3cdev *dev, struct sk_buff *skb) struct t3c_tid_entry *t3c_tid; t3c_tid = lookup_tid(&(T3C_DATA(dev))->tid_maps, hwtid); - if (t3c_tid->ctx && t3c_tid->client->handlers && + if (t3c_tid && t3c_tid->ctx && t3c_tid->client->handlers && t3c_tid->client->handlers[opcode]) { return t3c_tid->client->handlers[opcode] (dev, skb, t3c_tid->ctx); @@ -969,7 +994,7 @@ void cxgb_redirect(struct dst_entry *old, struct dst_entry *new) for (tid = 0; tid < ti->ntids; tid++) { te = lookup_tid(ti, tid); BUG_ON(!te); - if (te->ctx && te->client && te->client->redirect) { + if (te && te->ctx && te->client && te->client->redirect) { update_tcb = te->client->redirect(te->ctx, old, new, e); if (update_tcb) { l2t_hold(L2DATA(tdev), e); @@ -1212,7 +1237,8 @@ void __init cxgb3_offload_init(void) t3_register_cpl_handler(CPL_CLOSE_CON_RPL, do_hwtid_rpl); t3_register_cpl_handler(CPL_ABORT_REQ_RSS, do_abort_req_rss); t3_register_cpl_handler(CPL_ACT_ESTABLISH, do_act_establish); - t3_register_cpl_handler(CPL_SET_TCB_RPL, do_set_tcb_rpl); + t3_register_cpl_handler(CPL_SET_TCB_RPL, do_hwtid_rpl); + t3_register_cpl_handler(CPL_GET_TCB_RPL, do_hwtid_rpl); t3_register_cpl_handler(CPL_RDMA_TERMINATE, do_term); t3_register_cpl_handler(CPL_RDMA_EC_STATUS, do_hwtid_rpl); t3_register_cpl_handler(CPL_TRACE_PKT, do_trace); diff --git a/drivers/net/cxgb3/cxgb3_offload.h b/drivers/net/cxgb3/cxgb3_offload.h index 0e6beb6..f15446a 100644 --- a/drivers/net/cxgb3/cxgb3_offload.h +++ b/drivers/net/cxgb3/cxgb3_offload.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2006-2007 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/l2t.c b/drivers/net/cxgb3/l2t.c index 3c0cb85..d660af7 100644 --- a/drivers/net/cxgb3/l2t.c +++ b/drivers/net/cxgb3/l2t.c @@ -1,6 +1,5 @@ /* * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/l2t.h b/drivers/net/cxgb3/l2t.h index ba5d2cb..d790013 100644 --- a/drivers/net/cxgb3/l2t.h +++ b/drivers/net/cxgb3/l2t.h @@ -1,6 +1,5 @@ /* * Copyright (c) 2003-2007 Chelsio, Inc. All rights reserved. - * Copyright (c) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/mc5.c b/drivers/net/cxgb3/mc5.c index 644d62e..84c1ffa 100644 --- a/drivers/net/cxgb3/mc5.c +++ b/drivers/net/cxgb3/mc5.c @@ -328,6 +328,9 @@ int t3_mc5_init(struct mc5 *mc5, unsigned int nservers, unsigned int nfilters, unsigned int tcam_size = mc5->tcam_size; struct adapter *adap = mc5->adapter; + if (!tcam_size) + return 0; + if (nroutes > MAX_ROUTES || nroutes + nservers + nfilters > tcam_size) return -EINVAL; diff --git a/drivers/net/cxgb3/regs.h b/drivers/net/cxgb3/regs.h index b56c5f5..e5a5534 100644 --- a/drivers/net/cxgb3/regs.h +++ b/drivers/net/cxgb3/regs.h @@ -1206,6 +1206,14 @@ #define A_TP_RX_TRC_KEY0 0x120 +#define A_TP_TX_DROP_CNT_CH0 0x12d + +#define S_TXDROPCNTCH0RCVD 0 +#define M_TXDROPCNTCH0RCVD 0xffff +#define V_TXDROPCNTCH0RCVD(x) ((x) << S_TXDROPCNTCH0RCVD) +#define G_TXDROPCNTCH0RCVD(x) (((x) >> S_TXDROPCNTCH0RCVD) & \ + M_TXDROPCNTCH0RCVD) + #define A_ULPRX_CTL 0x500 #define S_ROUND_ROBIN 4 @@ -1226,9 +1234,15 @@ #define A_ULPRX_ISCSI_TAGMASK 0x514 +#define S_HPZ0 0 +#define M_HPZ0 0xf +#define V_HPZ0(x) ((x) << S_HPZ0) +#define G_HPZ0(x) (((x) >> S_HPZ0) & M_HPZ0) + #define A_ULPRX_TDDP_LLIMIT 0x51c #define A_ULPRX_TDDP_ULIMIT 0x520 +#define A_ULPRX_TDDP_PSZ 0x528 #define A_ULPRX_STAG_LLIMIT 0x52c @@ -1834,6 +1848,8 @@ #define V_TXPAUSEEN(x) ((x) << S_TXPAUSEEN) #define F_TXPAUSEEN V_TXPAUSEEN(1U) +#define A_XGM_TX_PAUSE_QUANTA 0x808 + #define A_XGM_RX_CTRL 0x80c #define S_RXEN 0 @@ -1920,11 +1936,20 @@ #define A_XGM_TXFIFO_CFG 0x888 +#define S_TXIPG 13 +#define M_TXIPG 0xff +#define V_TXIPG(x) ((x) << S_TXIPG) +#define G_TXIPG(x) (((x) >> S_TXIPG) & M_TXIPG) + #define S_TXFIFOTHRESH 4 #define M_TXFIFOTHRESH 0x1ff #define V_TXFIFOTHRESH(x) ((x) << S_TXFIFOTHRESH) +#define S_ENDROPPKT 21 +#define V_ENDROPPKT(x) ((x) << S_ENDROPPKT) +#define F_ENDROPPKT V_ENDROPPKT(1U) + #define A_XGM_SERDES_CTRL 0x890 #define A_XGM_SERDES_CTRL0 0x8e0 @@ -2190,6 +2215,13 @@ #define A_XGM_RX_MAX_PKT_SIZE_ERR_CNT 0x9a4 +#define A_XGM_TX_SPI4_SOP_EOP_CNT 0x9a8 + +#define S_TXSPI4SOPCNT 16 +#define M_TXSPI4SOPCNT 0xffff +#define V_TXSPI4SOPCNT(x) ((x) << S_TXSPI4SOPCNT) +#define G_TXSPI4SOPCNT(x) (((x) >> S_TXSPI4SOPCNT) & M_TXSPI4SOPCNT) + #define A_XGM_RX_SPI4_SOP_EOP_CNT 0x9ac #define XGMAC0_1_BASE_ADDR 0xa00 diff --git a/drivers/net/cxgb3/sge.c b/drivers/net/cxgb3/sge.c index 3f2cf8a..3666586 100644 --- a/drivers/net/cxgb3/sge.c +++ b/drivers/net/cxgb3/sge.c @@ -45,9 +45,25 @@ #define USE_GTS 0 #define SGE_RX_SM_BUF_SIZE 1536 + +/* + * If USE_RX_PAGE is defined, the small freelist populated with (partial) + * pages instead of skbs. Pages are carved up into RX_PAGE_SIZE chunks (must + * be a multiple of the host page size). + */ +#define USE_RX_PAGE +#define RX_PAGE_SIZE 2048 + +/* + * skb freelist packets are copied into a new skb (and the freelist one is + * reused) if their len is <= + */ #define SGE_RX_COPY_THRES 256 -# define SGE_RX_DROP_THRES 16 +/* + * Minimum number of freelist entries before we start dropping TUNNEL frames. + */ +#define SGE_RX_DROP_THRES 16 /* * Period of the Tx buffer reclaim timer. This timer does not need to run @@ -85,7 +101,10 @@ struct tx_sw_desc { /* SW state per Tx descriptor */ }; struct rx_sw_desc { /* SW state per Rx descriptor */ - struct sk_buff *skb; + union { + struct sk_buff *skb; + struct sge_fl_page page; + } t; DECLARE_PCI_UNMAP_ADDR(dma_addr); }; @@ -105,6 +124,15 @@ struct unmap_info { /* packet unmapping info, overlays skb->cb */ }; /* + * Holds unmapping information for Tx packets that need deferred unmapping. + * This structure lives at skb->head and must be allocated by callers. + */ +struct deferred_unmap_info { + struct pci_dev *pdev; + dma_addr_t addr[MAX_SKB_FRAGS + 1]; +}; + +/* * Maps a number of flits to the number of Tx descriptors that can hold them. * The formula is * @@ -252,10 +280,13 @@ static void free_tx_desc(struct adapter *adapter, struct sge_txq *q, struct pci_dev *pdev = adapter->pdev; unsigned int cidx = q->cidx; + const int need_unmap = need_skb_unmap() && + q->cntxt_id >= FW_TUNNEL_SGEEC_START; + d = &q->sdesc[cidx]; while (n--) { if (d->skb) { /* an SGL is present */ - if (need_skb_unmap()) + if (need_unmap) unmap_skb(d->skb, q, cidx, pdev); if (d->skb->priority == cidx) kfree_skb(d->skb); @@ -320,16 +351,27 @@ static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q) pci_unmap_single(pdev, pci_unmap_addr(d, dma_addr), q->buf_size, PCI_DMA_FROMDEVICE); - kfree_skb(d->skb); - d->skb = NULL; + + if (q->buf_size != RX_PAGE_SIZE) { + kfree_skb(d->t.skb); + d->t.skb = NULL; + } else { + if (d->t.page.frag.page) + put_page(d->t.page.frag.page); + d->t.page.frag.page = NULL; + } if (++cidx == q->size) cidx = 0; } + + if (q->page.frag.page) + put_page(q->page.frag.page); + q->page.frag.page = NULL; } /** * add_one_rx_buf - add a packet buffer to a free-buffer list - * @skb: the buffer to add + * @va: va of the buffer to add * @len: the buffer length * @d: the HW Rx descriptor to write * @sd: the SW Rx descriptor to write @@ -339,14 +381,13 @@ static void free_rx_bufs(struct pci_dev *pdev, struct sge_fl *q) * Add a buffer of the given length to the supplied HW and SW Rx * descriptors. */ -static inline void add_one_rx_buf(struct sk_buff *skb, unsigned int len, +static inline void add_one_rx_buf(unsigned char *va, unsigned int len, struct rx_desc *d, struct rx_sw_desc *sd, unsigned int gen, struct pci_dev *pdev) { dma_addr_t mapping; - sd->skb = skb; - mapping = pci_map_single(pdev, skb->data, len, PCI_DMA_FROMDEVICE); + mapping = pci_map_single(pdev, va, len, PCI_DMA_FROMDEVICE); pci_unmap_addr_set(sd, dma_addr, mapping); d->addr_lo = cpu_to_be32(mapping); @@ -371,14 +412,47 @@ static void refill_fl(struct adapter *adap, struct sge_fl *q, int n, gfp_t gfp) { struct rx_sw_desc *sd = &q->sdesc[q->pidx]; struct rx_desc *d = &q->desc[q->pidx]; + struct sge_fl_page *p = &q->page; while (n--) { - struct sk_buff *skb = alloc_skb(q->buf_size, gfp); + unsigned char *va; - if (!skb) - break; + if (unlikely(q->buf_size != RX_PAGE_SIZE)) { + struct sk_buff *skb = alloc_skb(q->buf_size, gfp); + + if (!skb) { + q->alloc_failed++; + break; + } + va = skb->data; + sd->t.skb = skb; + } else { + if (!p->frag.page) { + p->frag.page = alloc_pages(gfp, 0); + if (unlikely(!p->frag.page)) { + q->alloc_failed++; + break; + } else { + p->frag.size = RX_PAGE_SIZE; + p->frag.page_offset = 0; + p->va = page_address(p->frag.page); + } + } + + memcpy(&sd->t, p, sizeof(*p)); + va = p->va; + + p->frag.page_offset += RX_PAGE_SIZE; + BUG_ON(p->frag.page_offset > PAGE_SIZE); + p->va += RX_PAGE_SIZE; + if (p->frag.page_offset == PAGE_SIZE) + p->frag.page = NULL; + else + get_page(p->frag.page); + } + + add_one_rx_buf(va, q->buf_size, d, sd, q->gen, adap->pdev); - add_one_rx_buf(skb, q->buf_size, d, sd, q->gen, adap->pdev); d++; sd++; if (++q->pidx == q->size) { @@ -413,7 +487,7 @@ static void recycle_rx_buf(struct adapter *adap, struct sge_fl *q, struct rx_desc *from = &q->desc[idx]; struct rx_desc *to = &q->desc[q->pidx]; - q->sdesc[q->pidx] = q->sdesc[idx]; + memcpy(&q->sdesc[q->pidx], &q->sdesc[idx], sizeof(struct rx_sw_desc)); to->addr_lo = from->addr_lo; /* already big endian */ to->addr_hi = from->addr_hi; /* likewise */ wmb(); @@ -446,7 +520,7 @@ static void recycle_rx_buf(struct adapter *adap, struct sge_fl *q, * of the SW ring. */ static void *alloc_ring(struct pci_dev *pdev, size_t nelem, size_t elem_size, - size_t sw_size, dma_addr_t *phys, void *metadata) + size_t sw_size, dma_addr_t * phys, void *metadata) { size_t len = nelem * elem_size; void *s = NULL; @@ -576,61 +650,6 @@ static inline unsigned int flits_to_desc(unsigned int n) } /** - * get_packet - return the next ingress packet buffer from a free list - * @adap: the adapter that received the packet - * @fl: the SGE free list holding the packet - * @len: the packet length including any SGE padding - * @drop_thres: # of remaining buffers before we start dropping packets - * - * Get the next packet from a free list and complete setup of the - * sk_buff. If the packet is small we make a copy and recycle the - * original buffer, otherwise we use the original buffer itself. If a - * positive drop threshold is supplied packets are dropped and their - * buffers recycled if (a) the number of remaining buffers is under the - * threshold and the packet is too big to copy, or (b) the packet should - * be copied but there is no memory for the copy. - */ -static struct sk_buff *get_packet(struct adapter *adap, struct sge_fl *fl, - unsigned int len, unsigned int drop_thres) -{ - struct sk_buff *skb = NULL; - struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; - - prefetch(sd->skb->data); - - if (len <= SGE_RX_COPY_THRES) { - skb = alloc_skb(len, GFP_ATOMIC); - if (likely(skb != NULL)) { - __skb_put(skb, len); - pci_dma_sync_single_for_cpu(adap->pdev, - pci_unmap_addr(sd, - dma_addr), - len, PCI_DMA_FROMDEVICE); - memcpy(skb->data, sd->skb->data, len); - pci_dma_sync_single_for_device(adap->pdev, - pci_unmap_addr(sd, - dma_addr), - len, PCI_DMA_FROMDEVICE); - } else if (!drop_thres) - goto use_orig_buf; - recycle: - recycle_rx_buf(adap, fl, fl->cidx); - return skb; - } - - if (unlikely(fl->credits < drop_thres)) - goto recycle; - - use_orig_buf: - pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr), - fl->buf_size, PCI_DMA_FROMDEVICE); - skb = sd->skb; - skb_put(skb, len); - __refill_fl(adap, fl); - return skb; -} - -/** * get_imm_packet - return the next ingress packet buffer from a response * @resp: the response descriptor containing the packet data * @@ -642,7 +661,7 @@ static inline struct sk_buff *get_imm_packet(const struct rsp_desc *resp) if (skb) { __skb_put(skb, IMMED_PKT_SIZE); - memcpy(skb->data, resp->imm_data, IMMED_PKT_SIZE); + skb_copy_to_linear_data(skb, resp->imm_data, IMMED_PKT_SIZE); } return skb; } @@ -878,11 +897,11 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, d->flit[2] = 0; cntrl |= V_TXPKT_OPCODE(CPL_TX_PKT_LSO); hdr->cntrl = htonl(cntrl); - eth_type = skb->nh.raw - skb->data == ETH_HLEN ? + eth_type = skb_network_offset(skb) == ETH_HLEN ? CPL_ETH_II : CPL_ETH_II_VLAN; tso_info |= V_LSO_ETH_TYPE(eth_type) | - V_LSO_IPHDR_WORDS(skb->nh.iph->ihl) | - V_LSO_TCPHDR_WORDS(skb->h.th->doff); + V_LSO_IPHDR_WORDS(ip_hdr(skb)->ihl) | + V_LSO_TCPHDR_WORDS(tcp_hdr(skb)->doff); hdr->lso_info = htonl(tso_info); flits = 3; } else { @@ -894,7 +913,8 @@ static void write_tx_pkt_wr(struct adapter *adap, struct sk_buff *skb, if (skb->len <= WR_LEN - sizeof(*cpl)) { q->sdesc[pidx].skb = NULL; if (!skb->data_len) - memcpy(&d->flit[2], skb->data, skb->len); + skb_copy_from_linear_data(skb, &d->flit[2], + skb->len); else skb_copy_bits(skb, 0, &d->flit[2], skb->len); @@ -1227,6 +1247,50 @@ int t3_mgmt_tx(struct adapter *adap, struct sk_buff *skb) } /** + * deferred_unmap_destructor - unmap a packet when it is freed + * @skb: the packet + * + * This is the packet destructor used for Tx packets that need to remain + * mapped until they are freed rather than until their Tx descriptors are + * freed. + */ +static void deferred_unmap_destructor(struct sk_buff *skb) +{ + int i; + const dma_addr_t *p; + const struct skb_shared_info *si; + const struct deferred_unmap_info *dui; + const struct unmap_info *ui = (struct unmap_info *)skb->cb; + + dui = (struct deferred_unmap_info *)skb->head; + p = dui->addr; + + if (ui->len) + pci_unmap_single(dui->pdev, *p++, ui->len, PCI_DMA_TODEVICE); + + si = skb_shinfo(skb); + for (i = 0; i < si->nr_frags; i++) + pci_unmap_page(dui->pdev, *p++, si->frags[i].size, + PCI_DMA_TODEVICE); +} + +static void setup_deferred_unmapping(struct sk_buff *skb, struct pci_dev *pdev, + const struct sg_ent *sgl, int sgl_flits) +{ + dma_addr_t *p; + struct deferred_unmap_info *dui; + + dui = (struct deferred_unmap_info *)skb->head; + dui->pdev = pdev; + for (p = dui->addr; sgl_flits >= 3; sgl++, sgl_flits -= 3) { + *p++ = be64_to_cpu(sgl->addr[0]); + *p++ = be64_to_cpu(sgl->addr[1]); + } + if (sgl_flits) + *p = be64_to_cpu(sgl->addr[0]); +} + +/** * write_ofld_wr - write an offload work request * @adap: the adapter * @skb: the packet to send @@ -1256,14 +1320,20 @@ static void write_ofld_wr(struct adapter *adap, struct sk_buff *skb, /* Only TX_DATA builds SGLs */ from = (struct work_request_hdr *)skb->data; - memcpy(&d->flit[1], &from[1], skb->h.raw - skb->data - sizeof(*from)); + memcpy(&d->flit[1], &from[1], + skb_transport_offset(skb) - sizeof(*from)); - flits = (skb->h.raw - skb->data) / 8; + flits = skb_transport_offset(skb) / 8; sgp = ndesc == 1 ? (struct sg_ent *)&d->flit[flits] : sgl; - sgl_flits = make_sgl(skb, sgp, skb->h.raw, skb->tail - skb->h.raw, + sgl_flits = make_sgl(skb, sgp, skb_transport_header(skb), + skb->tail - skb->transport_header, adap->pdev); - if (need_skb_unmap()) - ((struct unmap_info *)skb->cb)->len = skb->tail - skb->h.raw; + if (need_skb_unmap()) { + setup_deferred_unmapping(skb, adap->pdev, sgp, sgl_flits); + skb->destructor = deferred_unmap_destructor; + ((struct unmap_info *)skb->cb)->len = (skb->tail - + skb->transport_header); + } write_wr_hdr_sgl(ndesc, skb, d, pidx, q, sgl, flits, sgl_flits, gen, from->wr_hi, from->wr_lo); @@ -1283,8 +1353,8 @@ static inline unsigned int calc_tx_descs_ofld(const struct sk_buff *skb) if (skb->len <= WR_LEN && cnt == 0) return 1; /* packet fits as immediate data */ - flits = (skb->h.raw - skb->data) / 8; /* headers */ - if (skb->tail != skb->h.raw) + flits = skb_transport_offset(skb) / 8; /* headers */ + if (skb->tail != skb->transport_header) cnt++; return flits_to_desc(flits + sgl_len(cnt)); } @@ -1554,7 +1624,9 @@ static inline int rx_offload(struct t3cdev *tdev, struct sge_rspq *rq, unsigned int gather_idx) { rq->offload_pkts++; - skb->mac.raw = skb->nh.raw = skb->h.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); if (rq->polling) { rx_gather[gather_idx++] = skb; @@ -1617,11 +1689,9 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)(skb->data + pad); struct port_info *pi; - rq->eth_pkts++; skb_pull(skb, sizeof(*p) + pad); - skb->dev = adap->port[p->iff]; skb->dev->last_rx = jiffies; - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, adap->port[p->iff]); pi = netdev_priv(skb->dev); if (pi->rx_csum_offload && p->csum_valid && p->csum == 0xffff && !p->fragment) { @@ -1645,6 +1715,85 @@ static void rx_eth(struct adapter *adap, struct sge_rspq *rq, netif_rx(skb); } +#define SKB_DATA_SIZE 128 + +static void skb_data_init(struct sk_buff *skb, struct sge_fl_page *p, + unsigned int len) +{ + skb->len = len; + if (len <= SKB_DATA_SIZE) { + skb_copy_to_linear_data(skb, p->va, len); + skb->tail += len; + put_page(p->frag.page); + } else { + skb_copy_to_linear_data(skb, p->va, SKB_DATA_SIZE); + skb_shinfo(skb)->frags[0].page = p->frag.page; + skb_shinfo(skb)->frags[0].page_offset = + p->frag.page_offset + SKB_DATA_SIZE; + skb_shinfo(skb)->frags[0].size = len - SKB_DATA_SIZE; + skb_shinfo(skb)->nr_frags = 1; + skb->data_len = len - SKB_DATA_SIZE; + skb->tail += SKB_DATA_SIZE; + skb->truesize += skb->data_len; + } +} + +/** +* get_packet - return the next ingress packet buffer from a free list +* @adap: the adapter that received the packet +* @fl: the SGE free list holding the packet +* @len: the packet length including any SGE padding +* @drop_thres: # of remaining buffers before we start dropping packets +* +* Get the next packet from a free list and complete setup of the +* sk_buff. If the packet is small we make a copy and recycle the +* original buffer, otherwise we use the original buffer itself. If a +* positive drop threshold is supplied packets are dropped and their +* buffers recycled if (a) the number of remaining buffers is under the +* threshold and the packet is too big to copy, or (b) the packet should +* be copied but there is no memory for the copy. +*/ +static struct sk_buff *get_packet(struct adapter *adap, struct sge_fl *fl, + unsigned int len, unsigned int drop_thres) +{ + struct sk_buff *skb = NULL; + struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; + + prefetch(sd->t.skb->data); + + if (len <= SGE_RX_COPY_THRES) { + skb = alloc_skb(len, GFP_ATOMIC); + if (likely(skb != NULL)) { + struct rx_desc *d = &fl->desc[fl->cidx]; + dma_addr_t mapping = + (dma_addr_t)((u64) be32_to_cpu(d->addr_hi) << 32 | + be32_to_cpu(d->addr_lo)); + + __skb_put(skb, len); + pci_dma_sync_single_for_cpu(adap->pdev, mapping, len, + PCI_DMA_FROMDEVICE); + skb_copy_from_linear_data(sd->t.skb, skb->data, len); + pci_dma_sync_single_for_device(adap->pdev, mapping, len, + PCI_DMA_FROMDEVICE); + } else if (!drop_thres) + goto use_orig_buf; +recycle: + recycle_rx_buf(adap, fl, fl->cidx); + return skb; + } + + if (unlikely(fl->credits < drop_thres)) + goto recycle; + +use_orig_buf: + pci_unmap_single(adap->pdev, pci_unmap_addr(sd, dma_addr), + fl->buf_size, PCI_DMA_FROMDEVICE); + skb = sd->t.skb; + skb_put(skb, len); + __refill_fl(adap, fl); + return skb; +} + /** * handle_rsp_cntrl_info - handles control information in a response * @qs: the queue set corresponding to the response @@ -1767,7 +1916,7 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs, q->next_holdoff = q->holdoff_tmr; while (likely(budget_left && is_new_response(r, q))) { - int eth, ethpad = 0; + int eth, ethpad = 2; struct sk_buff *skb = NULL; u32 len, flags = ntohl(r->flags); u32 rss_hi = *(const u32 *)r, rss_lo = r->rss_hdr.rss_hash_val; @@ -1794,18 +1943,56 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs, break; } q->imm_data++; + ethpad = 0; } else if ((len = ntohl(r->len_cq)) != 0) { - struct sge_fl *fl; + struct sge_fl *fl = + (len & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; + + if (fl->buf_size == RX_PAGE_SIZE) { + struct rx_sw_desc *sd = &fl->sdesc[fl->cidx]; + struct sge_fl_page *p = &sd->t.page; + + prefetch(p->va); + prefetch(p->va + L1_CACHE_BYTES); + + __refill_fl(adap, fl); + + pci_unmap_single(adap->pdev, + pci_unmap_addr(sd, dma_addr), + fl->buf_size, + PCI_DMA_FROMDEVICE); + + if (eth) { + if (unlikely(fl->credits < + SGE_RX_DROP_THRES)) + goto eth_recycle; + + skb = alloc_skb(SKB_DATA_SIZE, + GFP_ATOMIC); + if (unlikely(!skb)) { +eth_recycle: + q->rx_drops++; + recycle_rx_buf(adap, fl, + fl->cidx); + goto eth_done; + } + } else { + skb = alloc_skb(SKB_DATA_SIZE, + GFP_ATOMIC); + if (unlikely(!skb)) + goto no_mem; + } + + skb_data_init(skb, p, G_RSPD_LEN(len)); +eth_done: + fl->credits--; + q->eth_pkts++; + } else { + fl->credits--; + skb = get_packet(adap, fl, G_RSPD_LEN(len), + eth ? SGE_RX_DROP_THRES : 0); + } - fl = (len & F_RSPD_FLQ) ? &qs->fl[1] : &qs->fl[0]; - fl->credits--; - skb = get_packet(adap, fl, G_RSPD_LEN(len), - eth ? SGE_RX_DROP_THRES : 0); - if (!skb) - q->rx_drops++; - else if (r->rss_hdr.opcode == CPL_TRACE_PKT) - __skb_pull(skb, 2); - ethpad = 2; if (++fl->cidx == fl->size) fl->cidx = 0; } else @@ -1829,18 +2016,23 @@ static int process_responses(struct adapter *adap, struct sge_qset *qs, q->credits = 0; } - if (likely(skb != NULL)) { + if (skb) { + /* Preserve the RSS info in csum & priority */ + skb->csum = rss_hi; + skb->priority = rss_lo; + if (eth) rx_eth(adap, q, skb, ethpad); else { - /* Preserve the RSS info in csum & priority */ - skb->csum = rss_hi; - skb->priority = rss_lo; - ngathered = rx_offload(&adap->tdev, q, skb, - offload_skbs, ngathered); + if (unlikely(r->rss_hdr.opcode == + CPL_TRACE_PKT)) + __skb_pull(skb, ethpad); + + ngathered = rx_offload(&adap->tdev, q, + skb, offload_skbs, + ngathered); } } - --budget_left; } @@ -2320,10 +2512,23 @@ static void sge_timer_cb(unsigned long data) &adap->sge.qs[0].rspq.lock; if (spin_trylock_irq(lock)) { if (!napi_is_scheduled(qs->netdev)) { + u32 status = t3_read_reg(adap, A_SG_RSPQ_FL_STATUS); + if (qs->fl[0].credits < qs->fl[0].size) __refill_fl(adap, &qs->fl[0]); if (qs->fl[1].credits < qs->fl[1].size) __refill_fl(adap, &qs->fl[1]); + + if (status & (1 << qs->rspq.cntxt_id)) { + qs->rspq.starved++; + if (qs->rspq.credits) { + refill_rspq(adap, &qs->rspq, 1); + qs->rspq.credits--; + qs->rspq.restarted++; + t3_write_reg(adap, A_SG_RSPQ_FL_STATUS, + 1 << qs->rspq.cntxt_id); + } + } } spin_unlock_irq(lock); } @@ -2431,14 +2636,22 @@ int t3_sge_alloc_qset(struct adapter *adapter, unsigned int id, int nports, q->txq[TXQ_ETH].stop_thres = nports * flits_to_desc(sgl_len(MAX_SKB_FRAGS + 1) + 3); - if (ntxq == 1) { + if (!is_offload(adapter)) { +#ifdef USE_RX_PAGE + q->fl[0].buf_size = RX_PAGE_SIZE; +#else q->fl[0].buf_size = SGE_RX_SM_BUF_SIZE + 2 + sizeof(struct cpl_rx_pkt); +#endif q->fl[1].buf_size = MAX_FRAME_SIZE + 2 + sizeof(struct cpl_rx_pkt); } else { +#ifdef USE_RX_PAGE + q->fl[0].buf_size = RX_PAGE_SIZE; +#else q->fl[0].buf_size = SGE_RX_SM_BUF_SIZE + sizeof(struct cpl_rx_data); +#endif q->fl[1].buf_size = (16 * 1024) - SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); } @@ -2632,7 +2845,7 @@ void __devinit t3_sge_prep(struct adapter *adap, struct sge_params *p) q->polling = adap->params.rev > 0; q->coalesce_usecs = 5; q->rspq_size = 1024; - q->fl_size = 4096; + q->fl_size = 1024; q->jumbo_size = 512; q->txq_size[TXQ_ETH] = 1024; q->txq_size[TXQ_OFLD] = 1024; diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 365a7f5..fb485d0 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -438,23 +438,23 @@ static const struct adapter_info t3_adap_info[] = { {2, 0, 0, 0, F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, - SUPPORTED_OFFLOAD, + 0, &mi1_mdio_ops, "Chelsio PE9000"}, {2, 0, 0, 0, F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO2_OUT_VAL | F_GPIO4_OUT_VAL, F_GPIO3 | F_GPIO5, - SUPPORTED_OFFLOAD, + 0, &mi1_mdio_ops, "Chelsio T302"}, {1, 0, 0, 0, F_GPIO1_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO1_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, 0, - SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_OFFLOAD, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T310"}, {2, 0, 0, 0, F_GPIO1_OEN | F_GPIO2_OEN | F_GPIO4_OEN | F_GPIO5_OEN | F_GPIO6_OEN | F_GPIO7_OEN | F_GPIO10_OEN | F_GPIO11_OEN | F_GPIO1_OUT_VAL | F_GPIO5_OUT_VAL | F_GPIO6_OUT_VAL | F_GPIO10_OUT_VAL, 0, - SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_OFFLOAD, + SUPPORTED_10000baseT_Full | SUPPORTED_AUI, &mi1_mdio_ext_ops, "Chelsio T320"}, }; @@ -681,7 +681,8 @@ enum { SF_ERASE_SECTOR = 0xd8, /* erase sector */ FW_FLASH_BOOT_ADDR = 0x70000, /* start address of FW in flash */ - FW_VERS_ADDR = 0x77ffc /* flash address holding FW version */ + FW_VERS_ADDR = 0x77ffc, /* flash address holding FW version */ + FW_MIN_SIZE = 8 /* at least version and csum */ }; /** @@ -884,11 +885,13 @@ int t3_check_fw_version(struct adapter *adapter) major = G_FW_VERSION_MAJOR(vers); minor = G_FW_VERSION_MINOR(vers); - if (type == FW_VERSION_T3 && major == 3 && minor == 1) + if (type == FW_VERSION_T3 && major == FW_VERSION_MAJOR && + minor == FW_VERSION_MINOR) return 0; CH_ERR(adapter, "found wrong FW version(%u.%u), " - "driver needs version 3.1\n", major, minor); + "driver needs version %u.%u\n", major, minor, + FW_VERSION_MAJOR, FW_VERSION_MINOR); return -EINVAL; } @@ -933,7 +936,7 @@ int t3_load_fw(struct adapter *adapter, const u8 *fw_data, unsigned int size) const u32 *p = (const u32 *)fw_data; int ret, addr, fw_sector = FW_FLASH_BOOT_ADDR >> 16; - if (size & 3) + if ((size & 3) || size < FW_MIN_SIZE) return -EINVAL; if (size > FW_VERS_ADDR + 8 - FW_FLASH_BOOT_ADDR) return -EFBIG; @@ -1520,19 +1523,25 @@ static int mac_intr_handler(struct adapter *adap, unsigned int idx) */ int t3_phy_intr_handler(struct adapter *adapter) { - static const int intr_gpio_bits[] = { 8, 0x20 }; - + u32 mask, gpi = adapter_info(adapter)->gpio_intr; u32 i, cause = t3_read_reg(adapter, A_T3DBG_INT_CAUSE); for_each_port(adapter, i) { - if (cause & intr_gpio_bits[i]) { - struct cphy *phy = &adap2pinfo(adapter, i)->phy; - int phy_cause = phy->ops->intr_handler(phy); + struct port_info *p = adap2pinfo(adapter, i); + + mask = gpi - (gpi & (gpi - 1)); + gpi -= mask; + + if (!(p->port_type->caps & SUPPORTED_IRQ)) + continue; + + if (cause & mask) { + int phy_cause = p->phy.ops->intr_handler(&p->phy); if (phy_cause & cphy_cause_link_change) t3_link_changed(adapter, i); if (phy_cause & cphy_cause_fifo_error) - phy->fifo_errors++; + p->phy.fifo_errors++; } } @@ -2897,6 +2906,9 @@ static int mc7_init(struct mc7 *mc7, unsigned int mc7_clock, int mem_type) struct adapter *adapter = mc7->adapter; const struct mc7_timing_params *p = &mc7_timings[mem_type]; + if (!mc7->size) + return 0; + val = t3_read_reg(adapter, mc7->offset + A_MC7_CFG); slow = val & F_SLOW; width = G_WIDTH(val); @@ -3097,8 +3109,10 @@ int t3_init_hw(struct adapter *adapter, u32 fw_params) do { /* wait for uP to initialize */ msleep(20); } while (t3_read_reg(adapter, A_CIM_HOST_ACC_DATA) && --attempts); - if (!attempts) + if (!attempts) { + CH_ERR(adapter, "uP initialization timed out\n"); goto out_err; + } err = 0; out_err: @@ -3198,7 +3212,7 @@ static void __devinit mc7_prep(struct adapter *adapter, struct mc7 *mc7, mc7->name = name; mc7->offset = base_addr - MC7_PMRX_BASE_ADDR; cfg = t3_read_reg(adapter, mc7->offset + A_MC7_CFG); - mc7->size = mc7_calc_size(cfg); + mc7->size = mc7->size = G_DEN(cfg) == M_DEN ? 0 : mc7_calc_size(cfg); mc7->width = G_WIDTH(cfg); } @@ -3225,6 +3239,7 @@ void early_hw_init(struct adapter *adapter, const struct adapter_info *ai) V_I2C_CLKDIV(adapter->params.vpd.cclk / 80 - 1)); t3_write_reg(adapter, A_T3DBG_GPIO_EN, ai->gpio_out | F_GPIO0_OEN | F_GPIO0_OUT_VAL); + t3_write_reg(adapter, A_MC5_DB_SERVER_INDEX, 0); if (adapter->params.rev == 0 || !uses_xaui(adapter)) val |= F_ENRGMII; @@ -3241,15 +3256,17 @@ void early_hw_init(struct adapter *adapter, const struct adapter_info *ai) } /* - * Reset the adapter. PCIe cards lose their config space during reset, PCI-X + * Reset the adapter. + * Older PCIe cards lose their config space during reset, PCI-X * ones don't. */ int t3_reset_adapter(struct adapter *adapter) { - int i; + int i, save_and_restore_pcie = + adapter->params.rev < T3_REV_B2 && is_pcie(adapter); uint16_t devid = 0; - if (is_pcie(adapter)) + if (save_and_restore_pcie) pci_save_state(adapter->pdev); t3_write_reg(adapter, A_PL_RST, F_CRSTWRM | F_CRSTWRMMODE); @@ -3267,7 +3284,7 @@ int t3_reset_adapter(struct adapter *adapter) if (devid != 0x1425) return -1; - if (is_pcie(adapter)) + if (save_and_restore_pcie) pci_restore_state(adapter->pdev); return 0; } @@ -3321,7 +3338,13 @@ int __devinit t3_prep_adapter(struct adapter *adapter, p->tx_num_pgs = pm_num_pages(p->chan_tx_size, p->tx_pg_size); p->ntimer_qs = p->cm_size >= (128 << 20) || adapter->params.rev > 0 ? 12 : 6; + } + + adapter->params.offload = t3_mc7_size(&adapter->pmrx) && + t3_mc7_size(&adapter->pmtx) && + t3_mc7_size(&adapter->cm); + if (is_offload(adapter)) { adapter->params.mc5.nservers = DEFAULT_NSERVERS; adapter->params.mc5.nfilters = adapter->params.rev > 0 ? DEFAULT_NFILTERS : 0; diff --git a/drivers/net/cxgb3/t3cdev.h b/drivers/net/cxgb3/t3cdev.h index 9af3bcd..fa4099b 100644 --- a/drivers/net/cxgb3/t3cdev.h +++ b/drivers/net/cxgb3/t3cdev.h @@ -1,6 +1,5 @@ /* * Copyright (C) 2006-2007 Chelsio Communications. All rights reserved. - * Copyright (C) 2006-2007 Open Grid Computing, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU diff --git a/drivers/net/cxgb3/version.h b/drivers/net/cxgb3/version.h index 2b67dd5..042e27e 100644 --- a/drivers/net/cxgb3/version.h +++ b/drivers/net/cxgb3/version.h @@ -35,5 +35,10 @@ #define DRV_DESC "Chelsio T3 Network Driver" #define DRV_NAME "cxgb3" /* Driver version */ -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.0-ko" + +/* Firmware version */ +#define FW_VERSION_MAJOR 3 +#define FW_VERSION_MINOR 3 +#define FW_VERSION_MICRO 0 #endif /* __CHELSIO_VERSION_H */ diff --git a/drivers/net/cxgb3/xgmac.c b/drivers/net/cxgb3/xgmac.c index 907a272..a506792 100644 --- a/drivers/net/cxgb3/xgmac.c +++ b/drivers/net/cxgb3/xgmac.c @@ -124,9 +124,6 @@ int t3_mac_reset(struct cmac *mac) xaui_serdes_reset(mac); } - if (adap->params.rev > 0) - t3_write_reg(adap, A_XGM_PAUSE_TIMER + oft, 0xf000); - val = F_MAC_RESET_; if (is_10G(adap)) val |= F_PCS_RESET_; @@ -145,6 +142,58 @@ int t3_mac_reset(struct cmac *mac) return 0; } +int t3b2_mac_reset(struct cmac *mac) +{ + struct adapter *adap = mac->adapter; + unsigned int oft = mac->offset; + u32 val; + + if (!macidx(mac)) + t3_set_reg_field(adap, A_MPS_CFG, F_PORT0ACTIVE, 0); + else + t3_set_reg_field(adap, A_MPS_CFG, F_PORT1ACTIVE, 0); + + t3_write_reg(adap, A_XGM_RESET_CTRL + oft, F_MAC_RESET_); + t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + + msleep(10); + + /* Check for xgm Rx fifo empty */ + if (t3_wait_op_done(adap, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT + oft, + 0x80000000, 1, 5, 2)) { + CH_ERR(adap, "MAC %d Rx fifo drain failed\n", + macidx(mac)); + return -1; + } + + t3_write_reg(adap, A_XGM_RESET_CTRL + oft, 0); + t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + + val = F_MAC_RESET_; + if (is_10G(adap)) + val |= F_PCS_RESET_; + else if (uses_xaui(adap)) + val |= F_PCS_RESET_ | F_XG2G_RESET_; + else + val |= F_RGMII_RESET_ | F_XG2G_RESET_; + t3_write_reg(adap, A_XGM_RESET_CTRL + oft, val); + t3_read_reg(adap, A_XGM_RESET_CTRL + oft); /* flush */ + if ((val & F_PCS_RESET_) && adap->params.rev) { + msleep(1); + t3b_pcs_reset(mac); + } + t3_write_reg(adap, A_XGM_RX_CFG + oft, + F_DISPAUSEFRAMES | F_EN1536BFRAMES | + F_RMFCS | F_ENJUMBO | F_ENHASHMCAST); + + if (!macidx(mac)) + t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT0ACTIVE); + else + t3_set_reg_field(adap, A_MPS_CFG, 0, F_PORT1ACTIVE); + + return 0; +} + /* * Set the exact match register 'idx' to recognize the given Ethernet address. */ @@ -251,9 +300,11 @@ int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu) * Adjust the PAUSE frame watermarks. We always set the LWM, and the * HWM only if flow-control is enabled. */ - hwm = max(MAC_RXFIFO_SIZE - 3 * mtu, MAC_RXFIFO_SIZE / 2U); - hwm = min(hwm, 3 * MAC_RXFIFO_SIZE / 4 + 1024); - lwm = hwm - 1024; + hwm = max_t(unsigned int, MAC_RXFIFO_SIZE - 3 * mtu, + MAC_RXFIFO_SIZE * 38 / 100); + hwm = min(hwm, MAC_RXFIFO_SIZE - 8192); + lwm = min(3 * (int)mtu, MAC_RXFIFO_SIZE / 4); + v = t3_read_reg(adap, A_XGM_RXFIFO_CFG + mac->offset); v &= ~V_RXFIFOPAUSELWM(M_RXFIFOPAUSELWM); v |= V_RXFIFOPAUSELWM(lwm / 8); @@ -270,7 +321,15 @@ int t3_mac_set_mtu(struct cmac *mac, unsigned int mtu) thres = mtu > thres ? (mtu - thres + 7) / 8 : 0; thres = max(thres, 8U); /* need at least 8 */ t3_set_reg_field(adap, A_XGM_TXFIFO_CFG + mac->offset, - V_TXFIFOTHRESH(M_TXFIFOTHRESH), V_TXFIFOTHRESH(thres)); + V_TXFIFOTHRESH(M_TXFIFOTHRESH) | V_TXIPG(M_TXIPG), + V_TXFIFOTHRESH(thres) | V_TXIPG(1)); + + if (adap->params.rev > 0) + t3_write_reg(adap, A_XGM_PAUSE_TIMER + mac->offset, + (hwm - lwm) * 4 / 8); + t3_write_reg(adap, A_XGM_TX_PAUSE_QUANTA + mac->offset, + MAC_RXFIFO_SIZE * 4 * 8 / 512); + return 0; } @@ -298,12 +357,6 @@ int t3_mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex, int fc) V_PORTSPEED(M_PORTSPEED), val); } - val = t3_read_reg(adap, A_XGM_RXFIFO_CFG + oft); - val &= ~V_RXFIFOPAUSEHWM(M_RXFIFOPAUSEHWM); - if (fc & PAUSE_TX) - val |= V_RXFIFOPAUSEHWM(G_RXFIFOPAUSELWM(val) + 128); /* +1KB */ - t3_write_reg(adap, A_XGM_RXFIFO_CFG + oft, val); - t3_set_reg_field(adap, A_XGM_TX_CFG + oft, F_TXPAUSEEN, (fc & PAUSE_RX) ? F_TXPAUSEEN : 0); return 0; @@ -314,13 +367,28 @@ int t3_mac_enable(struct cmac *mac, int which) int idx = macidx(mac); struct adapter *adap = mac->adapter; unsigned int oft = mac->offset; - + struct mac_stats *s = &mac->stats; + if (which & MAC_DIRECTION_TX) { t3_write_reg(adap, A_XGM_TX_CTRL + oft, F_TXEN); t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); - t3_write_reg(adap, A_TP_PIO_DATA, 0xbf000001); + t3_write_reg(adap, A_TP_PIO_DATA, 0xc0ede401); t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); + + t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CNT_CH0 + idx); + mac->tx_mcnt = s->tx_frames; + mac->tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap, + A_TP_PIO_DATA))); + mac->tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, + A_XGM_TX_SPI4_SOP_EOP_CNT + + oft))); + mac->rx_mcnt = s->rx_frames; + mac->rx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, + A_XGM_RX_SPI4_SOP_EOP_CNT + + oft))); + mac->txen = F_TXEN; + mac->toggle_cnt = 0; } if (which & MAC_DIRECTION_RX) t3_write_reg(adap, A_XGM_RX_CTRL + oft, F_RXEN); @@ -331,19 +399,102 @@ int t3_mac_disable(struct cmac *mac, int which) { int idx = macidx(mac); struct adapter *adap = mac->adapter; + int val; if (which & MAC_DIRECTION_TX) { t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0); t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_CFG_CH0 + idx); t3_write_reg(adap, A_TP_PIO_DATA, 0xc000001f); t3_write_reg(adap, A_TP_PIO_ADDR, A_TP_TX_DROP_MODE); - t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 0); + t3_set_reg_field(adap, A_TP_PIO_DATA, 1 << idx, 1 << idx); + mac->txen = 0; } - if (which & MAC_DIRECTION_RX) + if (which & MAC_DIRECTION_RX) { + t3_set_reg_field(mac->adapter, A_XGM_RESET_CTRL + mac->offset, + F_PCS_RESET_, 0); + msleep(100); t3_write_reg(adap, A_XGM_RX_CTRL + mac->offset, 0); + val = F_MAC_RESET_; + if (is_10G(adap)) + val |= F_PCS_RESET_; + else if (uses_xaui(adap)) + val |= F_PCS_RESET_ | F_XG2G_RESET_; + else + val |= F_RGMII_RESET_ | F_XG2G_RESET_; + t3_write_reg(mac->adapter, A_XGM_RESET_CTRL + mac->offset, val); + } return 0; } +int t3b2_mac_watchdog_task(struct cmac *mac) +{ + struct adapter *adap = mac->adapter; + struct mac_stats *s = &mac->stats; + unsigned int tx_tcnt, tx_xcnt; + unsigned int tx_mcnt = s->tx_frames; + unsigned int rx_mcnt = s->rx_frames; + unsigned int rx_xcnt; + int status; + + if (tx_mcnt == mac->tx_mcnt) { + tx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, + A_XGM_TX_SPI4_SOP_EOP_CNT + + mac->offset))); + if (tx_xcnt == 0) { + t3_write_reg(adap, A_TP_PIO_ADDR, + A_TP_TX_DROP_CNT_CH0 + macidx(mac)); + tx_tcnt = (G_TXDROPCNTCH0RCVD(t3_read_reg(adap, + A_TP_PIO_DATA))); + } else { + mac->toggle_cnt = 0; + return 0; + } + } else { + mac->toggle_cnt = 0; + return 0; + } + + if (((tx_tcnt != mac->tx_tcnt) && + (tx_xcnt == 0) && (mac->tx_xcnt == 0)) || + ((mac->tx_mcnt == tx_mcnt) && + (tx_xcnt != 0) && (mac->tx_xcnt != 0))) { + if (mac->toggle_cnt > 4) + status = 2; + else + status = 1; + } else { + mac->toggle_cnt = 0; + return 0; + } + + if (rx_mcnt != mac->rx_mcnt) + rx_xcnt = (G_TXSPI4SOPCNT(t3_read_reg(adap, + A_XGM_RX_SPI4_SOP_EOP_CNT + + mac->offset))); + else + return 0; + + if (mac->rx_mcnt != s->rx_frames && rx_xcnt == 0 && mac->rx_xcnt == 0) + status = 2; + + mac->tx_tcnt = tx_tcnt; + mac->tx_xcnt = tx_xcnt; + mac->tx_mcnt = s->tx_frames; + mac->rx_xcnt = rx_xcnt; + mac->rx_mcnt = s->rx_frames; + if (status == 1) { + t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, 0); + t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset); /* flush */ + t3_write_reg(adap, A_XGM_TX_CTRL + mac->offset, mac->txen); + t3_read_reg(adap, A_XGM_TX_CTRL + mac->offset); /* flush */ + mac->toggle_cnt++; + } else if (status == 2) { + t3b2_mac_reset(mac); + mac->toggle_cnt = 0; + } + return status; +} + /* * This function is called periodically to accumulate the current values of the * RMON counters into the port statistics. Since the packet counters are only @@ -373,7 +524,11 @@ const struct mac_stats *t3_mac_update_stats(struct cmac *mac) RMON_UPDATE(mac, rx_symbol_errs, RX_SYM_CODE_ERR_FRAMES); RMON_UPDATE(mac, rx_too_long, RX_OVERSIZE_FRAMES); - mac->stats.rx_too_long += RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); + + v = RMON_READ(mac, A_XGM_RX_MAX_PKT_SIZE_ERR_CNT); + if (mac->adapter->params.rev == T3_REV_B2) + v &= 0x7fffffff; + mac->stats.rx_too_long += v; RMON_UPDATE(mac, rx_frames_64, RX_64B_FRAMES); RMON_UPDATE(mac, rx_frames_65_127, RX_65_127B_FRAMES); diff --git a/drivers/net/de600.c b/drivers/net/de600.c index 8396e41..dae97b8 100644 --- a/drivers/net/de600.c +++ b/drivers/net/de600.c @@ -38,12 +38,6 @@ static const char version[] = "de600.c: $Revision: 1.41-2.5 $, Bjorn Ekwall (bj /* Add more time here if your adapter won't work OK: */ #define DE600_SLOW_DOWN udelay(delay_time) - /* - * If you still have trouble reading/writing to the adapter, - * modify the following "#define": (see <asm/io.h> for more info) -#define REALLY_SLOW_IO - */ - /* use 0 for production, 1 for verification, >2 for debug */ #ifdef DE600_DEBUG #define PRINTK(x) if (de600_debug >= 2) printk x @@ -365,7 +359,6 @@ static void de600_rx_intr(struct net_device *dev) } /* else */ - skb->dev = dev; skb_reserve(skb,2); /* Align */ /* 'skb->data' points to the start of sk_buff data area. */ diff --git a/drivers/net/de620.c b/drivers/net/de620.c index b6ad0cb..dc48924 100644 --- a/drivers/net/de620.c +++ b/drivers/net/de620.c @@ -697,7 +697,6 @@ static int de620_rx_intr(struct net_device *dev) } else { /* Yep! Go get it! */ skb_reserve(skb,2); /* Align */ - skb->dev = dev; /* skb->data points to the start of sk_buff data area */ buffer = skb_put(skb,size); /* copy the packet into the buffer */ diff --git a/drivers/net/declance.c b/drivers/net/declance.c index 9f7e1db..95d854e 100644 --- a/drivers/net/declance.c +++ b/drivers/net/declance.c @@ -616,7 +616,6 @@ static int lance_rx(struct net_device *dev) } lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ diff --git a/drivers/net/defxx.c b/drivers/net/defxx.c index 07d2731..571d82f 100644 --- a/drivers/net/defxx.c +++ b/drivers/net/defxx.c @@ -3091,13 +3091,13 @@ static void dfx_rcv_queue_process( { /* Receive buffer allocated, pass receive packet up */ - memcpy(skb->data, p_buff + RCV_BUFF_K_PADDING, pkt_len+3); + skb_copy_to_linear_data(skb, + p_buff + RCV_BUFF_K_PADDING, + pkt_len + 3); } skb_reserve(skb,3); /* adjust data field so that it points to FC byte */ skb_put(skb, pkt_len); /* pass up packet length, NOT including CRC */ - skb->dev = bp->dev; /* pass up device pointer */ - skb->protocol = fddi_type_trans(skb, bp->dev); bp->rcv_total_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/depca.c b/drivers/net/depca.c index 5113eef..1834970 100644 --- a/drivers/net/depca.c +++ b/drivers/net/depca.c @@ -1044,7 +1044,6 @@ static int depca_rx(struct net_device *dev) unsigned char *buf; skb_reserve(skb, 2); /* 16 byte align the IP header */ buf = skb_put(skb, pkt_len); - skb->dev = dev; if (entry < lp->rx_old) { /* Wrapped buffer */ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ; memcpy_fromio(buf, lp->rx_buff[lp->rx_old], len); @@ -1491,8 +1490,9 @@ static void __init depca_platform_probe (void) depca_io_ports[i].device = pldev; if (platform_device_add(pldev)) { - platform_device_put(pldev); depca_io_ports[i].device = NULL; + pldev->dev.platform_data = NULL; + platform_device_put(pldev); continue; } diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c index a795202..df62c02 100644 --- a/drivers/net/dgrs.c +++ b/drivers/net/dgrs.c @@ -503,7 +503,6 @@ dgrs_rcv_frame( /* discarding the frame */ goto out; } - skb->dev = devN; skb_reserve(skb, 2); /* Align IP header */ again: @@ -742,7 +741,7 @@ static int dgrs_start_xmit(struct sk_buff *skb, struct net_device *devN) } amt = min_t(unsigned int, len, rbdp->size - count); - memcpy( (char *) S2H(rbdp->buf) + count, skb->data + i, amt); + skb_copy_from_linear_data_offset(skb, i, S2H(rbdp->buf) + count, amt); i += amt; count += amt; len -= amt; diff --git a/drivers/net/dl2k.c b/drivers/net/dl2k.c index 9d446a0..74ec64a 100644 --- a/drivers/net/dl2k.c +++ b/drivers/net/dl2k.c @@ -504,7 +504,6 @@ rio_timer (unsigned long data) break; } np->rx_skbuff[entry] = skb; - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = @@ -575,7 +574,6 @@ alloc_list (struct net_device *dev) dev->name); break; } - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve (skb, 2); /* 16 byte align the IP header. */ /* Rubicon now supports 40 bits of addressing space. */ np->rx_ring[i].fraginfo = @@ -866,7 +864,6 @@ receive_packet (struct net_device *dev) DMA_48BIT_MASK, np->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); eth_copy_and_sum (skb, @@ -910,7 +907,6 @@ receive_packet (struct net_device *dev) break; } np->rx_skbuff[entry] = skb; - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve (skb, 2); np->rx_ring[entry].fraginfo = diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c index 615d2b1..8cc1174 100644 --- a/drivers/net/dm9000.c +++ b/drivers/net/dm9000.c @@ -954,7 +954,6 @@ dm9000_rx(struct net_device *dev) /* Move data from DM9000 */ if (GoodPacket && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) { - skb->dev = dev; skb_reserve(skb, 2); rdptr = (u8 *) skb_put(skb, RxLen - 4); diff --git a/drivers/net/e100.c b/drivers/net/e100.c index 0cefef5..6169663 100644 --- a/drivers/net/e100.c +++ b/drivers/net/e100.c @@ -159,7 +159,7 @@ #define DRV_NAME "e100" #define DRV_EXT "-NAPI" -#define DRV_VERSION "3.5.17-k2"DRV_EXT +#define DRV_VERSION "3.5.17-k4"DRV_EXT #define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver" #define DRV_COPYRIGHT "Copyright(c) 1999-2006 Intel Corporation" #define PFX DRV_NAME ": " @@ -174,10 +174,13 @@ MODULE_VERSION(DRV_VERSION); static int debug = 3; static int eeprom_bad_csum_allow = 0; +static int use_io = 0; module_param(debug, int, 0); module_param(eeprom_bad_csum_allow, int, 0); +module_param(use_io, int, 0); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); MODULE_PARM_DESC(eeprom_bad_csum_allow, "Allow bad eeprom checksums"); +MODULE_PARM_DESC(use_io, "Force use of i/o access mode"); #define DPRINTK(nlevel, klevel, fmt, args...) \ (void)((NETIF_MSG_##nlevel & nic->msg_enable) && \ printk(KERN_##klevel PFX "%s: %s: " fmt, nic->netdev->name, \ @@ -282,12 +285,6 @@ enum scb_status { rus_mask = 0x3C, }; -enum ru_state { - RU_SUSPENDED = 0, - RU_RUNNING = 1, - RU_UNINITIALIZED = -1, -}; - enum scb_stat_ack { stat_ack_not_ours = 0x00, stat_ack_sw_gen = 0x04, @@ -529,7 +526,6 @@ struct nic { struct rx *rx_to_use; struct rx *rx_to_clean; struct rfd blank_rfd; - enum ru_state ru_running; spinlock_t cb_lock ____cacheline_aligned; spinlock_t cmd_lock; @@ -591,7 +587,7 @@ static inline void e100_write_flush(struct nic *nic) { /* Flush previous PCI writes through intermediate bridges * by doing a benign read */ - (void)readb(&nic->csr->scb.status); + (void)ioread8(&nic->csr->scb.status); } static void e100_enable_irq(struct nic *nic) @@ -599,7 +595,7 @@ static void e100_enable_irq(struct nic *nic) unsigned long flags; spin_lock_irqsave(&nic->cmd_lock, flags); - writeb(irq_mask_none, &nic->csr->scb.cmd_hi); + iowrite8(irq_mask_none, &nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irqrestore(&nic->cmd_lock, flags); } @@ -609,7 +605,7 @@ static void e100_disable_irq(struct nic *nic) unsigned long flags; spin_lock_irqsave(&nic->cmd_lock, flags); - writeb(irq_mask_all, &nic->csr->scb.cmd_hi); + iowrite8(irq_mask_all, &nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irqrestore(&nic->cmd_lock, flags); } @@ -618,11 +614,11 @@ static void e100_hw_reset(struct nic *nic) { /* Put CU and RU into idle with a selective reset to get * device off of PCI bus */ - writel(selective_reset, &nic->csr->port); + iowrite32(selective_reset, &nic->csr->port); e100_write_flush(nic); udelay(20); /* Now fully reset device */ - writel(software_reset, &nic->csr->port); + iowrite32(software_reset, &nic->csr->port); e100_write_flush(nic); udelay(20); /* Mask off our interrupt line - it's unmasked after reset */ @@ -639,7 +635,7 @@ static int e100_self_test(struct nic *nic) nic->mem->selftest.signature = 0; nic->mem->selftest.result = 0xFFFFFFFF; - writel(selftest | dma_addr, &nic->csr->port); + iowrite32(selftest | dma_addr, &nic->csr->port); e100_write_flush(nic); /* Wait 10 msec for self-test to complete */ msleep(10); @@ -677,23 +673,23 @@ static void e100_eeprom_write(struct nic *nic, u16 addr_len, u16 addr, u16 data) for(j = 0; j < 3; j++) { /* Chip select */ - writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); for(i = 31; i >= 0; i--) { ctrl = (cmd_addr_data[j] & (1 << i)) ? eecs | eedi : eecs; - writeb(ctrl, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); - writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); } /* Wait 10 msec for cmd to complete */ msleep(10); /* Chip deselect */ - writeb(0, &nic->csr->eeprom_ctrl_lo); + iowrite8(0, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); } }; @@ -709,21 +705,21 @@ static u16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr) cmd_addr_data = ((op_read << *addr_len) | addr) << 16; /* Chip select */ - writeb(eecs | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); /* Bit-bang to read word from eeprom */ for(i = 31; i >= 0; i--) { ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs; - writeb(ctrl, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); - writeb(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); + iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); /* Eeprom drives a dummy zero to EEDO after receiving * complete address. Use this to adjust addr_len. */ - ctrl = readb(&nic->csr->eeprom_ctrl_lo); + ctrl = ioread8(&nic->csr->eeprom_ctrl_lo); if(!(ctrl & eedo) && i > 16) { *addr_len -= (i - 16); i = 17; @@ -733,7 +729,7 @@ static u16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr) } /* Chip deselect */ - writeb(0, &nic->csr->eeprom_ctrl_lo); + iowrite8(0, &nic->csr->eeprom_ctrl_lo); e100_write_flush(nic); udelay(4); return le16_to_cpu(data); @@ -804,7 +800,7 @@ static int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr) /* Previous command is accepted when SCB clears */ for(i = 0; i < E100_WAIT_SCB_TIMEOUT; i++) { - if(likely(!readb(&nic->csr->scb.cmd_lo))) + if(likely(!ioread8(&nic->csr->scb.cmd_lo))) break; cpu_relax(); if(unlikely(i > E100_WAIT_SCB_FAST)) @@ -816,8 +812,8 @@ static int e100_exec_cmd(struct nic *nic, u8 cmd, dma_addr_t dma_addr) } if(unlikely(cmd != cuc_resume)) - writel(dma_addr, &nic->csr->scb.gen_ptr); - writeb(cmd, &nic->csr->scb.cmd_lo); + iowrite32(dma_addr, &nic->csr->scb.gen_ptr); + iowrite8(cmd, &nic->csr->scb.cmd_lo); err_unlock: spin_unlock_irqrestore(&nic->cmd_lock, flags); @@ -895,7 +891,7 @@ static u16 mdio_ctrl(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) */ spin_lock_irqsave(&nic->mdio_lock, flags); for (i = 100; i; --i) { - if (readl(&nic->csr->mdi_ctrl) & mdi_ready) + if (ioread32(&nic->csr->mdi_ctrl) & mdi_ready) break; udelay(20); } @@ -905,11 +901,11 @@ static u16 mdio_ctrl(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data) spin_unlock_irqrestore(&nic->mdio_lock, flags); return 0; /* No way to indicate timeout error */ } - writel((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl); + iowrite32((reg << 16) | (addr << 21) | dir | data, &nic->csr->mdi_ctrl); for (i = 0; i < 100; i++) { udelay(20); - if ((data_out = readl(&nic->csr->mdi_ctrl)) & mdi_ready) + if ((data_out = ioread32(&nic->csr->mdi_ctrl)) & mdi_ready) break; } spin_unlock_irqrestore(&nic->mdio_lock, flags); @@ -951,7 +947,7 @@ static void e100_get_defaults(struct nic *nic) ((nic->mac >= mac_82558_D101_A4) ? cb_cid : cb_i)); /* Template for a freshly allocated RFD */ - nic->blank_rfd.command = cpu_to_le16(cb_el); + nic->blank_rfd.command = cpu_to_le16(cb_el & cb_s); nic->blank_rfd.rbd = 0xFFFFFFFF; nic->blank_rfd.size = cpu_to_le16(VLAN_ETH_FRAME_LEN); @@ -1318,7 +1314,7 @@ static inline int e100_exec_cb_wait(struct nic *nic, struct sk_buff *skb, } /* ack any interupts, something could have been set */ - writeb(~0, &nic->csr->scb.stat_ack); + iowrite8(~0, &nic->csr->scb.stat_ack); /* if the command failed, or is not OK, notify and return */ if (!counter || !(cb->status & cpu_to_le16(cb_ok))) { @@ -1580,7 +1576,7 @@ static void e100_watchdog(unsigned long data) * accidentally, due to hardware that shares a register between the * interrupt mask bit and the SW Interrupt generation bit */ spin_lock_irq(&nic->cmd_lock); - writeb(readb(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); + iowrite8(ioread8(&nic->csr->scb.cmd_hi) | irq_sw_gen,&nic->csr->scb.cmd_hi); e100_write_flush(nic); spin_unlock_irq(&nic->cmd_lock); @@ -1746,19 +1742,11 @@ static int e100_alloc_cbs(struct nic *nic) return 0; } -static inline void e100_start_receiver(struct nic *nic, struct rx *rx) +static inline void e100_start_receiver(struct nic *nic) { - if(!nic->rxs) return; - if(RU_SUSPENDED != nic->ru_running) return; - - /* handle init time starts */ - if(!rx) rx = nic->rxs; - - /* (Re)start RU if suspended or idle and RFA is non-NULL */ - if(rx->skb) { - e100_exec_cmd(nic, ruc_start, rx->dma_addr); - nic->ru_running = RU_RUNNING; - } + /* Start if RFA is non-NULL */ + if(nic->rx_to_clean->skb) + e100_exec_cmd(nic, ruc_start, nic->rx_to_clean->dma_addr); } #define RFD_BUF_LEN (sizeof(struct rfd) + VLAN_ETH_FRAME_LEN) @@ -1769,7 +1757,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) /* Align, init, and map the RFD. */ skb_reserve(rx->skb, NET_IP_ALIGN); - memcpy(rx->skb->data, &nic->blank_rfd, sizeof(struct rfd)); + skb_copy_to_linear_data(rx->skb, &nic->blank_rfd, sizeof(struct rfd)); rx->dma_addr = pci_map_single(nic->pdev, rx->skb->data, RFD_BUF_LEN, PCI_DMA_BIDIRECTIONAL); @@ -1787,7 +1775,7 @@ static int e100_rx_alloc_skb(struct nic *nic, struct rx *rx) put_unaligned(cpu_to_le32(rx->dma_addr), (u32 *)&prev_rfd->link); wmb(); - prev_rfd->command &= ~cpu_to_le16(cb_el); + prev_rfd->command &= ~cpu_to_le16(cb_el & cb_s); pci_dma_sync_single_for_device(nic->pdev, rx->prev->dma_addr, sizeof(struct rfd), PCI_DMA_TODEVICE); } @@ -1825,10 +1813,6 @@ static int e100_rx_indicate(struct nic *nic, struct rx *rx, pci_unmap_single(nic->pdev, rx->dma_addr, RFD_BUF_LEN, PCI_DMA_FROMDEVICE); - /* this allows for a fast restart without re-enabling interrupts */ - if(le16_to_cpu(rfd->command) & cb_el) - nic->ru_running = RU_SUSPENDED; - /* Pull off the RFD and put the actual data (minus eth hdr) */ skb_reserve(skb, sizeof(struct rfd)); skb_put(skb, actual_size); @@ -1859,45 +1843,18 @@ static void e100_rx_clean(struct nic *nic, unsigned int *work_done, unsigned int work_to_do) { struct rx *rx; - int restart_required = 0; - struct rx *rx_to_start = NULL; - - /* are we already rnr? then pay attention!!! this ensures that - * the state machine progression never allows a start with a - * partially cleaned list, avoiding a race between hardware - * and rx_to_clean when in NAPI mode */ - if(RU_SUSPENDED == nic->ru_running) - restart_required = 1; /* Indicate newly arrived packets */ for(rx = nic->rx_to_clean; rx->skb; rx = nic->rx_to_clean = rx->next) { - int err = e100_rx_indicate(nic, rx, work_done, work_to_do); - if(-EAGAIN == err) { - /* hit quota so have more work to do, restart once - * cleanup is complete */ - restart_required = 0; - break; - } else if(-ENODATA == err) + if(e100_rx_indicate(nic, rx, work_done, work_to_do)) break; /* No more to clean */ } - /* save our starting point as the place we'll restart the receiver */ - if(restart_required) - rx_to_start = nic->rx_to_clean; - /* Alloc new skbs to refill list */ for(rx = nic->rx_to_use; !rx->skb; rx = nic->rx_to_use = rx->next) { if(unlikely(e100_rx_alloc_skb(nic, rx))) break; /* Better luck next time (see watchdog) */ } - - if(restart_required) { - // ack the rnr? - writeb(stat_ack_rnr, &nic->csr->scb.stat_ack); - e100_start_receiver(nic, rx_to_start); - if(work_done) - (*work_done)++; - } } static void e100_rx_clean_list(struct nic *nic) @@ -1905,8 +1862,6 @@ static void e100_rx_clean_list(struct nic *nic) struct rx *rx; unsigned int i, count = nic->params.rfds.count; - nic->ru_running = RU_UNINITIALIZED; - if(nic->rxs) { for(rx = nic->rxs, i = 0; i < count; rx++, i++) { if(rx->skb) { @@ -1928,7 +1883,6 @@ static int e100_rx_alloc_list(struct nic *nic) unsigned int i, count = nic->params.rfds.count; nic->rx_to_use = nic->rx_to_clean = NULL; - nic->ru_running = RU_UNINITIALIZED; if(!(nic->rxs = kcalloc(count, sizeof(struct rx), GFP_ATOMIC))) return -ENOMEM; @@ -1943,7 +1897,6 @@ static int e100_rx_alloc_list(struct nic *nic) } nic->rx_to_use = nic->rx_to_clean = nic->rxs; - nic->ru_running = RU_SUSPENDED; return 0; } @@ -1952,7 +1905,7 @@ static irqreturn_t e100_intr(int irq, void *dev_id) { struct net_device *netdev = dev_id; struct nic *nic = netdev_priv(netdev); - u8 stat_ack = readb(&nic->csr->scb.stat_ack); + u8 stat_ack = ioread8(&nic->csr->scb.stat_ack); DPRINTK(INTR, DEBUG, "stat_ack = 0x%02X\n", stat_ack); @@ -1961,11 +1914,7 @@ static irqreturn_t e100_intr(int irq, void *dev_id) return IRQ_NONE; /* Ack interrupt(s) */ - writeb(stat_ack, &nic->csr->scb.stat_ack); - - /* We hit Receive No Resource (RNR); restart RU after cleaning */ - if(stat_ack & stat_ack_rnr) - nic->ru_running = RU_SUSPENDED; + iowrite8(stat_ack, &nic->csr->scb.stat_ack); if(likely(netif_rx_schedule_prep(netdev))) { e100_disable_irq(nic); @@ -2058,7 +2007,7 @@ static int e100_up(struct nic *nic) if((err = e100_hw_init(nic))) goto err_clean_cbs; e100_set_multicast_list(nic->netdev); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); mod_timer(&nic->watchdog, jiffies); if((err = request_irq(nic->pdev->irq, e100_intr, IRQF_SHARED, nic->netdev->name, nic->netdev))) @@ -2107,7 +2056,7 @@ static void e100_tx_timeout_task(struct work_struct *work) struct net_device *netdev = nic->netdev; DPRINTK(TX_ERR, DEBUG, "scb.status=0x%02X\n", - readb(&nic->csr->scb.status)); + ioread8(&nic->csr->scb.status)); e100_down(netdev_priv(netdev)); e100_up(netdev_priv(netdev)); } @@ -2139,7 +2088,7 @@ static int e100_loopback_test(struct nic *nic, enum loopback loopback_mode) mdio_write(nic->netdev, nic->mii.phy_id, MII_BMCR, BMCR_LOOPBACK); - e100_start_receiver(nic, NULL); + e100_start_receiver(nic); if(!(skb = netdev_alloc_skb(nic->netdev, ETH_DATA_LEN))) { err = -ENOMEM; @@ -2230,9 +2179,9 @@ static void e100_get_regs(struct net_device *netdev, int i; regs->version = (1 << 24) | nic->rev_id; - buff[0] = readb(&nic->csr->scb.cmd_hi) << 24 | - readb(&nic->csr->scb.cmd_lo) << 16 | - readw(&nic->csr->scb.status); + buff[0] = ioread8(&nic->csr->scb.cmd_hi) << 24 | + ioread8(&nic->csr->scb.cmd_lo) << 16 | + ioread16(&nic->csr->scb.status); for(i = E100_PHY_REGS; i >= 0; i--) buff[1 + E100_PHY_REGS - i] = mdio_read(netdev, nic->mii.phy_id, i); @@ -2604,7 +2553,10 @@ static int __devinit e100_probe(struct pci_dev *pdev, SET_MODULE_OWNER(netdev); SET_NETDEV_DEV(netdev, &pdev->dev); - nic->csr = ioremap(pci_resource_start(pdev, 0), sizeof(struct csr)); + if (use_io) + DPRINTK(PROBE, INFO, "using i/o access mode\n"); + + nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr)); if(!nic->csr) { DPRINTK(PROBE, ERR, "Cannot map device registers, aborting.\n"); err = -ENOMEM; @@ -2651,11 +2603,16 @@ static int __devinit e100_probe(struct pci_dev *pdev, memcpy(netdev->dev_addr, nic->eeprom, ETH_ALEN); memcpy(netdev->perm_addr, nic->eeprom, ETH_ALEN); - if(!is_valid_ether_addr(netdev->perm_addr)) { - DPRINTK(PROBE, ERR, "Invalid MAC address from " - "EEPROM, aborting.\n"); - err = -EAGAIN; - goto err_out_free; + if (!is_valid_ether_addr(netdev->perm_addr)) { + if (!eeprom_bad_csum_allow) { + DPRINTK(PROBE, ERR, "Invalid MAC address from " + "EEPROM, aborting.\n"); + err = -EAGAIN; + goto err_out_free; + } else { + DPRINTK(PROBE, ERR, "Invalid MAC address from EEPROM, " + "you MUST configure one.\n"); + } } /* Wol magic packet can be enabled from eeprom */ @@ -2676,7 +2633,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, " "MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n", - (unsigned long long)pci_resource_start(pdev, 0), pdev->irq, + (unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0), pdev->irq, netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]); @@ -2685,7 +2642,7 @@ static int __devinit e100_probe(struct pci_dev *pdev, err_out_free: e100_free(nic); err_out_iounmap: - iounmap(nic->csr); + pci_iounmap(pdev, nic->csr); err_out_free_res: pci_release_regions(pdev); err_out_disable_pdev: diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h index 689f158..a9ea67e 100644 --- a/drivers/net/e1000/e1000.h +++ b/drivers/net/e1000/e1000.h @@ -155,9 +155,6 @@ struct e1000_adapter; /* Number of packet split data buffers (not including the header buffer) */ #define PS_PAGE_BUFFERS MAX_PS_BUFFERS-1 -/* only works for sizes that are powers of 2 */ -#define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1))) - /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct e1000_buffer { @@ -337,7 +334,6 @@ struct e1000_adapter { struct e1000_rx_ring test_rx_ring; - uint32_t *config_space; int msg_enable; #ifdef CONFIG_PCI_MSI boolean_t have_msi; diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c index 44ebc72..bb08375 100644 --- a/drivers/net/e1000/e1000_ethtool.c +++ b/drivers/net/e1000/e1000_ethtool.c @@ -166,7 +166,7 @@ e1000_get_settings(struct net_device *netdev, struct ethtool_cmd *ecmd) ecmd->transceiver = XCVR_EXTERNAL; } - if (netif_carrier_ok(adapter->netdev)) { + if (E1000_READ_REG(&adapter->hw, STATUS) & E1000_STATUS_LU) { e1000_get_speed_and_duplex(hw, &adapter->link_speed, &adapter->link_duplex); @@ -654,14 +654,11 @@ e1000_set_ringparam(struct net_device *netdev, e1000_mac_type mac_type = adapter->hw.mac_type; struct e1000_tx_ring *txdr, *tx_old; struct e1000_rx_ring *rxdr, *rx_old; - int i, err, tx_ring_size, rx_ring_size; + int i, err; if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending)) return -EINVAL; - tx_ring_size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues; - rx_ring_size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues; - while (test_and_set_bit(__E1000_RESETTING, &adapter->flags)) msleep(1); @@ -672,11 +669,11 @@ e1000_set_ringparam(struct net_device *netdev, rx_old = adapter->rx_ring; err = -ENOMEM; - txdr = kzalloc(tx_ring_size, GFP_KERNEL); + txdr = kcalloc(adapter->num_tx_queues, sizeof(struct e1000_tx_ring), GFP_KERNEL); if (!txdr) goto err_alloc_tx; - rxdr = kzalloc(rx_ring_size, GFP_KERNEL); + rxdr = kcalloc(adapter->num_rx_queues, sizeof(struct e1000_rx_ring), GFP_KERNEL); if (!rxdr) goto err_alloc_rx; @@ -686,12 +683,12 @@ e1000_set_ringparam(struct net_device *netdev, rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD); rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_RXD : E1000_MAX_82544_RXD)); - E1000_ROUNDUP(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); + rxdr->count = ALIGN(rxdr->count, REQ_RX_DESCRIPTOR_MULTIPLE); txdr->count = max(ring->tx_pending,(uint32_t)E1000_MIN_TXD); txdr->count = min(txdr->count,(uint32_t)(mac_type < e1000_82544 ? E1000_MAX_TXD : E1000_MAX_82544_TXD)); - E1000_ROUNDUP(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); + txdr->count = ALIGN(txdr->count, REQ_TX_DESCRIPTOR_MULTIPLE); for (i = 0; i < adapter->num_tx_queues; i++) txdr[i].count = txdr->count; @@ -742,7 +739,7 @@ err_setup: uint32_t pat, value; \ uint32_t test[] = \ {0x5A5A5A5A, 0xA5A5A5A5, 0x00000000, 0xFFFFFFFF}; \ - for (pat = 0; pat < sizeof(test)/sizeof(test[0]); pat++) { \ + for (pat = 0; pat < ARRAY_SIZE(test); pat++) { \ E1000_WRITE_REG(&adapter->hw, R, (test[pat] & W)); \ value = E1000_READ_REG(&adapter->hw, R); \ if (value != (test[pat] & W & M)) { \ @@ -1053,23 +1050,24 @@ e1000_setup_desc_rings(struct e1000_adapter *adapter) struct e1000_rx_ring *rxdr = &adapter->test_rx_ring; struct pci_dev *pdev = adapter->pdev; uint32_t rctl; - int size, i, ret_val; + int i, ret_val; /* Setup Tx descriptor ring and Tx buffers */ if (!txdr->count) txdr->count = E1000_DEFAULT_TXD; - size = txdr->count * sizeof(struct e1000_buffer); - if (!(txdr->buffer_info = kmalloc(size, GFP_KERNEL))) { + if (!(txdr->buffer_info = kcalloc(txdr->count, + sizeof(struct e1000_buffer), + GFP_KERNEL))) { ret_val = 1; goto err_nomem; } - memset(txdr->buffer_info, 0, size); txdr->size = txdr->count * sizeof(struct e1000_tx_desc); - E1000_ROUNDUP(txdr->size, 4096); - if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma))) { + txdr->size = ALIGN(txdr->size, 4096); + if (!(txdr->desc = pci_alloc_consistent(pdev, txdr->size, + &txdr->dma))) { ret_val = 2; goto err_nomem; } @@ -1116,12 +1114,12 @@ e1000_setup_desc_rings(struct e1000_adapter *adapter) if (!rxdr->count) rxdr->count = E1000_DEFAULT_RXD; - size = rxdr->count * sizeof(struct e1000_buffer); - if (!(rxdr->buffer_info = kmalloc(size, GFP_KERNEL))) { + if (!(rxdr->buffer_info = kcalloc(rxdr->count, + sizeof(struct e1000_buffer), + GFP_KERNEL))) { ret_val = 4; goto err_nomem; } - memset(rxdr->buffer_info, 0, size); rxdr->size = rxdr->count * sizeof(struct e1000_rx_desc); if (!(rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma))) { diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h index d671058..bd000b80 100644 --- a/drivers/net/e1000/e1000_hw.h +++ b/drivers/net/e1000/e1000_hw.h @@ -3253,7 +3253,7 @@ struct e1000_host_command_info { #define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable MDI/MDI-X feature, default 0=disabled */ #define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDIX-X, 0=force MDI */ #define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */ -#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorthm is completed */ +#define IFE_PMC_AUTO_MDIX_COMPLETE 0x0010 /* Resolution algorithm is completed */ #define IFE_PMC_MDIX_MODE_SHIFT 6 #define IFE_PHC_MDIX_RESET_ALL_MASK 0x0000 /* Disable auto MDI-X */ diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c index 619c892..3a03a74 100644 --- a/drivers/net/e1000/e1000_main.c +++ b/drivers/net/e1000/e1000_main.c @@ -376,7 +376,7 @@ e1000_update_mng_vlan(struct e1000_adapter *adapter) uint16_t vid = adapter->hw.mng_cookie.vlan_id; uint16_t old_vid = adapter->mng_vlan_id; if (adapter->vlgrp) { - if (!adapter->vlgrp->vlan_devices[vid]) { + if (!vlan_group_get_device(adapter->vlgrp, vid)) { if (adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) { e1000_vlan_rx_add_vid(netdev, vid); @@ -386,7 +386,7 @@ e1000_update_mng_vlan(struct e1000_adapter *adapter) if ((old_vid != (uint16_t)E1000_MNG_VLAN_NONE) && (vid != old_vid) && - !adapter->vlgrp->vlan_devices[old_vid]) + !vlan_group_get_device(adapter->vlgrp, old_vid)) e1000_vlan_rx_kill_vid(netdev, old_vid); } else adapter->mng_vlan_id = vid; @@ -409,25 +409,21 @@ e1000_release_hw_control(struct e1000_adapter *adapter) { uint32_t ctrl_ext; uint32_t swsm; - uint32_t extcnf; /* Let firmware taken over control of h/w */ switch (adapter->hw.mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); - break; case e1000_82573: swsm = E1000_READ_REG(&adapter->hw, SWSM); E1000_WRITE_REG(&adapter->hw, SWSM, swsm & ~E1000_SWSM_DRV_LOAD); + break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: case e1000_ich8lan: - extcnf = E1000_READ_REG(&adapter->hw, CTRL_EXT); + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - extcnf & ~E1000_CTRL_EXT_DRV_LOAD); + ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); break; default: break; @@ -450,26 +446,21 @@ e1000_get_hw_control(struct e1000_adapter *adapter) { uint32_t ctrl_ext; uint32_t swsm; - uint32_t extcnf; /* Let firmware know the driver has taken over */ switch (adapter->hw.mac_type) { - case e1000_82571: - case e1000_82572: - case e1000_80003es2lan: - ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); - E1000_WRITE_REG(&adapter->hw, CTRL_EXT, - ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); - break; case e1000_82573: swsm = E1000_READ_REG(&adapter->hw, SWSM); E1000_WRITE_REG(&adapter->hw, SWSM, swsm | E1000_SWSM_DRV_LOAD); break; + case e1000_82571: + case e1000_82572: + case e1000_80003es2lan: case e1000_ich8lan: - extcnf = E1000_READ_REG(&adapter->hw, EXTCNF_CTRL); - E1000_WRITE_REG(&adapter->hw, EXTCNF_CTRL, - extcnf | E1000_EXTCNF_CTRL_SWFLAG); + ctrl_ext = E1000_READ_REG(&adapter->hw, CTRL_EXT); + E1000_WRITE_REG(&adapter->hw, CTRL_EXT, + ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); break; default: break; @@ -522,14 +513,15 @@ e1000_release_manageability(struct e1000_adapter *adapter) } } -int -e1000_up(struct e1000_adapter *adapter) +/** + * e1000_configure - configure the hardware for RX and TX + * @adapter = private board structure + **/ +static void e1000_configure(struct e1000_adapter *adapter) { struct net_device *netdev = adapter->netdev; int i; - /* hardware has been reset, we need to reload some things */ - e1000_set_multi(netdev); e1000_restore_vlan(adapter); @@ -548,14 +540,20 @@ e1000_up(struct e1000_adapter *adapter) } adapter->tx_queue_len = netdev->tx_queue_len; +} + +int e1000_up(struct e1000_adapter *adapter) +{ + /* hardware has been reset, we need to reload some things */ + e1000_configure(adapter); + + clear_bit(__E1000_DOWN, &adapter->flags); #ifdef CONFIG_E1000_NAPI - netif_poll_enable(netdev); + netif_poll_enable(adapter->netdev); #endif e1000_irq_enable(adapter); - clear_bit(__E1000_DOWN, &adapter->flags); - /* fire a link change interrupt to start the watchdog */ E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); return 0; @@ -640,15 +638,15 @@ e1000_down(struct e1000_adapter *adapter) * reschedule our watchdog timer */ set_bit(__E1000_DOWN, &adapter->flags); +#ifdef CONFIG_E1000_NAPI + netif_poll_disable(netdev); +#endif e1000_irq_disable(adapter); del_timer_sync(&adapter->tx_fifo_stall_timer); del_timer_sync(&adapter->watchdog_timer); del_timer_sync(&adapter->phy_info_timer); -#ifdef CONFIG_E1000_NAPI - netif_poll_disable(netdev); -#endif netdev->tx_queue_len = adapter->tx_queue_len; adapter->link_speed = 0; adapter->link_duplex = 0; @@ -750,9 +748,9 @@ e1000_reset(struct e1000_adapter *adapter) VLAN_TAG_SIZE; min_tx_space = min_rx_space; min_tx_space *= 2; - E1000_ROUNDUP(min_tx_space, 1024); + min_tx_space = ALIGN(min_tx_space, 1024); min_tx_space >>= 10; - E1000_ROUNDUP(min_rx_space, 1024); + min_rx_space = ALIGN(min_rx_space, 1024); min_rx_space >>= 10; /* If current Tx allocation is less than the min Tx FIFO size, @@ -1356,31 +1354,27 @@ e1000_sw_init(struct e1000_adapter *adapter) static int __devinit e1000_alloc_queues(struct e1000_adapter *adapter) { - int size; - - size = sizeof(struct e1000_tx_ring) * adapter->num_tx_queues; - adapter->tx_ring = kmalloc(size, GFP_KERNEL); + adapter->tx_ring = kcalloc(adapter->num_tx_queues, + sizeof(struct e1000_tx_ring), GFP_KERNEL); if (!adapter->tx_ring) return -ENOMEM; - memset(adapter->tx_ring, 0, size); - size = sizeof(struct e1000_rx_ring) * adapter->num_rx_queues; - adapter->rx_ring = kmalloc(size, GFP_KERNEL); + adapter->rx_ring = kcalloc(adapter->num_rx_queues, + sizeof(struct e1000_rx_ring), GFP_KERNEL); if (!adapter->rx_ring) { kfree(adapter->tx_ring); return -ENOMEM; } - memset(adapter->rx_ring, 0, size); #ifdef CONFIG_E1000_NAPI - size = sizeof(struct net_device) * adapter->num_rx_queues; - adapter->polling_netdev = kmalloc(size, GFP_KERNEL); + adapter->polling_netdev = kcalloc(adapter->num_rx_queues, + sizeof(struct net_device), + GFP_KERNEL); if (!adapter->polling_netdev) { kfree(adapter->tx_ring); kfree(adapter->rx_ring); return -ENOMEM; } - memset(adapter->polling_netdev, 0, size); #endif return E1000_SUCCESS; @@ -1410,21 +1404,17 @@ e1000_open(struct net_device *netdev) return -EBUSY; /* allocate transmit descriptors */ - if ((err = e1000_setup_all_tx_resources(adapter))) + err = e1000_setup_all_tx_resources(adapter); + if (err) goto err_setup_tx; /* allocate receive descriptors */ - if ((err = e1000_setup_all_rx_resources(adapter))) - goto err_setup_rx; - - err = e1000_request_irq(adapter); + err = e1000_setup_all_rx_resources(adapter); if (err) - goto err_req_irq; + goto err_setup_rx; e1000_power_up_phy(adapter); - if ((err = e1000_up(adapter))) - goto err_up; adapter->mng_vlan_id = E1000_MNG_VLAN_NONE; if ((adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) { @@ -1437,12 +1427,33 @@ e1000_open(struct net_device *netdev) e1000_check_mng_mode(&adapter->hw)) e1000_get_hw_control(adapter); + /* before we allocate an interrupt, we must be ready to handle it. + * Setting DEBUG_SHIRQ in the kernel makes it fire an interrupt + * as soon as we call pci_request_irq, so we have to setup our + * clean_rx handler before we do so. */ + e1000_configure(adapter); + + err = e1000_request_irq(adapter); + if (err) + goto err_req_irq; + + /* From here on the code is the same as e1000_up() */ + clear_bit(__E1000_DOWN, &adapter->flags); + +#ifdef CONFIG_E1000_NAPI + netif_poll_enable(netdev); +#endif + + e1000_irq_enable(adapter); + + /* fire a link status change interrupt to start the watchdog */ + E1000_WRITE_REG(&adapter->hw, ICS, E1000_ICS_LSC); + return E1000_SUCCESS; -err_up: - e1000_power_down_phy(adapter); - e1000_free_irq(adapter); err_req_irq: + e1000_release_hw_control(adapter); + e1000_power_down_phy(adapter); e1000_free_all_rx_resources(adapter); err_setup_rx: e1000_free_all_tx_resources(adapter); @@ -1482,7 +1493,7 @@ e1000_close(struct net_device *netdev) if ((adapter->hw.mng_cookie.status & E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) && !(adapter->vlgrp && - adapter->vlgrp->vlan_devices[adapter->mng_vlan_id])) { + vlan_group_get_device(adapter->vlgrp, adapter->mng_vlan_id))) { e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id); } @@ -1545,7 +1556,7 @@ e1000_setup_tx_resources(struct e1000_adapter *adapter, /* round up to nearest 4K */ txdr->size = txdr->count * sizeof(struct e1000_tx_desc); - E1000_ROUNDUP(txdr->size, 4096); + txdr->size = ALIGN(txdr->size, 4096); txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); if (!txdr->desc) { @@ -1759,18 +1770,18 @@ e1000_setup_rx_resources(struct e1000_adapter *adapter, } memset(rxdr->buffer_info, 0, size); - size = sizeof(struct e1000_ps_page) * rxdr->count; - rxdr->ps_page = kmalloc(size, GFP_KERNEL); + rxdr->ps_page = kcalloc(rxdr->count, sizeof(struct e1000_ps_page), + GFP_KERNEL); if (!rxdr->ps_page) { vfree(rxdr->buffer_info); DPRINTK(PROBE, ERR, "Unable to allocate memory for the receive descriptor ring\n"); return -ENOMEM; } - memset(rxdr->ps_page, 0, size); - size = sizeof(struct e1000_ps_page_dma) * rxdr->count; - rxdr->ps_page_dma = kmalloc(size, GFP_KERNEL); + rxdr->ps_page_dma = kcalloc(rxdr->count, + sizeof(struct e1000_ps_page_dma), + GFP_KERNEL); if (!rxdr->ps_page_dma) { vfree(rxdr->buffer_info); kfree(rxdr->ps_page); @@ -1778,7 +1789,6 @@ e1000_setup_rx_resources(struct e1000_adapter *adapter, "Unable to allocate memory for the receive descriptor ring\n"); return -ENOMEM; } - memset(rxdr->ps_page_dma, 0, size); if (adapter->hw.mac_type <= e1000_82547_rev_2) desc_len = sizeof(struct e1000_rx_desc); @@ -1788,7 +1798,7 @@ e1000_setup_rx_resources(struct e1000_adapter *adapter, /* Round up to nearest 4K */ rxdr->size = rxdr->count * desc_len; - E1000_ROUNDUP(rxdr->size, 4096); + rxdr->size = ALIGN(rxdr->size, 4096); rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); @@ -2652,7 +2662,7 @@ e1000_watchdog(unsigned long data) netif_carrier_on(netdev); netif_wake_queue(netdev); - mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); + mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); adapter->smartspeed = 0; } else { /* make sure the receive unit is started */ @@ -2669,7 +2679,7 @@ e1000_watchdog(unsigned long data) DPRINTK(LINK, INFO, "NIC Link is Down\n"); netif_carrier_off(netdev); netif_stop_queue(netdev); - mod_timer(&adapter->phy_info_timer, jiffies + 2 * HZ); + mod_timer(&adapter->phy_info_timer, round_jiffies(jiffies + 2 * HZ)); /* 80003ES2LAN workaround-- * For packet buffer work-around on link down event; @@ -2721,7 +2731,7 @@ e1000_watchdog(unsigned long data) e1000_rar_set(&adapter->hw, adapter->hw.mac_addr, 0); /* Reset the timer */ - mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); + mod_timer(&adapter->watchdog_timer, round_jiffies(jiffies + 2 * HZ)); } enum latency_range { @@ -2887,33 +2897,30 @@ e1000_tso(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, return err; } - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); mss = skb_shinfo(skb)->gso_size; if (skb->protocol == htons(ETH_P_IP)) { - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, - IPPROTO_TCP, - 0); + struct iphdr *iph = ip_hdr(skb); + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); cmd_length = E1000_TXD_CMD_IP; - ipcse = skb->h.raw - skb->data - 1; + ipcse = skb_transport_offset(skb) - 1; } else if (skb->protocol == htons(ETH_P_IPV6)) { - skb->nh.ipv6h->payload_len = 0; - skb->h.th->check = - ~csum_ipv6_magic(&skb->nh.ipv6h->saddr, - &skb->nh.ipv6h->daddr, - 0, - IPPROTO_TCP, - 0); + ipv6_hdr(skb)->payload_len = 0; + tcp_hdr(skb)->check = + ~csum_ipv6_magic(&ipv6_hdr(skb)->saddr, + &ipv6_hdr(skb)->daddr, + 0, IPPROTO_TCP, 0); ipcse = 0; } - ipcss = skb->nh.raw - skb->data; - ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; - tucss = skb->h.raw - skb->data; - tucso = (void *)&(skb->h.th->check) - (void *)skb->data; + ipcss = skb_network_offset(skb); + ipcso = (void *)&(ip_hdr(skb)->check) - (void *)skb->data; + tucss = skb_transport_offset(skb); + tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; tucse = 0; cmd_length |= (E1000_TXD_CMD_DEXT | E1000_TXD_CMD_TSE | @@ -2954,7 +2961,7 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, uint8_t css; if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - css = skb->h.raw - skb->data; + css = skb_transport_offset(skb); i = tx_ring->next_to_use; buffer_info = &tx_ring->buffer_info[i]; @@ -2962,7 +2969,8 @@ e1000_tx_csum(struct e1000_adapter *adapter, struct e1000_tx_ring *tx_ring, context_desc->lower_setup.ip_config = 0; context_desc->upper_setup.tcp_fields.tucss = css; - context_desc->upper_setup.tcp_fields.tucso = css + skb->csum; + context_desc->upper_setup.tcp_fields.tucso = + css + skb->csum_offset; context_desc->upper_setup.tcp_fields.tucse = 0; context_desc->tcp_seg_setup.data = 0; context_desc->cmd_and_length = cpu_to_le32(E1000_TXD_CMD_DEXT); @@ -3162,7 +3170,7 @@ e1000_82547_fifo_workaround(struct e1000_adapter *adapter, struct sk_buff *skb) uint32_t fifo_space = adapter->tx_fifo_size - adapter->tx_fifo_head; uint32_t skb_fifo_len = skb->len + E1000_FIFO_HDR; - E1000_ROUNDUP(skb_fifo_len, E1000_FIFO_HDR); + skb_fifo_len = ALIGN(skb_fifo_len, E1000_FIFO_HDR); if (adapter->link_duplex != HALF_DUPLEX) goto no_fifo_stall_required; @@ -3296,7 +3304,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* TSO Workaround for 82571/2/3 Controllers -- if skb->data * points to just header, pull a few bytes of payload from * frags into skb->data */ - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); if (skb->data_len && (hdr_len == (skb->len - skb->data_len))) { switch (adapter->hw.mac_type) { unsigned int pull_size; @@ -3307,7 +3315,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) * NOTE: this is a TSO only workaround * if end byte alignment not correct move us * into the next dword */ - if ((unsigned long)(skb->tail - 1) & 4) + if ((unsigned long)(skb_tail_pointer(skb) - 1) & 4) break; /* fall through */ case e1000_82571: @@ -3363,12 +3371,9 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev) (adapter->hw.mac_type == e1000_82573)) e1000_transfer_dhcp_info(adapter, skb); - local_irq_save(flags); - if (!spin_trylock(&tx_ring->tx_lock)) { + if (!spin_trylock_irqsave(&tx_ring->tx_lock, flags)) /* Collision - tell upper layer to requeue */ - local_irq_restore(flags); return NETDEV_TX_LOCKED; - } /* need: count + 2 desc gap to keep tail from touching * head, otherwise try next time */ @@ -3796,7 +3801,7 @@ e1000_intr_msi(int irq, void *data) for (i = 0; i < E1000_MAX_INTR; i++) if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) & - e1000_clean_tx_irq(adapter, adapter->tx_ring))) + !e1000_clean_tx_irq(adapter, adapter->tx_ring))) break; if (likely(adapter->itr_setting & 3)) @@ -3899,7 +3904,7 @@ e1000_intr(int irq, void *data) for (i = 0; i < E1000_MAX_INTR; i++) if (unlikely(!adapter->clean_rx(adapter, adapter->rx_ring) & - e1000_clean_tx_irq(adapter, adapter->tx_ring))) + !e1000_clean_tx_irq(adapter, adapter->tx_ring))) break; if (likely(adapter->itr_setting & 3)) @@ -3949,7 +3954,7 @@ e1000_clean(struct net_device *poll_dev, int *budget) poll_dev->quota -= work_done; /* If no Tx and not enough Rx work done, exit the polling mode */ - if ((tx_cleaned && (work_done < work_to_do)) || + if ((!tx_cleaned && (work_done == 0)) || !netif_running(poll_dev)) { quit_polling: if (likely(adapter->itr_setting & 3)) @@ -3979,7 +3984,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, #ifdef CONFIG_E1000_NAPI unsigned int count = 0; #endif - boolean_t cleaned = TRUE; + boolean_t cleaned = FALSE; unsigned int total_tx_bytes=0, total_tx_packets=0; i = tx_ring->next_to_clean; @@ -4013,10 +4018,7 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter, #ifdef CONFIG_E1000_NAPI #define E1000_TX_WEIGHT 64 /* weight of a sort for tx, to avoid endless transmit cleanup */ - if (count++ == E1000_TX_WEIGHT) { - cleaned = FALSE; - break; - } + if (count++ == E1000_TX_WEIGHT) break; #endif } @@ -4230,9 +4232,12 @@ e1000_clean_rx_irq(struct e1000_adapter *adapter, netdev_alloc_skb(netdev, length + NET_IP_ALIGN); if (new_skb) { skb_reserve(new_skb, NET_IP_ALIGN); - memcpy(new_skb->data - NET_IP_ALIGN, - skb->data - NET_IP_ALIGN, - length + NET_IP_ALIGN); + skb_copy_to_linear_data_offset(new_skb, + -NET_IP_ALIGN, + (skb->data - + NET_IP_ALIGN), + (length + + NET_IP_ALIGN)); /* save the skb in buffer_info as good */ buffer_info->skb = skb; skb = new_skb; @@ -4394,7 +4399,7 @@ e1000_clean_rx_irq_ps(struct e1000_adapter *adapter, PCI_DMA_FROMDEVICE); vaddr = kmap_atomic(ps_page->ps_page[0], KM_SKB_DATA_SOFTIRQ); - memcpy(skb->tail, vaddr, l1); + memcpy(skb_tail_pointer(skb), vaddr, l1); kunmap_atomic(vaddr, KM_SKB_DATA_SOFTIRQ); pci_dma_sync_single_for_device(pdev, ps_page_dma->ps_page_dma[0], @@ -4998,10 +5003,7 @@ e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid) uint32_t vfta, index; e1000_irq_disable(adapter); - - if (adapter->vlgrp) - adapter->vlgrp->vlan_devices[vid] = NULL; - + vlan_group_set_device(adapter->vlgrp, vid, NULL); e1000_irq_enable(adapter); if ((adapter->hw.mng_cookie.status & @@ -5027,7 +5029,7 @@ e1000_restore_vlan(struct e1000_adapter *adapter) if (adapter->vlgrp) { uint16_t vid; for (vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) { - if (!adapter->vlgrp->vlan_devices[vid]) + if (!vlan_group_get_device(adapter->vlgrp, vid)) continue; e1000_vlan_rx_add_vid(adapter->netdev, vid); } @@ -5071,58 +5073,6 @@ e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx) return 0; } -#ifdef CONFIG_PM -/* Save/restore 16 or 64 dwords of PCI config space depending on which - * bus we're on (PCI(X) vs. PCI-E) - */ -#define PCIE_CONFIG_SPACE_LEN 256 -#define PCI_CONFIG_SPACE_LEN 64 -static int -e1000_pci_save_state(struct e1000_adapter *adapter) -{ - struct pci_dev *dev = adapter->pdev; - int size; - int i; - - if (adapter->hw.mac_type >= e1000_82571) - size = PCIE_CONFIG_SPACE_LEN; - else - size = PCI_CONFIG_SPACE_LEN; - - WARN_ON(adapter->config_space != NULL); - - adapter->config_space = kmalloc(size, GFP_KERNEL); - if (!adapter->config_space) { - DPRINTK(PROBE, ERR, "unable to allocate %d bytes\n", size); - return -ENOMEM; - } - for (i = 0; i < (size / 4); i++) - pci_read_config_dword(dev, i * 4, &adapter->config_space[i]); - return 0; -} - -static void -e1000_pci_restore_state(struct e1000_adapter *adapter) -{ - struct pci_dev *dev = adapter->pdev; - int size; - int i; - - if (adapter->config_space == NULL) - return; - - if (adapter->hw.mac_type >= e1000_82571) - size = PCIE_CONFIG_SPACE_LEN; - else - size = PCI_CONFIG_SPACE_LEN; - for (i = 0; i < (size / 4); i++) - pci_write_config_dword(dev, i * 4, adapter->config_space[i]); - kfree(adapter->config_space); - adapter->config_space = NULL; - return; -} -#endif /* CONFIG_PM */ - static int e1000_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -5142,9 +5092,7 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state) } #ifdef CONFIG_PM - /* Implement our own version of pci_save_state(pdev) because pci- - * express adapters have 256-byte config spaces. */ - retval = e1000_pci_save_state(adapter); + retval = pci_save_state(pdev); if (retval) return retval; #endif @@ -5231,7 +5179,7 @@ e1000_resume(struct pci_dev *pdev) uint32_t err; pci_set_power_state(pdev, PCI_D0); - e1000_pci_restore_state(adapter); + pci_restore_state(pdev); if ((err = pci_enable_device(pdev))) { printk(KERN_ERR "e1000: Cannot enable PCI device from suspend\n"); return err; diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c index f8862e2..f485874 100644 --- a/drivers/net/e1000/e1000_param.c +++ b/drivers/net/e1000/e1000_param.c @@ -305,7 +305,7 @@ e1000_check_options(struct e1000_adapter *adapter) if (num_TxDescriptors > bd) { tx_ring->count = TxDescriptors[bd]; e1000_validate_option(&tx_ring->count, &opt, adapter); - E1000_ROUNDUP(tx_ring->count, + tx_ring->count = ALIGN(tx_ring->count, REQ_TX_DESCRIPTOR_MULTIPLE); } else { tx_ring->count = opt.def; @@ -331,7 +331,7 @@ e1000_check_options(struct e1000_adapter *adapter) if (num_RxDescriptors > bd) { rx_ring->count = RxDescriptors[bd]; e1000_validate_option(&rx_ring->count, &opt, adapter); - E1000_ROUNDUP(rx_ring->count, + rx_ring->count = ALIGN(rx_ring->count, REQ_RX_DESCRIPTOR_MULTIPLE); } else { rx_ring->count = opt.def; diff --git a/drivers/net/eepro.c b/drivers/net/eepro.c index b446309..39654e1 100644 --- a/drivers/net/eepro.c +++ b/drivers/net/eepro.c @@ -1591,7 +1591,6 @@ eepro_rx(struct net_device *dev) break; } - skb->dev = dev; skb_reserve(skb,2); if (lp->version == LAN595) diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c index e28bb1e..6c267c3 100644 --- a/drivers/net/eepro100.c +++ b/drivers/net/eepro100.c @@ -1793,7 +1793,6 @@ speedo_rx(struct net_device *dev) copying to a properly sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != 0) { - skb->dev = dev; skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ /* 'skb_put()' points to the start of sk_buff data area. */ pci_dma_sync_single_for_cpu(sp->pdev, sp->rx_ring_dma[entry], @@ -1805,8 +1804,9 @@ speedo_rx(struct net_device *dev) eth_copy_and_sum(skb, sp->rx_skbuff[entry]->data, pkt_len, 0); skb_put(skb, pkt_len); #else - memcpy(skb_put(skb, pkt_len), sp->rx_skbuff[entry]->data, - pkt_len); + skb_copy_from_linear_data(sp->rx_skbuff[entry], + skb_put(skb, pkt_len), + pkt_len); #endif pci_dma_sync_single_for_device(sp->pdev, sp->rx_ring_dma[entry], sizeof(struct RxFD) + pkt_len, diff --git a/drivers/net/eexpress.c b/drivers/net/eexpress.c index 4a50fcb..7934ea3 100644 --- a/drivers/net/eexpress.c +++ b/drivers/net/eexpress.c @@ -115,6 +115,7 @@ #include <linux/mca-legacy.h> #include <linux/spinlock.h> #include <linux/bitops.h> +#include <linux/jiffies.h> #include <asm/system.h> #include <asm/io.h> @@ -556,7 +557,7 @@ static void unstick_cu(struct net_device *dev) if (lp->started) { - if ((jiffies - dev->trans_start)>50) + if (time_after(jiffies, dev->trans_start + 50)) { if (lp->tx_link==lp->last_tx_restart) { @@ -612,7 +613,7 @@ static void unstick_cu(struct net_device *dev) } else { - if ((jiffies-lp->init_time)>10) + if (time_after(jiffies, lp->init_time + 10)) { unsigned short status = scb_status(dev); printk(KERN_WARNING "%s: i82586 startup timed out, status %04x, resetting...\n", @@ -714,13 +715,6 @@ static int eexp_xmit(struct sk_buff *buf, struct net_device *dev) * check to make sure we've not become wedged. */ -/* - * Handle an EtherExpress interrupt - * If we've finished initializing, start the RU and CU up. - * If we've already started, reap tx buffers, handle any received packets, - * check to make sure we've not become wedged. - */ - static unsigned short eexp_start_irq(struct net_device *dev, unsigned short status) { @@ -783,7 +777,7 @@ static unsigned short eexp_start_irq(struct net_device *dev, static void eexp_cmd_clear(struct net_device *dev) { unsigned long int oldtime = jiffies; - while (scb_rdcmd(dev) && ((jiffies-oldtime)<10)); + while (scb_rdcmd(dev) && (time_before(jiffies, oldtime + 10))); if (scb_rdcmd(dev)) { printk("%s: command didn't clear\n", dev->name); } @@ -983,7 +977,6 @@ static void eexp_hw_rx_pio(struct net_device *dev) lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); outw(pbuf+10, ioaddr+READ_PTR); insw(ioaddr+DATAPORT, skb_put(skb,pkt_len),(pkt_len+1)>>1); @@ -1657,7 +1650,7 @@ eexp_set_multicast(struct net_device *dev) #endif oj = jiffies; while ((SCB_CUstat(scb_status(dev)) == 2) && - ((jiffies-oj) < 2000)); + (time_before(jiffies, oj + 2000))); if (SCB_CUstat(scb_status(dev)) == 2) printk("%s: warning, CU didn't stop\n", dev->name); lp->started &= ~(STARTED_CU); diff --git a/drivers/net/ehea/ehea.h b/drivers/net/ehea/ehea.h index 272e1ec..602872d 100644 --- a/drivers/net/ehea/ehea.h +++ b/drivers/net/ehea/ehea.h @@ -39,7 +39,7 @@ #include <asm/io.h> #define DRV_NAME "ehea" -#define DRV_VERSION "EHEA_0045" +#define DRV_VERSION "EHEA_0058" #define EHEA_MSG_DEFAULT (NETIF_MSG_LINK | NETIF_MSG_TIMER \ | NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) @@ -78,10 +78,7 @@ #define EHEA_RQ2_PKT_SIZE 1522 #define EHEA_L_PKT_SIZE 256 /* low latency */ -#define EHEA_POLL_MAX_RWQE 1000 - /* Send completion signaling */ -#define EHEA_SIG_IV_LONG 1 /* Protection Domain Identifier */ #define EHEA_PD_ID 0xaabcdeff @@ -108,11 +105,7 @@ #define EHEA_CACHE_LINE 128 /* Memory Regions */ -#define EHEA_MR_MAX_TX_PAGES 20 -#define EHEA_MR_TX_DATA_PN 3 #define EHEA_MR_ACC_CTRL 0x00800000 -#define EHEA_RWQES_PER_MR_RQ2 10 -#define EHEA_RWQES_PER_MR_RQ3 10 #define EHEA_WATCH_DOG_TIMEOUT 10*HZ @@ -311,6 +304,7 @@ struct ehea_cq { * Memory Region */ struct ehea_mr { + struct ehea_adapter *adapter; u64 handle; u64 vaddr; u32 lkey; @@ -319,17 +313,12 @@ struct ehea_mr { /* * Port state information */ -struct port_state { - int poll_max_processed; +struct port_stats { int poll_receive_errors; - int ehea_poll; int queue_stopped; - int min_swqe_avail; - u64 sqc_stop_sum; - int pkt_send; - int pkt_xmit; - int send_tasklet; - int nwqe; + int err_tcp_cksum; + int err_ip_cksum; + int err_frame_crc; }; #define EHEA_IRQ_NAME_SIZE 20 @@ -348,6 +337,7 @@ struct ehea_q_skb_arr { * Port resources */ struct ehea_port_res { + struct port_stats p_stats; struct ehea_mr send_mr; /* send memory region */ struct ehea_mr recv_mr; /* receive memory region */ spinlock_t xmit_lock; @@ -357,9 +347,8 @@ struct ehea_port_res { struct ehea_qp *qp; struct ehea_cq *send_cq; struct ehea_cq *recv_cq; - struct ehea_eq *send_eq; - struct ehea_eq *recv_eq; - spinlock_t send_lock; + struct ehea_eq *eq; + struct net_device *d_netdev; struct ehea_q_skb_arr rq1_skba; struct ehea_q_skb_arr rq2_skba; struct ehea_q_skb_arr rq3_skba; @@ -369,21 +358,18 @@ struct ehea_port_res { int swqe_refill_th; atomic_t swqe_avail; int swqe_ll_count; - int swqe_count; u32 swqe_id_counter; u64 tx_packets; - struct tasklet_struct send_comp_task; - spinlock_t recv_lock; - struct port_state p_state; u64 rx_packets; u32 poll_counter; }; +#define EHEA_MAX_PORTS 16 struct ehea_adapter { u64 handle; - u8 num_ports; - struct ehea_port *port[16]; + struct ibmebus_dev *ebus_dev; + struct ehea_port *port[EHEA_MAX_PORTS]; struct ehea_eq *neq; /* notification event queue */ struct workqueue_struct *ehea_wq; struct tasklet_struct neq_tasklet; @@ -406,7 +392,7 @@ struct ehea_port { struct net_device *netdev; struct net_device_stats stats; struct ehea_port_res port_res[EHEA_MAX_PORT_RES]; - struct device_node *of_dev_node; /* Open Firmware Device Node */ + struct of_device ofdev; /* Open Firmware Device */ struct ehea_mc_list *mc_list; /* Multicast MAC addresses */ struct vlan_group *vgrp; struct ehea_eq *qp_eq; @@ -415,7 +401,9 @@ struct ehea_port { char int_aff_name[EHEA_IRQ_NAME_SIZE]; int allmulti; /* Indicates IFF_ALLMULTI state */ int promisc; /* Indicates IFF_PROMISC state */ + int num_tx_qps; int num_add_tx_qps; + int num_mcs; int resets; u64 mac_addr; u32 logical_port_id; diff --git a/drivers/net/ehea/ehea_ethtool.c b/drivers/net/ehea/ehea_ethtool.c index 9f57c2e..decec8c 100644 --- a/drivers/net/ehea/ehea_ethtool.c +++ b/drivers/net/ehea/ehea_ethtool.c @@ -144,8 +144,8 @@ static int ehea_nway_reset(struct net_device *dev) static void ehea_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - strlcpy(info->driver, DRV_NAME, sizeof(info->driver) - 1); - strlcpy(info->version, DRV_VERSION, sizeof(info->version) - 1); + strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); + strlcpy(info->version, DRV_VERSION, sizeof(info->version)); } static u32 ehea_get_msglevel(struct net_device *dev) @@ -166,33 +166,23 @@ static u32 ehea_get_rx_csum(struct net_device *dev) } static char ehea_ethtool_stats_keys[][ETH_GSTRING_LEN] = { - {"poll_max_processed"}, - {"queue_stopped"}, - {"min_swqe_avail"}, - {"poll_receive_err"}, - {"pkt_send"}, - {"pkt_xmit"}, - {"send_tasklet"}, - {"ehea_poll"}, - {"nwqe"}, - {"swqe_available_0"}, {"sig_comp_iv"}, {"swqe_refill_th"}, {"port resets"}, - {"rxo"}, - {"rx64"}, - {"rx65"}, - {"rx128"}, - {"rx256"}, - {"rx512"}, - {"rx1024"}, - {"txo"}, - {"tx64"}, - {"tx65"}, - {"tx128"}, - {"tx256"}, - {"tx512"}, - {"tx1024"}, + {"Receive errors"}, + {"TCP cksum errors"}, + {"IP cksum errors"}, + {"Frame cksum errors"}, + {"num SQ stopped"}, + {"SQ stopped"}, + {"PR0 free_swqes"}, + {"PR1 free_swqes"}, + {"PR2 free_swqes"}, + {"PR3 free_swqes"}, + {"PR4 free_swqes"}, + {"PR5 free_swqes"}, + {"PR6 free_swqes"}, + {"PR7 free_swqes"}, }; static void ehea_get_strings(struct net_device *dev, u32 stringset, u8 *data) @@ -211,63 +201,44 @@ static int ehea_get_stats_count(struct net_device *dev) static void ehea_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { - u64 hret; - int i; + int i, k, tmp; struct ehea_port *port = netdev_priv(dev); - struct ehea_adapter *adapter = port->adapter; - struct ehea_port_res *pr = &port->port_res[0]; - struct port_state *p_state = &pr->p_state; - struct hcp_ehea_port_cb6 *cb6; for (i = 0; i < ehea_get_stats_count(dev); i++) data[i] = 0; - i = 0; - data[i++] = p_state->poll_max_processed; - data[i++] = p_state->queue_stopped; - data[i++] = p_state->min_swqe_avail; - data[i++] = p_state->poll_receive_errors; - data[i++] = p_state->pkt_send; - data[i++] = p_state->pkt_xmit; - data[i++] = p_state->send_tasklet; - data[i++] = p_state->ehea_poll; - data[i++] = p_state->nwqe; - data[i++] = atomic_read(&port->port_res[0].swqe_avail); data[i++] = port->sig_comp_iv; data[i++] = port->port_res[0].swqe_refill_th; data[i++] = port->resets; - cb6 = kzalloc(PAGE_SIZE, GFP_KERNEL); - if (!cb6) { - ehea_error("no mem for cb6"); - return; - } + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.poll_receive_errors; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_tcp_cksum; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_ip_cksum; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.err_frame_crc; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp += port->port_res[k].p_stats.queue_stopped; + data[i++] = tmp; + + for (k = 0, tmp = 0; k < EHEA_MAX_PORT_RES; k++) + tmp |= port->port_res[k].queue_stopped; + data[i++] = tmp; + + for (k = 0; k < 8; k++) + data[i++] = atomic_read(&port->port_res[k].swqe_avail); - hret = ehea_h_query_ehea_port(adapter->handle, port->logical_port_id, - H_PORT_CB6, H_PORT_CB6_ALL, cb6); - if (netif_msg_hw(port)) - ehea_dump(cb6, sizeof(*cb6), "ehea_get_ethtool_stats"); - - if (hret == H_SUCCESS) { - data[i++] = cb6->rxo; - data[i++] = cb6->rx64; - data[i++] = cb6->rx65; - data[i++] = cb6->rx128; - data[i++] = cb6->rx256; - data[i++] = cb6->rx512; - data[i++] = cb6->rx1024; - data[i++] = cb6->txo; - data[i++] = cb6->tx64; - data[i++] = cb6->tx65; - data[i++] = cb6->tx128; - data[i++] = cb6->tx256; - data[i++] = cb6->tx512; - data[i++] = cb6->tx1024; - } else - ehea_error("query_ehea_port failed"); - - kfree(cb6); } const struct ethtool_ops ehea_ethtool_ops = { diff --git a/drivers/net/ehea/ehea_main.c b/drivers/net/ehea/ehea_main.c index 38b2fa4..c7a5614 100644 --- a/drivers/net/ehea/ehea_main.c +++ b/drivers/net/ehea/ehea_main.c @@ -51,13 +51,18 @@ static int rq1_entries = EHEA_DEF_ENTRIES_RQ1; static int rq2_entries = EHEA_DEF_ENTRIES_RQ2; static int rq3_entries = EHEA_DEF_ENTRIES_RQ3; static int sq_entries = EHEA_DEF_ENTRIES_SQ; +static int use_mcs = 0; +static int num_tx_qps = EHEA_NUM_TX_QP; module_param(msg_level, int, 0); module_param(rq1_entries, int, 0); module_param(rq2_entries, int, 0); module_param(rq3_entries, int, 0); module_param(sq_entries, int, 0); +module_param(use_mcs, int, 0); +module_param(num_tx_qps, int, 0); +MODULE_PARM_DESC(num_tx_qps, "Number of TX-QPS"); MODULE_PARM_DESC(msg_level, "msg_level"); MODULE_PARM_DESC(rq3_entries, "Number of entries for Receive Queue 3 " "[2^x - 1], x = [6..14]. Default = " @@ -71,12 +76,35 @@ MODULE_PARM_DESC(rq1_entries, "Number of entries for Receive Queue 1 " MODULE_PARM_DESC(sq_entries, " Number of entries for the Send Queue " "[2^x - 1], x = [6..14]. Default = " __MODULE_STRING(EHEA_DEF_ENTRIES_SQ) ")"); +MODULE_PARM_DESC(use_mcs, " 0:NAPI, 1:Multiple receive queues, Default = 1 "); + +static int port_name_cnt = 0; + +static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev, + const struct of_device_id *id); + +static int __devexit ehea_remove(struct ibmebus_dev *dev); + +static struct of_device_id ehea_device_table[] = { + { + .name = "lhea", + .compatible = "IBM,lhea", + }, + {}, +}; + +static struct ibmebus_driver ehea_driver = { + .name = "ehea", + .id_table = ehea_device_table, + .probe = ehea_probe_adapter, + .remove = ehea_remove, +}; void ehea_dump(void *adr, int len, char *msg) { int x; unsigned char *deb = adr; for (x = 0; x < len; x += 16) { - printk(DRV_NAME "%s adr=%p ofs=%04x %016lx %016lx\n", msg, + printk(DRV_NAME " %s adr=%p ofs=%04x %016lx %016lx\n", msg, deb, x, *((u64*)&deb[0]), *((u64*)&deb[8])); deb += 16; } @@ -197,7 +225,7 @@ static int ehea_refill_rq_def(struct ehea_port_res *pr, struct sk_buff *skb = netdev_alloc_skb(dev, packet_size); if (!skb) { ehea_error("%s: no mem for skb/%d wqes filled", - dev->name, i); + pr->port->netdev->name, i); q_skba->os_skbs = fill_wqes - i; ret = -ENOMEM; break; @@ -321,6 +349,13 @@ static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq, { struct sk_buff *skb; + if (cqe->status & EHEA_CQE_STAT_ERR_TCP) + pr->p_stats.err_tcp_cksum++; + if (cqe->status & EHEA_CQE_STAT_ERR_IP) + pr->p_stats.err_ip_cksum++; + if (cqe->status & EHEA_CQE_STAT_ERR_CRC) + pr->p_stats.err_frame_crc++; + if (netif_msg_rx_err(pr->port)) { ehea_error("CQE Error for QP %d", pr->qp->init_attr.qp_nr); ehea_dump(cqe, sizeof(*cqe), "CQE"); @@ -345,10 +380,11 @@ static int ehea_treat_poll_error(struct ehea_port_res *pr, int rq, return 0; } -static int ehea_poll(struct net_device *dev, int *budget) +static struct ehea_cqe *ehea_proc_rwqes(struct net_device *dev, + struct ehea_port_res *pr, + int *budget) { - struct ehea_port *port = netdev_priv(dev); - struct ehea_port_res *pr = &port->port_res[0]; + struct ehea_port *port = pr->port; struct ehea_qp *qp = pr->qp; struct ehea_cqe *cqe; struct sk_buff *skb; @@ -359,14 +395,12 @@ static int ehea_poll(struct net_device *dev, int *budget) int skb_arr_rq2_len = pr->rq2_skba.len; int skb_arr_rq3_len = pr->rq3_skba.len; int processed, processed_rq1, processed_rq2, processed_rq3; - int wqe_index, last_wqe_index, rq, intreq, my_quota, port_reset; + int wqe_index, last_wqe_index, rq, my_quota, port_reset; processed = processed_rq1 = processed_rq2 = processed_rq3 = 0; last_wqe_index = 0; my_quota = min(*budget, dev->quota); - my_quota = min(my_quota, EHEA_POLL_MAX_RWQE); - /* rq0 is low latency RQ */ cqe = ehea_poll_rq1(qp, &wqe_index); while ((my_quota > 0) && cqe) { ehea_inc_rq1(qp); @@ -386,13 +420,14 @@ static int ehea_poll(struct net_device *dev, int *budget) if (unlikely(!skb)) { if (netif_msg_rx_err(port)) ehea_error("LL rq1: skb=NULL"); - skb = netdev_alloc_skb(dev, + + skb = netdev_alloc_skb(port->netdev, EHEA_L_PKT_SIZE); if (!skb) break; } - memcpy(skb->data, ((char*)cqe) + 64, - cqe->num_bytes_transfered - 4); + skb_copy_to_linear_data(skb, ((char*)cqe) + 64, + cqe->num_bytes_transfered - 4); ehea_fill_skb(dev, skb, cqe); } else if (rq == 2) { /* RQ2 */ skb = get_skb_by_index(skb_arr_rq2, @@ -402,7 +437,7 @@ static int ehea_poll(struct net_device *dev, int *budget) ehea_error("rq2: skb=NULL"); break; } - ehea_fill_skb(dev, skb, cqe); + ehea_fill_skb(port->netdev, skb, cqe); processed_rq2++; } else { /* RQ3 */ skb = get_skb_by_index(skb_arr_rq3, @@ -412,7 +447,7 @@ static int ehea_poll(struct net_device *dev, int *budget) ehea_error("rq3: skb=NULL"); break; } - ehea_fill_skb(dev, skb, cqe); + ehea_fill_skb(port->netdev, skb, cqe); processed_rq3++; } @@ -421,9 +456,8 @@ static int ehea_poll(struct net_device *dev, int *budget) cqe->vlan_tag); else netif_receive_skb(skb); - - } else { /* Error occured */ - pr->p_state.poll_receive_errors++; + } else { + pr->p_stats.poll_receive_errors++; port_reset = ehea_treat_poll_error(pr, rq, cqe, &processed_rq2, &processed_rq3); @@ -433,72 +467,32 @@ static int ehea_poll(struct net_device *dev, int *budget) cqe = ehea_poll_rq1(qp, &wqe_index); } - dev->quota -= processed; - *budget -= processed; - - pr->p_state.ehea_poll += 1; pr->rx_packets += processed; + *budget -= processed; ehea_refill_rq1(pr, last_wqe_index, processed_rq1); ehea_refill_rq2(pr, processed_rq2); ehea_refill_rq3(pr, processed_rq3); - intreq = ((pr->p_state.ehea_poll & 0xF) == 0xF); - - if (!cqe || intreq) { - netif_rx_complete(dev); - ehea_reset_cq_ep(pr->recv_cq); - ehea_reset_cq_n1(pr->recv_cq); - cqe = hw_qeit_get_valid(&qp->hw_rqueue1); - if (!cqe || intreq) - return 0; - if (!netif_rx_reschedule(dev, my_quota)) - return 0; - } - return 1; + cqe = ehea_poll_rq1(qp, &wqe_index); + return cqe; } -void free_sent_skbs(struct ehea_cqe *cqe, struct ehea_port_res *pr) +static struct ehea_cqe *ehea_proc_cqes(struct ehea_port_res *pr, int my_quota) { struct sk_buff *skb; - int index, max_index_mask, i; - - index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id); - max_index_mask = pr->sq_skba.len - 1; - for (i = 0; i < EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); i++) { - skb = pr->sq_skba.arr[index]; - if (likely(skb)) { - dev_kfree_skb(skb); - pr->sq_skba.arr[index] = NULL; - } else { - ehea_error("skb=NULL, wr_id=%lX, loop=%d, index=%d", - cqe->wr_id, i, index); - } - index--; - index &= max_index_mask; - } -} - -#define MAX_SENDCOMP_QUOTA 400 -void ehea_send_irq_tasklet(unsigned long data) -{ - struct ehea_port_res *pr = (struct ehea_port_res*)data; struct ehea_cq *send_cq = pr->send_cq; struct ehea_cqe *cqe; - int quota = MAX_SENDCOMP_QUOTA; + int quota = my_quota; int cqe_counter = 0; int swqe_av = 0; + int index; unsigned long flags; - do { - cqe = ehea_poll_cq(send_cq); - if (!cqe) { - ehea_reset_cq_ep(send_cq); - ehea_reset_cq_n1(send_cq); - cqe = ehea_poll_cq(send_cq); - if (!cqe) - break; - } + cqe = ehea_poll_cq(send_cq); + while(cqe && (quota > 0)) { + ehea_inc_cq(send_cq); + cqe_counter++; rmb(); if (cqe->status & EHEA_CQE_STAT_ERR_MASK) { @@ -514,17 +508,25 @@ void ehea_send_irq_tasklet(unsigned long data) ehea_dump(cqe, sizeof(*cqe), "CQE"); if (likely(EHEA_BMASK_GET(EHEA_WR_ID_TYPE, cqe->wr_id) - == EHEA_SWQE2_TYPE)) - free_sent_skbs(cqe, pr); + == EHEA_SWQE2_TYPE)) { + + index = EHEA_BMASK_GET(EHEA_WR_ID_INDEX, cqe->wr_id); + skb = pr->sq_skba.arr[index]; + dev_kfree_skb(skb); + pr->sq_skba.arr[index] = NULL; + } swqe_av += EHEA_BMASK_GET(EHEA_WR_ID_REFILL, cqe->wr_id); quota--; - } while (quota > 0); + + cqe = ehea_poll_cq(send_cq); + }; ehea_update_feca(send_cq, cqe_counter); atomic_add(swqe_av, &pr->swqe_avail); spin_lock_irqsave(&pr->netif_queue, flags); + if (pr->queue_stopped && (atomic_read(&pr->swqe_avail) >= pr->swqe_refill_th)) { netif_wake_queue(pr->port->netdev); @@ -532,22 +534,55 @@ void ehea_send_irq_tasklet(unsigned long data) } spin_unlock_irqrestore(&pr->netif_queue, flags); - if (unlikely(cqe)) - tasklet_hi_schedule(&pr->send_comp_task); + return cqe; } -static irqreturn_t ehea_send_irq_handler(int irq, void *param) +#define EHEA_NAPI_POLL_NUM_BEFORE_IRQ 16 + +static int ehea_poll(struct net_device *dev, int *budget) { - struct ehea_port_res *pr = param; - tasklet_hi_schedule(&pr->send_comp_task); - return IRQ_HANDLED; + struct ehea_port_res *pr = dev->priv; + struct ehea_cqe *cqe; + struct ehea_cqe *cqe_skb = NULL; + int force_irq, wqe_index; + + cqe = ehea_poll_rq1(pr->qp, &wqe_index); + cqe_skb = ehea_poll_cq(pr->send_cq); + + force_irq = (pr->poll_counter > EHEA_NAPI_POLL_NUM_BEFORE_IRQ); + + if ((!cqe && !cqe_skb) || force_irq) { + pr->poll_counter = 0; + netif_rx_complete(dev); + ehea_reset_cq_ep(pr->recv_cq); + ehea_reset_cq_ep(pr->send_cq); + ehea_reset_cq_n1(pr->recv_cq); + ehea_reset_cq_n1(pr->send_cq); + cqe = ehea_poll_rq1(pr->qp, &wqe_index); + cqe_skb = ehea_poll_cq(pr->send_cq); + + if (!cqe && !cqe_skb) + return 0; + + if (!netif_rx_reschedule(dev, dev->quota)) + return 0; + } + + cqe = ehea_proc_rwqes(dev, pr, budget); + cqe_skb = ehea_proc_cqes(pr, 300); + + if (cqe || cqe_skb) + pr->poll_counter++; + + return 1; } static irqreturn_t ehea_recv_irq_handler(int irq, void *param) { struct ehea_port_res *pr = param; - struct ehea_port *port = pr->port; - netif_rx_schedule(port->netdev); + + netif_rx_schedule(pr->d_netdev); + return IRQ_HANDLED; } @@ -555,6 +590,7 @@ static irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param) { struct ehea_port *port = param; struct ehea_eqe *eqe; + struct ehea_qp *qp; u32 qp_token; eqe = ehea_poll_eq(port->qp_eq); @@ -563,9 +599,14 @@ static irqreturn_t ehea_qp_aff_irq_handler(int irq, void *param) qp_token = EHEA_BMASK_GET(EHEA_EQE_QP_TOKEN, eqe->entry); ehea_error("QP aff_err: entry=0x%lx, token=0x%x", eqe->entry, qp_token); + + qp = port->port_res[qp_token].qp; + ehea_error_data(port->adapter, qp->fw_handle); eqe = ehea_poll_eq(port->qp_eq); } + queue_work(port->adapter->ehea_wq, &port->reset_task); + return IRQ_HANDLED; } @@ -574,7 +615,7 @@ static struct ehea_port *ehea_get_port(struct ehea_adapter *adapter, { int i; - for (i = 0; i < adapter->num_ports; i++) + for (i = 0; i < EHEA_MAX_PORTS; i++) if (adapter->port[i]) if (adapter->port[i]->logical_port_id == logical_port) return adapter->port[i]; @@ -644,19 +685,25 @@ int ehea_sense_port_attr(struct ehea_port *port) } port->autoneg = 1; + port->num_mcs = cb0->num_default_qps; /* Number of default QPs */ - port->num_def_qps = cb0->num_default_qps; + if (use_mcs) + port->num_def_qps = cb0->num_default_qps; + else + port->num_def_qps = 1; if (!port->num_def_qps) { ret = -EINVAL; goto out_free; } - if (port->num_def_qps >= EHEA_NUM_TX_QP) + port->num_tx_qps = num_tx_qps; + + if (port->num_def_qps >= port->num_tx_qps) port->num_add_tx_qps = 0; else - port->num_add_tx_qps = EHEA_NUM_TX_QP - port->num_def_qps; + port->num_add_tx_qps = port->num_tx_qps - port->num_def_qps; ret = 0; out_free: @@ -876,23 +923,6 @@ static int ehea_reg_interrupts(struct net_device *dev) struct ehea_port_res *pr; int i, ret; - for (i = 0; i < port->num_def_qps; i++) { - pr = &port->port_res[i]; - snprintf(pr->int_recv_name, EHEA_IRQ_NAME_SIZE - 1 - , "%s-recv%d", dev->name, i); - ret = ibmebus_request_irq(NULL, pr->recv_eq->attr.ist1, - ehea_recv_irq_handler, - IRQF_DISABLED, pr->int_recv_name, pr); - if (ret) { - ehea_error("failed registering irq for ehea_recv_int:" - "port_res_nr:%d, ist=%X", i, - pr->recv_eq->attr.ist1); - goto out_free_seq; - } - if (netif_msg_ifup(port)) - ehea_info("irq_handle 0x%X for funct ehea_recv_int %d " - "registered", pr->recv_eq->attr.ist1, i); - } snprintf(port->int_aff_name, EHEA_IRQ_NAME_SIZE - 1, "%s-aff", dev->name); @@ -910,41 +940,41 @@ static int ehea_reg_interrupts(struct net_device *dev) ehea_info("irq_handle 0x%X for function qp_aff_irq_handler " "registered", port->qp_eq->attr.ist1); + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) { pr = &port->port_res[i]; snprintf(pr->int_send_name, EHEA_IRQ_NAME_SIZE - 1, - "%s-send%d", dev->name, i); - ret = ibmebus_request_irq(NULL, pr->send_eq->attr.ist1, - ehea_send_irq_handler, + "%s-queue%d", dev->name, i); + ret = ibmebus_request_irq(NULL, pr->eq->attr.ist1, + ehea_recv_irq_handler, IRQF_DISABLED, pr->int_send_name, pr); if (ret) { - ehea_error("failed registering irq for ehea_send " + ehea_error("failed registering irq for ehea_queue " "port_res_nr:%d, ist=%X", i, - pr->send_eq->attr.ist1); + pr->eq->attr.ist1); goto out_free_req; } if (netif_msg_ifup(port)) - ehea_info("irq_handle 0x%X for function ehea_send_int " - "%d registered", pr->send_eq->attr.ist1, i); + ehea_info("irq_handle 0x%X for function ehea_queue_int " + "%d registered", pr->eq->attr.ist1, i); } out: return ret; + out_free_req: while (--i >= 0) { - u32 ist = port->port_res[i].send_eq->attr.ist1; + u32 ist = port->port_res[i].eq->attr.ist1; ibmebus_free_irq(NULL, ist, &port->port_res[i]); } + out_free_qpeq: ibmebus_free_irq(NULL, port->qp_eq->attr.ist1, port); i = port->num_def_qps; -out_free_seq: - while (--i >= 0) { - u32 ist = port->port_res[i].recv_eq->attr.ist1; - ibmebus_free_irq(NULL, ist, &port->port_res[i]); - } + goto out; + } static void ehea_free_interrupts(struct net_device *dev) @@ -954,21 +984,13 @@ static void ehea_free_interrupts(struct net_device *dev) int i; /* send */ + for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) { pr = &port->port_res[i]; - ibmebus_free_irq(NULL, pr->send_eq->attr.ist1, pr); + ibmebus_free_irq(NULL, pr->eq->attr.ist1, pr); if (netif_msg_intr(port)) ehea_info("free send irq for res %d with handle 0x%X", - i, pr->send_eq->attr.ist1); - } - - /* receive */ - for (i = 0; i < port->num_def_qps; i++) { - pr = &port->port_res[i]; - ibmebus_free_irq(NULL, pr->recv_eq->attr.ist1, pr); - if (netif_msg_intr(port)) - ehea_info("free recv irq for res %d with handle 0x%X", - i, pr->recv_eq->attr.ist1); + i, pr->eq->attr.ist1); } /* associated events */ @@ -997,8 +1019,13 @@ static int ehea_configure_port(struct ehea_port *port) PXLY_RC_VLAN_FILTER) | EHEA_BMASK_SET(PXLY_RC_JUMBO_FRAME, 1); - for (i = 0; i < port->num_def_qps; i++) - cb0->default_qpn_arr[i] = port->port_res[0].qp->init_attr.qp_nr; + for (i = 0; i < port->num_mcs; i++) + if (use_mcs) + cb0->default_qpn_arr[i] = + port->port_res[i].qp->init_attr.qp_nr; + else + cb0->default_qpn_arr[i] = + port->port_res[0].qp->init_attr.qp_nr; if (netif_msg_ifup(port)) ehea_dump(cb0, sizeof(*cb0), "ehea_configure_port"); @@ -1021,52 +1048,35 @@ out: return ret; } -static int ehea_gen_smrs(struct ehea_port_res *pr) +int ehea_gen_smrs(struct ehea_port_res *pr) { - u64 hret; + int ret; struct ehea_adapter *adapter = pr->port->adapter; - hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle, - adapter->mr.vaddr, EHEA_MR_ACC_CTRL, - adapter->pd, &pr->send_mr); - if (hret != H_SUCCESS) + ret = ehea_gen_smr(adapter, &adapter->mr, &pr->send_mr); + if (ret) goto out; - hret = ehea_h_register_smr(adapter->handle, adapter->mr.handle, - adapter->mr.vaddr, EHEA_MR_ACC_CTRL, - adapter->pd, &pr->recv_mr); - if (hret != H_SUCCESS) - goto out_freeres; + ret = ehea_gen_smr(adapter, &adapter->mr, &pr->recv_mr); + if (ret) + goto out_free; return 0; -out_freeres: - hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle); - if (hret != H_SUCCESS) - ehea_error("failed freeing SMR"); +out_free: + ehea_rem_mr(&pr->send_mr); out: + ehea_error("Generating SMRS failed\n"); return -EIO; } -static int ehea_rem_smrs(struct ehea_port_res *pr) +int ehea_rem_smrs(struct ehea_port_res *pr) { - struct ehea_adapter *adapter = pr->port->adapter; - int ret = 0; - u64 hret; - - hret = ehea_h_free_resource(adapter->handle, pr->send_mr.handle); - if (hret != H_SUCCESS) { - ret = -EIO; - ehea_error("failed freeing send SMR for pr=%p", pr); - } - - hret = ehea_h_free_resource(adapter->handle, pr->recv_mr.handle); - if (hret != H_SUCCESS) { - ret = -EIO; - ehea_error("failed freeing recv SMR for pr=%p", pr); - } - - return ret; + if ((ehea_rem_mr(&pr->send_mr)) + || (ehea_rem_mr(&pr->recv_mr))) + return -EIO; + else + return 0; } static int ehea_init_q_skba(struct ehea_q_skb_arr *q_skba, int max_q_entries) @@ -1097,25 +1107,17 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr, memset(pr, 0, sizeof(struct ehea_port_res)); pr->port = port; - spin_lock_init(&pr->send_lock); - spin_lock_init(&pr->recv_lock); spin_lock_init(&pr->xmit_lock); spin_lock_init(&pr->netif_queue); - pr->recv_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); - if (!pr->recv_eq) { - ehea_error("create_eq failed (recv_eq)"); - goto out_free; - } - - pr->send_eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); - if (!pr->send_eq) { - ehea_error("create_eq failed (send_eq)"); + pr->eq = ehea_create_eq(adapter, eq_type, EHEA_MAX_ENTRIES_EQ, 0); + if (!pr->eq) { + ehea_error("create_eq failed (eq)"); goto out_free; } pr->recv_cq = ehea_create_cq(adapter, pr_cfg->max_entries_rcq, - pr->recv_eq->fw_handle, + pr->eq->fw_handle, port->logical_port_id); if (!pr->recv_cq) { ehea_error("create_cq failed (cq_recv)"); @@ -1123,7 +1125,7 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr, } pr->send_cq = ehea_create_cq(adapter, pr_cfg->max_entries_scq, - pr->send_eq->fw_handle, + pr->eq->fw_handle, port->logical_port_id); if (!pr->send_cq) { ehea_error("create_cq failed (cq_send)"); @@ -1188,11 +1190,20 @@ static int ehea_init_port_res(struct ehea_port *port, struct ehea_port_res *pr, ret = -EIO; goto out_free; } - tasklet_init(&pr->send_comp_task, ehea_send_irq_tasklet, - (unsigned long)pr); + atomic_set(&pr->swqe_avail, init_attr->act_nr_send_wqes - 1); kfree(init_attr); + + pr->d_netdev = alloc_netdev(0, "", ether_setup); + if (!pr->d_netdev) + goto out_free; + pr->d_netdev->priv = pr; + pr->d_netdev->weight = 64; + pr->d_netdev->poll = ehea_poll; + set_bit(__LINK_STATE_START, &pr->d_netdev->state); + strcpy(pr->d_netdev->name, port->netdev->name); + ret = 0; goto out; @@ -1205,8 +1216,7 @@ out_free: ehea_destroy_qp(pr->qp); ehea_destroy_cq(pr->send_cq); ehea_destroy_cq(pr->recv_cq); - ehea_destroy_eq(pr->send_eq); - ehea_destroy_eq(pr->recv_eq); + ehea_destroy_eq(pr->eq); out: return ret; } @@ -1215,13 +1225,14 @@ static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr) { int ret, i; + free_netdev(pr->d_netdev); + ret = ehea_destroy_qp(pr->qp); if (!ret) { ehea_destroy_cq(pr->send_cq); ehea_destroy_cq(pr->recv_cq); - ehea_destroy_eq(pr->send_eq); - ehea_destroy_eq(pr->recv_eq); + ehea_destroy_eq(pr->eq); for (i = 0; i < pr->rq1_skba.len; i++) if (pr->rq1_skba.arr[i]) @@ -1256,8 +1267,8 @@ static int ehea_clean_portres(struct ehea_port *port, struct ehea_port_res *pr) static inline void write_ip_start_end(struct ehea_swqe *swqe, const struct sk_buff *skb) { - swqe->ip_start = (u8)(((u64)skb->nh.iph) - ((u64)skb->data)); - swqe->ip_end = (u8)(swqe->ip_start + skb->nh.iph->ihl * 4 - 1); + swqe->ip_start = skb_network_offset(skb); + swqe->ip_end = (u8)(swqe->ip_start + ip_hdrlen(skb) - 1); } static inline void write_tcp_offset_end(struct ehea_swqe *swqe, @@ -1294,13 +1305,13 @@ static void write_swqe2_TSO(struct sk_buff *skb, /* copy only eth/ip/tcp headers to immediate data and * the rest of skb->data to sg1entry */ - headersize = ETH_HLEN + (skb->nh.iph->ihl * 4) + (skb->h.th->doff * 4); + headersize = ETH_HLEN + ip_hdrlen(skb) + tcp_hdrlen(skb); skb_data_size = skb->len - skb->data_len; if (skb_data_size >= headersize) { /* copy immediate data */ - memcpy(imm_data, skb->data, headersize); + skb_copy_from_linear_data(skb, imm_data, headersize); swqe->immediate_data_length = headersize; if (skb_data_size > headersize) { @@ -1331,7 +1342,7 @@ static void write_swqe2_nonTSO(struct sk_buff *skb, */ if (skb_data_size >= SWQE2_MAX_IMM) { /* copy immediate data */ - memcpy(imm_data, skb->data, SWQE2_MAX_IMM); + skb_copy_from_linear_data(skb, imm_data, SWQE2_MAX_IMM); swqe->immediate_data_length = SWQE2_MAX_IMM; @@ -1344,7 +1355,7 @@ static void write_swqe2_nonTSO(struct sk_buff *skb, swqe->descriptors++; } } else { - memcpy(imm_data, skb->data, skb_data_size); + skb_copy_from_linear_data(skb, imm_data, skb_data_size); swqe->immediate_data_length = skb_data_size; } } @@ -1682,6 +1693,7 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev, struct ehea_swqe *swqe, u32 lkey) { if (skb->protocol == htons(ETH_P_IP)) { + const struct iphdr *iph = ip_hdr(skb); /* IPv4 */ swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IP_CHECKSUM @@ -1691,15 +1703,15 @@ static void ehea_xmit2(struct sk_buff *skb, struct net_device *dev, write_ip_start_end(swqe, skb); - if (skb->nh.iph->protocol == IPPROTO_UDP) { - if ((skb->nh.iph->frag_off & IP_MF) || - (skb->nh.iph->frag_off & IP_OFFSET)) + if (iph->protocol == IPPROTO_UDP) { + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control &= ~EHEA_SWQE_TCP_CHECKSUM; else write_udp_offset_end(swqe, skb); - } else if (skb->nh.iph->protocol == IPPROTO_TCP) { + } else if (iph->protocol == IPPROTO_TCP) { write_tcp_offset_end(swqe, skb); } @@ -1725,10 +1737,11 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev, int i; if (skb->protocol == htons(ETH_P_IP)) { + const struct iphdr *iph = ip_hdr(skb); /* IPv4 */ write_ip_start_end(swqe, skb); - if (skb->nh.iph->protocol == IPPROTO_TCP) { + if (iph->protocol == IPPROTO_TCP) { swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IP_CHECKSUM | EHEA_SWQE_TCP_CHECKSUM @@ -1736,9 +1749,9 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev, write_tcp_offset_end(swqe, skb); - } else if (skb->nh.iph->protocol == IPPROTO_UDP) { - if ((skb->nh.iph->frag_off & IP_MF) || - (skb->nh.iph->frag_off & IP_OFFSET)) + } else if (iph->protocol == IPPROTO_UDP) { + if ((iph->frag_off & IP_MF) || + (iph->frag_off & IP_OFFSET)) /* IP fragment, so don't change cs */ swqe->tx_control |= EHEA_SWQE_CRC | EHEA_SWQE_IMM_DATA_PRESENT; @@ -1764,10 +1777,11 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev, /* copy (immediate) data */ if (nfrags == 0) { /* data is in a single piece */ - memcpy(imm_data, skb->data, skb->len); + skb_copy_from_linear_data(skb, imm_data, skb->len); } else { /* first copy data from the skb->data buffer ... */ - memcpy(imm_data, skb->data, skb->len - skb->data_len); + skb_copy_from_linear_data(skb, imm_data, + skb->len - skb->data_len); imm_data += skb->len - skb->data_len; /* ... then copy data from the fragments */ @@ -1783,6 +1797,22 @@ static void ehea_xmit3(struct sk_buff *skb, struct net_device *dev, dev_kfree_skb(skb); } +static inline int ehea_hash_skb(struct sk_buff *skb, int num_qps) +{ + struct tcphdr *tcp; + u32 tmp; + + if ((skb->protocol == htons(ETH_P_IP)) && + (skb->nh.iph->protocol == IPPROTO_TCP)) { + tcp = (struct tcphdr*)(skb->nh.raw + (skb->nh.iph->ihl * 4)); + tmp = (tcp->source + (tcp->dest << 16)) % 31; + tmp += skb->nh.iph->daddr % 31; + return tmp % num_qps; + } + else + return 0; +} + static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ehea_port *port = netdev_priv(dev); @@ -1790,9 +1820,17 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) unsigned long flags; u32 lkey; int swqe_index; - struct ehea_port_res *pr = &port->port_res[0]; + struct ehea_port_res *pr; - spin_lock(&pr->xmit_lock); + pr = &port->port_res[ehea_hash_skb(skb, port->num_tx_qps)]; + + if (!spin_trylock(&pr->xmit_lock)) + return NETDEV_TX_BUSY; + + if (pr->queue_stopped) { + spin_unlock(&pr->xmit_lock); + return NETDEV_TX_BUSY; + } swqe = ehea_get_swqe(pr->qp, &swqe_index); memset(swqe, 0, SWQE_HEADER_SIZE); @@ -1815,6 +1853,7 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) swqe->wr_id = EHEA_BMASK_SET(EHEA_WR_ID_TYPE, EHEA_SWQE2_TYPE) | EHEA_BMASK_SET(EHEA_WR_ID_COUNT, pr->swqe_id_counter) + | EHEA_BMASK_SET(EHEA_WR_ID_REFILL, 1) | EHEA_BMASK_SET(EHEA_WR_ID_INDEX, pr->sq_skba.index); pr->sq_skba.arr[pr->sq_skba.index] = skb; @@ -1823,14 +1862,7 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) lkey = pr->send_mr.lkey; ehea_xmit2(skb, dev, swqe, lkey); - - if (pr->swqe_count >= (EHEA_SIG_IV_LONG - 1)) { - swqe->wr_id |= EHEA_BMASK_SET(EHEA_WR_ID_REFILL, - EHEA_SIG_IV_LONG); - swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION; - pr->swqe_count = 0; - } else - pr->swqe_count += 1; + swqe->tx_control |= EHEA_SWQE_SIGNALLED_COMPLETION; } pr->swqe_id_counter += 1; @@ -1850,6 +1882,7 @@ static int ehea_start_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) { spin_lock_irqsave(&pr->netif_queue, flags); if (unlikely(atomic_read(&pr->swqe_avail) <= 1)) { + pr->p_stats.queue_stopped++; netif_stop_queue(dev); pr->queue_stopped = 1; } @@ -1933,8 +1966,7 @@ static void ehea_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) int index; u64 hret; - if (port->vgrp) - port->vgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(port->vgrp, vid, NULL); cb1 = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!cb1) { @@ -2052,7 +2084,7 @@ static int ehea_port_res_setup(struct ehea_port *port, int def_qps, } pr_cfg.max_entries_rcq = rq1_entries + rq2_entries + rq3_entries; - pr_cfg.max_entries_scq = sq_entries; + pr_cfg.max_entries_scq = sq_entries * 2; pr_cfg.max_entries_sq = sq_entries; pr_cfg.max_entries_rq1 = rq1_entries; pr_cfg.max_entries_rq2 = rq2_entries; @@ -2101,6 +2133,28 @@ static int ehea_clean_all_portres(struct ehea_port *port) return ret; } +static void ehea_remove_adapter_mr (struct ehea_adapter *adapter) +{ + int i; + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i]) + return; + + ehea_rem_mr(&adapter->mr); +} + +static int ehea_add_adapter_mr (struct ehea_adapter *adapter) +{ + int i; + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i]) + return 0; + + return ehea_reg_kernel_mr(adapter, &adapter->mr); +} + static int ehea_up(struct net_device *dev) { int ret, i; @@ -2200,8 +2254,10 @@ static int ehea_down(struct net_device *dev) ehea_drop_multicast_list(dev); ehea_free_interrupts(dev); - for (i = 0; i < port->num_def_qps + port->num_add_tx_qps; i++) - tasklet_kill(&port->port_res[i].send_comp_task); + for (i = 0; i < port->num_def_qps; i++) + while (test_bit(__LINK_STATE_RX_SCHED, + &port->port_res[i].d_netdev->state)) + msleep(1); ehea_broadcast_reg_helper(port, H_DEREG_BCMC); ret = ehea_clean_all_portres(port); @@ -2268,8 +2324,6 @@ static void ehea_tx_watchdog(struct net_device *dev) int ehea_sense_adapter_attr(struct ehea_adapter *adapter) { struct hcp_query_ehea *cb; - struct device_node *lhea_dn = NULL; - struct device_node *eth_dn = NULL; u64 hret; int ret; @@ -2286,18 +2340,6 @@ int ehea_sense_adapter_attr(struct ehea_adapter *adapter) goto out_herr; } - /* Determine the number of available logical ports - * by counting the child nodes of the lhea OFDT entry - */ - adapter->num_ports = 0; - lhea_dn = of_find_node_by_name(lhea_dn, "lhea"); - do { - eth_dn = of_get_next_child(lhea_dn, eth_dn); - if (eth_dn) - adapter->num_ports++; - } while ( eth_dn ); - of_node_put(lhea_dn); - adapter->max_mc_mac = cb->max_mc_mac - 1; ret = 0; @@ -2307,79 +2349,188 @@ out: return ret; } -static int ehea_setup_single_port(struct ehea_port *port, - struct device_node *dn) +int ehea_get_jumboframe_status(struct ehea_port *port, int *jumbo) { - int ret; - u64 hret; - struct net_device *dev = port->netdev; - struct ehea_adapter *adapter = port->adapter; struct hcp_ehea_port_cb4 *cb4; - u32 *dn_log_port_id; - int jumbo = 0; - - sema_init(&port->port_lock, 1); - port->state = EHEA_PORT_DOWN; - port->sig_comp_iv = sq_entries / 10; - - if (!dn) { - ehea_error("bad device node: dn=%p", dn); - ret = -EINVAL; - goto out; - } - - port->of_dev_node = dn; - - /* Determine logical port id */ - dn_log_port_id = (u32*)get_property(dn, "ibm,hea-port-no", NULL); - - if (!dn_log_port_id) { - ehea_error("bad device node: dn_log_port_id=%p", - dn_log_port_id); - ret = -EINVAL; - goto out; - } - port->logical_port_id = *dn_log_port_id; - - port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL); - if (!port->mc_list) { - ret = -ENOMEM; - goto out; - } - - INIT_LIST_HEAD(&port->mc_list->list); + u64 hret; + int ret = 0; - ret = ehea_sense_port_attr(port); - if (ret) - goto out; + *jumbo = 0; - /* Enable Jumbo frames */ + /* (Try to) enable *jumbo frames */ cb4 = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!cb4) { ehea_error("no mem for cb4"); + ret = -ENOMEM; + goto out; } else { - hret = ehea_h_query_ehea_port(adapter->handle, + hret = ehea_h_query_ehea_port(port->adapter->handle, port->logical_port_id, H_PORT_CB4, H_PORT_CB4_JUMBO, cb4); - if (hret == H_SUCCESS) { if (cb4->jumbo_frame) - jumbo = 1; + *jumbo = 1; else { cb4->jumbo_frame = 1; - hret = ehea_h_modify_ehea_port(adapter->handle, + hret = ehea_h_modify_ehea_port(port->adapter-> + handle, port-> - logical_port_id, + logical_port_id, H_PORT_CB4, H_PORT_CB4_JUMBO, cb4); if (hret == H_SUCCESS) - jumbo = 1; + *jumbo = 1; } - } + } else + ret = -EINVAL; + kfree(cb4); } +out: + return ret; +} + +static ssize_t ehea_show_port_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev); + return sprintf(buf, "0x%X", port->logical_port_id); +} + +static DEVICE_ATTR(log_port_id, S_IRUSR | S_IRGRP | S_IROTH, ehea_show_port_id, + NULL); + +static void __devinit logical_port_release(struct device *dev) +{ + struct ehea_port *port = container_of(dev, struct ehea_port, ofdev.dev); + of_node_put(port->ofdev.node); +} + +static int ehea_driver_sysfs_add(struct device *dev, + struct device_driver *driver) +{ + int ret; + + ret = sysfs_create_link(&driver->kobj, &dev->kobj, + kobject_name(&dev->kobj)); + if (ret == 0) { + ret = sysfs_create_link(&dev->kobj, &driver->kobj, + "driver"); + if (ret) + sysfs_remove_link(&driver->kobj, + kobject_name(&dev->kobj)); + } + return ret; +} + +static void ehea_driver_sysfs_remove(struct device *dev, + struct device_driver *driver) +{ + struct device_driver *drv = driver; + + if (drv) { + sysfs_remove_link(&drv->kobj, kobject_name(&dev->kobj)); + sysfs_remove_link(&dev->kobj, "driver"); + } +} + +static struct device *ehea_register_port(struct ehea_port *port, + struct device_node *dn) +{ + int ret; + + port->ofdev.node = of_node_get(dn); + port->ofdev.dev.parent = &port->adapter->ebus_dev->ofdev.dev; + port->ofdev.dev.bus = &ibmebus_bus_type; + + sprintf(port->ofdev.dev.bus_id, "port%d", port_name_cnt++); + port->ofdev.dev.release = logical_port_release; + + ret = of_device_register(&port->ofdev); + if (ret) { + ehea_error("failed to register device. ret=%d", ret); + goto out; + } + + ret = device_create_file(&port->ofdev.dev, &dev_attr_log_port_id); + if (ret) { + ehea_error("failed to register attributes, ret=%d", ret); + goto out_unreg_of_dev; + } + + ret = ehea_driver_sysfs_add(&port->ofdev.dev, &ehea_driver.driver); + if (ret) { + ehea_error("failed to register sysfs driver link"); + goto out_rem_dev_file; + } + + return &port->ofdev.dev; + +out_rem_dev_file: + device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id); +out_unreg_of_dev: + of_device_unregister(&port->ofdev); +out: + return NULL; +} + +static void ehea_unregister_port(struct ehea_port *port) +{ + ehea_driver_sysfs_remove(&port->ofdev.dev, &ehea_driver.driver); + device_remove_file(&port->ofdev.dev, &dev_attr_log_port_id); + of_device_unregister(&port->ofdev); +} + +struct ehea_port *ehea_setup_single_port(struct ehea_adapter *adapter, + u32 logical_port_id, + struct device_node *dn) +{ + int ret; + struct net_device *dev; + struct ehea_port *port; + struct device *port_dev; + int jumbo; + + /* allocate memory for the port structures */ + dev = alloc_etherdev(sizeof(struct ehea_port)); + + if (!dev) { + ehea_error("no mem for net_device"); + ret = -ENOMEM; + goto out_err; + } + + port = netdev_priv(dev); + + sema_init(&port->port_lock, 1); + port->state = EHEA_PORT_DOWN; + port->sig_comp_iv = sq_entries / 10; + + port->adapter = adapter; + port->netdev = dev; + port->logical_port_id = logical_port_id; + + port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT); + + port->mc_list = kzalloc(sizeof(struct ehea_mc_list), GFP_KERNEL); + if (!port->mc_list) { + ret = -ENOMEM; + goto out_free_ethdev; + } + + INIT_LIST_HEAD(&port->mc_list->list); + + ret = ehea_sense_port_attr(port); + if (ret) + goto out_free_mc_list; + + port_dev = ehea_register_port(port, dn); + if (!port_dev) + goto out_free_mc_list; + + SET_NETDEV_DEV(dev, port_dev); /* initialize net_device structure */ SET_MODULE_OWNER(dev); @@ -2412,84 +2563,225 @@ static int ehea_setup_single_port(struct ehea_port *port, ret = register_netdev(dev); if (ret) { ehea_error("register_netdev failed. ret=%d", ret); - goto out_free; + goto out_unreg_port; } + ret = ehea_get_jumboframe_status(port, &jumbo); + if (ret) + ehea_error("failed determining jumbo frame status for %s", + port->netdev->name); + ehea_info("%s: Jumbo frames are %sabled", dev->name, jumbo == 1 ? "en" : "dis"); - port->netdev = dev; - ret = 0; - goto out; + return port; -out_free: +out_unreg_port: + ehea_unregister_port(port); + +out_free_mc_list: kfree(port->mc_list); -out: - return ret; + +out_free_ethdev: + free_netdev(dev); + +out_err: + ehea_error("setting up logical port with id=%d failed, ret=%d", + logical_port_id, ret); + return NULL; +} + +static void ehea_shutdown_single_port(struct ehea_port *port) +{ + unregister_netdev(port->netdev); + ehea_unregister_port(port); + kfree(port->mc_list); + free_netdev(port->netdev); } static int ehea_setup_ports(struct ehea_adapter *adapter) { - int ret; - int port_setup_ok = 0; + struct device_node *lhea_dn; + struct device_node *eth_dn = NULL; + + u32 *dn_log_port_id; + int i = 0; + + lhea_dn = adapter->ebus_dev->ofdev.node; + while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) { + + dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no", + NULL); + if (!dn_log_port_id) { + ehea_error("bad device node: eth_dn name=%s", + eth_dn->full_name); + continue; + } + + if (ehea_add_adapter_mr(adapter)) { + ehea_error("creating MR failed"); + of_node_put(eth_dn); + return -EIO; + } + + adapter->port[i] = ehea_setup_single_port(adapter, + *dn_log_port_id, + eth_dn); + if (adapter->port[i]) + ehea_info("%s -> logical port id #%d", + adapter->port[i]->netdev->name, + *dn_log_port_id); + else + ehea_remove_adapter_mr(adapter); + + i++; + }; + + return 0; +} + +static struct device_node *ehea_get_eth_dn(struct ehea_adapter *adapter, + u32 logical_port_id) +{ + struct device_node *lhea_dn; + struct device_node *eth_dn = NULL; + u32 *dn_log_port_id; + + lhea_dn = adapter->ebus_dev->ofdev.node; + while ((eth_dn = of_get_next_child(lhea_dn, eth_dn))) { + + dn_log_port_id = (u32*)get_property(eth_dn, "ibm,hea-port-no", + NULL); + if (dn_log_port_id) + if (*dn_log_port_id == logical_port_id) + return eth_dn; + }; + + return NULL; +} + +static ssize_t ehea_probe_port(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehea_adapter *adapter = dev->driver_data; struct ehea_port *port; - struct device_node *dn = NULL; - struct net_device *dev; + struct device_node *eth_dn = NULL; int i; - /* get port properties for all ports */ - for (i = 0; i < adapter->num_ports; i++) { + u32 logical_port_id; - if (adapter->port[i]) - continue; /* port already up and running */ + sscanf(buf, "%X", &logical_port_id); - /* allocate memory for the port structures */ - dev = alloc_etherdev(sizeof(struct ehea_port)); + port = ehea_get_port(adapter, logical_port_id); - if (!dev) { - ehea_error("no mem for net_device"); - break; - } + if (port) { + ehea_info("adding port with logical port id=%d failed. port " + "already configured as %s.", logical_port_id, + port->netdev->name); + return -EINVAL; + } - port = netdev_priv(dev); - port->adapter = adapter; - port->netdev = dev; - adapter->port[i] = port; - port->msg_enable = netif_msg_init(msg_level, EHEA_MSG_DEFAULT); + eth_dn = ehea_get_eth_dn(adapter, logical_port_id); - dn = of_find_node_by_name(dn, "ethernet"); - ret = ehea_setup_single_port(port, dn); - if (ret) { - /* Free mem for this port struct. The others will be - processed on rollback */ - free_netdev(dev); - adapter->port[i] = NULL; - ehea_error("eHEA port %d setup failed, ret=%d", i, ret); - } + if (!eth_dn) { + ehea_info("no logical port with id %d found", logical_port_id); + return -EINVAL; } - of_node_put(dn); + if (ehea_add_adapter_mr(adapter)) { + ehea_error("creating MR failed"); + return -EIO; + } - /* Check for succesfully set up ports */ - for (i = 0; i < adapter->num_ports; i++) - if (adapter->port[i]) - port_setup_ok++; + port = ehea_setup_single_port(adapter, logical_port_id, eth_dn); - if (port_setup_ok) - ret = 0; /* At least some ports are setup correctly */ - else - ret = -EINVAL; + of_node_put(eth_dn); + + if (port) { + for (i=0; i < EHEA_MAX_PORTS; i++) + if (!adapter->port[i]) { + adapter->port[i] = port; + break; + } + + ehea_info("added %s (logical port id=%d)", port->netdev->name, + logical_port_id); + } else { + ehea_remove_adapter_mr(adapter); + return -EIO; + } + + return (ssize_t) count; +} + +static ssize_t ehea_remove_port(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct ehea_adapter *adapter = dev->driver_data; + struct ehea_port *port; + int i; + u32 logical_port_id; + + sscanf(buf, "%X", &logical_port_id); + + port = ehea_get_port(adapter, logical_port_id); + + if (port) { + ehea_info("removed %s (logical port id=%d)", port->netdev->name, + logical_port_id); + + ehea_shutdown_single_port(port); + + for (i=0; i < EHEA_MAX_PORTS; i++) + if (adapter->port[i] == port) { + adapter->port[i] = NULL; + break; + } + } else { + ehea_error("removing port with logical port id=%d failed. port " + "not configured.", logical_port_id); + return -EINVAL; + } + + ehea_remove_adapter_mr(adapter); + + return (ssize_t) count; +} + +static DEVICE_ATTR(probe_port, S_IWUSR, NULL, ehea_probe_port); +static DEVICE_ATTR(remove_port, S_IWUSR, NULL, ehea_remove_port); +int ehea_create_device_sysfs(struct ibmebus_dev *dev) +{ + int ret = device_create_file(&dev->ofdev.dev, &dev_attr_probe_port); + if (ret) + goto out; + + ret = device_create_file(&dev->ofdev.dev, &dev_attr_remove_port); +out: return ret; } -static int __devinit ehea_probe(struct ibmebus_dev *dev, - const struct of_device_id *id) +void ehea_remove_device_sysfs(struct ibmebus_dev *dev) +{ + device_remove_file(&dev->ofdev.dev, &dev_attr_probe_port); + device_remove_file(&dev->ofdev.dev, &dev_attr_remove_port); +} + +static int __devinit ehea_probe_adapter(struct ibmebus_dev *dev, + const struct of_device_id *id) { struct ehea_adapter *adapter; u64 *adapter_handle; int ret; + if (!dev || !dev->ofdev.node) { + ehea_error("Invalid ibmebus device probed"); + return -EINVAL; + } + adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); if (!adapter) { ret = -ENOMEM; @@ -2497,6 +2789,8 @@ static int __devinit ehea_probe(struct ibmebus_dev *dev, goto out; } + adapter->ebus_dev = dev; + adapter_handle = (u64*)get_property(dev->ofdev.node, "ibm,hea-handle", NULL); if (adapter_handle) @@ -2513,26 +2807,21 @@ static int __devinit ehea_probe(struct ibmebus_dev *dev, dev->ofdev.dev.driver_data = adapter; - ret = ehea_reg_mr_adapter(adapter); - if (ret) { - dev_err(&dev->ofdev.dev, "reg_mr_adapter failed\n"); - goto out_free_ad; - } /* initialize adapter and ports */ /* get adapter properties */ ret = ehea_sense_adapter_attr(adapter); if (ret) { dev_err(&dev->ofdev.dev, "sense_adapter_attr failed: %d", ret); - goto out_free_res; + goto out_free_ad; } - dev_info(&dev->ofdev.dev, "%d eHEA ports found\n", adapter->num_ports); adapter->neq = ehea_create_eq(adapter, EHEA_NEQ, EHEA_MAX_ENTRIES_EQ, 1); if (!adapter->neq) { + ret = -EIO; dev_err(&dev->ofdev.dev, "NEQ creation failed"); - goto out_free_res; + goto out_free_ad; } tasklet_init(&adapter->neq_tasklet, ehea_neq_tasklet, @@ -2547,18 +2836,27 @@ static int __devinit ehea_probe(struct ibmebus_dev *dev, } adapter->ehea_wq = create_workqueue("ehea_wq"); - if (!adapter->ehea_wq) + if (!adapter->ehea_wq) { + ret = -EIO; goto out_free_irq; + } + + ret = ehea_create_device_sysfs(dev); + if (ret) + goto out_kill_wq; ret = ehea_setup_ports(adapter); if (ret) { dev_err(&dev->ofdev.dev, "setup_ports failed"); - goto out_kill_wq; + goto out_rem_dev_sysfs; } ret = 0; goto out; +out_rem_dev_sysfs: + ehea_remove_device_sysfs(dev); + out_kill_wq: destroy_workqueue(adapter->ehea_wq); @@ -2568,45 +2866,32 @@ out_free_irq: out_kill_eq: ehea_destroy_eq(adapter->neq); -out_free_res: - ehea_h_free_resource(adapter->handle, adapter->mr.handle); - out_free_ad: kfree(adapter); out: return ret; } -static void ehea_shutdown_single_port(struct ehea_port *port) -{ - unregister_netdev(port->netdev); - kfree(port->mc_list); - free_netdev(port->netdev); -} - static int __devexit ehea_remove(struct ibmebus_dev *dev) { struct ehea_adapter *adapter = dev->ofdev.dev.driver_data; - u64 hret; int i; - for (i = 0; i < adapter->num_ports; i++) + for (i = 0; i < EHEA_MAX_PORTS; i++) if (adapter->port[i]) { ehea_shutdown_single_port(adapter->port[i]); adapter->port[i] = NULL; } + + ehea_remove_device_sysfs(dev); + destroy_workqueue(adapter->ehea_wq); ibmebus_free_irq(NULL, adapter->neq->attr.ist1, adapter); tasklet_kill(&adapter->neq_tasklet); ehea_destroy_eq(adapter->neq); - - hret = ehea_h_free_resource(adapter->handle, adapter->mr.handle); - if (hret) { - dev_err(&dev->ofdev.dev, "free_resource_mr failed"); - return -EIO; - } + ehea_remove_adapter_mr(adapter); kfree(adapter); return 0; } @@ -2639,21 +2924,6 @@ static int check_module_parm(void) return ret; } -static struct of_device_id ehea_device_table[] = { - { - .name = "lhea", - .compatible = "IBM,lhea", - }, - {}, -}; - -static struct ibmebus_driver ehea_driver = { - .name = "ehea", - .id_table = ehea_device_table, - .probe = ehea_probe, - .remove = ehea_remove, -}; - int __init ehea_module_init(void) { int ret; diff --git a/drivers/net/ehea/ehea_phyp.c b/drivers/net/ehea/ehea_phyp.c index 37716e05e8..95c4a7f 100644 --- a/drivers/net/ehea/ehea_phyp.c +++ b/drivers/net/ehea/ehea_phyp.c @@ -478,12 +478,14 @@ u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle) 0, 0, 0, 0, 0, 0); /* R7-R12 */ } -u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle) +u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle, + u64 force_bit) { return ehea_plpar_hcall_norets(H_FREE_RESOURCE, adapter_handle, /* R4 */ res_handle, /* R5 */ - 0, 0, 0, 0, 0); /* R6-R10 */ + force_bit, + 0, 0, 0, 0); /* R7-R10 */ } u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr, @@ -612,3 +614,13 @@ u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle, event_mask, /* R6 */ 0, 0, 0, 0); /* R7-R12 */ } + +u64 ehea_h_error_data(const u64 adapter_handle, const u64 ressource_handle, + void *rblock) +{ + return ehea_plpar_hcall_norets(H_ERROR_DATA, + adapter_handle, /* R4 */ + ressource_handle, /* R5 */ + virt_to_abs(rblock), /* R6 */ + 0, 0, 0, 0); /* R7-R12 */ +} diff --git a/drivers/net/ehea/ehea_phyp.h b/drivers/net/ehea/ehea_phyp.h index 919f94b..d17a45a 100644 --- a/drivers/net/ehea/ehea_phyp.h +++ b/drivers/net/ehea/ehea_phyp.h @@ -414,7 +414,11 @@ u64 ehea_h_register_rpage(const u64 adapter_handle, u64 ehea_h_disable_and_get_hea(const u64 adapter_handle, const u64 qp_handle); -u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle); +#define FORCE_FREE 1 +#define NORMAL_FREE 0 + +u64 ehea_h_free_resource(const u64 adapter_handle, const u64 res_handle, + u64 force_bit); u64 ehea_h_alloc_resource_mr(const u64 adapter_handle, const u64 vaddr, const u64 length, const u32 access_ctrl, @@ -454,4 +458,7 @@ u64 ehea_h_reg_dereg_bcmc(const u64 adapter_handle, const u16 port_num, u64 ehea_h_reset_events(const u64 adapter_handle, const u64 neq_handle, const u64 event_mask); +u64 ehea_h_error_data(const u64 adapter_handle, const u64 ressource_handle, + void *rblock); + #endif /* __EHEA_PHYP_H__ */ diff --git a/drivers/net/ehea/ehea_qmr.c b/drivers/net/ehea/ehea_qmr.c index f143e13..f24a886 100644 --- a/drivers/net/ehea/ehea_qmr.c +++ b/drivers/net/ehea/ehea_qmr.c @@ -197,7 +197,7 @@ out_kill_hwq: hw_queue_dtor(&cq->hw_queue); out_freeres: - ehea_h_free_resource(adapter->handle, cq->fw_handle); + ehea_h_free_resource(adapter->handle, cq->fw_handle, FORCE_FREE); out_freemem: kfree(cq); @@ -206,25 +206,38 @@ out_nomem: return NULL; } -int ehea_destroy_cq(struct ehea_cq *cq) +u64 ehea_destroy_cq_res(struct ehea_cq *cq, u64 force) { - u64 adapter_handle, hret; + u64 hret; + u64 adapter_handle = cq->adapter->handle; + + /* deregister all previous registered pages */ + hret = ehea_h_free_resource(adapter_handle, cq->fw_handle, force); + if (hret != H_SUCCESS) + return hret; + + hw_queue_dtor(&cq->hw_queue); + kfree(cq); + return hret; +} + +int ehea_destroy_cq(struct ehea_cq *cq) +{ + u64 hret; if (!cq) return 0; - adapter_handle = cq->adapter->handle; + if ((hret = ehea_destroy_cq_res(cq, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(cq->adapter, cq->fw_handle); + hret = ehea_destroy_cq_res(cq, FORCE_FREE); + } - /* deregister all previous registered pages */ - hret = ehea_h_free_resource(adapter_handle, cq->fw_handle); if (hret != H_SUCCESS) { ehea_error("destroy CQ failed"); return -EIO; } - hw_queue_dtor(&cq->hw_queue); - kfree(cq); - return 0; } @@ -297,7 +310,7 @@ out_kill_hwq: hw_queue_dtor(&eq->hw_queue); out_freeres: - ehea_h_free_resource(adapter->handle, eq->fw_handle); + ehea_h_free_resource(adapter->handle, eq->fw_handle, FORCE_FREE); out_freemem: kfree(eq); @@ -316,27 +329,41 @@ struct ehea_eqe *ehea_poll_eq(struct ehea_eq *eq) return eqe; } -int ehea_destroy_eq(struct ehea_eq *eq) +u64 ehea_destroy_eq_res(struct ehea_eq *eq, u64 force) { u64 hret; unsigned long flags; - if (!eq) - return 0; - spin_lock_irqsave(&eq->spinlock, flags); - hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle); + hret = ehea_h_free_resource(eq->adapter->handle, eq->fw_handle, force); spin_unlock_irqrestore(&eq->spinlock, flags); - if (hret != H_SUCCESS) { - ehea_error("destroy_eq failed"); - return -EIO; - } + if (hret != H_SUCCESS) + return hret; hw_queue_dtor(&eq->hw_queue); kfree(eq); + return hret; +} + +int ehea_destroy_eq(struct ehea_eq *eq) +{ + u64 hret; + if (!eq) + return 0; + + if ((hret = ehea_destroy_eq_res(eq, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(eq->adapter, eq->fw_handle); + hret = ehea_destroy_eq_res(eq, FORCE_FREE); + } + + if (hret != H_SUCCESS) { + ehea_error("destroy EQ failed"); + return -EIO; + } + return 0; } @@ -471,40 +498,56 @@ out_kill_hwsq: out_freeres: ehea_h_disable_and_get_hea(adapter->handle, qp->fw_handle); - ehea_h_free_resource(adapter->handle, qp->fw_handle); + ehea_h_free_resource(adapter->handle, qp->fw_handle, FORCE_FREE); out_freemem: kfree(qp); return NULL; } -int ehea_destroy_qp(struct ehea_qp *qp) +u64 ehea_destroy_qp_res(struct ehea_qp *qp, u64 force) { - u64 hret; - struct ehea_qp_init_attr *qp_attr = &qp->init_attr; + u64 hret; + struct ehea_qp_init_attr *qp_attr = &qp->init_attr; - if (!qp) - return 0; - hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle); - if (hret != H_SUCCESS) { - ehea_error("destroy_qp failed"); - return -EIO; - } + ehea_h_disable_and_get_hea(qp->adapter->handle, qp->fw_handle); + hret = ehea_h_free_resource(qp->adapter->handle, qp->fw_handle, force); + if (hret != H_SUCCESS) + return hret; - hw_queue_dtor(&qp->hw_squeue); - hw_queue_dtor(&qp->hw_rqueue1); + hw_queue_dtor(&qp->hw_squeue); + hw_queue_dtor(&qp->hw_rqueue1); - if (qp_attr->rq_count > 1) - hw_queue_dtor(&qp->hw_rqueue2); - if (qp_attr->rq_count > 2) - hw_queue_dtor(&qp->hw_rqueue3); - kfree(qp); + if (qp_attr->rq_count > 1) + hw_queue_dtor(&qp->hw_rqueue2); + if (qp_attr->rq_count > 2) + hw_queue_dtor(&qp->hw_rqueue3); + kfree(qp); - return 0; + return hret; } -int ehea_reg_mr_adapter(struct ehea_adapter *adapter) +int ehea_destroy_qp(struct ehea_qp *qp) +{ + u64 hret; + if (!qp) + return 0; + + if ((hret = ehea_destroy_qp_res(qp, NORMAL_FREE)) == H_R_STATE) { + ehea_error_data(qp->adapter, qp->fw_handle); + hret = ehea_destroy_qp_res(qp, FORCE_FREE); + } + + if (hret != H_SUCCESS) { + ehea_error("destroy QP failed"); + return -EIO; + } + + return 0; +} + +int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr) { int i, k, ret; u64 hret, pt_abs, start, end, nr_pages; @@ -525,14 +568,14 @@ int ehea_reg_mr_adapter(struct ehea_adapter *adapter) hret = ehea_h_alloc_resource_mr(adapter->handle, start, end - start, acc_ctrl, adapter->pd, - &adapter->mr.handle, &adapter->mr.lkey); + &mr->handle, &mr->lkey); if (hret != H_SUCCESS) { ehea_error("alloc_resource_mr failed"); ret = -EIO; goto out; } - adapter->mr.vaddr = KERNELBASE; + mr->vaddr = KERNELBASE; k = 0; while (nr_pages > 0) { @@ -544,7 +587,7 @@ int ehea_reg_mr_adapter(struct ehea_adapter *adapter) EHEA_PAGESIZE))); hret = ehea_h_register_rpage_mr(adapter->handle, - adapter->mr.handle, 0, + mr->handle, 0, 0, (u64)pt_abs, num_pages); nr_pages -= num_pages; @@ -553,32 +596,115 @@ int ehea_reg_mr_adapter(struct ehea_adapter *adapter) (k * EHEA_PAGESIZE))); hret = ehea_h_register_rpage_mr(adapter->handle, - adapter->mr.handle, 0, + mr->handle, 0, 0, abs_adr,1); nr_pages--; } if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) { ehea_h_free_resource(adapter->handle, - adapter->mr.handle); - ehea_error("register_rpage_mr failed: hret = %lX", - hret); + mr->handle, FORCE_FREE); + ehea_error("register_rpage_mr failed"); ret = -EIO; goto out; } } if (hret != H_SUCCESS) { - ehea_h_free_resource(adapter->handle, adapter->mr.handle); - ehea_error("register_rpage failed for last page: hret = %lX", - hret); + ehea_h_free_resource(adapter->handle, mr->handle, + FORCE_FREE); + ehea_error("register_rpage failed for last page"); ret = -EIO; goto out; } + + mr->adapter = adapter; ret = 0; out: kfree(pt); return ret; } +int ehea_rem_mr(struct ehea_mr *mr) +{ + u64 hret; + + if (!mr || !mr->adapter) + return -EINVAL; + hret = ehea_h_free_resource(mr->adapter->handle, mr->handle, + FORCE_FREE); + if (hret != H_SUCCESS) { + ehea_error("destroy MR failed"); + return -EIO; + } + + return 0; +} + +int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, + struct ehea_mr *shared_mr) +{ + u64 hret; + + hret = ehea_h_register_smr(adapter->handle, old_mr->handle, + old_mr->vaddr, EHEA_MR_ACC_CTRL, + adapter->pd, shared_mr); + if (hret != H_SUCCESS) + return -EIO; + + shared_mr->adapter = adapter; + + return 0; +} + +void print_error_data(u64 *data) +{ + int length; + u64 type = EHEA_BMASK_GET(ERROR_DATA_TYPE, data[2]); + u64 resource = data[1]; + + length = EHEA_BMASK_GET(ERROR_DATA_LENGTH, data[0]); + + if (length > EHEA_PAGESIZE) + length = EHEA_PAGESIZE; + + if (type == 0x8) /* Queue Pair */ + ehea_error("QP (resource=%lX) state: AER=0x%lX, AERR=0x%lX, " + "port=%lX", resource, data[6], data[12], data[22]); + + if (type == 0x4) /* Completion Queue */ + ehea_error("CQ (resource=%lX) state: AER=0x%lX", resource, + data[6]); + + if (type == 0x3) /* Event Queue */ + ehea_error("EQ (resource=%lX) state: AER=0x%lX", resource, + data[6]); + + ehea_dump(data, length, "error data"); +} + +void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle) +{ + unsigned long ret; + u64 *rblock; + + rblock = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!rblock) { + ehea_error("Cannot allocate rblock memory."); + return; + } + + ret = ehea_h_error_data(adapter->handle, + res_handle, + rblock); + + if (ret == H_R_STATE) + ehea_error("No error data is available: %lX.", res_handle); + else if (ret == H_SUCCESS) + print_error_data(rblock); + else + ehea_error("Error data could not be fetched: %lX", res_handle); + + kfree(rblock); +} diff --git a/drivers/net/ehea/ehea_qmr.h b/drivers/net/ehea/ehea_qmr.h index 7efdc96..c0eb3e0 100644 --- a/drivers/net/ehea/ehea_qmr.h +++ b/drivers/net/ehea/ehea_qmr.h @@ -142,6 +142,8 @@ struct ehea_rwqe { #define EHEA_CQE_STAT_ERR_MASK 0x721F #define EHEA_CQE_STAT_FAT_ERR_MASK 0x1F #define EHEA_CQE_STAT_ERR_TCP 0x4000 +#define EHEA_CQE_STAT_ERR_IP 0x2000 +#define EHEA_CQE_STAT_ERR_CRC 0x1000 struct ehea_cqe { u64 wr_id; /* work request ID from WQE */ @@ -180,6 +182,9 @@ struct ehea_eqe { u64 entry; }; +#define ERROR_DATA_LENGTH EHEA_BMASK_IBM(52,63) +#define ERROR_DATA_TYPE EHEA_BMASK_IBM(0,7) + static inline void *hw_qeit_calc(struct hw_queue *queue, u64 q_offset) { struct ehea_page *current_page; @@ -317,6 +322,11 @@ static inline struct ehea_cqe *ehea_poll_rq1(struct ehea_qp *qp, int *wqe_index) return hw_qeit_get_valid(queue); } +static inline void ehea_inc_cq(struct ehea_cq *cq) +{ + hw_qeit_inc(&cq->hw_queue); +} + static inline void ehea_inc_rq1(struct ehea_qp *qp) { hw_qeit_inc(&qp->hw_rqueue1); @@ -324,7 +334,7 @@ static inline void ehea_inc_rq1(struct ehea_qp *qp) static inline struct ehea_cqe *ehea_poll_cq(struct ehea_cq *my_cq) { - return hw_qeit_get_inc_valid(&my_cq->hw_queue); + return hw_qeit_get_valid(&my_cq->hw_queue); } #define EHEA_CQ_REGISTER_ORIG 0 @@ -353,6 +363,13 @@ struct ehea_qp *ehea_create_qp(struct ehea_adapter * adapter, u32 pd, int ehea_destroy_qp(struct ehea_qp *qp); -int ehea_reg_mr_adapter(struct ehea_adapter *adapter); +int ehea_reg_kernel_mr(struct ehea_adapter *adapter, struct ehea_mr *mr); + +int ehea_gen_smr(struct ehea_adapter *adapter, struct ehea_mr *old_mr, + struct ehea_mr *shared_mr); + +int ehea_rem_mr(struct ehea_mr *mr); + +void ehea_error_data(struct ehea_adapter *adapter, u64 res_handle); #endif /* __EHEA_QMR_H__ */ diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c index 3a6a83d..4e3f14c 100644 --- a/drivers/net/epic100.c +++ b/drivers/net/epic100.c @@ -934,7 +934,6 @@ static void epic_init_ring(struct net_device *dev) ep->rx_skbuff[i] = skb; if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* 16 byte align the IP header. */ ep->rx_ring[i].bufaddr = pci_map_single(ep->pci_dev, skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE); @@ -1199,7 +1198,6 @@ static int epic_rx(struct net_device *dev, int budget) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(ep->pci_dev, ep->rx_ring[entry].bufaddr, @@ -1236,7 +1234,6 @@ static int epic_rx(struct net_device *dev, int budget) skb = ep->rx_skbuff[entry] = dev_alloc_skb(ep->rx_buf_sz); if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ ep->rx_ring[entry].bufaddr = pci_map_single(ep->pci_dev, skb->data, ep->rx_buf_sz, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/eth16i.c b/drivers/net/eth16i.c index 93283e3..04abf59 100644 --- a/drivers/net/eth16i.c +++ b/drivers/net/eth16i.c @@ -1175,7 +1175,6 @@ static void eth16i_rx(struct net_device *dev) break; } - skb->dev = dev; skb_reserve(skb,2); /* diff --git a/drivers/net/ewrk3.c b/drivers/net/ewrk3.c index c8c41f0..cb0792c 100644 --- a/drivers/net/ewrk3.c +++ b/drivers/net/ewrk3.c @@ -414,10 +414,9 @@ ewrk3_hw_init(struct net_device *dev, u_long iobase) icr &= 0x70; outb(icr, EWRK3_ICR); /* Disable all the IRQs */ - if (nicsr == (CSR_TXD | CSR_RXD)) + if (nicsr != (CSR_TXD | CSR_RXD)) return -ENXIO; - /* Check that the EEPROM is alive and well and not living on Pluto... */ for (chksum = 0, i = 0; i < EEPROM_MAX; i += 2) { union { @@ -994,7 +993,6 @@ static int ewrk3_rx(struct net_device *dev) if ((skb = dev_alloc_skb(pkt_len + 2)) != NULL) { unsigned char *p; - skb->dev = dev; skb_reserve(skb, 2); /* Align to 16 bytes */ p = skb_put(skb, pkt_len); diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c index 38a13f4..abe9b08 100644 --- a/drivers/net/fealnx.c +++ b/drivers/net/fealnx.c @@ -1719,7 +1719,6 @@ static int netdev_rx(struct net_device *dev) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, np->cur_rx->buffer, diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 6764281..255b091 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -647,7 +647,6 @@ while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) { printk("%s: Memory squeeze, dropping packet.\n", dev->name); fep->stats.rx_dropped++; } else { - skb->dev = dev; skb_put(skb,pkt_len-4); /* Make room */ eth_copy_and_sum(skb, data, pkt_len-4, 0); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c index 77f747a..e824d5d 100644 --- a/drivers/net/fec_8xx/fec_main.c +++ b/drivers/net/fec_8xx/fec_main.c @@ -551,7 +551,9 @@ static int fec_enet_rx_common(struct net_device *dev, int *budget) skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb + skbn->data, + pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -561,7 +563,6 @@ static int fec_enet_rx_common(struct net_device *dev, int *budget) skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c index a363148..7a01802 100644 --- a/drivers/net/forcedeth.c +++ b/drivers/net/forcedeth.c @@ -839,7 +839,7 @@ enum { NV_MSIX_INT_DISABLED, NV_MSIX_INT_ENABLED }; -static int msix = NV_MSIX_INT_ENABLED; +static int msix = NV_MSIX_INT_DISABLED; /* * DMA 64bit @@ -1385,11 +1385,12 @@ static int nv_alloc_rx(struct net_device *dev) while (np->put_rx.orig != less_rx) { struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); if (skb) { - skb->dev = dev; np->put_rx_ctx->skb = skb; - np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, - skb->end-skb->data, PCI_DMA_FROMDEVICE); - np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, + skb->data, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb_tailroom(skb); np->put_rx.orig->buf = cpu_to_le32(np->put_rx_ctx->dma); wmb(); np->put_rx.orig->flaglen = cpu_to_le32(np->rx_buf_sz | NV_RX_AVAIL); @@ -1416,11 +1417,12 @@ static int nv_alloc_rx_optimized(struct net_device *dev) while (np->put_rx.ex != less_rx) { struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz + NV_RX_ALLOC_PAD); if (skb) { - skb->dev = dev; np->put_rx_ctx->skb = skb; - np->put_rx_ctx->dma = pci_map_single(np->pci_dev, skb->data, - skb->end-skb->data, PCI_DMA_FROMDEVICE); - np->put_rx_ctx->dma_len = skb->end-skb->data; + np->put_rx_ctx->dma = pci_map_single(np->pci_dev, + skb->data, + skb_tailroom(skb), + PCI_DMA_FROMDEVICE); + np->put_rx_ctx->dma_len = skb_tailroom(skb); np->put_rx.ex->bufhigh = cpu_to_le64(np->put_rx_ctx->dma) >> 32; np->put_rx.ex->buflow = cpu_to_le64(np->put_rx_ctx->dma) & 0x0FFFFFFFF; wmb(); @@ -1604,8 +1606,9 @@ static void nv_drain_rx(struct net_device *dev) wmb(); if (np->rx_skb[i].skb) { pci_unmap_single(np->pci_dev, np->rx_skb[i].dma, - np->rx_skb[i].skb->end-np->rx_skb[i].skb->data, - PCI_DMA_FROMDEVICE); + (skb_end_pointer(np->rx_skb[i].skb) - + np->rx_skb[i].skb->data), + PCI_DMA_FROMDEVICE); dev_kfree_skb(np->rx_skb[i].skb); np->rx_skb[i].skb = NULL; } @@ -2050,9 +2053,10 @@ static void nv_tx_timeout(struct net_device *dev) nv_drain_tx(dev); nv_init_tx(dev); setup_hw_rings(dev, NV_SETUP_TX_RING); - netif_wake_queue(dev); } + netif_wake_queue(dev); + /* 4) restart tx engine */ nv_start_tx(dev); spin_unlock_irq(&np->lock); @@ -3104,13 +3108,17 @@ static int nv_napi_poll(struct net_device *dev, int *budget) struct fe_priv *np = netdev_priv(dev); u8 __iomem *base = get_hwbase(dev); unsigned long flags; + int retcode; - if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) + if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { pkts = nv_rx_process(dev, limit); - else + retcode = nv_alloc_rx(dev); + } else { pkts = nv_rx_process_optimized(dev, limit); + retcode = nv_alloc_rx_optimized(dev); + } - if (nv_alloc_rx(dev)) { + if (retcode) { spin_lock_irqsave(&np->lock, flags); if (!np->in_shutdown) mod_timer(&np->oom_kick, jiffies + OOM_REFILL); @@ -3532,7 +3540,10 @@ static void nv_do_nic_poll(unsigned long data) pci_push(base); if (!using_multi_irqs(dev)) { - nv_nic_irq(0, dev); + if (np->desc_ver == DESC_VER_3) + nv_nic_irq_optimized(0, dev); + else + nv_nic_irq(0, dev); if (np->msi_flags & NV_MSI_X_ENABLED) enable_irq_lockdep(np->msi_x_entry[NV_MSI_X_VECTOR_ALL].vector); else @@ -4368,11 +4379,12 @@ static int nv_loopback_test(struct net_device *dev) ret = 0; goto out; } + test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data, + skb_tailroom(tx_skb), + PCI_DMA_FROMDEVICE); pkt_data = skb_put(tx_skb, pkt_len); for (i = 0; i < pkt_len; i++) pkt_data[i] = (u8)(i & 0xff); - test_dma_addr = pci_map_single(np->pci_dev, tx_skb->data, - tx_skb->end-tx_skb->data, PCI_DMA_FROMDEVICE); if (np->desc_ver == DESC_VER_1 || np->desc_ver == DESC_VER_2) { np->tx_ring.orig[0].buf = cpu_to_le32(test_dma_addr); @@ -4429,7 +4441,7 @@ static int nv_loopback_test(struct net_device *dev) } pci_unmap_page(np->pci_dev, test_dma_addr, - tx_skb->end-tx_skb->data, + (skb_end_pointer(tx_skb) - tx_skb->data), PCI_DMA_TODEVICE); dev_kfree_skb_any(tx_skb); out: @@ -5370,19 +5382,19 @@ static struct pci_device_id pci_tbl[] = { }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_20), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_21), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_22), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP65 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_23), - .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_CHECKSUM|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, + .driver_data = DEV_NEED_TIMERIRQ|DEV_NEED_LINKTIMER|DEV_HAS_LARGEDESC|DEV_HAS_HIGH_DMA|DEV_HAS_POWER_CNTRL|DEV_HAS_MSI|DEV_HAS_PAUSEFRAME_TX|DEV_HAS_STATISTICS_V2|DEV_HAS_TEST_EXTENDED|DEV_HAS_MGMT_UNIT, }, { /* MCP67 Ethernet Controller */ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_24), diff --git a/drivers/net/fs_enet/fs_enet-main.c b/drivers/net/fs_enet/fs_enet-main.c index 4a05c14..e2ddd61 100644 --- a/drivers/net/fs_enet/fs_enet-main.c +++ b/drivers/net/fs_enet/fs_enet-main.c @@ -160,7 +160,8 @@ static int fs_enet_rx_napi(struct net_device *dev, int *budget) skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb, + skbn->data, pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -170,7 +171,6 @@ static int fs_enet_rx_napi(struct net_device *dev, int *budget) skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; @@ -294,7 +294,8 @@ static int fs_enet_rx_non_napi(struct net_device *dev) skbn = dev_alloc_skb(pkt_len + 2); if (skbn != NULL) { skb_reserve(skbn, 2); /* align IP header */ - memcpy(skbn->data, skb->data, pkt_len); + skb_copy_from_linear_data(skb, + skbn->data, pkt_len); /* swap */ skbt = skb; skb = skbn; @@ -304,7 +305,6 @@ static int fs_enet_rx_non_napi(struct net_device *dev) skbn = dev_alloc_skb(ENET_RX_FRSIZE); if (skbn != NULL) { - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ skb->protocol = eth_type_trans(skb, dev); received++; @@ -516,7 +516,6 @@ void fs_init_bds(struct net_device *dev) break; } fep->rx_skbuff[i] = skb; - skb->dev = dev; CBDW_BUFADDR(bdp, dma_map_single(fep->dev, skb->data, L1_CACHE_ALIGN(PKT_MAXBUF_SIZE), diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index 1be4a84..b666a0c 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -10,6 +10,7 @@ * Maintainer: Kumar Gala * * Copyright (c) 2002-2006 Freescale Semiconductor, Inc. + * Copyright (c) 2007 MontaVista Software, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -941,18 +942,18 @@ static inline void gfar_tx_checksum(struct sk_buff *skb, struct txfcb *fcb) /* Tell the controller what the protocol is */ /* And provide the already calculated phcs */ - if (skb->nh.iph->protocol == IPPROTO_UDP) { + if (ip_hdr(skb)->protocol == IPPROTO_UDP) { flags |= TXFCB_UDP; - fcb->phcs = skb->h.uh->check; + fcb->phcs = udp_hdr(skb)->check; } else - fcb->phcs = skb->h.th->check; + fcb->phcs = udp_hdr(skb)->check; /* l3os is the distance between the start of the * frame (skb->data) and the start of the IP hdr. * l4os is the distance between the start of the * l3 hdr and the l4 hdr */ - fcb->l3os = (u16)(skb->nh.raw - skb->data - GMAC_FCB_LEN); - fcb->l4os = (u16)(skb->h.raw - skb->nh.raw); + fcb->l3os = (u16)(skb_network_offset(skb) - GMAC_FCB_LEN); + fcb->l4os = skb_network_header_len(skb); fcb->flags = flags; } @@ -1131,8 +1132,7 @@ static void gfar_vlan_rx_kill_vid(struct net_device *dev, uint16_t vid) spin_lock_irqsave(&priv->rxlock, flags); - if (priv->vlgrp) - priv->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(priv->vlgrp, vid, NULL); spin_unlock_irqrestore(&priv->rxlock, flags); } @@ -1295,8 +1295,6 @@ struct sk_buff * gfar_new_skb(struct net_device *dev, struct rxbd8 *bdp) */ skb_reserve(skb, alignamount); - skb->dev = dev; - bdp->bufPtr = dma_map_single(NULL, skb->data, priv->rx_buffer_size, DMA_FROM_DEVICE); @@ -1612,71 +1610,17 @@ static irqreturn_t gfar_interrupt(int irq, void *dev_id) /* Save ievent for future reference */ u32 events = gfar_read(&priv->regs->ievent); - /* Clear IEVENT */ - gfar_write(&priv->regs->ievent, events); - /* Check for reception */ - if ((events & IEVENT_RXF0) || (events & IEVENT_RXB0)) + if (events & IEVENT_RX_MASK) gfar_receive(irq, dev_id); /* Check for transmit completion */ - if ((events & IEVENT_TXF) || (events & IEVENT_TXB)) + if (events & IEVENT_TX_MASK) gfar_transmit(irq, dev_id); - /* Update error statistics */ - if (events & IEVENT_TXE) { - priv->stats.tx_errors++; - - if (events & IEVENT_LC) - priv->stats.tx_window_errors++; - if (events & IEVENT_CRL) - priv->stats.tx_aborted_errors++; - if (events & IEVENT_XFUN) { - if (netif_msg_tx_err(priv)) - printk(KERN_WARNING "%s: tx underrun. dropped packet\n", dev->name); - priv->stats.tx_dropped++; - priv->extra_stats.tx_underrun++; - - /* Reactivate the Tx Queues */ - gfar_write(&priv->regs->tstat, TSTAT_CLEAR_THALT); - } - } - if (events & IEVENT_BSY) { - priv->stats.rx_errors++; - priv->extra_stats.rx_bsy++; - - gfar_receive(irq, dev_id); - -#ifndef CONFIG_GFAR_NAPI - /* Clear the halt bit in RSTAT */ - gfar_write(&priv->regs->rstat, RSTAT_CLEAR_RHALT); -#endif - - if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", - dev->name, - gfar_read(&priv->regs->rstat)); - } - if (events & IEVENT_BABR) { - priv->stats.rx_errors++; - priv->extra_stats.rx_babr++; - - if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: babbling error\n", dev->name); - } - if (events & IEVENT_EBERR) { - priv->extra_stats.eberr++; - if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: EBERR\n", dev->name); - } - if ((events & IEVENT_RXC) && (netif_msg_rx_err(priv))) - printk(KERN_DEBUG "%s: control frame\n", dev->name); - - if (events & IEVENT_BABT) { - priv->extra_stats.tx_babt++; - if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: babt error\n", dev->name); - } + /* Check for errors */ + if (events & IEVENT_ERR_MASK) + gfar_error(irq, dev_id); return IRQ_HANDLED; } @@ -1938,7 +1882,7 @@ static irqreturn_t gfar_error(int irq, void *dev_id) /* Hmm... */ if (netif_msg_rx_err(priv) || netif_msg_tx_err(priv)) printk(KERN_DEBUG "%s: error interrupt (ievent=0x%08x imask=0x%08x)\n", - dev->name, events, gfar_read(&priv->regs->imask)); + dev->name, events, gfar_read(&priv->regs->imask)); /* Update the error counters */ if (events & IEVENT_TXE) { @@ -1950,8 +1894,8 @@ static irqreturn_t gfar_error(int irq, void *dev_id) priv->stats.tx_aborted_errors++; if (events & IEVENT_XFUN) { if (netif_msg_tx_err(priv)) - printk(KERN_DEBUG "%s: underrun. packet dropped.\n", - dev->name); + printk(KERN_DEBUG "%s: TX FIFO underrun, " + "packet dropped.\n", dev->name); priv->stats.tx_dropped++; priv->extra_stats.tx_underrun++; @@ -1973,30 +1917,28 @@ static irqreturn_t gfar_error(int irq, void *dev_id) #endif if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: busy error (rhalt: %x)\n", - dev->name, - gfar_read(&priv->regs->rstat)); + printk(KERN_DEBUG "%s: busy error (rstat: %x)\n", + dev->name, gfar_read(&priv->regs->rstat)); } if (events & IEVENT_BABR) { priv->stats.rx_errors++; priv->extra_stats.rx_babr++; if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: babbling error\n", dev->name); + printk(KERN_DEBUG "%s: babbling RX error\n", dev->name); } if (events & IEVENT_EBERR) { priv->extra_stats.eberr++; if (netif_msg_rx_err(priv)) - printk(KERN_DEBUG "%s: EBERR\n", dev->name); + printk(KERN_DEBUG "%s: bus error\n", dev->name); } if ((events & IEVENT_RXC) && netif_msg_rx_status(priv)) - if (netif_msg_rx_status(priv)) - printk(KERN_DEBUG "%s: control frame\n", dev->name); + printk(KERN_DEBUG "%s: control frame\n", dev->name); if (events & IEVENT_BABT) { priv->extra_stats.tx_babt++; if (netif_msg_tx_err(priv)) - printk(KERN_DEBUG "%s: babt error\n", dev->name); + printk(KERN_DEBUG "%s: babbling TX error\n", dev->name); } return IRQ_HANDLED; } diff --git a/drivers/net/gianfar_sysfs.c b/drivers/net/gianfar_sysfs.c index 45ffb5d..aec9ab1 100644 --- a/drivers/net/gianfar_sysfs.c +++ b/drivers/net/gianfar_sysfs.c @@ -38,13 +38,15 @@ #include "gianfar.h" #define GFAR_ATTR(_name) \ -static ssize_t gfar_show_##_name(struct class_device *cdev, char *buf); \ -static ssize_t gfar_set_##_name(struct class_device *cdev, \ +static ssize_t gfar_show_##_name(struct device *dev, \ + struct device_attribute *attr, char *buf); \ +static ssize_t gfar_set_##_name(struct device *dev, \ + struct device_attribute *attr, \ const char *buf, size_t count); \ -static CLASS_DEVICE_ATTR(_name, 0644, gfar_show_##_name, gfar_set_##_name) +static DEVICE_ATTR(_name, 0644, gfar_show_##_name, gfar_set_##_name) #define GFAR_CREATE_FILE(_dev, _name) \ - class_device_create_file(&_dev->class_dev, &class_device_attr_##_name) + device_create_file(&_dev->dev, &dev_attr_##_name) GFAR_ATTR(bd_stash); GFAR_ATTR(rx_stash_size); @@ -53,29 +55,28 @@ GFAR_ATTR(fifo_threshold); GFAR_ATTR(fifo_starve); GFAR_ATTR(fifo_starve_off); -#define to_net_dev(cd) container_of(cd, struct net_device, class_dev) - -static ssize_t gfar_show_bd_stash(struct class_device *cdev, char *buf) +static ssize_t gfar_show_bd_stash(struct device *dev, + struct device_attribute *attr, char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); - return sprintf(buf, "%s\n", priv->bd_stash_en? "on" : "off"); + return sprintf(buf, "%s\n", priv->bd_stash_en ? "on" : "off"); } -static ssize_t gfar_set_bd_stash(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_bd_stash(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); int new_setting = 0; u32 temp; unsigned long flags; /* Find out the new setting */ - if (!strncmp("on", buf, count-1) || !strncmp("1", buf, count-1)) + if (!strncmp("on", buf, count - 1) || !strncmp("1", buf, count - 1)) new_setting = 1; - else if (!strncmp("off", buf, count-1) || !strncmp("0", buf, count-1)) + else if (!strncmp("off", buf, count - 1) + || !strncmp("0", buf, count - 1)) new_setting = 0; else return count; @@ -99,19 +100,19 @@ static ssize_t gfar_set_bd_stash(struct class_device *cdev, return count; } -static ssize_t gfar_show_rx_stash_size(struct class_device *cdev, char *buf) +static ssize_t gfar_show_rx_stash_size(struct device *dev, + struct device_attribute *attr, char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%d\n", priv->rx_stash_size); } -static ssize_t gfar_set_rx_stash_size(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_rx_stash_size(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); unsigned int length = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -145,21 +146,21 @@ static ssize_t gfar_set_rx_stash_size(struct class_device *cdev, return count; } - /* Stashing will only be enabled when rx_stash_size != 0 */ -static ssize_t gfar_show_rx_stash_index(struct class_device *cdev, char *buf) +static ssize_t gfar_show_rx_stash_index(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%d\n", priv->rx_stash_index); } -static ssize_t gfar_set_rx_stash_index(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_rx_stash_index(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); unsigned short index = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -183,19 +184,20 @@ static ssize_t gfar_set_rx_stash_index(struct class_device *cdev, return count; } -static ssize_t gfar_show_fifo_threshold(struct class_device *cdev, char *buf) +static ssize_t gfar_show_fifo_threshold(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%d\n", priv->fifo_threshold); } -static ssize_t gfar_set_fifo_threshold(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_fifo_threshold(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); unsigned int length = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -217,20 +219,19 @@ static ssize_t gfar_set_fifo_threshold(struct class_device *cdev, return count; } -static ssize_t gfar_show_fifo_starve(struct class_device *cdev, char *buf) +static ssize_t gfar_show_fifo_starve(struct device *dev, + struct device_attribute *attr, char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%d\n", priv->fifo_starve); } - -static ssize_t gfar_set_fifo_starve(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_fifo_starve(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); unsigned int num = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; @@ -252,19 +253,20 @@ static ssize_t gfar_set_fifo_starve(struct class_device *cdev, return count; } -static ssize_t gfar_show_fifo_starve_off(struct class_device *cdev, char *buf) +static ssize_t gfar_show_fifo_starve_off(struct device *dev, + struct device_attribute *attr, + char *buf) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); return sprintf(buf, "%d\n", priv->fifo_starve_off); } -static ssize_t gfar_set_fifo_starve_off(struct class_device *cdev, - const char *buf, size_t count) +static ssize_t gfar_set_fifo_starve_off(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { - struct net_device *dev = to_net_dev(cdev); - struct gfar_private *priv = netdev_priv(dev); + struct gfar_private *priv = netdev_priv(to_net_dev(dev)); unsigned int num = simple_strtoul(buf, NULL, 0); u32 temp; unsigned long flags; diff --git a/drivers/net/hamachi.c b/drivers/net/hamachi.c index c3c0d67..2521b11 100644 --- a/drivers/net/hamachi.c +++ b/drivers/net/hamachi.c @@ -1568,7 +1568,6 @@ static int hamachi_rx(struct net_device *dev) printk(KERN_ERR "%s: rx_copybreak non-zero " "not good with RX_CHECKSUM\n", dev->name); #endif - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(hmp->pci_dev, hmp->rx_ring[entry].addr, diff --git a/drivers/net/hamradio/Kconfig b/drivers/net/hamradio/Kconfig index feb0ada..6e90619 100644 --- a/drivers/net/hamradio/Kconfig +++ b/drivers/net/hamradio/Kconfig @@ -138,7 +138,7 @@ config BAYCOM_SER_HDX ---help--- This is one of two drivers for Baycom style simple amateur radio modems that connect to a serial interface. The driver supports the - ser12 design in full-duplex mode. This is the old driver. It is + ser12 design in half-duplex mode. This is the old driver. It is still provided in case your serial interface chip does not work with the full-duplex driver. This driver is depreciated. To configure the driver, use the sethdlc utility available in the standard ax25 @@ -190,3 +190,4 @@ config YAM To compile this driver as a module, choose M here: the module will be called yam. + diff --git a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c index 153b6dc..84aa211 100644 --- a/drivers/net/hamradio/baycom_epp.c +++ b/drivers/net/hamradio/baycom_epp.c @@ -52,6 +52,7 @@ #include <linux/hdlcdrv.h> #include <linux/baycom.h> #include <linux/jiffies.h> +#include <linux/random.h> #include <net/ax25.h> #include <asm/uaccess.h> @@ -433,16 +434,6 @@ static void encode_hdlc(struct baycom_state *bc) /* ---------------------------------------------------------------------- */ -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - -/* ---------------------------------------------------------------------- */ - static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) { struct parport *pp = bc->pdev->port; @@ -464,7 +455,7 @@ static int transmit(struct baycom_state *bc, int cnt, unsigned char stat) if ((--bc->hdlctx.slotcnt) > 0) return 0; bc->hdlctx.slotcnt = bc->ch_params.slottime; - if ((random_num() % 256) > bc->ch_params.ppersist) + if ((random32() % 256) > bc->ch_params.ppersist) return 0; } } diff --git a/drivers/net/hamradio/baycom_ser_fdx.c b/drivers/net/hamradio/baycom_ser_fdx.c index 59214e7..17ac697 100644 --- a/drivers/net/hamradio/baycom_ser_fdx.c +++ b/drivers/net/hamradio/baycom_ser_fdx.c @@ -75,12 +75,14 @@ #include <linux/ioport.h> #include <linux/string.h> #include <linux/init.h> -#include <asm/uaccess.h> -#include <asm/io.h> #include <linux/hdlcdrv.h> #include <linux/baycom.h> #include <linux/jiffies.h> +#include <asm/uaccess.h> +#include <asm/io.h> +#include <asm/irq.h> + /* --------------------------------------------------------------------- */ #define BAYCOM_DEBUG @@ -413,11 +415,18 @@ static int ser12_open(struct net_device *dev) if (!dev || !bc) return -ENXIO; - if (!dev->base_addr || dev->base_addr > 0x1000-SER12_EXTENT || - dev->irq < 2 || dev->irq > 15) + if (!dev->base_addr || dev->base_addr > 0xffff-SER12_EXTENT || + dev->irq < 2 || dev->irq > NR_IRQS) { + printk(KERN_INFO "baycom_ser_fdx: invalid portnumber (max %u) " + "or irq (2 <= irq <= %d)\n", + 0xffff-SER12_EXTENT, NR_IRQS); return -ENXIO; - if (bc->baud < 300 || bc->baud > 4800) + } + if (bc->baud < 300 || bc->baud > 4800) { + printk(KERN_INFO "baycom_ser_fdx: invalid baudrate " + "(300...4800)\n"); return -EINVAL; + } if (!request_region(dev->base_addr, SER12_EXTENT, "baycom_ser_fdx")) { printk(KERN_WARNING "BAYCOM_SER_FSX: I/O port 0x%04lx busy \n", dev->base_addr); diff --git a/drivers/net/hamradio/bpqether.c b/drivers/net/hamradio/bpqether.c index d254269..656f278 100644 --- a/drivers/net/hamradio/bpqether.c +++ b/drivers/net/hamradio/bpqether.c @@ -282,7 +282,7 @@ static int bpq_xmit(struct sk_buff *skb, struct net_device *dev) } skb->protocol = ax25_type_trans(skb, dev); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev->hard_header(skb, dev, ETH_P_BPQ, bpq->dest_addr, NULL, 0); bpq->stats.tx_packets++; bpq->stats.tx_bytes+=skb->len; diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c index 0fbb414..3be8c50 100644 --- a/drivers/net/hamradio/dmascc.c +++ b/drivers/net/hamradio/dmascc.c @@ -930,7 +930,7 @@ static int scc_send_packet(struct sk_buff *skb, struct net_device *dev) /* Transfer data to DMA buffer */ i = priv->tx_head; - memcpy(priv->tx_buf[i], skb->data + 1, skb->len - 1); + skb_copy_from_linear_data_offset(skb, 1, priv->tx_buf[i], skb->len - 1); priv->tx_len[i] = skb->len - 1; /* Clear interrupts while we touch our circular buffers */ diff --git a/drivers/net/hamradio/hdlcdrv.c b/drivers/net/hamradio/hdlcdrv.c index 452873e..b33adc6 100644 --- a/drivers/net/hamradio/hdlcdrv.c +++ b/drivers/net/hamradio/hdlcdrv.c @@ -56,6 +56,7 @@ #include <linux/if_arp.h> #include <linux/skbuff.h> #include <linux/hdlcdrv.h> +#include <linux/random.h> #include <net/ax25.h> #include <asm/uaccess.h> @@ -316,7 +317,9 @@ void hdlcdrv_transmitter(struct net_device *dev, struct hdlcdrv_state *s) dev_kfree_skb_irq(skb); break; } - memcpy(s->hdlctx.buffer, skb->data+1, pkt_len); + skb_copy_from_linear_data_offset(skb, 1, + s->hdlctx.buffer, + pkt_len); dev_kfree_skb_irq(skb); s->hdlctx.bp = s->hdlctx.buffer; append_crc_ccitt(s->hdlctx.buffer, pkt_len); @@ -371,16 +374,6 @@ static void start_tx(struct net_device *dev, struct hdlcdrv_state *s) /* ---------------------------------------------------------------------- */ -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - -/* ---------------------------------------------------------------------- */ - void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s) { if (!s || s->magic != HDLCDRV_MAGIC || s->hdlctx.ptt || !s->skb) @@ -396,7 +389,7 @@ void hdlcdrv_arbitrate(struct net_device *dev, struct hdlcdrv_state *s) if ((--s->hdlctx.slotcnt) > 0) return; s->hdlctx.slotcnt = s->ch_params.slottime; - if ((random_num() % 256) > s->ch_params.ppersist) + if ((random32() % 256) > s->ch_params.ppersist) return; start_tx(dev, s); } diff --git a/drivers/net/hamradio/yam.c b/drivers/net/hamradio/yam.c index 08f2711..467559d 100644 --- a/drivers/net/hamradio/yam.c +++ b/drivers/net/hamradio/yam.c @@ -50,6 +50,7 @@ #include <linux/slab.h> #include <linux/errno.h> #include <linux/bitops.h> +#include <linux/random.h> #include <asm/io.h> #include <asm/system.h> #include <linux/interrupt.h> @@ -566,14 +567,6 @@ static void yam_start_tx(struct net_device *dev, struct yam_port *yp) ptt_on(dev); } -static unsigned short random_seed; - -static inline unsigned short random_num(void) -{ - random_seed = 28629 * random_seed + 157; - return random_seed; -} - static void yam_arbitrate(struct net_device *dev) { struct yam_port *yp = netdev_priv(dev); @@ -600,7 +593,7 @@ static void yam_arbitrate(struct net_device *dev) yp->slotcnt = yp->slot / 10; /* is random > persist ? */ - if ((random_num() % 256) > yp->pers) + if ((random32() % 256) > yp->pers) return; yam_start_tx(dev, yp); @@ -645,7 +638,9 @@ static void yam_tx_byte(struct net_device *dev, struct yam_port *yp) dev_kfree_skb_any(skb); break; } - memcpy(yp->tx_buf, skb->data + 1, yp->tx_len); + skb_copy_from_linear_data_offset(skb, 1, + yp->tx_buf, + yp->tx_len); dev_kfree_skb_any(skb); yp->tx_count = 0; yp->tx_crcl = 0x21; diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c index 7dc5185..8118a67 100644 --- a/drivers/net/hp100.c +++ b/drivers/net/hp100.c @@ -1816,7 +1816,6 @@ static void hp100_rx(struct net_device *dev) u_char *ptr; skb_reserve(skb,2); - skb->dev = dev; /* ptr to start of the sk_buff data area */ skb_put(skb, pkt_len); diff --git a/drivers/net/ibm_emac/ibm_emac_core.c b/drivers/net/ibm_emac/ibm_emac_core.c index dd8ad87..3d82d46 100644 --- a/drivers/net/ibm_emac/ibm_emac_core.c +++ b/drivers/net/ibm_emac/ibm_emac_core.c @@ -1338,7 +1338,7 @@ static inline int emac_rx_sg_append(struct ocp_enet_private *dev, int slot) dev_kfree_skb(dev->rx_sg_skb); dev->rx_sg_skb = NULL; } else { - cacheable_memcpy(dev->rx_sg_skb->tail, + cacheable_memcpy(skb_tail_pointer(dev->rx_sg_skb), dev->rx_skb[slot]->data, len); skb_put(dev->rx_sg_skb, len); emac_recycle_rx_skb(dev, slot, len); @@ -1398,7 +1398,6 @@ static int emac_poll_rx(void *param, int budget) skb_put(skb, len); push_packet: - skb->dev = dev->ndev; skb->protocol = eth_type_trans(skb, dev->ndev); emac_rx_csum(dev, skb, ctrl); diff --git a/drivers/net/ibmlana.c b/drivers/net/ibmlana.c index 3f946c8..fe85d6f 100644 --- a/drivers/net/ibmlana.c +++ b/drivers/net/ibmlana.c @@ -601,7 +601,6 @@ static void irqrx_handler(struct net_device *dev) /* set up skb fields */ - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 458db05..3bec0f7 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -93,7 +93,7 @@ static void ibmveth_proc_unregister_driver(void); static void ibmveth_proc_register_adapter(struct ibmveth_adapter *adapter); static void ibmveth_proc_unregister_adapter(struct ibmveth_adapter *adapter); static irqreturn_t ibmveth_interrupt(int irq, void *dev_instance); -static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); +static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter); static struct kobj_type ktype_veth_pool; #ifdef CONFIG_PROC_FS @@ -389,7 +389,7 @@ static void ibmveth_rxq_recycle_buffer(struct ibmveth_adapter *adapter) } } -static inline void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) +static void ibmveth_rxq_harvest_buffer(struct ibmveth_adapter *adapter) { ibmveth_remove_buffer_from_pool(adapter, adapter->rx_queue.queue_addr[adapter->rx_queue.index].correlator); @@ -798,7 +798,6 @@ static int ibmveth_poll(struct net_device *netdev, int *budget) skb_reserve(skb, offset); skb_put(skb, length); - skb->dev = netdev; skb->protocol = eth_type_trans(skb, netdev); netif_receive_skb(skb); /* send it up */ @@ -954,14 +953,16 @@ static int __devinit ibmveth_probe(struct vio_dev *dev, const struct vio_device_ ibmveth_debug_printk_no_adapter("entering ibmveth_probe for UA 0x%x\n", dev->unit_address); - mac_addr_p = (unsigned char *) vio_get_attribute(dev, VETH_MAC_ADDR, 0); + mac_addr_p = (unsigned char *) vio_get_attribute(dev, + VETH_MAC_ADDR, NULL); if(!mac_addr_p) { printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find VETH_MAC_ADDR " "attribute\n", __FILE__, __LINE__); return 0; } - mcastFilterSize_p= (unsigned int *) vio_get_attribute(dev, VETH_MCAST_FILTER_SIZE, 0); + mcastFilterSize_p = (unsigned int *) vio_get_attribute(dev, + VETH_MCAST_FILTER_SIZE, NULL); if(!mcastFilterSize_p) { printk(KERN_ERR "(%s:%3.3d) ERROR: Can't find " "VETH_MCAST_FILTER_SIZE attribute\n", diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c index ca2b21f..07b4c0d 100644 --- a/drivers/net/ifb.c +++ b/drivers/net/ifb.c @@ -96,17 +96,24 @@ static void ri_tasklet(unsigned long dev) skb->tc_verd = SET_TC_NCLS(skb->tc_verd); stats->tx_packets++; stats->tx_bytes +=skb->len; + + skb->dev = __dev_get_by_index(skb->iif); + if (!skb->dev) { + dev_kfree_skb(skb); + stats->tx_dropped++; + break; + } + skb->iif = _dev->ifindex; + if (from & AT_EGRESS) { dp->st_rx_frm_egr++; dev_queue_xmit(skb); } else if (from & AT_INGRESS) { - dp->st_rx_frm_ing++; + skb_pull(skb, skb->dev->hard_header_len); netif_rx(skb); - } else { - dev_kfree_skb(skb); - stats->tx_dropped++; - } + } else + BUG(); } if (netif_tx_trylock(_dev)) { @@ -157,26 +164,10 @@ static int ifb_xmit(struct sk_buff *skb, struct net_device *dev) stats->rx_packets++; stats->rx_bytes+=skb->len; - if (!from || !skb->input_dev) { -dropped: + if (!(from & (AT_INGRESS|AT_EGRESS)) || !skb->iif) { dev_kfree_skb(skb); stats->rx_dropped++; return ret; - } else { - /* - * note we could be going - * ingress -> egress or - * egress -> ingress - */ - skb->dev = skb->input_dev; - skb->input_dev = dev; - if (from & AT_INGRESS) { - skb_pull(skb, skb->dev->hard_header_len); - } else { - if (!(from & AT_EGRESS)) { - goto dropped; - } - } } if (skb_queue_len(&dp->rq) >= dev->tx_queue_len) { diff --git a/drivers/net/ioc3-eth.c b/drivers/net/ioc3-eth.c index f0d30cf..f749e07 100644 --- a/drivers/net/ioc3-eth.c +++ b/drivers/net/ioc3-eth.c @@ -633,8 +633,6 @@ static inline void ioc3_rx(struct ioc3_private *ip) ip->rx_skbs[rx_entry] = NULL; /* Poison */ - new_skb->dev = priv_netdev(ip); - /* Because we reserve afterwards. */ skb_put(new_skb, (1664 + RX_OFFSET)); rxb = (struct ioc3_erxbuf *) new_skb->data; @@ -836,13 +834,17 @@ static int ioc3_mii_init(struct ioc3_private *ip) } ip->mii.phy_id = i; + +out: + return res; +} + +static void ioc3_mii_start(struct ioc3_private *ip) +{ ip->ioc3_timer.expires = jiffies + (12 * HZ)/10; /* 1.2 sec. */ ip->ioc3_timer.data = (unsigned long) ip; ip->ioc3_timer.function = &ioc3_timer; add_timer(&ip->ioc3_timer); - -out: - return res; } static inline void ioc3_clean_rx_ring(struct ioc3_private *ip) @@ -936,7 +938,6 @@ static void ioc3_alloc_rings(struct net_device *dev) } ip->rx_skbs[i] = skb; - skb->dev = dev; /* Because we reserve afterwards. */ skb_put(skb, (1664 + RX_OFFSET)); @@ -1071,6 +1072,7 @@ static int ioc3_open(struct net_device *dev) ip->ehar_h = 0; ip->ehar_l = 0; ioc3_init(dev); + ioc3_mii_start(ip); netif_start_queue(dev); return 0; @@ -1274,6 +1276,7 @@ static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto out_stop; } + ioc3_mii_start(ip); ioc3_ssram_disc(ip); ioc3_get_eaddr(ip); @@ -1314,6 +1317,7 @@ static int ioc3_probe(struct pci_dev *pdev, const struct pci_device_id *ent) out_stop: ioc3_stop(ip); + del_timer_sync(&ip->ioc3_timer); ioc3_free_rings(ip); out_res: pci_release_regions(pdev); @@ -1335,6 +1339,8 @@ static void __devexit ioc3_remove_one (struct pci_dev *pdev) struct ioc3 *ioc3 = ip->regs; unregister_netdev(dev); + del_timer_sync(&ip->ioc3_timer); + iounmap(ioc3); pci_release_regions(pdev); free_netdev(dev); @@ -1387,9 +1393,9 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) * manually. */ if (skb->ip_summed == CHECKSUM_PARTIAL) { - int proto = ntohs(skb->nh.iph->protocol); + const struct iphdr *ih = ip_hdr(skb); + const int proto = ntohs(ih->protocol); unsigned int csoff; - struct iphdr *ih = skb->nh.iph; uint32_t csum, ehsum; uint16_t *eh; @@ -1416,11 +1422,11 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) csoff = ETH_HLEN + (ih->ihl << 2); if (proto == IPPROTO_UDP) { csoff += offsetof(struct udphdr, check); - skb->h.uh->check = csum; + udp_hdr(skb)->check = csum; } if (proto == IPPROTO_TCP) { csoff += offsetof(struct tcphdr, check); - skb->h.th->check = csum; + tcp_hdr(skb)->check = csum; } w0 = ETXD_DOCHECKSUM | (csoff << ETXD_CHKOFF_SHIFT); @@ -1437,7 +1443,7 @@ static int ioc3_start_xmit(struct sk_buff *skb, struct net_device *dev) if (len <= 104) { /* Short packet, let's copy it directly into the ring. */ - memcpy(desc->data, skb->data, skb->len); + skb_copy_from_linear_data(skb, desc->data, skb->len); if (len < ETH_ZLEN) { /* Very short packet, pad with zeros at the end. */ memset(desc->data + len, 0, ETH_ZLEN - len); @@ -1492,6 +1498,7 @@ static void ioc3_timeout(struct net_device *dev) ioc3_stop(ip); ioc3_init(dev); ioc3_mii_init(ip); + ioc3_mii_start(ip); spin_unlock_irq(&ip->ioc3_lock); diff --git a/drivers/net/irda/ali-ircc.c b/drivers/net/irda/ali-ircc.c index cebf8c3..f9c889c 100644 --- a/drivers/net/irda/ali-ircc.c +++ b/drivers/net/irda/ali-ircc.c @@ -1472,9 +1472,8 @@ static int ali_ircc_fir_hard_xmit(struct sk_buff *skb, struct net_device *dev) self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); - + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); self->tx_fifo.len++; self->tx_fifo.free++; @@ -1924,7 +1923,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) /* Copy frame without CRC, CRC is removed by hardware*/ skb_put(skb, len); - memcpy(skb->data, self->rx_buff.data, len); + skb_copy_to_linear_data(skb, self->rx_buff.data, len); /* Move to next frame */ self->rx_buff.data += len; @@ -1932,7 +1931,7 @@ static int ali_ircc_dma_receive_complete(struct ali_ircc_cb *self) self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/irda/au1k_ir.c b/drivers/net/irda/au1k_ir.c index 37914dc..4dbdfaa 100644 --- a/drivers/net/irda/au1k_ir.c +++ b/drivers/net/irda/au1k_ir.c @@ -526,7 +526,7 @@ static int au1k_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) if (aup->speed == 4000000) { /* FIR */ - memcpy((void *)pDB->vaddr, skb->data, skb->len); + skb_copy_from_linear_data(skb, pDB->vaddr, skb->len); ptxd->count_0 = skb->len & 0xff; ptxd->count_1 = (skb->len >> 8) & 0xff; @@ -604,9 +604,9 @@ static int au1k_irda_rx(struct net_device *dev) skb_put(skb, count); else skb_put(skb, count-2); - memcpy(skb->data, (void *)pDB->vaddr, count-2); + skb_copy_to_linear_data(skb, pDB->vaddr, count - 2); skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); prxd->count_0 = 0; diff --git a/drivers/net/irda/donauboe.c b/drivers/net/irda/donauboe.c index 11af0ae..3ca47bf 100644 --- a/drivers/net/irda/donauboe.c +++ b/drivers/net/irda/donauboe.c @@ -1119,7 +1119,7 @@ dumpbufs(skb->data,skb->len,'>'); else { len = skb->len; - memcpy (self->tx_bufs[self->txs], skb->data, len); + skb_copy_from_linear_data(skb, self->tx_bufs[self->txs], len); } self->ring->tx[self->txs].len = len & 0x0fff; @@ -1282,11 +1282,11 @@ dumpbufs(self->rx_bufs[self->rxs],len,'<'); skb_reserve (skb, 1); skb_put (skb, len); - memcpy (skb->data, self->rx_bufs[self->rxs], len); - + skb_copy_to_linear_data(skb, self->rx_bufs[self->rxs], + len); self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons (ETH_P_IRDA); } else diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c index 340ee99..0ac240c 100644 --- a/drivers/net/irda/irda-usb.c +++ b/drivers/net/irda/irda-usb.c @@ -441,7 +441,7 @@ static int irda_usb_hard_xmit(struct sk_buff *skb, struct net_device *netdev) goto drop; } - memcpy(self->tx_buff + self->header_length, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff + self->header_length, skb->len); /* Change setting for next frame */ if (self->capability & IUC_STIR421X) { @@ -902,7 +902,7 @@ static void irda_usb_receive(struct urb *urb) if(docopy) { /* Copy packet, so we can recycle the original */ - memcpy(newskb->data, skb->data, urb->actual_length); + skb_copy_from_linear_data(skb, newskb->data, urb->actual_length); /* Deliver this new skb */ dataskb = newskb; /* And hook the old skb to the URB @@ -921,7 +921,7 @@ static void irda_usb_receive(struct urb *urb) /* Ask the networking layer to queue the packet for the IrDA stack */ dataskb->dev = self->netdev; - dataskb->mac.raw = dataskb->data; + skb_reset_mac_header(dataskb); dataskb->protocol = htons(ETH_P_IRDA); len = dataskb->len; netif_rx(dataskb); @@ -1057,6 +1057,8 @@ static int stir421x_fw_upload(struct irda_usb_cb *self, if (ret < 0) break; + + mdelay(10); } kfree(patch_block); diff --git a/drivers/net/irda/mcs7780.c b/drivers/net/irda/mcs7780.c index f0c61f3..0de8672 100644 --- a/drivers/net/irda/mcs7780.c +++ b/drivers/net/irda/mcs7780.c @@ -200,14 +200,14 @@ static inline int mcs_setup_transceiver_vishay(struct mcs_cb *mcs) /* Setup a communication between mcs7780 and agilent chip. */ static inline int mcs_setup_transceiver_agilent(struct mcs_cb *mcs) { - IRDA_WARNING("This transceiver type is not supported yet."); + IRDA_WARNING("This transceiver type is not supported yet.\n"); return 1; } /* Setup a communication between mcs7780 and sharp chip. */ static inline int mcs_setup_transceiver_sharp(struct mcs_cb *mcs) { - IRDA_WARNING("This transceiver type is not supported yet."); + IRDA_WARNING("This transceiver type is not supported yet.\n"); return 1; } @@ -279,7 +279,7 @@ static inline int mcs_setup_transceiver(struct mcs_cb *mcs) break; default: - IRDA_WARNING("Unknown transceiver type: %d", + IRDA_WARNING("Unknown transceiver type: %d\n", mcs->transceiver_type); ret = 1; } @@ -318,7 +318,7 @@ static inline int mcs_setup_transceiver(struct mcs_cb *mcs) return ret; error: - IRDA_ERROR("%s", msg); + IRDA_ERROR("%s\n", msg); return ret; } @@ -353,7 +353,7 @@ static unsigned mcs_wrap_fir_skb(const struct sk_buff *skb, __u8 *buf) buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data into the tx buffer. */ - memcpy(buf+2, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf + 2, skb->len); /* put the fcs in the last four bytes in little endian order. */ buf[len - 4] = fcs & 0xff; buf[len - 3] = (fcs >> 8) & 0xff; @@ -377,7 +377,7 @@ static unsigned mcs_wrap_mir_skb(const struct sk_buff *skb, __u8 *buf) buf[0] = len & 0xff; buf[1] = (len >> 8) & 0xff; /* copy the data */ - memcpy(buf+2, skb->data, skb->len); + skb_copy_from_linear_data(skb, buf + 2, skb->len); /* put the fcs in last two bytes in little endian order. */ buf[len - 2] = fcs & 0xff; buf[len - 1] = (fcs >> 8) & 0xff; @@ -426,9 +426,9 @@ static void mcs_unwrap_mir(struct mcs_cb *mcs, __u8 *buf, int len) } skb_reserve(skb, 1); - memcpy(skb->data, buf, new_len); + skb_copy_to_linear_data(skb, buf, new_len); skb_put(skb, new_len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; @@ -479,9 +479,9 @@ static void mcs_unwrap_fir(struct mcs_cb *mcs, __u8 *buf, int len) } skb_reserve(skb, 1); - memcpy(skb->data, buf, new_len); + skb_copy_to_linear_data(skb, buf, new_len); skb_put(skb, new_len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = mcs->netdev; @@ -587,7 +587,7 @@ static int mcs_speed_change(struct mcs_cb *mcs) } while(cnt++ < 100 && (rval & MCS_IRINTX)); if(cnt >= 100) { - IRDA_ERROR("unable to change speed"); + IRDA_ERROR("unable to change speed\n"); ret = -EIO; goto error; } @@ -638,7 +638,7 @@ static int mcs_speed_change(struct mcs_cb *mcs) default: ret = 1; - IRDA_WARNING("Unknown transceiver type: %d", + IRDA_WARNING("Unknown transceiver type: %d\n", mcs->transceiver_type); } if (unlikely(ret)) @@ -733,7 +733,7 @@ static int mcs_net_open(struct net_device *netdev) sprintf(hwname, "usb#%d", mcs->usbdev->devnum); mcs->irlap = irlap_open(netdev, &mcs->qos, hwname); if (!mcs->irlap) { - IRDA_ERROR("mcs7780: irlap_open failed"); + IRDA_ERROR("mcs7780: irlap_open failed\n"); goto error2; } @@ -862,7 +862,7 @@ static int mcs_hard_xmit(struct sk_buff *skb, struct net_device *ndev) mcs->out_buf, wraplen, mcs_send_irq, mcs); if ((ret = usb_submit_urb(mcs->tx_urb, GFP_ATOMIC))) { - IRDA_ERROR("failed tx_urb: %d", ret); + IRDA_ERROR("failed tx_urb: %d\n", ret); switch (ret) { case -ENODEV: case -EPIPE: @@ -897,7 +897,7 @@ static int mcs_probe(struct usb_interface *intf, if (!ndev) goto error1; - IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.", udev->devnum); + IRDA_DEBUG(1, "MCS7780 USB-IrDA bridge found at %d.\n", udev->devnum); /* what is it realy for? */ SET_MODULE_OWNER(ndev); @@ -905,7 +905,7 @@ static int mcs_probe(struct usb_interface *intf, ret = usb_reset_configuration(udev); if (ret != 0) { - IRDA_ERROR("mcs7780: usb reset configuration failed"); + IRDA_ERROR("mcs7780: usb reset configuration failed\n"); goto error2; } @@ -950,7 +950,7 @@ static int mcs_probe(struct usb_interface *intf, if (ret != 0) goto error2; - IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s", + IRDA_DEBUG(1, "IrDA: Registered MosChip MCS7780 device as %s\n", ndev->name); mcs->transceiver_type = transceiver_type; @@ -981,7 +981,7 @@ static void mcs_disconnect(struct usb_interface *intf) free_netdev(mcs->netdev); usb_set_intfdata(intf, NULL); - IRDA_DEBUG(0, "MCS7780 now disconnected."); + IRDA_DEBUG(0, "MCS7780 now disconnected.\n"); } /* Module insertion */ @@ -992,7 +992,7 @@ static int __init mcs_init(void) /* register this driver with the USB subsystem */ result = usb_register(&mcs_driver); if (result) - IRDA_ERROR("usb_register failed. Error number %d", result); + IRDA_ERROR("usb_register failed. Error number %d\n", result); return result; } diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c index 29b5ccd..d96c897 100644 --- a/drivers/net/irda/nsc-ircc.c +++ b/drivers/net/irda/nsc-ircc.c @@ -1466,9 +1466,8 @@ static int nsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); - + skb_copy_from_linear_data(skb, self->tx_fifo.queue[self->tx_fifo.free].start, + skb->len); self->tx_fifo.len++; self->tx_fifo.free++; @@ -1869,10 +1868,14 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) /* Copy frame without CRC */ if (self->io.speed < 4000000) { skb_put(skb, len-2); - memcpy(skb->data, self->rx_buff.data, len-2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); } else { skb_put(skb, len-4); - memcpy(skb->data, self->rx_buff.data, len-4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); } /* Move to next frame */ @@ -1881,7 +1884,7 @@ static int nsc_ircc_dma_receive_complete(struct nsc_ircc_cb *self, int iobase) self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c index 9137e23..fb196fd 100644 --- a/drivers/net/irda/pxaficp_ir.c +++ b/drivers/net/irda/pxaficp_ir.c @@ -321,15 +321,22 @@ static void pxa_irda_fir_dma_tx_irq(int channel, void *data) pxa_irda_set_speed(si, si->newspeed); si->newspeed = 0; } else { + int i = 64; + ICCR0 = 0; pxa_irda_fir_dma_rx_start(si); + while ((ICSR1 & ICSR1_RNE) && i--) + (void)ICDR; ICCR0 = ICCR0_ITR | ICCR0_RXE; + + if (i < 0) + printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); } netif_wake_queue(dev); } /* EIF(Error in FIFO/End in Frame) handler for FIR */ -static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev) +static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev, int icsr0) { unsigned int len, stat, data; @@ -350,7 +357,7 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev) } if (stat & ICSR1_ROR) { printk(KERN_DEBUG "pxa_ir: fir receive overrun\n"); - si->stats.rx_frame_errors++; + si->stats.rx_over_errors++; } } else { si->dma_rx_buff[len++] = data; @@ -362,7 +369,15 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev) if (stat & ICSR1_EOF) { /* end of frame. */ - struct sk_buff *skb = alloc_skb(len+1,GFP_ATOMIC); + struct sk_buff *skb; + + if (icsr0 & ICSR0_FRE) { + printk(KERN_ERR "pxa_ir: dropping erroneous frame\n"); + si->stats.rx_dropped++; + return; + } + + skb = alloc_skb(len+1,GFP_ATOMIC); if (!skb) { printk(KERN_ERR "pxa_ir: fir out of memory for receive skb\n"); si->stats.rx_dropped++; @@ -371,12 +386,12 @@ static void pxa_irda_fir_irq_eif(struct pxa_irda *si, struct net_device *dev) /* Align IP header to 20 bytes */ skb_reserve(skb, 1); - memcpy(skb->data, si->dma_rx_buff, len); + skb_copy_to_linear_data(skb, si->dma_rx_buff, len); skb_put(skb, len); /* Feed it to IrLAP */ skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); @@ -392,7 +407,7 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) { struct net_device *dev = dev_id; struct pxa_irda *si = netdev_priv(dev); - int icsr0; + int icsr0, i = 64; /* stop RX DMA */ DCSR(si->rxdma) &= ~DCSR_RUN; @@ -412,13 +427,18 @@ static irqreturn_t pxa_irda_fir_irq(int irq, void *dev_id) if (icsr0 & ICSR0_EIF) { /* An error in FIFO occured, or there is a end of frame */ - pxa_irda_fir_irq_eif(si, dev); + pxa_irda_fir_irq_eif(si, dev, icsr0); } ICCR0 = 0; pxa_irda_fir_dma_rx_start(si); + while ((ICSR1 & ICSR1_RNE) && i--) + (void)ICDR; ICCR0 = ICCR0_ITR | ICCR0_RXE; + if (i < 0) + printk(KERN_ERR "pxa_ir: cannot clear Rx FIFO!\n"); + return IRQ_HANDLED; } @@ -464,7 +484,7 @@ static int pxa_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) unsigned long mtt = irda_get_mtt(skb); si->dma_tx_buff_len = skb->len; - memcpy(si->dma_tx_buff, skb->data, skb->len); + skb_copy_from_linear_data(skb, si->dma_tx_buff, skb->len); if (mtt) while ((unsigned)(OSCR - si->last_oscr)/4 < mtt) diff --git a/drivers/net/irda/sa1100_ir.c b/drivers/net/irda/sa1100_ir.c index 937372d..056639f 100644 --- a/drivers/net/irda/sa1100_ir.c +++ b/drivers/net/irda/sa1100_ir.c @@ -504,7 +504,7 @@ static void sa1100_irda_fir_error(struct sa1100_irda *si, struct net_device *dev skb_put(skb, len); skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); si->stats.rx_packets++; si->stats.rx_bytes += len; diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c index 31c6233..198bf3b 100644 --- a/drivers/net/irda/smsc-ircc2.c +++ b/drivers/net/irda/smsc-ircc2.c @@ -315,6 +315,7 @@ static struct smsc_chip __initdata lpc_chips_flat[] = { /* Base address 0x2E or 0x4E */ { "47N227", KEY55_1|FIR|SERx4, 0x5a, 0x00 }, + { "47N227", KEY55_1|FIR|SERx4, 0x7a, 0x00 }, { "47N267", KEY55_1|FIR|SERx4, 0x5e, 0x00 }, { NULL } }; @@ -1161,7 +1162,7 @@ static int smsc_ircc_hard_xmit_fir(struct sk_buff *skb, struct net_device *dev) self->new_speed = speed; } - memcpy(self->tx_buff.head, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff.head, skb->len); self->tx_buff.len = skb->len; self->tx_buff.data = self->tx_buff.head; @@ -1412,7 +1413,7 @@ static void smsc_ircc_dma_receive_complete(struct smsc_ircc_cb *self) self->stats.rx_bytes += len; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); } diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c index 20d306f..755aa44 100644 --- a/drivers/net/irda/stir4200.c +++ b/drivers/net/irda/stir4200.c @@ -52,7 +52,6 @@ #include <linux/kthread.h> #include <linux/freezer.h> #include <net/irda/irda.h> -#include <net/irda/irlap.h> #include <net/irda/irda_device.h> #include <net/irda/wrapper.h> #include <net/irda/crc.h> @@ -349,7 +348,7 @@ static void fir_eof(struct stir_cb *stir) } skb_reserve(nskb, 1); skb = nskb; - memcpy(nskb->data, rx_buff->data, len); + skb_copy_to_linear_data(nskb, rx_buff->data, len); } else { nskb = dev_alloc_skb(rx_buff->truesize); if (unlikely(!nskb)) { @@ -364,7 +363,7 @@ static void fir_eof(struct stir_cb *stir) skb_put(skb, len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); skb->dev = stir->netdev; diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c index c3ed9b3..ff53585 100644 --- a/drivers/net/irda/via-ircc.c +++ b/drivers/net/irda/via-ircc.c @@ -925,8 +925,8 @@ static int via_ircc_hard_xmit_fir(struct sk_buff *skb, self->tx_fifo.tail += skb->len; self->stats.tx_bytes += skb->len; - memcpy(self->tx_fifo.queue[self->tx_fifo.free].start, skb->data, - skb->len); + skb_copy_from_linear_data(skb, + self->tx_fifo.queue[self->tx_fifo.free].start, skb->len); self->tx_fifo.len++; self->tx_fifo.free++; //F01 if (self->tx_fifo.len == 1) { @@ -1125,7 +1125,7 @@ static int via_ircc_dma_receive_complete(struct via_ircc_cb *self, self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); return TRUE; @@ -1189,7 +1189,7 @@ F01_E */ skb_reserve(skb, 1); skb_put(skb, len - 4); - memcpy(skb->data, self->rx_buff.data, len - 4); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); IRDA_DEBUG(2, "%s(): len=%x.rx_buff=%p\n", __FUNCTION__, len - 4, self->rx_buff.data); @@ -1198,7 +1198,7 @@ F01_E */ self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); @@ -1234,7 +1234,7 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase) } skb_reserve(skb, 1); skb_put(skb, len - 4 + 1); - memcpy(skb->data, self->rx_buff.data, len - 4 + 1); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4 + 1); st_fifo->tail++; st_fifo->len++; if (st_fifo->tail > MAX_RX_WINDOW) @@ -1244,7 +1244,7 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase) self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); if (st_fifo->len < (MAX_RX_WINDOW + 2)) { @@ -1303,7 +1303,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) } skb_reserve(skb, 1); skb_put(skb, len - 4); - memcpy(skb->data, self->rx_buff.data, len - 4); + skb_copy_to_linear_data(skb, self->rx_buff.data, len - 4); IRDA_DEBUG(2, "%s(): len=%x.head=%x\n", __FUNCTION__, len - 4, st_fifo->head); @@ -1313,7 +1313,7 @@ static int RxTimerHandler(struct via_ircc_cb *self, int iobase) self->stats.rx_bytes += len; self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); } //while diff --git a/drivers/net/irda/vlsi_ir.c b/drivers/net/irda/vlsi_ir.c index 3457e9d..c4be973 100644 --- a/drivers/net/irda/vlsi_ir.c +++ b/drivers/net/irda/vlsi_ir.c @@ -595,7 +595,7 @@ static int vlsi_process_rx(struct vlsi_ring *r, struct ring_descr *rd) rd->skb = NULL; skb->dev = ndev; memcpy(skb_put(skb,len), rd->buf, len); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); if (in_interrupt()) netif_rx(skb); else @@ -993,7 +993,7 @@ static int vlsi_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev) goto drop; } else - memcpy(rd->buf, skb->data, len); + skb_copy_from_linear_data(skb, rd->buf, len); } rd->skb = skb; /* remember skb for tx-complete stats */ diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index 4212657..5182e80 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -529,7 +529,7 @@ int w83977af_hard_xmit(struct sk_buff *skb, struct net_device *dev) /* Decide if we should use PIO or DMA transfer */ if (self->io.speed > PIO_MAX_SPEED) { self->tx_buff.data = self->tx_buff.head; - memcpy(self->tx_buff.data, skb->data, skb->len); + skb_copy_from_linear_data(skb, self->tx_buff.data, skb->len); self->tx_buff.len = skb->len; mtt = irda_get_mtt(skb); @@ -908,10 +908,14 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self) /* Copy frame without CRC */ if (self->io.speed < 4000000) { skb_put(skb, len-2); - memcpy(skb->data, self->rx_buff.data, len-2); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 2); } else { skb_put(skb, len-4); - memcpy(skb->data, self->rx_buff.data, len-4); + skb_copy_to_linear_data(skb, + self->rx_buff.data, + len - 4); } /* Move to next frame */ @@ -919,7 +923,7 @@ int w83977af_dma_receive_complete(struct w83977af_ir *self) self->stats.rx_packets++; skb->dev = self->netdev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = htons(ETH_P_IRDA); netif_rx(skb); self->netdev->last_rx = jiffies; diff --git a/drivers/net/iseries_veth.c b/drivers/net/iseries_veth.c index 0e9ba3c..347d50c 100644 --- a/drivers/net/iseries_veth.c +++ b/drivers/net/iseries_veth.c @@ -1540,7 +1540,6 @@ static void veth_receive(struct veth_lpar_connection *cnx, } skb_put(skb, length); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); /* send it up */ diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h index cf30a10..c8e9086 100644 --- a/drivers/net/ixgb/ixgb.h +++ b/drivers/net/ixgb/ixgb.h @@ -111,9 +111,6 @@ struct ixgb_adapter; /* How many Rx Buffers do we bundle into one write to the hardware ? */ #define IXGB_RX_BUFFER_WRITE 8 /* Must be power of 2 */ -/* only works for sizes that are powers of 2 */ -#define IXGB_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1))) - /* wrapper around a pointer to a socket buffer, * so a DMA handle can be stored along with the buffer */ struct ixgb_buffer { diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c index d6628bd..afde848 100644 --- a/drivers/net/ixgb/ixgb_ethtool.c +++ b/drivers/net/ixgb/ixgb_ethtool.c @@ -577,11 +577,11 @@ ixgb_set_ringparam(struct net_device *netdev, rxdr->count = max(ring->rx_pending,(uint32_t)MIN_RXD); rxdr->count = min(rxdr->count,(uint32_t)MAX_RXD); - IXGB_ROUNDUP(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); + rxdr->count = ALIGN(rxdr->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); txdr->count = max(ring->tx_pending,(uint32_t)MIN_TXD); txdr->count = min(txdr->count,(uint32_t)MAX_TXD); - IXGB_ROUNDUP(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); + txdr->count = ALIGN(txdr->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); if(netif_running(adapter->netdev)) { /* Try to get new resources before deleting old */ diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c index 0c36828..6d2b059 100644 --- a/drivers/net/ixgb/ixgb_main.c +++ b/drivers/net/ixgb/ixgb_main.c @@ -685,7 +685,7 @@ ixgb_setup_tx_resources(struct ixgb_adapter *adapter) /* round up to nearest 4K */ txdr->size = txdr->count * sizeof(struct ixgb_tx_desc); - IXGB_ROUNDUP(txdr->size, 4096); + txdr->size = ALIGN(txdr->size, 4096); txdr->desc = pci_alloc_consistent(pdev, txdr->size, &txdr->dma); if(!txdr->desc) { @@ -774,7 +774,7 @@ ixgb_setup_rx_resources(struct ixgb_adapter *adapter) /* Round up to nearest 4K */ rxdr->size = rxdr->count * sizeof(struct ixgb_rx_desc); - IXGB_ROUNDUP(rxdr->size, 4096); + rxdr->size = ALIGN(rxdr->size, 4096); rxdr->desc = pci_alloc_consistent(pdev, rxdr->size, &rxdr->dma); @@ -1182,24 +1182,27 @@ ixgb_tso(struct ixgb_adapter *adapter, struct sk_buff *skb) if (likely(skb_is_gso(skb))) { struct ixgb_buffer *buffer_info; + struct iphdr *iph; + if (skb_header_cloned(skb)) { err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC); if (err) return err; } - hdr_len = ((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + hdr_len = skb_transport_offset(skb) + tcp_hdrlen(skb); mss = skb_shinfo(skb)->gso_size; - skb->nh.iph->tot_len = 0; - skb->nh.iph->check = 0; - skb->h.th->check = ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - ipcss = skb->nh.raw - skb->data; - ipcso = (void *)&(skb->nh.iph->check) - (void *)skb->data; - ipcse = skb->h.raw - skb->data - 1; - tucss = skb->h.raw - skb->data; - tucso = (void *)&(skb->h.th->check) - (void *)skb->data; + iph = ip_hdr(skb); + iph->tot_len = 0; + iph->check = 0; + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, 0); + ipcss = skb_network_offset(skb); + ipcso = (void *)&(iph->check) - (void *)skb->data; + ipcse = skb_transport_offset(skb) - 1; + tucss = skb_transport_offset(skb); + tucso = (void *)&(tcp_hdr(skb)->check) - (void *)skb->data; tucse = 0; i = adapter->tx_ring.next_to_use; @@ -1243,7 +1246,7 @@ ixgb_tx_csum(struct ixgb_adapter *adapter, struct sk_buff *skb) if(likely(skb->ip_summed == CHECKSUM_PARTIAL)) { struct ixgb_buffer *buffer_info; - css = skb->h.raw - skb->data; + css = skb_transport_offset(skb); cso = css + skb->csum_offset; i = adapter->tx_ring.next_to_use; @@ -2014,9 +2017,12 @@ ixgb_clean_rx_irq(struct ixgb_adapter *adapter) netdev_alloc_skb(netdev, length + NET_IP_ALIGN); if (new_skb) { skb_reserve(new_skb, NET_IP_ALIGN); - memcpy(new_skb->data - NET_IP_ALIGN, - skb->data - NET_IP_ALIGN, - length + NET_IP_ALIGN); + skb_copy_to_linear_data_offset(new_skb, + -NET_IP_ALIGN, + (skb->data - + NET_IP_ALIGN), + (length + + NET_IP_ALIGN)); /* save the skb in buffer_info as good */ buffer_info->skb = skb; skb = new_skb; @@ -2213,8 +2219,7 @@ ixgb_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid) ixgb_irq_disable(adapter); - if(adapter->vlgrp) - adapter->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(adapter->vlgrp, vid, NULL); ixgb_irq_enable(adapter); @@ -2234,7 +2239,7 @@ ixgb_restore_vlan(struct ixgb_adapter *adapter) if(adapter->vlgrp) { uint16_t vid; for(vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) { - if(!adapter->vlgrp->vlan_devices[vid]) + if(!vlan_group_get_device(adapter->vlgrp, vid)) continue; ixgb_vlan_rx_add_vid(adapter->netdev, vid); } diff --git a/drivers/net/ixgb/ixgb_param.c b/drivers/net/ixgb/ixgb_param.c index b27442a..5d5ddab 100644 --- a/drivers/net/ixgb/ixgb_param.c +++ b/drivers/net/ixgb/ixgb_param.c @@ -245,8 +245,6 @@ ixgb_validate_option(int *value, struct ixgb_option *opt) return -1; } -#define LIST_LEN(l) (sizeof(l) / sizeof(l[0])) - /** * ixgb_check_options - Range Checking for Command Line Parameters * @adapter: board private structure @@ -284,7 +282,7 @@ ixgb_check_options(struct ixgb_adapter *adapter) } else { tx_ring->count = opt.def; } - IXGB_ROUNDUP(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); + tx_ring->count = ALIGN(tx_ring->count, IXGB_REQ_TX_DESCRIPTOR_MULTIPLE); } { /* Receive Descriptor Count */ struct ixgb_option opt = { @@ -303,7 +301,7 @@ ixgb_check_options(struct ixgb_adapter *adapter) } else { rx_ring->count = opt.def; } - IXGB_ROUNDUP(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); + rx_ring->count = ALIGN(rx_ring->count, IXGB_REQ_RX_DESCRIPTOR_MULTIPLE); } { /* Receive Checksum Offload Enable */ struct ixgb_option opt = { @@ -335,7 +333,7 @@ ixgb_check_options(struct ixgb_adapter *adapter) .name = "Flow Control", .err = "reading default settings from EEPROM", .def = ixgb_fc_tx_pause, - .arg = { .l = { .nr = LIST_LEN(fc_list), + .arg = { .l = { .nr = ARRAY_SIZE(fc_list), .p = fc_list }} }; diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c index a4eccb1..6683afc 100644 --- a/drivers/net/ixp2000/ixpdev.c +++ b/drivers/net/ixp2000/ixpdev.c @@ -110,11 +110,10 @@ static int ixpdev_rx(struct net_device *dev, int *budget) skb = dev_alloc_skb(desc->pkt_length + 2); if (likely(skb != NULL)) { - skb->dev = nds[desc->channel]; skb_reserve(skb, 2); eth_copy_and_sum(skb, buf, desc->pkt_length, 0); skb_put(skb, desc->pkt_length); - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, nds[desc->channel]); skb->dev->last_rx = jiffies; diff --git a/drivers/net/lance.c b/drivers/net/lance.c index a384332..0fe96c8 100644 --- a/drivers/net/lance.c +++ b/drivers/net/lance.c @@ -988,7 +988,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev) if (lance_debug > 5) printk("%s: bouncing a high-memory packet (%#x).\n", dev->name, (u32)isa_virt_to_bus(skb->data)); - memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len); + skb_copy_from_linear_data(skb, &lp->tx_bounce_buffs[entry], skb->len); lp->tx_ring[entry].base = ((u32)isa_virt_to_bus((lp->tx_bounce_buffs + entry)) & 0xffffff) | 0x83000000; dev_kfree_skb(skb); @@ -1184,7 +1184,6 @@ lance_rx(struct net_device *dev) } break; } - skb->dev = dev; skb_reserve(skb,2); /* 16 byte align */ skb_put(skb,pkt_len); /* Make room */ eth_copy_and_sum(skb, diff --git a/drivers/net/lasi_82596.c b/drivers/net/lasi_82596.c index ea392f2..0edcd12 100644 --- a/drivers/net/lasi_82596.c +++ b/drivers/net/lasi_82596.c @@ -384,7 +384,7 @@ struct i596_private { struct device *dev; }; -static char init_setup[] = +static const char init_setup[] = { 0x8E, /* length, prefetch on */ 0xC8, /* fifo to 8, monitor off */ @@ -683,7 +683,7 @@ static int init_i596_mem(struct net_device *dev) enable_irq(dev->irq); /* enable IRQs from LAN */ DEB(DEB_INIT, printk("%s: queuing CmdConfigure\n", dev->name)); - memcpy(lp->cf_cmd.i596_config, init_setup, 14); + memcpy(lp->cf_cmd.i596_config, init_setup, sizeof(init_setup)); lp->cf_cmd.cmd.command = CmdConfigure; CHECK_WBACK(lp, &(lp->cf_cmd), sizeof(struct cf_cmd)); i596_add_cmd(dev, &lp->cf_cmd.cmd); @@ -801,7 +801,6 @@ memory_squeeze: lp->stats.rx_dropped++; } else { - skb->dev = dev; if (!rx_in_place) { /* 16 byte align the data fields */ dma_sync_single_for_cpu(lp->dev, (dma_addr_t)WSWAPchar(rbd->b_data), PKT_BUF_SZ, DMA_FROM_DEVICE); @@ -1156,32 +1155,12 @@ static int __devinit i82596_probe(struct net_device *dev, dma_addr_t dma_addr; /* This lot is ensure things have been cache line aligned. */ - if (sizeof(struct i596_rfd) != 32) { - printk("82596: sizeof(struct i596_rfd) = %d\n", - (int)sizeof(struct i596_rfd)); - return -ENODEV; - } - if ((sizeof(struct i596_rbd) % 32) != 0) { - printk("82596: sizeof(struct i596_rbd) = %d\n", - (int)sizeof(struct i596_rbd)); - return -ENODEV; - } - if ((sizeof(struct tx_cmd) % 32) != 0) { - printk("82596: sizeof(struct tx_cmd) = %d\n", - (int)sizeof(struct tx_cmd)); - return -ENODEV; - } - if (sizeof(struct i596_tbd) != 32) { - printk("82596: sizeof(struct i596_tbd) = %d\n", - (int)sizeof(struct i596_tbd)); - return -ENODEV; - } + BUILD_BUG_ON(sizeof(struct i596_rfd) != 32); + BUILD_BUG_ON(sizeof(struct i596_rbd) & 31); + BUILD_BUG_ON(sizeof(struct tx_cmd) & 31); + BUILD_BUG_ON(sizeof(struct i596_tbd) != 32); #ifndef __LP64__ - if (sizeof(struct i596_private) > 4096) { - printk("82596: sizeof(struct i596_private) = %d\n", - (int)sizeof(struct i596_private)); - return -ENODEV; - } + BUILD_BUG_ON(sizeof(struct i596_private) > 4096); #endif if (!dev->base_addr || !dev->irq) diff --git a/drivers/net/lib8390.c b/drivers/net/lib8390.c index e726c06..5c86e73 100644 --- a/drivers/net/lib8390.c +++ b/drivers/net/lib8390.c @@ -722,7 +722,6 @@ static void ei_receive(struct net_device *dev) else { skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c index 2b739fd..6ba6ed2 100644 --- a/drivers/net/loopback.c +++ b/drivers/net/loopback.c @@ -75,8 +75,9 @@ static DEFINE_PER_CPU(struct pcpu_lstats, pcpu_lstats); #ifdef LOOPBACK_TSO static void emulate_large_send_offload(struct sk_buff *skb) { - struct iphdr *iph = skb->nh.iph; - struct tcphdr *th = (struct tcphdr*)(skb->nh.raw + (iph->ihl * 4)); + struct iphdr *iph = ip_hdr(skb); + struct tcphdr *th = (struct tcphdr *)(skb_network_header(skb) + + (iph->ihl * 4)); unsigned int doffset = (iph->ihl + th->doff) * 4; unsigned int mtu = skb_shinfo(skb)->gso_size + doffset; unsigned int offset = 0; @@ -90,10 +91,11 @@ static void emulate_large_send_offload(struct sk_buff *skb) if (!nskb) break; skb_reserve(nskb, 32); - nskb->mac.raw = nskb->data - 14; - nskb->nh.raw = nskb->data; - iph = nskb->nh.iph; - memcpy(nskb->data, skb->nh.raw, doffset); + skb_set_mac_header(nskb, -ETH_HLEN); + skb_reset_network_header(nskb); + iph = ip_hdr(nskb); + skb_copy_to_linear_data(nskb, skb_network_header(skb), + doffset); if (skb_copy_bits(skb, doffset + offset, nskb->data + doffset, @@ -108,7 +110,7 @@ static void emulate_large_send_offload(struct sk_buff *skb) memcpy(nskb->cb, skb->cb, sizeof(skb->cb)); nskb->pkt_type = skb->pkt_type; - th = (struct tcphdr*)(nskb->nh.raw + iph->ihl*4); + th = (struct tcphdr *)(skb_network_header(nskb) + iph->ihl * 4); iph->tot_len = htons(frag_size + doffset); iph->id = htons(id); iph->check = 0; @@ -137,7 +139,6 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) skb_orphan(skb); skb->protocol = eth_type_trans(skb,dev); - skb->dev = dev; #ifndef LOOPBACK_MUST_CHECKSUM skb->ip_summed = CHECKSUM_UNNECESSARY; #endif @@ -145,7 +146,7 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) #ifdef LOOPBACK_TSO if (skb_is_gso(skb)) { BUG_ON(skb->protocol != htons(ETH_P_IP)); - BUG_ON(skb->nh.iph->protocol != IPPROTO_TCP); + BUG_ON(ip_hdr(skb)->protocol != IPPROTO_TCP); emulate_large_send_offload(skb); return 0; @@ -163,11 +164,9 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev) return 0; } -static struct net_device_stats loopback_stats; - static struct net_device_stats *get_stats(struct net_device *dev) { - struct net_device_stats *stats = &loopback_stats; + struct net_device_stats *stats = &dev->stats; unsigned long bytes = 0; unsigned long packets = 0; int i; @@ -207,7 +206,6 @@ static const struct ethtool_ops loopback_ethtool_ops = { struct net_device loopback_dev = { .name = "lo", .get_stats = &get_stats, - .priv = &loopback_stats, .mtu = (16 * 1024) + 20 + 20 + 12, .hard_start_xmit = loopback_xmit, .hard_header = eth_header, diff --git a/drivers/net/lp486e.c b/drivers/net/lp486e.c index 177c502..5fc18da 100644 --- a/drivers/net/lp486e.c +++ b/drivers/net/lp486e.c @@ -676,7 +676,6 @@ i596_rx_one(struct net_device *dev, struct i596_private *lp, return 1; } - skb->dev = dev; memcpy(skb_put(skb,pkt_len), rfd->data, pkt_len); skb->protocol = eth_type_trans(skb,dev); diff --git a/drivers/net/mac89x0.c b/drivers/net/mac89x0.c index e960138..90e695d 100644 --- a/drivers/net/mac89x0.c +++ b/drivers/net/mac89x0.c @@ -530,7 +530,6 @@ net_rx(struct net_device *dev) return; } skb_put(skb, length); - skb->dev = dev; memcpy_fromio(skb->data, dev->mem_start + PP_RxFrame, length); diff --git a/drivers/net/macb.c b/drivers/net/macb.c index a41418b..0e04f7a 100644 --- a/drivers/net/macb.c +++ b/drivers/net/macb.c @@ -357,7 +357,6 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, } skb_reserve(skb, RX_OFFSET); - skb->dev = bp->dev; skb->ip_summed = CHECKSUM_NONE; skb_put(skb, len); @@ -368,9 +367,10 @@ static int macb_rx_frame(struct macb *bp, unsigned int first_frag, BUG_ON(frag != last_frag); frag_len = len - offset; } - memcpy(skb->data + offset, - bp->rx_buffers + (RX_BUFFER_SIZE * frag), - frag_len); + skb_copy_to_linear_data_offset(skb, offset, + (bp->rx_buffers + + (RX_BUFFER_SIZE * frag)), + frag_len); offset += RX_BUFFER_SIZE; bp->rx_ring[frag].addr &= ~MACB_BIT(RX_USED); wmb(); @@ -576,7 +576,8 @@ static int macb_start_xmit(struct sk_buff *skb, struct net_device *dev) int i; dev_dbg(&bp->pdev->dev, "start_xmit: len %u head %p data %p tail %p end %p\n", - skb->len, skb->head, skb->data, skb->tail, skb->end); + skb->len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb)); dev_dbg(&bp->pdev->dev, "data:"); for (i = 0; i < 16; i++) @@ -881,27 +882,15 @@ static struct net_device_stats *macb_get_stats(struct net_device *dev) static int macb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - ret = mii_ethtool_gset(&bp->mii, cmd); - spin_unlock_irqrestore(&bp->lock, flags); - return ret; + return mii_ethtool_gset(&bp->mii, cmd); } static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - ret = mii_ethtool_sset(&bp->mii, cmd); - spin_unlock_irqrestore(&bp->lock, flags); - return ret; + return mii_ethtool_sset(&bp->mii, cmd); } static void macb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -930,17 +919,11 @@ static struct ethtool_ops macb_ethtool_ops = { static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; if (!netif_running(dev)) return -EINVAL; - spin_lock_irqsave(&bp->lock, flags); - ret = generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL); - spin_unlock_irqrestore(&bp->lock, flags); - - return ret; + return generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL); } static ssize_t macb_mii_show(const struct device *_dev, char *buf, diff --git a/drivers/net/mace.c b/drivers/net/mace.c index 9ec24f0..b3bd623 100644 --- a/drivers/net/mace.c +++ b/drivers/net/mace.c @@ -939,7 +939,6 @@ static irqreturn_t mace_rxdma_intr(int irq, void *dev_id) else /* Ethernet header; mace includes FCS */ nb -= 8; skb_put(skb, nb); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); mp->stats.rx_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/macmace.c b/drivers/net/macmace.c index 5d541e8..27911c0 100644 --- a/drivers/net/macmace.c +++ b/drivers/net/macmace.c @@ -420,8 +420,7 @@ static int mace_xmit_start(struct sk_buff *skb, struct net_device *dev) mp->stats.tx_bytes += skb->len; /* We need to copy into our xmit buffer to take care of alignment and caching issues */ - - memcpy((void *) mp->tx_ring, skb->data, skb->len); + skb_copy_from_linear_data(skb, mp->tx_ring, skb->len); /* load the Tx DMA and fire it off */ @@ -621,7 +620,6 @@ static void mace_dma_rx_frame(struct net_device *dev, struct mace_frame *mf) skb_reserve(skb,2); memcpy(skb_put(skb, mf->len), mf->data, mf->len); - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/meth.c b/drivers/net/meth.c index d38b7c7..0343ea1 100644 --- a/drivers/net/meth.c +++ b/drivers/net/meth.c @@ -170,7 +170,7 @@ static int mdio_probe(struct meth_private *priv) static void meth_check_link(struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long mii_advertising = mdio_read(priv, 4); unsigned long mii_partner = mdio_read(priv, 5); unsigned long negotiated = mii_advertising & mii_partner; @@ -268,7 +268,7 @@ static void meth_free_rx_ring(struct meth_private *priv) int meth_reset(struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); /* Reset card */ mace->eth.mac_ctrl = SGI_MAC_RESET; @@ -310,7 +310,7 @@ int meth_reset(struct net_device *dev) */ static int meth_open(struct net_device *dev) { - struct meth_private *priv = dev->priv; + struct meth_private *priv = netdev_priv(dev); int ret; priv->phy_addr = -1; /* No PHY is known yet... */ @@ -354,7 +354,7 @@ out_free_tx_ring: static int meth_release(struct net_device *dev) { - struct meth_private *priv = dev->priv; + struct meth_private *priv = netdev_priv(dev); DPRINTK("Stopping queue\n"); netif_stop_queue(dev); /* can't transmit any more */ @@ -376,7 +376,7 @@ static void meth_rx(struct net_device* dev, unsigned long int_status) { struct sk_buff *skb; unsigned long status; - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long fifo_rptr = (int_status & METH_INT_RX_RPTR_MASK) >> 8; spin_lock(&priv->meth_lock); @@ -421,7 +421,6 @@ static void meth_rx(struct net_device* dev, unsigned long int_status) /* Write metadata, and then pass to the receive level */ skb_put(skb_c, len); priv->rx_skbs[priv->rx_write] = skb; - skb_c->dev = dev; skb_c->protocol = eth_type_trans(skb_c, dev); dev->last_rx = jiffies; priv->stats.rx_packets++; @@ -466,14 +465,14 @@ static void meth_rx(struct net_device* dev, unsigned long int_status) static int meth_tx_full(struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); return (priv->tx_count >= TX_RING_ENTRIES - 1); } static void meth_tx_cleanup(struct net_device* dev, unsigned long int_status) { - struct meth_private *priv = dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long status; struct sk_buff *skb; unsigned long rptr = (int_status&TX_INFO_RPTR) >> 16; @@ -536,7 +535,7 @@ static void meth_tx_cleanup(struct net_device* dev, unsigned long int_status) static void meth_error(struct net_device* dev, unsigned status) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); printk(KERN_WARNING "meth: error status: 0x%08x\n",status); /* check for errors too... */ @@ -570,7 +569,7 @@ static void meth_error(struct net_device* dev, unsigned status) static irqreturn_t meth_interrupt(int irq, void *dev_id) { struct net_device *dev = (struct net_device *)dev_id; - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long status; status = mace->eth.int_stat; @@ -609,7 +608,7 @@ static void meth_tx_short_prepare(struct meth_private *priv, desc->header.raw = METH_TX_CMD_INT_EN | (len-1) | ((128-len) << 16); /* maybe I should set whole thing to 0 first... */ - memcpy(desc->data.dt + (120 - len), skb->data, skb->len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - len), skb->len); if (skb->len < len) memset(desc->data.dt + 120 - len + skb->len, 0, len-skb->len); } @@ -627,8 +626,8 @@ static void meth_tx_1page_prepare(struct meth_private *priv, /* unaligned part */ if (unaligned_len) { - memcpy(desc->data.dt + (120 - unaligned_len), - skb->data, unaligned_len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), + unaligned_len); desc->header.raw |= (128 - unaligned_len) << 16; } @@ -653,8 +652,8 @@ static void meth_tx_2page_prepare(struct meth_private *priv, desc->header.raw = METH_TX_CMD_INT_EN | TX_CATBUF1 | TX_CATBUF2| (skb->len - 1); /* unaligned part */ if (unaligned_len){ - memcpy(desc->data.dt + (120 - unaligned_len), - skb->data, unaligned_len); + skb_copy_from_linear_data(skb, desc->data.dt + (120 - unaligned_len), + unaligned_len); desc->header.raw |= (128 - unaligned_len) << 16; } @@ -695,7 +694,7 @@ static void meth_add_to_tx_ring(struct meth_private *priv, struct sk_buff *skb) */ static int meth_tx(struct sk_buff *skb, struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long flags; spin_lock_irqsave(&priv->meth_lock, flags); @@ -726,7 +725,7 @@ static int meth_tx(struct sk_buff *skb, struct net_device *dev) */ static void meth_tx_timeout(struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); unsigned long flags; printk(KERN_WARNING "%s: transmit timed out\n", dev->name); @@ -778,7 +777,7 @@ static int meth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) */ static struct net_device_stats *meth_stats(struct net_device *dev) { - struct meth_private *priv = (struct meth_private *) dev->priv; + struct meth_private *priv = netdev_priv(dev); return &priv->stats; } @@ -807,7 +806,7 @@ static struct net_device *meth_init(void) dev->irq = MACE_ETHERNET_IRQ; dev->base_addr = (unsigned long)&mace->eth; - priv = (struct meth_private *) dev->priv; + priv = netdev_priv(dev); spin_lock_init(&priv->meth_lock); ret = register_netdev(dev); diff --git a/drivers/net/mii.c b/drivers/net/mii.c index 2912a34..9205605 100644 --- a/drivers/net/mii.c +++ b/drivers/net/mii.c @@ -33,6 +33,13 @@ #include <linux/ethtool.h> #include <linux/mii.h> +/** + * mii_ethtool_gset - get settings that are specified in @ecmd + * @mii: MII interface + * @ecmd: requested ethtool_cmd + * + * Returns 0 for success, negative on error. + */ int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { struct net_device *dev = mii->dev; @@ -114,6 +121,13 @@ int mii_ethtool_gset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) return 0; } +/** + * mii_ethtool_sset - set settings that are specified in @ecmd + * @mii: MII interface + * @ecmd: requested ethtool_cmd + * + * Returns 0 for success, negative on error. + */ int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) { struct net_device *dev = mii->dev; @@ -207,6 +221,10 @@ int mii_ethtool_sset(struct mii_if_info *mii, struct ethtool_cmd *ecmd) return 0; } +/** + * mii_check_gmii_support - check if the MII supports Gb interfaces + * @mii: the MII interface + */ int mii_check_gmii_support(struct mii_if_info *mii) { int reg; @@ -221,6 +239,12 @@ int mii_check_gmii_support(struct mii_if_info *mii) return 0; } +/** + * mii_link_ok - is link status up/ok + * @mii: the MII interface + * + * Returns 1 if the MII reports link status up/ok, 0 otherwise. + */ int mii_link_ok (struct mii_if_info *mii) { /* first, a dummy read, needed to latch some MII phys */ @@ -230,6 +254,12 @@ int mii_link_ok (struct mii_if_info *mii) return 0; } +/** + * mii_nway_restart - restart NWay (autonegotiation) for this interface + * @mii: the MII interface + * + * Returns 0 on success, negative on error. + */ int mii_nway_restart (struct mii_if_info *mii) { int bmcr; @@ -247,6 +277,14 @@ int mii_nway_restart (struct mii_if_info *mii) return r; } +/** + * mii_check_link - check MII link status + * @mii: MII interface + * + * If the link status changed (previous != current), call + * netif_carrier_on() if current link status is Up or call + * netif_carrier_off() if current link status is Down. + */ void mii_check_link (struct mii_if_info *mii) { int cur_link = mii_link_ok(mii); @@ -258,6 +296,15 @@ void mii_check_link (struct mii_if_info *mii) netif_carrier_off(mii->dev); } +/** + * mii_check_media - check the MII interface for a duplex change + * @mii: the MII interface + * @ok_to_print: OK to print link up/down messages + * @init_media: OK to save duplex mode in @mii + * + * Returns 1 if the duplex mode changed, 0 if not. + * If the media type is forced, always returns 0. + */ unsigned int mii_check_media (struct mii_if_info *mii, unsigned int ok_to_print, unsigned int init_media) @@ -326,6 +373,16 @@ unsigned int mii_check_media (struct mii_if_info *mii, return 0; /* duplex did not change */ } +/** + * generic_mii_ioctl - main MII ioctl interface + * @mii_if: the MII interface + * @mii_data: MII ioctl data structure + * @cmd: MII ioctl command + * @duplex_chg_out: pointer to @duplex_changed status if there was no + * ioctl error + * + * Returns 0 on success, negative on error. + */ int generic_mii_ioctl(struct mii_if_info *mii_if, struct mii_ioctl_data *mii_data, int cmd, unsigned int *duplex_chg_out) diff --git a/drivers/net/mipsnet.c b/drivers/net/mipsnet.c index f42b9e2..638a279 100644 --- a/drivers/net/mipsnet.c +++ b/drivers/net/mipsnet.c @@ -26,8 +26,6 @@ struct mipsnet_priv { struct net_device_stats stats; }; -static struct platform_device *mips_plat_dev; - static char mipsnet_string[] = "mipsnet"; /* @@ -101,7 +99,6 @@ static inline ssize_t mipsnet_get_fromdev(struct net_device *dev, size_t count) if (ioiocpy_frommipsnet(dev, skb_put(skb, len), len)) return -EFAULT; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -298,64 +295,17 @@ static struct device_driver mipsnet_driver = { .remove = __devexit_p(mipsnet_device_remove), }; -static void mipsnet_platform_release(struct device *device) -{ - struct platform_device *pldev; - - /* free device */ - pldev = to_platform_device(device); - kfree(pldev); -} - static int __init mipsnet_init_module(void) { - struct platform_device *pldev; int err; printk(KERN_INFO "MIPSNet Ethernet driver. Version: %s. " "(c)2005 MIPS Technologies, Inc.\n", MIPSNET_VERSION); - if (driver_register(&mipsnet_driver)) { + err = driver_register(&mipsnet_driver); + if (err) printk(KERN_ERR "Driver registration failed\n"); - err = -ENODEV; - goto out; - } - - if (!(pldev = kmalloc (sizeof (*pldev), GFP_KERNEL))) { - err = -ENOMEM; - goto out_unregister_driver; - } - - memset (pldev, 0, sizeof (*pldev)); - pldev->name = mipsnet_string; - pldev->id = 0; - pldev->dev.release = mipsnet_platform_release; - if (platform_device_register(pldev)) { - err = -ENODEV; - goto out_free_pldev; - } - - if (!pldev->dev.driver) { - /* - * The driver was not bound to this device, there was - * no hardware at this address. Unregister it, as the - * release fuction will take care of freeing the - * allocated structure - */ - platform_device_unregister (pldev); - } - - mips_plat_dev = pldev; - - return 0; - -out_free_pldev: - kfree(pldev); - -out_unregister_driver: - driver_unregister(&mipsnet_driver); -out: return err; } diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index d98e53e..1799eee 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -51,8 +51,8 @@ #include "mv643xx_eth.h" /* Static function declarations */ -static void eth_port_uc_addr_get(struct net_device *dev, - unsigned char *MacAddr); +static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr); +static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr); static void eth_port_set_multicast_list(struct net_device *); static void mv643xx_eth_port_enable_tx(unsigned int port_num, unsigned int queues); @@ -147,13 +147,13 @@ static void mv643xx_eth_rx_refill_descs(struct net_device *dev) int unaligned; while (mp->rx_desc_count < mp->rx_ring_size) { - skb = dev_alloc_skb(ETH_RX_SKB_SIZE + ETH_DMA_ALIGN); + skb = dev_alloc_skb(ETH_RX_SKB_SIZE + dma_get_cache_alignment()); if (!skb) break; mp->rx_desc_count++; - unaligned = (u32)skb->data & (ETH_DMA_ALIGN - 1); + unaligned = (u32)skb->data & (dma_get_cache_alignment() - 1); if (unaligned) - skb_reserve(skb, ETH_DMA_ALIGN - unaligned); + skb_reserve(skb, dma_get_cache_alignment() - unaligned); pkt_info.cmd_sts = ETH_RX_ENABLE_INTERRUPT; pkt_info.byte_cnt = ETH_RX_SKB_SIZE; pkt_info.buf_ptr = dma_map_single(NULL, skb->data, @@ -434,7 +434,6 @@ static int mv643xx_eth_receive_queue(struct net_device *dev, int budget) * received packet */ skb_put(skb, pkt_info.byte_cnt - 4); - skb->dev = dev; if (pkt_info.cmd_sts & ETH_LAYER_4_CHECKSUM_OK) { skb->ip_summed = CHECKSUM_UNNECESSARY; @@ -787,6 +786,12 @@ static int mv643xx_eth_open(struct net_device *dev) unsigned int size; int err; + /* Clear any pending ethernet port interrupts */ + mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + /* wait for previous write to complete */ + mv_read (MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num)); + err = request_irq(dev->irq, mv643xx_eth_int_handler, IRQF_SHARED | IRQF_SAMPLE_RANDOM, dev->name, dev); if (err) { @@ -875,10 +880,6 @@ static int mv643xx_eth_open(struct net_device *dev) mv643xx_eth_rx_refill_descs(dev); /* Fill RX ring with skb's */ - /* Clear any pending ethernet port interrupts */ - mv_write(MV643XX_ETH_INTERRUPT_CAUSE_REG(port_num), 0); - mv_write(MV643XX_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); - eth_port_start(dev); /* Interrupt Coalescing */ @@ -1160,15 +1161,15 @@ static void eth_tx_submit_descs_for_skb(struct mv643xx_private *mp, cmd_sts |= ETH_GEN_TCP_UDP_CHECKSUM | ETH_GEN_IP_V_4_CHECKSUM | - skb->nh.iph->ihl << ETH_TX_IHL_SHIFT; + ip_hdr(skb)->ihl << ETH_TX_IHL_SHIFT; - switch (skb->nh.iph->protocol) { + switch (ip_hdr(skb)->protocol) { case IPPROTO_UDP: cmd_sts |= ETH_UDP_FRAME; - desc->l4i_chk = skb->h.uh->check; + desc->l4i_chk = udp_hdr(skb)->check; break; case IPPROTO_TCP: - desc->l4i_chk = skb->h.th->check; + desc->l4i_chk = tcp_hdr(skb)->check; break; default: BUG(); @@ -1309,7 +1310,7 @@ static void mv643xx_init_ethtool_cmd(struct net_device *dev, int phy_address, static int mv643xx_eth_probe(struct platform_device *pdev) { struct mv643xx_eth_platform_data *pd; - int port_num = pdev->id; + int port_num; struct mv643xx_private *mp; struct net_device *dev; u8 *p; @@ -1319,6 +1320,12 @@ static int mv643xx_eth_probe(struct platform_device *pdev) int duplex = DUPLEX_HALF; int speed = 0; /* default to auto-negotiation */ + pd = pdev->dev.platform_data; + if (pd == NULL) { + printk(KERN_ERR "No mv643xx_eth_platform_data\n"); + return -ENODEV; + } + dev = alloc_etherdev(sizeof(struct mv643xx_private)); if (!dev) return -ENOMEM; @@ -1331,8 +1338,6 @@ static int mv643xx_eth_probe(struct platform_device *pdev) BUG_ON(!res); dev->irq = res->start; - mp->port_num = port_num; - dev->open = mv643xx_eth_open; dev->stop = mv643xx_eth_stop; dev->hard_start_xmit = mv643xx_eth_start_xmit; @@ -1373,39 +1378,38 @@ static int mv643xx_eth_probe(struct platform_device *pdev) spin_lock_init(&mp->lock); + port_num = mp->port_num = pd->port_number; + /* set default config values */ - eth_port_uc_addr_get(dev, dev->dev_addr); + eth_port_uc_addr_get(port_num, dev->dev_addr); mp->rx_ring_size = MV643XX_ETH_PORT_DEFAULT_RECEIVE_QUEUE_SIZE; mp->tx_ring_size = MV643XX_ETH_PORT_DEFAULT_TRANSMIT_QUEUE_SIZE; - pd = pdev->dev.platform_data; - if (pd) { - if (pd->mac_addr) - memcpy(dev->dev_addr, pd->mac_addr, 6); - - if (pd->phy_addr || pd->force_phy_addr) - ethernet_phy_set(port_num, pd->phy_addr); + if (is_valid_ether_addr(pd->mac_addr)) + memcpy(dev->dev_addr, pd->mac_addr, 6); - if (pd->rx_queue_size) - mp->rx_ring_size = pd->rx_queue_size; + if (pd->phy_addr || pd->force_phy_addr) + ethernet_phy_set(port_num, pd->phy_addr); - if (pd->tx_queue_size) - mp->tx_ring_size = pd->tx_queue_size; + if (pd->rx_queue_size) + mp->rx_ring_size = pd->rx_queue_size; - if (pd->tx_sram_size) { - mp->tx_sram_size = pd->tx_sram_size; - mp->tx_sram_addr = pd->tx_sram_addr; - } + if (pd->tx_queue_size) + mp->tx_ring_size = pd->tx_queue_size; - if (pd->rx_sram_size) { - mp->rx_sram_size = pd->rx_sram_size; - mp->rx_sram_addr = pd->rx_sram_addr; - } + if (pd->tx_sram_size) { + mp->tx_sram_size = pd->tx_sram_size; + mp->tx_sram_addr = pd->tx_sram_addr; + } - duplex = pd->duplex; - speed = pd->speed; + if (pd->rx_sram_size) { + mp->rx_sram_size = pd->rx_sram_size; + mp->rx_sram_addr = pd->rx_sram_addr; } + duplex = pd->duplex; + speed = pd->speed; + /* Hook up MII support for ethtool */ mp->mii.dev = dev; mp->mii.mdio_read = mv643xx_mdio_read; @@ -1509,9 +1513,23 @@ static int mv643xx_eth_shared_remove(struct platform_device *pdev) return 0; } +static void mv643xx_eth_shutdown(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct mv643xx_private *mp = netdev_priv(dev); + unsigned int port_num = mp->port_num; + + /* Mask all interrupts on ethernet port */ + mv_write(MV643XX_ETH_INTERRUPT_MASK_REG(port_num), 0); + mv_read (MV643XX_ETH_INTERRUPT_MASK_REG(port_num)); + + eth_port_reset(port_num); +} + static struct platform_driver mv643xx_eth_driver = { .probe = mv643xx_eth_probe, .remove = mv643xx_eth_remove, + .shutdown = mv643xx_eth_shutdown, .driver = { .name = MV643XX_ETH_NAME, }, @@ -1821,26 +1839,9 @@ static void eth_port_start(struct net_device *dev) } /* - * eth_port_uc_addr_set - This function Set the port Unicast address. - * - * DESCRIPTION: - * This function Set the port Ethernet MAC address. - * - * INPUT: - * unsigned int eth_port_num Port number. - * char * p_addr Address to be set - * - * OUTPUT: - * Set MAC address low and high registers. also calls - * eth_port_set_filter_table_entry() to set the unicast - * table with the proper information. - * - * RETURN: - * N/A. - * + * eth_port_uc_addr_set - Write a MAC address into the port's hw registers */ -static void eth_port_uc_addr_set(unsigned int eth_port_num, - unsigned char *p_addr) +static void eth_port_uc_addr_set(unsigned int port_num, unsigned char *p_addr) { unsigned int mac_h; unsigned int mac_l; @@ -1850,40 +1851,24 @@ static void eth_port_uc_addr_set(unsigned int eth_port_num, mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) | (p_addr[2] << 8) | (p_addr[3] << 0); - mv_write(MV643XX_ETH_MAC_ADDR_LOW(eth_port_num), mac_l); - mv_write(MV643XX_ETH_MAC_ADDR_HIGH(eth_port_num), mac_h); + mv_write(MV643XX_ETH_MAC_ADDR_LOW(port_num), mac_l); + mv_write(MV643XX_ETH_MAC_ADDR_HIGH(port_num), mac_h); - /* Accept frames of this address */ - table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(eth_port_num); + /* Accept frames with this address */ + table = MV643XX_ETH_DA_FILTER_UNICAST_TABLE_BASE(port_num); eth_port_set_filter_table_entry(table, p_addr[5] & 0x0f); } /* - * eth_port_uc_addr_get - This function retrieves the port Unicast address - * (MAC address) from the ethernet hw registers. - * - * DESCRIPTION: - * This function retrieves the port Ethernet MAC address. - * - * INPUT: - * unsigned int eth_port_num Port number. - * char *MacAddr pointer where the MAC address is stored - * - * OUTPUT: - * Copy the MAC address to the location pointed to by MacAddr - * - * RETURN: - * N/A. - * + * eth_port_uc_addr_get - Read the MAC address from the port's hw registers */ -static void eth_port_uc_addr_get(struct net_device *dev, unsigned char *p_addr) +static void eth_port_uc_addr_get(unsigned int port_num, unsigned char *p_addr) { - struct mv643xx_private *mp = netdev_priv(dev); unsigned int mac_h; unsigned int mac_l; - mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(mp->port_num)); - mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(mp->port_num)); + mac_h = mv_read(MV643XX_ETH_MAC_ADDR_HIGH(port_num)); + mac_l = mv_read(MV643XX_ETH_MAC_ADDR_LOW(port_num)); p_addr[0] = (mac_h >> 24) & 0xff; p_addr[1] = (mac_h >> 16) & 0xff; diff --git a/drivers/net/mv643xx_eth.h b/drivers/net/mv643xx_eth.h index 33c5faf..82f8c0c 100644 --- a/drivers/net/mv643xx_eth.h +++ b/drivers/net/mv643xx_eth.h @@ -9,6 +9,8 @@ #include <linux/mv643xx.h> +#include <asm/dma-mapping.h> + /* Checksum offload for Tx works for most packets, but * fails if previous packet sent did not use hw csum */ @@ -42,23 +44,12 @@ #define MAX_DESCS_PER_SKB 1 #endif -/* - * The MV643XX HW requires 8-byte alignment. However, when I/O - * is non-cache-coherent, we need to ensure that the I/O buffers - * we use don't share cache lines with other data. - */ -#if defined(CONFIG_DMA_NONCOHERENT) || defined(CONFIG_NOT_COHERENT_CACHE) -#define ETH_DMA_ALIGN L1_CACHE_BYTES -#else -#define ETH_DMA_ALIGN 8 -#endif - #define ETH_VLAN_HLEN 4 #define ETH_FCS_LEN 4 #define ETH_HW_IP_ALIGN 2 /* hw aligns IP header */ #define ETH_WRAPPER_LEN (ETH_HW_IP_ALIGN + ETH_HLEN + \ ETH_VLAN_HLEN + ETH_FCS_LEN) -#define ETH_RX_SKB_SIZE (dev->mtu + ETH_WRAPPER_LEN + ETH_DMA_ALIGN) +#define ETH_RX_SKB_SIZE (dev->mtu + ETH_WRAPPER_LEN + dma_get_cache_alignment()) #define ETH_RX_QUEUES_ENABLED (1 << 0) /* use only Q0 for receive */ #define ETH_TX_QUEUES_ENABLED (1 << 0) /* use only Q0 for transmit */ @@ -355,10 +346,6 @@ static void eth_port_init(struct mv643xx_private *mp); static void eth_port_reset(unsigned int eth_port_num); static void eth_port_start(struct net_device *dev); -/* Port MAC address routines */ -static void eth_port_uc_addr_set(unsigned int eth_port_num, - unsigned char *p_addr); - /* PHY and MIB routines */ static void ethernet_phy_reset(unsigned int eth_port_num); diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index 030924f..16e3c43 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -1,7 +1,7 @@ /************************************************************************* * myri10ge.c: Myricom Myri-10G Ethernet driver. * - * Copyright (C) 2005, 2006 Myricom, Inc. + * Copyright (C) 2005 - 2007 Myricom, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -16,17 +16,17 @@ * may be used to endorse or promote products derived from this software * without specific prior written permission. * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. * * * If the eeprom on your board is not recent enough, you will need to get a @@ -71,7 +71,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.2.0" +#define MYRI10GE_VERSION_STR "1.3.0-1.233" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -181,6 +181,7 @@ struct myri10ge_priv { int intr_coal_delay; __be32 __iomem *intr_coal_delay_ptr; int mtrr; + int wc_enabled; int wake_queue; int stop_queue; int down_cnt; @@ -195,6 +196,10 @@ struct myri10ge_priv { char *fw_name; char eeprom_strings[MYRI10GE_EEPROM_STRINGS_SIZE]; char fw_version[128]; + int fw_ver_major; + int fw_ver_minor; + int fw_ver_tiny; + int adopted_rx_filter_bug; u8 mac_addr[6]; /* eeprom mac address */ unsigned long serial_number; int vendor_specific_offset; @@ -229,7 +234,7 @@ static int myri10ge_msi = 1; /* enable msi by default */ module_param(myri10ge_msi, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(myri10ge_msi, "Enable Message Signalled Interrupts\n"); -static int myri10ge_intr_coal_delay = 25; +static int myri10ge_intr_coal_delay = 75; module_param(myri10ge_intr_coal_delay, int, S_IRUGO); MODULE_PARM_DESC(myri10ge_intr_coal_delay, "Interrupt coalescing delay\n"); @@ -274,7 +279,7 @@ static int myri10ge_fill_thresh = 256; module_param(myri10ge_fill_thresh, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(myri10ge_fill_thresh, "Number of empty rx slots allowed\n"); -static int myri10ge_wcfifo = 1; +static int myri10ge_wcfifo = 0; module_param(myri10ge_wcfifo, int, S_IRUGO); MODULE_PARM_DESC(myri10ge_wcfifo, "Enable WC Fifo when WC is enabled\n"); @@ -447,7 +452,6 @@ myri10ge_validate_firmware(struct myri10ge_priv *mgp, struct mcp_gen_header *hdr) { struct device *dev = &mgp->pdev->dev; - int major, minor; /* check firmware type */ if (ntohl(hdr->mcp_type) != MCP_TYPE_ETH) { @@ -458,9 +462,11 @@ myri10ge_validate_firmware(struct myri10ge_priv *mgp, /* save firmware version for ethtool */ strncpy(mgp->fw_version, hdr->version, sizeof(mgp->fw_version)); - sscanf(mgp->fw_version, "%d.%d", &major, &minor); + sscanf(mgp->fw_version, "%d.%d.%d", &mgp->fw_ver_major, + &mgp->fw_ver_minor, &mgp->fw_ver_tiny); - if (!(major == MXGEFW_VERSION_MAJOR && minor == MXGEFW_VERSION_MINOR)) { + if (!(mgp->fw_ver_major == MXGEFW_VERSION_MAJOR + && mgp->fw_ver_minor == MXGEFW_VERSION_MINOR)) { dev_err(dev, "Found firmware version %s\n", mgp->fw_version); dev_err(dev, "Driver needs %d.%d\n", MXGEFW_VERSION_MAJOR, MXGEFW_VERSION_MINOR); @@ -561,6 +567,18 @@ static int myri10ge_adopt_running_firmware(struct myri10ge_priv *mgp) memcpy_fromio(hdr, mgp->sram + hdr_offset, bytes); status = myri10ge_validate_firmware(mgp, hdr); kfree(hdr); + + /* check to see if adopted firmware has bug where adopting + * it will cause broadcasts to be filtered unless the NIC + * is kept in ALLMULTI mode */ + if (mgp->fw_ver_major == 1 && mgp->fw_ver_minor == 4 && + mgp->fw_ver_tiny >= 4 && mgp->fw_ver_tiny <= 11) { + mgp->adopted_rx_filter_bug = 1; + dev_warn(dev, "Adopting fw %d.%d.%d: " + "working around rx filter bug\n", + mgp->fw_ver_major, mgp->fw_ver_minor, + mgp->fw_ver_tiny); + } return status; } @@ -700,6 +718,8 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) int status; size_t bytes; u32 len; + struct page *dmatest_page; + dma_addr_t dmatest_bus; /* try to send a reset command to the card to see if it * is alive */ @@ -709,6 +729,11 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) dev_err(&mgp->pdev->dev, "failed reset\n"); return -ENXIO; } + dmatest_page = alloc_page(GFP_KERNEL); + if (!dmatest_page) + return -ENOMEM; + dmatest_bus = pci_map_page(mgp->pdev, dmatest_page, 0, PAGE_SIZE, + DMA_BIDIRECTIONAL); /* Now exchange information about interrupts */ @@ -747,8 +772,8 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) len = mgp->tx.boundary; - cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus); - cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus); + cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x10000; status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0); if (status == 0) @@ -757,8 +782,8 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) else dev_warn(&mgp->pdev->dev, "DMA read benchmark failed: %d\n", status); - cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus); - cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus); + cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x1; status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0); if (status == 0) @@ -768,8 +793,8 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) dev_warn(&mgp->pdev->dev, "DMA write benchmark failed: %d\n", status); - cmd.data0 = MYRI10GE_LOWPART_TO_U32(mgp->rx_done.bus); - cmd.data1 = MYRI10GE_HIGHPART_TO_U32(mgp->rx_done.bus); + cmd.data0 = MYRI10GE_LOWPART_TO_U32(dmatest_bus); + cmd.data1 = MYRI10GE_HIGHPART_TO_U32(dmatest_bus); cmd.data2 = len * 0x10001; status = myri10ge_send_cmd(mgp, MXGEFW_DMA_TEST, &cmd, 0); if (status == 0) @@ -779,6 +804,9 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) dev_warn(&mgp->pdev->dev, "DMA read/write benchmark failed: %d\n", status); + pci_unmap_page(mgp->pdev, dmatest_bus, PAGE_SIZE, DMA_BIDIRECTIONAL); + put_page(dmatest_page); + memset(mgp->rx_done.entry, 0, bytes); /* reset mcp/driver shared state back to 0 */ @@ -794,6 +822,8 @@ static int myri10ge_reset(struct myri10ge_priv *mgp) status = myri10ge_update_mac_address(mgp, mgp->dev->dev_addr); myri10ge_change_promisc(mgp, 0, 0); myri10ge_change_pause(mgp, mgp->pause); + if (mgp->adopted_rx_filter_bug) + (void)myri10ge_send_cmd(mgp, MXGEFW_ENABLE_ALLMULTI, &cmd, 1); return status; } @@ -849,7 +879,7 @@ myri10ge_rx_skb_build(struct sk_buff *skb, u8 * va, * skb_pull() (for ether_pad and eth_type_trans()) requires * the beginning of the packet in skb_headlen(), move it * manually */ - memcpy(skb->data, va, hlen); + skb_copy_to_linear_data(skb, va, hlen); skb_shinfo(skb)->frags[0].page_offset += hlen; skb_shinfo(skb)->frags[0].size -= hlen; skb->data_len -= hlen; @@ -870,9 +900,7 @@ myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, /* try to refill entire ring */ while (rx->fill_cnt != (rx->cnt + rx->mask + 1)) { idx = rx->fill_cnt & rx->mask; - - if ((bytes < MYRI10GE_ALLOC_SIZE / 2) && - (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE)) { + if (rx->page_offset + bytes <= MYRI10GE_ALLOC_SIZE) { /* we can use part of previous page */ get_page(rx->page); } else { @@ -903,6 +931,13 @@ myri10ge_alloc_rx_pages(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, /* start next packet on a cacheline boundary */ rx->page_offset += SKB_DATA_ALIGN(bytes); + +#if MYRI10GE_ALLOC_SIZE > 4096 + /* don't cross a 4KB boundary */ + if ((rx->page_offset >> 12) != + ((rx->page_offset + bytes - 1) >> 12)) + rx->page_offset = (rx->page_offset + 4096) & ~4095; +#endif rx->fill_cnt++; /* copy 8 descriptors to the firmware at a time */ @@ -985,7 +1020,6 @@ myri10ge_rx_done(struct myri10ge_priv *mgp, struct myri10ge_rx_buf *rx, skb_shinfo(skb)->nr_frags = 0; } skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; if (mgp->csum_flag) { if ((skb->protocol == htons(ETH_P_IP)) || @@ -1356,7 +1390,7 @@ myri10ge_get_ethtool_stats(struct net_device *netdev, data[i] = ((unsigned long *)&mgp->stats)[i]; data[i++] = (unsigned int)mgp->tx.boundary; - data[i++] = (unsigned int)(mgp->mtrr >= 0); + data[i++] = (unsigned int)mgp->wc_enabled; data[i++] = (unsigned int)mgp->pdev->irq; data[i++] = (unsigned int)mgp->msi_enabled; data[i++] = (unsigned int)mgp->read_dma; @@ -1437,6 +1471,8 @@ static int myri10ge_allocate_rings(struct net_device *dev) status = myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_SEND_RING_SIZE, &cmd, 0); tx_ring_size = cmd.data0; status |= myri10ge_send_cmd(mgp, MXGEFW_CMD_GET_RX_RING_SIZE, &cmd, 0); + if (status != 0) + return status; rx_ring_size = cmd.data0; tx_ring_entries = tx_ring_size / sizeof(struct mcp_kreq_ether_send); @@ -1444,6 +1480,8 @@ static int myri10ge_allocate_rings(struct net_device *dev) mgp->tx.mask = tx_ring_entries - 1; mgp->rx_small.mask = mgp->rx_big.mask = rx_ring_entries - 1; + status = -ENOMEM; + /* allocate the host shadow rings */ bytes = 8 + (MYRI10GE_MAX_SEND_DESC_TSO + 4) @@ -1716,7 +1754,7 @@ static int myri10ge_open(struct net_device *dev) goto abort_with_irq; } - if (myri10ge_wcfifo && mgp->mtrr >= 0) { + if (myri10ge_wcfifo && mgp->wc_enabled) { mgp->tx.wc_fifo = (u8 __iomem *) mgp->sram + MXGEFW_ETH_SEND_4; mgp->rx_small.wc_fifo = (u8 __iomem *) mgp->sram + MXGEFW_ETH_RECV_SMALL; @@ -1973,10 +2011,9 @@ again: mss = 0; max_segments = MXGEFW_MAX_SEND_DESC; - if (skb->len > (dev->mtu + ETH_HLEN)) { + if (skb_is_gso(skb)) { mss = skb_shinfo(skb)->gso_size; - if (mss != 0) - max_segments = MYRI10GE_MAX_SEND_DESC_TSO; + max_segments = MYRI10GE_MAX_SEND_DESC_TSO; } if ((unlikely(avail < max_segments))) { @@ -1992,7 +2029,7 @@ again: odd_flag = 0; flags = (MXGEFW_FLAGS_NO_TSO | MXGEFW_FLAGS_FIRST); if (likely(skb->ip_summed == CHECKSUM_PARTIAL)) { - cksum_offset = (skb->h.raw - skb->data); + cksum_offset = skb_transport_offset(skb); pseudo_hdr_offset = cksum_offset + skb->csum_offset; /* If the headers are excessively large, then we must * fall back to a software checksum */ @@ -2017,7 +2054,7 @@ again: * send loop that we are still in the * header portion of the TSO packet. * TSO header must be at most 134 bytes long */ - cum_len = -((skb->h.raw - skb->data) + (skb->h.th->doff << 2)); + cum_len = -(skb_transport_offset(skb) + tcp_hdrlen(skb)); /* for TSO, pseudo_hdr_offset holds mss. * The firmware figures out where to put @@ -2239,7 +2276,7 @@ static void myri10ge_set_multicast_list(struct net_device *dev) myri10ge_change_promisc(mgp, dev->flags & IFF_PROMISC, 1); /* This firmware is known to not support multicast */ - if (!mgp->fw_multicast_support) + if (!mgp->fw_multicast_support || mgp->adopted_rx_filter_bug) return; /* Disable multicast filtering */ @@ -2449,6 +2486,12 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp) #define PCI_DEVICE_ID_INTEL_E5000_PCIE23 0x25f7 #define PCI_DEVICE_ID_INTEL_E5000_PCIE47 0x25fa +#define PCI_DEVICE_ID_INTEL_6300ESB_PCIEE1 0x3510 +#define PCI_DEVICE_ID_INTEL_6300ESB_PCIEE4 0x351b +#define PCI_DEVICE_ID_INTEL_E3000_PCIE 0x2779 +#define PCI_DEVICE_ID_INTEL_E3010_PCIE 0x277a +#define PCI_DEVICE_ID_SERVERWORKS_HT2100_PCIE_FIRST 0x140 +#define PCI_DEVICE_ID_SERVERWORKS_HT2100_PCIE_LAST 0x142 static void myri10ge_select_firmware(struct myri10ge_priv *mgp) { @@ -2480,6 +2523,24 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) ((bridge->vendor == PCI_VENDOR_ID_SERVERWORKS && bridge->device == PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE) + /* ServerWorks HT2100 */ + || (bridge->vendor == PCI_VENDOR_ID_SERVERWORKS + && bridge->device >= + PCI_DEVICE_ID_SERVERWORKS_HT2100_PCIE_FIRST + && bridge->device <= + PCI_DEVICE_ID_SERVERWORKS_HT2100_PCIE_LAST) + /* All Intel E3000/E3010 PCIE ports */ + || (bridge->vendor == PCI_VENDOR_ID_INTEL + && (bridge->device == + PCI_DEVICE_ID_INTEL_E3000_PCIE + || bridge->device == + PCI_DEVICE_ID_INTEL_E3010_PCIE)) + /* All Intel 6310/6311/6321ESB PCIE ports */ + || (bridge->vendor == PCI_VENDOR_ID_INTEL + && bridge->device >= + PCI_DEVICE_ID_INTEL_6300ESB_PCIEE1 + && bridge->device <= + PCI_DEVICE_ID_INTEL_6300ESB_PCIEE4) /* All Intel E5000 PCIE ports */ || (bridge->vendor == PCI_VENDOR_ID_INTEL && bridge->device >= @@ -2491,6 +2552,12 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp) bridge->vendor, bridge->device); mgp->tx.boundary = 4096; mgp->fw_name = myri10ge_fw_aligned; + } else if (bridge && + bridge->vendor == PCI_VENDOR_ID_SGI && + bridge->device == 0x4002 /* TIOCE pcie-port */ ) { + /* this pcie bridge does not support 4K rdma request */ + mgp->tx.boundary = 2048; + mgp->fw_name = myri10ge_fw_aligned; } } else { if (myri10ge_force_firmware == 1) { @@ -2811,9 +2878,12 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) mgp->board_span = pci_resource_len(pdev, 0); mgp->iomem_base = pci_resource_start(pdev, 0); mgp->mtrr = -1; + mgp->wc_enabled = 0; #ifdef CONFIG_MTRR mgp->mtrr = mtrr_add(mgp->iomem_base, mgp->board_span, MTRR_TYPE_WRCOMB, 1); + if (mgp->mtrr >= 0) + mgp->wc_enabled = 1; #endif /* Hack. need to get rid of these magic numbers */ mgp->sram_size = @@ -2908,7 +2978,7 @@ static int myri10ge_probe(struct pci_dev *pdev, const struct pci_device_id *ent) dev_info(dev, "%s IRQ %d, tx bndry %d, fw %s, WC %s\n", (mgp->msi_enabled ? "MSI" : "xPIC"), netdev->irq, mgp->tx.boundary, mgp->fw_name, - (mgp->mtrr >= 0 ? "Enabled" : "Disabled")); + (mgp->wc_enabled ? "Enabled" : "Disabled")); return 0; diff --git a/drivers/net/myri_sbus.c b/drivers/net/myri_sbus.c index ee26ef5..13444da 100644 --- a/drivers/net/myri_sbus.c +++ b/drivers/net/myri_sbus.c @@ -368,7 +368,7 @@ static __be16 myri_type_trans(struct sk_buff *skb, struct net_device *dev) struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw = (((unsigned char *)skb->data) + MYRI_PAD_LEN); + skb_set_mac_header(skb, MYRI_PAD_LEN); skb_pull(skb, dev->hard_header_len); eth = eth_hdr(skb); @@ -502,7 +502,7 @@ static void myri_rx(struct myri_eth *mp, struct net_device *dev) copy_skb->dev = dev; DRX(("resv_and_put ")); skb_put(copy_skb, len); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); /* Reuse original ring buffer. */ DRX(("reuse ")); diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c index ffa0afd..a8d7ff2 100644 --- a/drivers/net/natsemi.c +++ b/drivers/net/natsemi.c @@ -244,6 +244,9 @@ enum { MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */ }; +enum { + NATSEMI_FLAG_IGNORE_PHY = 0x1, +}; /* array of board data directly indexed by pci_tbl[x].driver_data */ static const struct { @@ -251,11 +254,13 @@ static const struct { unsigned long flags; unsigned int eeprom_size; } natsemi_pci_info[] __devinitdata = { + { "Aculab E1/T1 PMXc cPCI carrier card", NATSEMI_FLAG_IGNORE_PHY, 128 }, { "NatSemi DP8381[56]", 0, 24 }, }; static const struct pci_device_id natsemi_pci_tbl[] __devinitdata = { - { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { PCI_VENDOR_ID_NS, 0x0020, 0x12d9, 0x000c, 0, 0, 0 }, + { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 }, { } /* terminate list */ }; MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl); @@ -568,6 +573,8 @@ struct netdev_private { u32 intr_status; /* Do not touch the nic registers */ int hands_off; + /* Don't pay attention to the reported link state. */ + int ignore_phy; /* external phy that is used: only valid if dev->if_port != PORT_TP */ int mii; int phy_addr_external; @@ -696,7 +703,10 @@ static void __devinit natsemi_init_media (struct net_device *dev) struct netdev_private *np = netdev_priv(dev); u32 tmp; - netif_carrier_off(dev); + if (np->ignore_phy) + netif_carrier_on(dev); + else + netif_carrier_off(dev); /* get the initial settings from hardware */ tmp = mdio_read(dev, MII_BMCR); @@ -806,8 +816,13 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, np->hands_off = 0; np->intr_status = 0; np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size; + if (natsemi_pci_info[chip_idx].flags & NATSEMI_FLAG_IGNORE_PHY) + np->ignore_phy = 1; + else + np->ignore_phy = 0; /* Initial port: + * - If configured to ignore the PHY set up for external. * - If the nic was configured to use an external phy and if find_mii * finds a phy: use external port, first phy that replies. * - Otherwise: internal port. @@ -815,7 +830,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, * The address would be used to access a phy over the mii bus, but * the internal phy is accessed through mapped registers. */ - if (readl(ioaddr + ChipConfig) & CfgExtPhy) + if (np->ignore_phy || readl(ioaddr + ChipConfig) & CfgExtPhy) dev->if_port = PORT_MII; else dev->if_port = PORT_TP; @@ -825,7 +840,9 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, if (dev->if_port != PORT_TP) { np->phy_addr_external = find_mii(dev); - if (np->phy_addr_external == PHY_ADDR_NONE) { + /* If we're ignoring the PHY it doesn't matter if we can't + * find one. */ + if (!np->ignore_phy && np->phy_addr_external == PHY_ADDR_NONE) { dev->if_port = PORT_TP; np->phy_addr_external = PHY_ADDR_INTERNAL; } @@ -891,6 +908,8 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev, printk("%02x, IRQ %d", dev->dev_addr[i], irq); if (dev->if_port == PORT_TP) printk(", port TP.\n"); + else if (np->ignore_phy) + printk(", port MII, ignoring PHY\n"); else printk(", port MII, phy ad %d.\n", np->phy_addr_external); } @@ -1571,9 +1590,13 @@ static void check_link(struct net_device *dev) { struct netdev_private *np = netdev_priv(dev); void __iomem * ioaddr = ns_ioaddr(dev); - int duplex; + int duplex = np->duplex; u16 bmsr; + /* If we are ignoring the PHY then don't try reading it. */ + if (np->ignore_phy) + goto propagate_state; + /* The link status field is latched: it remains low after a temporary * link failure until it's read. We need the current link status, * thus read twice. @@ -1585,7 +1608,7 @@ static void check_link(struct net_device *dev) if (netif_carrier_ok(dev)) { if (netif_msg_link(np)) printk(KERN_NOTICE "%s: link down.\n", - dev->name); + dev->name); netif_carrier_off(dev); undo_cable_magic(dev); } @@ -1609,6 +1632,7 @@ static void check_link(struct net_device *dev) duplex = 1; } +propagate_state: /* if duplex is set then bit 28 must be set, too */ if (duplex ^ !!(np->rx_config & RxAcceptTx)) { if (netif_msg_link(np)) @@ -1688,7 +1712,7 @@ static void init_registers(struct net_device *dev) /* Enable interrupts by setting the interrupt mask. */ writel(DEFAULT_INTR, ioaddr + IntrMask); - writel(1, ioaddr + IntrEnable); + natsemi_irq_enable(dev); writel(RxOn | TxOn, ioaddr + ChipCmd); writel(StatsClear, ioaddr + StatsCtrl); /* Clear Stats */ @@ -2000,6 +2024,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) struct netdev_private *np = netdev_priv(dev); void __iomem * ioaddr = ns_ioaddr(dev); unsigned entry; + unsigned long flags; /* Note: Ordering is important here, set the field with the "ownership" bit last, and only then increment cur_tx. */ @@ -2013,7 +2038,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]); - spin_lock_irq(&np->lock); + spin_lock_irqsave(&np->lock, flags); if (!np->hands_off) { np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len); @@ -2032,7 +2057,7 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) dev_kfree_skb_irq(skb); np->stats.tx_dropped++; } - spin_unlock_irq(&np->lock); + spin_unlock_irqrestore(&np->lock, flags); dev->trans_start = jiffies; @@ -2094,28 +2119,35 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) struct netdev_private *np = netdev_priv(dev); void __iomem * ioaddr = ns_ioaddr(dev); - if (np->hands_off) + /* Reading IntrStatus automatically acknowledges so don't do + * that while interrupts are disabled, (for example, while a + * poll is scheduled). */ + if (np->hands_off || !readl(ioaddr + IntrEnable)) return IRQ_NONE; - /* Reading automatically acknowledges. */ np->intr_status = readl(ioaddr + IntrStatus); + if (!np->intr_status) + return IRQ_NONE; + if (netif_msg_intr(np)) printk(KERN_DEBUG "%s: Interrupt, status %#08x, mask %#08x.\n", dev->name, np->intr_status, readl(ioaddr + IntrMask)); - if (!np->intr_status) - return IRQ_NONE; - prefetch(&np->rx_skbuff[np->cur_rx % RX_RING_SIZE]); if (netif_rx_schedule_prep(dev)) { /* Disable interrupts and register for poll */ natsemi_irq_disable(dev); __netif_rx_schedule(dev); - } + } else + printk(KERN_WARNING + "%s: Ignoring interrupt, status %#08x, mask %#08x.\n", + dev->name, np->intr_status, + readl(ioaddr + IntrMask)); + return IRQ_HANDLED; } @@ -2131,6 +2163,20 @@ static int natsemi_poll(struct net_device *dev, int *budget) int work_done = 0; do { + if (netif_msg_intr(np)) + printk(KERN_DEBUG + "%s: Poll, status %#08x, mask %#08x.\n", + dev->name, np->intr_status, + readl(ioaddr + IntrMask)); + + /* netdev_rx() may read IntrStatus again if the RX state + * machine falls over so do it first. */ + if (np->intr_status & + (IntrRxDone | IntrRxIntr | RxStatusFIFOOver | + IntrRxErr | IntrRxOverrun)) { + netdev_rx(dev, &work_done, work_to_do); + } + if (np->intr_status & (IntrTxDone | IntrTxIntr | IntrTxIdle | IntrTxErr)) { spin_lock(&np->lock); @@ -2142,12 +2188,6 @@ static int natsemi_poll(struct net_device *dev, int *budget) if (np->intr_status & IntrAbnormalSummary) netdev_error(dev, np->intr_status); - if (np->intr_status & - (IntrRxDone | IntrRxIntr | RxStatusFIFOOver | - IntrRxErr | IntrRxOverrun)) { - netdev_rx(dev, &work_done, work_to_do); - } - *budget -= work_done; dev->quota -= work_done; @@ -2198,6 +2238,8 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) pkt_len = (desc_status & DescSizeMask) - 4; if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){ if (desc_status & DescMore) { + unsigned long flags; + if (netif_msg_rx_err(np)) printk(KERN_WARNING "%s: Oversized(?) Ethernet " @@ -2212,12 +2254,12 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) * reset procedure documented in * AN-1287. */ - spin_lock_irq(&np->lock); + spin_lock_irqsave(&np->lock, flags); reset_rx(dev); reinit_rx(dev); writel(np->ring_dma, ioaddr + RxRingPtr); check_link(dev); - spin_unlock_irq(&np->lock); + spin_unlock_irqrestore(&np->lock, flags); /* We'll enable RX on exit from this * function. */ @@ -2247,7 +2289,6 @@ static void netdev_rx(struct net_device *dev, int *work_done, int work_to_do) * without copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + RX_OFFSET)) != NULL) { - skb->dev = dev; /* 16 byte align the IP header */ skb_reserve(skb, RX_OFFSET); pci_dma_sync_single_for_cpu(np->pci_dev, @@ -2819,6 +2860,15 @@ static int netdev_set_ecmd(struct net_device *dev, struct ethtool_cmd *ecmd) } /* + * If we're ignoring the PHY then autoneg and the internal + * transciever are really not going to work so don't let the + * user select them. + */ + if (np->ignore_phy && (ecmd->autoneg == AUTONEG_ENABLE || + ecmd->port == PORT_TP)) + return -EINVAL; + + /* * maxtxpkt, maxrxpkt: ignored for now. * * transceiver: @@ -3024,7 +3074,7 @@ static void enable_wol_mode(struct net_device *dev, int enable_intr) * Could be used to send a netlink message. */ writel(WOLPkt | LinkChange, ioaddr + IntrMask); - writel(1, ioaddr + IntrEnable); + natsemi_irq_enable(dev); } } @@ -3155,7 +3205,7 @@ static int natsemi_suspend (struct pci_dev *pdev, pm_message_t state) disable_irq(dev->irq); spin_lock_irq(&np->lock); - writel(0, ioaddr + IntrEnable); + natsemi_irq_disable(dev); np->hands_off = 1; natsemi_stop_rxtx(dev); netif_stop_queue(dev); diff --git a/drivers/net/netx-eth.c b/drivers/net/netx-eth.c index a53644f..2b8da0a 100644 --- a/drivers/net/netx-eth.c +++ b/drivers/net/netx-eth.c @@ -168,7 +168,6 @@ static void netx_eth_receive(struct net_device *ndev) FIFO_PTR_SEGMENT(seg) | FIFO_PTR_FRAMENO(frameno)); ndev->last_rx = jiffies; - skb->dev = ndev; skb->protocol = eth_type_trans(skb, ndev); netif_rx(skb); priv->stats.rx_packets++; diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index 3f3896e..ad6688e 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -64,14 +64,16 @@ #include "netxen_nic_hw.h" #define _NETXEN_NIC_LINUX_MAJOR 3 -#define _NETXEN_NIC_LINUX_MINOR 3 -#define _NETXEN_NIC_LINUX_SUBVERSION 3 -#define NETXEN_NIC_LINUX_VERSIONID "3.3.3" +#define _NETXEN_NIC_LINUX_MINOR 4 +#define _NETXEN_NIC_LINUX_SUBVERSION 2 +#define NETXEN_NIC_LINUX_VERSIONID "3.4.2" #define NUM_FLASH_SECTORS (64) #define FLASH_SECTOR_SIZE (64 * 1024) #define FLASH_TOTAL_SIZE (NUM_FLASH_SECTORS * FLASH_SECTOR_SIZE) +#define PHAN_VENDOR_ID 0x4040 + #define RCV_DESC_RINGSIZE \ (sizeof(struct rcv_desc) * adapter->max_rx_desc_count) #define STATUS_DESC_RINGSIZE \ @@ -82,7 +84,7 @@ (sizeof(struct netxen_cmd_buffer) * adapter->max_tx_desc_count) #define RCV_BUFFSIZE \ (sizeof(struct netxen_rx_buffer) * rcv_desc->max_rx_desc_count) -#define find_diff_among(a,b,range) ((a)<(b)?((b)-(a)):((b)+(range)-(a))) +#define find_diff_among(a,b,range) ((a)<=(b)?((b)-(a)):((b)+(range)-(a))) #define NETXEN_NETDEV_STATUS 0x1 #define NETXEN_RCV_PRODUCER_OFFSET 0 @@ -129,8 +131,8 @@ extern struct workqueue_struct *netxen_workq; #define FIRST_PAGE_GROUP_START 0 #define FIRST_PAGE_GROUP_END 0x100000 -#define SECOND_PAGE_GROUP_START 0x4000000 -#define SECOND_PAGE_GROUP_END 0x66BC000 +#define SECOND_PAGE_GROUP_START 0x6000000 +#define SECOND_PAGE_GROUP_END 0x68BC000 #define THIRD_PAGE_GROUP_START 0x70E4000 #define THIRD_PAGE_GROUP_END 0x8000000 @@ -203,6 +205,8 @@ enum { #define MAX_CMD_DESCRIPTORS 1024 #define MAX_RCV_DESCRIPTORS 16384 +#define MAX_CMD_DESCRIPTORS_HOST (MAX_CMD_DESCRIPTORS / 4) +#define MAX_RCV_DESCRIPTORS_1G (MAX_RCV_DESCRIPTORS / 4) #define MAX_JUMBO_RCV_DESCRIPTORS 1024 #define MAX_LRO_RCV_DESCRIPTORS 64 #define MAX_RCVSTATUS_DESCRIPTORS MAX_RCV_DESCRIPTORS @@ -228,8 +232,11 @@ enum { (((index) + (count)) & ((length) - 1)) #define MPORT_SINGLE_FUNCTION_MODE 0x1111 +#define MPORT_MULTI_FUNCTION_MODE 0x2222 +#include "netxen_nic_phan_reg.h" extern unsigned long long netxen_dma_mask; +extern unsigned long last_schedule_time; /* * NetXen host-peg signal message structure @@ -252,7 +259,7 @@ typedef u32 netxen_ctx_msg; #define netxen_set_msg_ctxid(config_word, val) \ ((config_word) &= ~(0x3ff<<18), (config_word) |= (val & 0x3ff) << 18) #define netxen_set_msg_opcode(config_word, val) \ - ((config_word) &= ~(0xf<<24), (config_word) |= (val & 0xf) << 24) + ((config_word) &= ~(0xf<<28), (config_word) |= (val & 0xf) << 28) struct netxen_rcv_context { __le64 rcv_ring_addr; @@ -297,20 +304,22 @@ struct netxen_ring_ctx { #define netxen_set_cmd_desc_port(cmd_desc, var) \ ((cmd_desc)->port_ctxid |= ((var) & 0x0F)) +#define netxen_set_cmd_desc_ctxid(cmd_desc, var) \ + ((cmd_desc)->port_ctxid |= ((var) & 0xF0)) #define netxen_set_cmd_desc_flags(cmd_desc, val) \ ((cmd_desc)->flags_opcode &= ~cpu_to_le16(0x7f), \ (cmd_desc)->flags_opcode |= cpu_to_le16((val) & 0x7f)) #define netxen_set_cmd_desc_opcode(cmd_desc, val) \ ((cmd_desc)->flags_opcode &= ~cpu_to_le16(0x3f<<7), \ - (cmd_desc)->flags_opcode |= cpu_to_le16((val) & (0x3f<<7))) + (cmd_desc)->flags_opcode |= cpu_to_le16(((val & 0x3f)<<7))) #define netxen_set_cmd_desc_num_of_buff(cmd_desc, val) \ ((cmd_desc)->num_of_buffers_total_length &= ~cpu_to_le32(0xff), \ (cmd_desc)->num_of_buffers_total_length |= cpu_to_le32((val) & 0xff)) #define netxen_set_cmd_desc_totallength(cmd_desc, val) \ - ((cmd_desc)->num_of_buffers_total_length &= cpu_to_le32(0xff), \ - (cmd_desc)->num_of_buffers_total_length |= cpu_to_le32(val << 24)) + ((cmd_desc)->num_of_buffers_total_length &= ~cpu_to_le32(0xffffff00), \ + (cmd_desc)->num_of_buffers_total_length |= cpu_to_le32(val << 8)) #define netxen_get_cmd_desc_opcode(cmd_desc) \ ((le16_to_cpu((cmd_desc)->flags_opcode) >> 7) & 0x003F) @@ -439,7 +448,7 @@ struct status_desc { /* Bit pattern: 0-6 lro_count indicates frag sequence, 7 last_frag indicates last frag */ u8 lro; -} __attribute__ ((aligned(8))); +} __attribute__ ((aligned(16))); enum { NETXEN_RCV_PEG_0 = 0, @@ -700,10 +709,8 @@ extern char netxen_nic_driver_name[]; #else #define DPRINTK(klevel, fmt, args...) do { \ printk(KERN_##klevel PFX "%s: %s: " fmt, __FUNCTION__,\ - (adapter != NULL && \ - adapter->port[0] != NULL && \ - adapter->port[0]->netdev != NULL) ? \ - adapter->port[0]->netdev->name : NULL, \ + (adapter != NULL && adapter->netdev != NULL) ? \ + adapter->netdev->name : NULL, \ ## args); } while(0) #endif @@ -719,6 +726,18 @@ struct netxen_skb_frag { u32 length; }; +#define _netxen_set_bits(config_word, start, bits, val) {\ + unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start));\ + unsigned long long __tvalue = (val); \ + (config_word) &= ~__tmask; \ + (config_word) |= (((__tvalue) << (start)) & __tmask); \ +} + +#define _netxen_clear_bits(config_word, start, bits) {\ + unsigned long long __tmask = (((1ULL << (bits)) - 1) << (start)); \ + (config_word) &= ~__tmask; \ +} + /* Following defines are for the state of the buffers */ #define NETXEN_BUFFER_FREE 0 #define NETXEN_BUFFER_BUSY 1 @@ -763,6 +782,8 @@ struct netxen_hardware_context { void __iomem *pci_base0; void __iomem *pci_base1; void __iomem *pci_base2; + unsigned long first_page_group_end; + unsigned long first_page_group_start; void __iomem *db_base; unsigned long db_len; @@ -777,6 +798,7 @@ struct netxen_hardware_context { struct pci_dev *cmd_desc_pdev; dma_addr_t cmd_desc_phys_addr; struct netxen_adapter *adapter; + int pci_func; }; #define RCV_RING_LRO RCV_DESC_LRO @@ -785,17 +807,27 @@ struct netxen_hardware_context { #define ETHERNET_FCS_SIZE 4 struct netxen_adapter_stats { - u64 ints; - u64 hostints; - u64 otherints; - u64 process_rcv; - u64 process_xmit; - u64 noxmitdone; - u64 xmitcsummed; - u64 post_called; - u64 posted; - u64 lastposted; - u64 goodskbposts; + u64 rcvdbadskb; + u64 xmitcalled; + u64 xmitedframes; + u64 xmitfinished; + u64 badskblen; + u64 nocmddescriptor; + u64 polled; + u64 uphappy; + u64 updropped; + u64 uplcong; + u64 uphcong; + u64 upmcong; + u64 updunno; + u64 skbfreed; + u64 txdropped; + u64 txnullskb; + u64 csummed; + u64 no_rcv; + u64 rxbytes; + u64 txbytes; + u64 ints; }; /* @@ -843,13 +875,20 @@ struct netxen_dummy_dma { struct netxen_adapter { struct netxen_hardware_context ahw; - int port_count; /* Number of configured ports */ - int active_ports; /* Number of open ports */ - struct netxen_port *port[NETXEN_MAX_PORTS]; /* ptr to each port */ + + struct netxen_adapter *master; + struct net_device *netdev; + struct pci_dev *pdev; + struct net_device_stats net_stats; + unsigned char mac_addr[ETH_ALEN]; + int mtu; + int portnum; + spinlock_t tx_lock; spinlock_t lock; struct work_struct watchdog_task; struct timer_list watchdog_timer; + struct work_struct tx_timeout_task; u32 curr_window; @@ -872,6 +911,15 @@ struct netxen_adapter { u32 temp; struct netxen_adapter_stats stats; + + u16 portno; + u16 link_speed; + u16 link_duplex; + u16 state; + u16 link_autoneg; + int rcsum; + int status; + spinlock_t stats_lock; struct netxen_cmd_buffer *cmd_buf_arr; /* Command buffers for xmit */ @@ -888,65 +936,23 @@ struct netxen_adapter { struct netxen_ring_ctx *ctx_desc; struct pci_dev *ctx_desc_pdev; dma_addr_t ctx_desc_phys_addr; - int (*enable_phy_interrupts) (struct netxen_adapter *, int); - int (*disable_phy_interrupts) (struct netxen_adapter *, int); + int (*enable_phy_interrupts) (struct netxen_adapter *); + int (*disable_phy_interrupts) (struct netxen_adapter *); void (*handle_phy_intr) (struct netxen_adapter *); - int (*macaddr_set) (struct netxen_port *, netxen_ethernet_macaddr_t); - int (*set_mtu) (struct netxen_port *, int); - int (*set_promisc) (struct netxen_adapter *, int, - netxen_niu_prom_mode_t); - int (*unset_promisc) (struct netxen_adapter *, int, - netxen_niu_prom_mode_t); - int (*phy_read) (struct netxen_adapter *, long phy, long reg, u32 *); - int (*phy_write) (struct netxen_adapter *, long phy, long reg, u32 val); + int (*macaddr_set) (struct netxen_adapter *, netxen_ethernet_macaddr_t); + int (*set_mtu) (struct netxen_adapter *, int); + int (*set_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t); + int (*unset_promisc) (struct netxen_adapter *, netxen_niu_prom_mode_t); + int (*phy_read) (struct netxen_adapter *, long reg, u32 *); + int (*phy_write) (struct netxen_adapter *, long reg, u32 val); int (*init_port) (struct netxen_adapter *, int); void (*init_niu) (struct netxen_adapter *); - int (*stop_port) (struct netxen_adapter *, int); + int (*stop_port) (struct netxen_adapter *); }; /* netxen_adapter structure */ /* Max number of xmit producer threads that can run simultaneously */ #define MAX_XMIT_PRODUCERS 16 -struct netxen_port_stats { - u64 rcvdbadskb; - u64 xmitcalled; - u64 xmitedframes; - u64 xmitfinished; - u64 badskblen; - u64 nocmddescriptor; - u64 polled; - u64 uphappy; - u64 updropped; - u64 uplcong; - u64 uphcong; - u64 upmcong; - u64 updunno; - u64 skbfreed; - u64 txdropped; - u64 txnullskb; - u64 csummed; - u64 no_rcv; - u64 rxbytes; - u64 txbytes; -}; - -struct netxen_port { - struct netxen_adapter *adapter; - - u16 portnum; /* GBE port number */ - u16 link_speed; - u16 link_duplex; - u16 link_autoneg; - - int flags; - - struct net_device *netdev; - struct pci_dev *pdev; - struct net_device_stats net_stats; - struct netxen_port_stats stats; - struct work_struct tx_timeout_task; -}; - #define PCI_OFFSET_FIRST_RANGE(adapter, off) \ ((adapter)->ahw.pci_base0 + (off)) #define PCI_OFFSET_SECOND_RANGE(adapter, off) \ @@ -984,32 +990,26 @@ static inline void __iomem *pci_base(struct netxen_adapter *adapter, return NULL; } -int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port); -int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port); +int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter); +int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter); void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter); void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter); void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter, int port, long enable); void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter, int port, long enable); -int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, long reg, +int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, __u32 * readval); -int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long phy, +int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, __u32 val); /* Functions available from netxen_nic_hw.c */ -int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu); -int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu); +int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu); +int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu); void netxen_nic_init_niu_gb(struct netxen_adapter *adapter); void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw); void netxen_nic_reg_write(struct netxen_adapter *adapter, u64 off, u32 val); @@ -1024,6 +1024,7 @@ int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, int len); void netxen_crb_writelit_adapter(struct netxen_adapter *adapter, unsigned long off, int data); +int netxen_nic_erase_pxe(struct netxen_adapter *adapter); /* Functions from netxen_nic_init.c */ void netxen_free_adapter_offload(struct netxen_adapter *adapter); @@ -1040,6 +1041,7 @@ int netxen_flash_unlock(struct netxen_adapter *adapter); int netxen_backup_crbinit(struct netxen_adapter *adapter); int netxen_flash_erase_secondary(struct netxen_adapter *adapter); int netxen_flash_erase_primary(struct netxen_adapter *adapter); +void netxen_halt_pegs(struct netxen_adapter *adapter); int netxen_rom_fast_write(struct netxen_adapter *adapter, int addr, int data); int netxen_rom_se(struct netxen_adapter *adapter, int addr); @@ -1047,11 +1049,8 @@ int netxen_do_rom_se(struct netxen_adapter *adapter, int addr); /* Functions from netxen_nic_isr.c */ void netxen_nic_isr_other(struct netxen_adapter *adapter); -void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 port, - u32 link); -void netxen_handle_port_int(struct netxen_adapter *adapter, u32 port, - u32 enable); -void netxen_nic_stop_all_ports(struct netxen_adapter *adapter); +void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link); +void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable); void netxen_initialize_adapter_sw(struct netxen_adapter *adapter); void netxen_initialize_adapter_hw(struct netxen_adapter *adapter); void *netxen_alloc(struct pci_dev *pdev, size_t sz, dma_addr_t * ptr, @@ -1106,6 +1105,7 @@ static inline void netxen_nic_enable_int(struct netxen_adapter *adapter) if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { mask = 0xbff; + writel(0X0, NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); writel(mask, PCI_OFFSET_SECOND_RANGE(adapter, ISR_INT_TARGET_MASK)); } @@ -1170,4 +1170,5 @@ extern int netxen_rom_fast_read(struct netxen_adapter *adapter, int addr, extern struct ethtool_ops netxen_nic_ethtool_ops; +extern int physical_port[]; /* physical port # from virtual port.*/ #endif /* __NETXEN_NIC_H_ */ diff --git a/drivers/net/netxen/netxen_nic_ethtool.c b/drivers/net/netxen/netxen_nic_ethtool.c index cc0efe2..16fabb3 100644 --- a/drivers/net/netxen/netxen_nic_ethtool.c +++ b/drivers/net/netxen/netxen_nic_ethtool.c @@ -40,8 +40,8 @@ #include <linux/ethtool.h> #include <linux/version.h> -#include "netxen_nic_hw.h" #include "netxen_nic.h" +#include "netxen_nic_hw.h" #include "netxen_nic_phan_reg.h" struct netxen_nic_stats { @@ -50,8 +50,8 @@ struct netxen_nic_stats { int stat_offset; }; -#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_port *)0)->m), \ - offsetof(struct netxen_port, m) +#define NETXEN_NIC_STAT(m) sizeof(((struct netxen_adapter *)0)->m), \ + offsetof(struct netxen_adapter, m) #define NETXEN_NIC_PORT_WINDOW 0x10000 #define NETXEN_NIC_INVALID_DATA 0xDEADBEEF @@ -82,8 +82,7 @@ static const struct netxen_nic_stats netxen_nic_gstrings_stats[] = { #define NETXEN_NIC_STATS_LEN ARRAY_SIZE(netxen_nic_gstrings_stats) static const char netxen_nic_gstrings_test[][ETH_GSTRING_LEN] = { - "Register_Test_offline", "EEPROM_Test_offline", - "Interrupt_Test_offline", "Loopback_Test_offline", + "Register_Test_on_offline", "Link_Test_on_offline" }; @@ -101,8 +100,7 @@ static int netxen_nic_get_eeprom_len(struct net_device *dev) static void netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); u32 fw_major = 0; u32 fw_minor = 0; u32 fw_build = 0; @@ -116,7 +114,7 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) fw_build = readl(NETXEN_CRB_NORMALIZE(adapter, NETXEN_FW_VERSION_SUB)); sprintf(drvinfo->fw_version, "%d.%d.%d", fw_major, fw_minor, fw_build); - strncpy(drvinfo->bus_info, pci_name(port->pdev), 32); + strncpy(drvinfo->bus_info, pci_name(adapter->pdev), 32); drvinfo->n_stats = NETXEN_NIC_STATS_LEN; drvinfo->testinfo_len = NETXEN_NIC_TEST_LEN; drvinfo->regdump_len = NETXEN_NIC_REGS_LEN; @@ -126,8 +124,7 @@ netxen_nic_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *drvinfo) static int netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); struct netxen_board_info *boardinfo = &adapter->ahw.boardcfg; /* read which mode */ @@ -147,8 +144,8 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ecmd->port = PORT_TP; if (netif_running(dev)) { - ecmd->speed = port->link_speed; - ecmd->duplex = port->link_duplex; + ecmd->speed = adapter->link_speed; + ecmd->duplex = adapter->link_duplex; } else return -EIO; /* link absent */ } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { @@ -166,7 +163,7 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) } else return -EIO; - ecmd->phy_address = port->portnum; + ecmd->phy_address = adapter->portnum; ecmd->transceiver = XCVR_EXTERNAL; switch ((netxen_brdtype_t) boardinfo->board_type) { @@ -180,7 +177,7 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) ecmd->port = PORT_TP; ecmd->autoneg = (boardinfo->board_type == NETXEN_BRDTYPE_P2_SB31_10G_CX4) ? - (AUTONEG_DISABLE) : (port->link_autoneg); + (AUTONEG_DISABLE) : (adapter->link_autoneg); break; case NETXEN_BRDTYPE_P2_SB31_10G_HMEZ: case NETXEN_BRDTYPE_P2_SB31_10G_IMEZ: @@ -207,23 +204,22 @@ netxen_nic_get_settings(struct net_device *dev, struct ethtool_cmd *ecmd) static int netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 status; /* read which mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { /* autonegotiation */ if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, + && adapter->phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, ecmd->autoneg) != 0) return -EIO; else - port->link_autoneg = ecmd->autoneg; + adapter->link_autoneg = ecmd->autoneg; if (adapter->phy_read - && adapter->phy_read(adapter, port->portnum, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) != 0) return -EIO; @@ -246,13 +242,13 @@ netxen_nic_set_settings(struct net_device *dev, struct ethtool_cmd *ecmd) if (ecmd->duplex == DUPLEX_FULL) netxen_set_phy_duplex(status); if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, + && adapter->phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, *((int *)&status)) != 0) return -EIO; else { - port->link_speed = ecmd->speed; - port->link_duplex = ecmd->duplex; + adapter->link_speed = ecmd->speed; + adapter->link_duplex = ecmd->duplex; } } else return -EOPNOTSUPP; @@ -361,15 +357,14 @@ static struct netxen_niu_regs niu_registers[] = { static void netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 mode, *regs_buff = p; void __iomem *addr; int i, window; memset(p, 0, NETXEN_NIC_REGS_LEN); regs->version = (1 << 24) | (adapter->ahw.revision_id << 16) | - (port->pdev)->device; + (adapter->pdev)->device; /* which mode */ NETXEN_NIC_LOCKED_READ_REG(NETXEN_NIU_MODE, ®s_buff[0]); mode = regs_buff[0]; @@ -384,7 +379,8 @@ netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) for (i = 3; niu_registers[mode].reg[i - 3] != -1; i++) { /* GB: port specific registers */ if (mode == 0 && i >= 19) - window = port->portnum * NETXEN_NIC_PORT_WINDOW; + window = physical_port[adapter->portnum] * + NETXEN_NIC_PORT_WINDOW; NETXEN_NIC_LOCKED_READ_REG(niu_registers[mode]. reg[i - 3] + window, @@ -394,32 +390,26 @@ netxen_nic_get_regs(struct net_device *dev, struct ethtool_regs *regs, void *p) } } -static void -netxen_nic_get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) -{ - wol->supported = WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC; - /* options can be added depending upon the mode */ - wol->wolopts = 0; -} - -static u32 netxen_nic_get_link(struct net_device *dev) +static u32 netxen_nic_test_link(struct net_device *dev) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 status; + int val; /* read which mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { if (adapter->phy_read - && adapter->phy_read(adapter, port->portnum, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) != 0) return -EIO; - else - return (netxen_get_phy_link(status)); + else { + val = netxen_get_phy_link(status); + return !val; + } } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - int val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); - return val == XG_LINK_UP; + val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + return (val == XG_LINK_UP) ? 0 : 1; } return -EIO; } @@ -428,15 +418,15 @@ static int netxen_nic_get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int offset; int ret; if (eeprom->len == 0) return -EINVAL; - eeprom->magic = (port->pdev)->vendor | ((port->pdev)->device << 16); + eeprom->magic = (adapter->pdev)->vendor | + ((adapter->pdev)->device << 16); offset = eeprom->offset; ret = netxen_rom_fast_read_words(adapter, offset, bytes, @@ -451,14 +441,14 @@ static int netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 * bytes) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int offset = eeprom->offset; static int flash_start; static int ready_to_flash; int ret; if (flash_start == 0) { + netxen_halt_pegs(adapter); ret = netxen_flash_unlock(adapter); if (ret < 0) { printk(KERN_ERR "%s: Flash unlock failed.\n", @@ -467,6 +457,7 @@ netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, } printk(KERN_INFO "%s: flash unlocked. \n", netxen_nic_driver_name); + last_schedule_time = jiffies; ret = netxen_flash_erase_secondary(adapter); if (ret != FLASH_SUCCESS) { printk(KERN_ERR "%s: Flash erase failed.\n", @@ -520,8 +511,7 @@ netxen_nic_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, static void netxen_nic_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ring) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); int i; ring->rx_pending = 0; @@ -545,19 +535,45 @@ static void netxen_nic_get_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; + int port = physical_port[adapter->portnum]; if (adapter->ahw.board_type == NETXEN_NIC_GBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + return; /* get flow control settings */ - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - &val); + netxen_nic_read_w0(adapter,NETXEN_NIU_GB_MAC_CONFIG_0(port), + &val); pause->rx_pause = netxen_gb_get_rx_flowctl(val); - pause->tx_pause = netxen_gb_get_tx_flowctl(val); - /* get autoneg settings */ - pause->autoneg = port->link_autoneg; + netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val); + switch (port) { + case 0: + pause->tx_pause = !(netxen_gb_get_gb0_mask(val)); + break; + case 1: + pause->tx_pause = !(netxen_gb_get_gb1_mask(val)); + break; + case 2: + pause->tx_pause = !(netxen_gb_get_gb2_mask(val)); + break; + case 3: + default: + pause->tx_pause = !(netxen_gb_get_gb3_mask(val)); + break; + } + } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) + return; + pause->rx_pause = 1; + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val); + if (port == 0) + pause->tx_pause = !(netxen_xg_get_xg0_mask(val)); + else + pause->tx_pause = !(netxen_xg_get_xg1_mask(val)); + } else { + printk(KERN_ERR"%s: Unknown board type: %x\n", + netxen_nic_driver_name, adapter->ahw.board_type); } } @@ -565,140 +581,95 @@ static int netxen_nic_set_pauseparam(struct net_device *dev, struct ethtool_pauseparam *pause) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(dev); __u32 val; - unsigned int autoneg; - + int port = physical_port[adapter->portnum]; /* read mode */ if (adapter->ahw.board_type == NETXEN_NIC_GBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + return -EIO; /* set flow control */ netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - (u32 *) & val); - if (pause->tx_pause) - netxen_gb_tx_flowctl(val); - else - netxen_gb_unset_tx_flowctl(val); + NETXEN_NIU_GB_MAC_CONFIG_0(port), &val); + if (pause->rx_pause) netxen_gb_rx_flowctl(val); else netxen_gb_unset_rx_flowctl(val); - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MAC_CONFIG_0(port->portnum), - *&val); + netxen_nic_write_w0(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), + val); /* set autoneg */ - autoneg = pause->autoneg; - if (adapter->phy_write - && adapter->phy_write(adapter, port->portnum, - NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, - autoneg) != 0) + netxen_nic_read_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, &val); + switch (port) { + case 0: + if (pause->tx_pause) + netxen_gb_unset_gb0_mask(val); + else + netxen_gb_set_gb0_mask(val); + break; + case 1: + if (pause->tx_pause) + netxen_gb_unset_gb1_mask(val); + else + netxen_gb_set_gb1_mask(val); + break; + case 2: + if (pause->tx_pause) + netxen_gb_unset_gb2_mask(val); + else + netxen_gb_set_gb2_mask(val); + break; + case 3: + default: + if (pause->tx_pause) + netxen_gb_unset_gb3_mask(val); + else + netxen_gb_set_gb3_mask(val); + break; + } + netxen_nic_write_w0(adapter, NETXEN_NIU_GB_PAUSE_CTL, val); + } else if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) return -EIO; - else { - port->link_autoneg = pause->autoneg; - return 0; + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, &val); + if (port == 0) { + if (pause->tx_pause) + netxen_xg_unset_xg0_mask(val); + else + netxen_xg_set_xg0_mask(val); + } else { + if (pause->tx_pause) + netxen_xg_unset_xg1_mask(val); + else + netxen_xg_set_xg1_mask(val); } - } else - return -EOPNOTSUPP; + netxen_nic_write_w0(adapter, NETXEN_NIU_XG_PAUSE_CTL, val); + } else { + printk(KERN_ERR "%s: Unknown board type: %x\n", + netxen_nic_driver_name, + adapter->ahw.board_type); + } + return 0; } static int netxen_nic_reg_test(struct net_device *dev) { - struct netxen_port *port = netdev_priv(dev); - struct netxen_adapter *adapter = port->adapter; - u32 data_read, data_written, save; - __u32 mode; - - /* - * first test the "Read Only" registers by writing which mode - */ - netxen_nic_read_w0(adapter, NETXEN_NIU_MODE, &mode); - if (netxen_get_niu_enable_ge(mode)) { /* GB Mode */ - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_STATUS(port->portnum), - &data_read); - - save = data_read; - if (data_read) - data_written = data_read & NETXEN_NIC_INVALID_DATA; - else - data_written = NETXEN_NIC_INVALID_DATA; - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_STATUS(port-> - portnum), - data_written); - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_STATUS(port->portnum), - &data_read); - - if (data_written == data_read) { - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_STATUS(port-> - portnum), - save); - - return 0; - } - - /* netxen_niu_gb_mii_mgmt_indicators is read only */ - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_INDICATE(port-> - portnum), - &data_read); - - save = data_read; - if (data_read) - data_written = data_read & NETXEN_NIC_INVALID_DATA; - else - data_written = NETXEN_NIC_INVALID_DATA; - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_INDICATE(port-> - portnum), - data_written); + struct netxen_adapter *adapter = netdev_priv(dev); + u32 data_read, data_written; - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_INDICATE(port-> - portnum), - &data_read); - - if (data_written == data_read) { - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MII_MGMT_INDICATE - (port->portnum), save); - return 0; - } - - /* netxen_niu_gb_interface_status is read only */ - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_INTERFACE_STATUS(port-> - portnum), - &data_read); - - save = data_read; - if (data_read) - data_written = data_read & NETXEN_NIC_INVALID_DATA; - else - data_written = NETXEN_NIC_INVALID_DATA; - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_INTERFACE_STATUS(port-> - portnum), - data_written); + netxen_nic_read_w0(adapter, NETXEN_PCIX_PH_REG(0), &data_read); + if ((data_read & 0xffff) != PHAN_VENDOR_ID) + return 1; - netxen_nic_read_w0(adapter, - NETXEN_NIU_GB_INTERFACE_STATUS(port-> - portnum), - &data_read); + data_written = (u32)0xa5a5a5a5; - if (data_written == data_read) { - netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_INTERFACE_STATUS - (port->portnum), save); + netxen_nic_reg_write(adapter, CRB_SCRATCHPAD_TEST, data_written); + data_read = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_SCRATCHPAD_TEST)); + if (data_written != data_read) + return 1; - return 0; - } - } /* GB Mode */ - return 1; + return 0; } static int netxen_nic_diag_test_count(struct net_device *dev) @@ -710,29 +681,12 @@ static void netxen_nic_diag_test(struct net_device *dev, struct ethtool_test *eth_test, u64 * data) { - if (eth_test->flags == ETH_TEST_FL_OFFLINE) { /* offline tests */ - /* link test */ - if (!(data[4] = (u64) netxen_nic_get_link(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - - if (netif_running(dev)) - dev->stop(dev); - - /* register tests */ - if (!(data[0] = netxen_nic_reg_test(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - /* other tests pass as of now */ - data[1] = data[2] = data[3] = 1; - if (netif_running(dev)) - dev->open(dev); - } else { /* online tests */ - /* link test */ - if (!(data[4] = (u64) netxen_nic_get_link(dev))) - eth_test->flags |= ETH_TEST_FL_FAILED; - - /* other tests pass by default */ - data[0] = data[1] = data[2] = data[3] = 1; - } + memset(data, 0, sizeof(uint64_t) * NETXEN_NIC_TEST_LEN); + if ((data[0] = netxen_nic_reg_test(dev))) + eth_test->flags |= ETH_TEST_FL_FAILED; + /* link test */ + if ((data[1] = (u64) netxen_nic_test_link(dev))) + eth_test->flags |= ETH_TEST_FL_FAILED; } static void @@ -764,12 +718,13 @@ static void netxen_nic_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 * data) { - struct netxen_port *port = netdev_priv(dev); + struct netxen_adapter *adapter = netdev_priv(dev); int index; for (index = 0; index < NETXEN_NIC_STATS_LEN; index++) { char *p = - (char *)port + netxen_nic_gstrings_stats[index].stat_offset; + (char *)adapter + + netxen_nic_gstrings_stats[index].stat_offset; data[index] = (netxen_nic_gstrings_stats[index].sizeof_stat == sizeof(u64)) ? *(u64 *) p : *(u32 *) p; @@ -782,8 +737,7 @@ struct ethtool_ops netxen_nic_ethtool_ops = { .get_drvinfo = netxen_nic_get_drvinfo, .get_regs_len = netxen_nic_get_regs_len, .get_regs = netxen_nic_get_regs, - .get_wol = netxen_nic_get_wol, - .get_link = netxen_nic_get_link, + .get_link = ethtool_op_get_link, .get_eeprom_len = netxen_nic_get_eeprom_len, .get_eeprom = netxen_nic_get_eeprom, .set_eeprom = netxen_nic_set_eeprom, diff --git a/drivers/net/netxen/netxen_nic_hdr.h b/drivers/net/netxen/netxen_nic_hdr.h index fe8b675..608e37b 100644 --- a/drivers/net/netxen/netxen_nic_hdr.h +++ b/drivers/net/netxen/netxen_nic_hdr.h @@ -467,6 +467,8 @@ enum { #define NETXEN_PCI_OCM1 (0x05100000UL) #define NETXEN_PCI_OCM1_MAX (0x051fffffUL) #define NETXEN_PCI_CRBSPACE (0x06000000UL) +#define NETXEN_PCI_128MB_SIZE (0x08000000UL) +#define NETXEN_PCI_32MB_SIZE (0x02000000UL) #define NETXEN_CRB_CAM NETXEN_PCI_CRB_WINDOW(NETXEN_HW_PX_MAP_CRB_CAM) @@ -484,6 +486,7 @@ enum { /* 10 seconds before we give up */ #define NETXEN_NIU_PHY_WAITMAX 50 #define NETXEN_NIU_MAX_GBE_PORTS 4 +#define NETXEN_NIU_MAX_XG_PORTS 2 #define NETXEN_NIU_MODE (NETXEN_CRB_NIU + 0x00000) @@ -527,6 +530,7 @@ enum { #define NETXEN_NIU_XG_PAUSE_CTL (NETXEN_CRB_NIU + 0x00098) #define NETXEN_NIU_XG_PAUSE_LEVEL (NETXEN_CRB_NIU + 0x000dc) #define NETXEN_NIU_XG_SEL (NETXEN_CRB_NIU + 0x00128) +#define NETXEN_NIU_GB_PAUSE_CTL (NETXEN_CRB_NIU + 0x0030c) #define NETXEN_NIU_FULL_LEVEL_XG (NETXEN_CRB_NIU + 0x00450) @@ -649,11 +653,19 @@ enum { #define PCIX_MS_WINDOW (0x10204) #define PCIX_SN_WINDOW (0x10208) #define PCIX_CRB_WINDOW (0x10210) +#define PCIX_CRB_WINDOW_F0 (0x10210) +#define PCIX_CRB_WINDOW_F1 (0x10230) +#define PCIX_CRB_WINDOW_F2 (0x10250) +#define PCIX_CRB_WINDOW_F3 (0x10270) #define PCIX_TARGET_STATUS (0x10118) #define PCIX_TARGET_MASK (0x10128) #define PCIX_MSI_F0 (0x13000) +#define PCIX_MSI_F1 (0x13004) +#define PCIX_MSI_F2 (0x13008) +#define PCIX_MSI_F3 (0x1300c) +#define PCIX_MSI_F(i) (0x13000+((i)*4)) #define PCIX_PS_MEM_SPACE (0x90000) diff --git a/drivers/net/netxen/netxen_nic_hw.c b/drivers/net/netxen/netxen_nic_hw.c index f263232..baff17a 100644 --- a/drivers/net/netxen/netxen_nic_hw.c +++ b/drivers/net/netxen/netxen_nic_hw.c @@ -33,8 +33,225 @@ #include "netxen_nic.h" #include "netxen_nic_hw.h" +#define DEFINE_GLOBAL_RECV_CRB #include "netxen_nic_phan_reg.h" + +#include <net/ip.h> + +struct netxen_recv_crb recv_crb_registers[] = { + /* + * Instance 0. + */ + { + /* rcv_desc_crb: */ + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x100), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x104), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x108), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x10c), + + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x110), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x114), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x118), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x11c), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x120), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x124), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x128), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x12c), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x130), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x134), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x138), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x13c), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x140), + + }, + /* + * Instance 1, + */ + { + /* rcv_desc_crb: */ + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x144), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x148), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x14c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x150), + + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x154), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x158), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x15c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x160), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x164), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x168), + /* crb_globalrcv_ring: */ + NETXEN_NIC_REG(0x16c), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x170), + } + + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x174), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x178), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x17c), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x180), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x184), + }, + /* + * Instance 2, + */ + { + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x1d8), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x1dc), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x1f0), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x1f4), + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x1f8), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x1fc), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x200), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x204), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x208), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x20c), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x210), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x214), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x218), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x21c), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x220), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x224), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x228), + }, + /* + * Instance 3, + */ + { + { + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x22c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x230), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x234), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x238), + }, + /* Jumbo frames */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x23c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x240), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x244), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x248), + }, + /* LRO */ + { + /* crb_rcv_producer_offset: */ + NETXEN_NIC_REG(0x24c), + /* crb_rcv_consumer_offset: */ + NETXEN_NIC_REG(0x250), + /* crb_gloablrcv_ring: */ + NETXEN_NIC_REG(0x254), + /* crb_rcv_ring_size */ + NETXEN_NIC_REG(0x258), + } + }, + /* crb_rcvstatus_ring: */ + NETXEN_NIC_REG(0x25c), + /* crb_rcv_status_producer: */ + NETXEN_NIC_REG(0x260), + /* crb_rcv_status_consumer: */ + NETXEN_NIC_REG(0x264), + /* crb_rcvpeg_state: */ + NETXEN_NIC_REG(0x268), + /* crb_status_ring_size */ + NETXEN_NIC_REG(0x26c), + }, +}; + +u64 ctx_addr_sig_regs[][3] = { + {NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)}, + {NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)}, + {NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)}, + {NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)} +}; + + /* PCI Windowing for DDR regions. */ #define ADDR_IN_RANGE(addr, low, high) \ @@ -68,8 +285,7 @@ void netxen_free_hw_resources(struct netxen_adapter *adapter); int netxen_nic_set_mac(struct net_device *netdev, void *p) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct sockaddr *addr = p; if (netif_running(netdev)) @@ -82,7 +298,7 @@ int netxen_nic_set_mac(struct net_device *netdev, void *p) memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); if (adapter->macaddr_set) - adapter->macaddr_set(port, addr->sa_data); + adapter->macaddr_set(adapter, addr->sa_data); return 0; } @@ -92,56 +308,19 @@ int netxen_nic_set_mac(struct net_device *netdev, void *p) */ void netxen_nic_set_multi(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct dev_mc_list *mc_ptr; - __u32 netxen_mac_addr_cntl_data = 0; mc_ptr = netdev->mc_list; if (netdev->flags & IFF_PROMISC) { if (adapter->set_promisc) adapter->set_promisc(adapter, - port->portnum, NETXEN_NIU_PROMISC_MODE); } else { - if (adapter->unset_promisc && - adapter->ahw.boardcfg.board_type - != NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) + if (adapter->unset_promisc) adapter->unset_promisc(adapter, - port->portnum, NETXEN_NIU_NON_PROMISC_MODE); } - if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x03); - netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_enable_xtnd0(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd1(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd2(netxen_mac_addr_cntl_data); - netxen_nic_mcr_set_enable_xtnd3(netxen_mac_addr_cntl_data); - } else { - netxen_nic_mcr_set_mode_select(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool0(netxen_mac_addr_cntl_data, 0x00); - netxen_nic_mcr_set_id_pool1(netxen_mac_addr_cntl_data, 0x01); - netxen_nic_mcr_set_id_pool2(netxen_mac_addr_cntl_data, 0x02); - netxen_nic_mcr_set_id_pool3(netxen_mac_addr_cntl_data, 0x03); - } - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_MAC_ADDR_CNTL_REG)); - if (adapter->ahw.board_type == NETXEN_NIC_XGBE) { - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, - NETXEN_MULTICAST_ADDR_HI_0)); - } else { - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, - NETXEN_MULTICAST_ADDR_HI_1)); - } - netxen_mac_addr_cntl_data = 0; - writel(netxen_mac_addr_cntl_data, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_NIU_GB_DROP_WRONGADDR)); } /* @@ -150,8 +329,7 @@ void netxen_nic_set_multi(struct net_device *netdev) */ int netxen_nic_change_mtu(struct net_device *netdev, int mtu) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int eff_mtu = mtu + NETXEN_ENET_HEADER_SIZE + NETXEN_ETH_FCS_SIZE; if ((eff_mtu > NETXEN_MAX_MTU) || (eff_mtu < NETXEN_MIN_MTU)) { @@ -161,7 +339,7 @@ int netxen_nic_change_mtu(struct net_device *netdev, int mtu) } if (adapter->set_mtu) - adapter->set_mtu(port, mtu); + adapter->set_mtu(adapter, mtu); netdev->mtu = mtu; return 0; @@ -178,9 +356,9 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) void *addr; int loops = 0, err = 0; int ctx, ring; - u32 card_cmdring = 0; struct netxen_recv_context *recv_ctx; struct netxen_rcv_desc_ctx *rcv_desc; + int func_id = adapter->portnum; DPRINTK(INFO, "crb_base: %lx %x", NETXEN_PCI_CRBSPACE, PCI_OFFSET_SECOND_RANGE(adapter, NETXEN_PCI_CRBSPACE)); @@ -189,11 +367,6 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) DPRINTK(INFO, "cam RAM: %lx %x", NETXEN_CAM_RAM_BASE, pci_base_offset(adapter, NETXEN_CAM_RAM_BASE)); - /* Window 1 call */ - card_cmdring = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_CMDRING)); - - DPRINTK(INFO, "Command Peg sends 0x%x for cmdring base\n", - card_cmdring); for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { DPRINTK(INFO, "Command Peg ready..waiting for rcv peg\n"); @@ -227,8 +400,8 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) (dma_addr_t *) & adapter->ctx_desc_phys_addr, &adapter->ctx_desc_pdev); - printk("ctx_desc_phys_addr: 0x%llx\n", - (u64) adapter->ctx_desc_phys_addr); + printk(KERN_INFO "ctx_desc_phys_addr: 0x%llx\n", + (unsigned long long) adapter->ctx_desc_phys_addr); if (addr == NULL) { DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); err = -ENOMEM; @@ -236,17 +409,20 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) } memset(addr, 0, sizeof(struct netxen_ring_ctx)); adapter->ctx_desc = (struct netxen_ring_ctx *)addr; + adapter->ctx_desc->ctx_id = cpu_to_le32(adapter->portnum); adapter->ctx_desc->cmd_consumer_offset = cpu_to_le64(adapter->ctx_desc_phys_addr + sizeof(struct netxen_ring_ctx)); adapter->cmd_consumer = (uint32_t *) (((char *)addr) + sizeof(struct netxen_ring_ctx)); - addr = pci_alloc_consistent(adapter->ahw.pdev, - sizeof(struct cmd_desc_type0) * - adapter->max_tx_desc_count, - (dma_addr_t *) & hw->cmd_desc_phys_addr); - printk("cmd_desc_phys_addr: 0x%llx\n", (u64) hw->cmd_desc_phys_addr); + addr = netxen_alloc(adapter->ahw.pdev, + sizeof(struct cmd_desc_type0) * + adapter->max_tx_desc_count, + (dma_addr_t *) & hw->cmd_desc_phys_addr, + &adapter->ahw.cmd_desc_pdev); + printk(KERN_INFO "cmd_desc_phys_addr: 0x%llx\n", + (unsigned long long) hw->cmd_desc_phys_addr); if (addr == NULL) { DPRINTK(ERR, "bad return from pci_alloc_consistent\n"); @@ -304,11 +480,11 @@ int netxen_nic_hw_resources(struct netxen_adapter *adapter) /* Window = 1 */ writel(lower32(adapter->ctx_desc_phys_addr), - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO)); + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_LO(func_id))); writel(upper32(adapter->ctx_desc_phys_addr), - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI)); - writel(NETXEN_CTX_SIGNATURE, - NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG)); + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_ADDR_REG_HI(func_id))); + writel(NETXEN_CTX_SIGNATURE | func_id, + NETXEN_CRB_NORMALIZE(adapter, CRB_CTX_SIGNATURE_REG(func_id))); return err; } @@ -335,10 +511,6 @@ void netxen_free_hw_resources(struct netxen_adapter *adapter) adapter->ahw.cmd_desc_phys_addr); adapter->ahw.cmd_desc_head = NULL; } - /* Special handling: there are 2 ports on this board */ - if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) { - adapter->ahw.max_ports = 2; - } for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { recv_ctx = &adapter->recv_ctx[ctx]; @@ -369,22 +541,20 @@ void netxen_tso_check(struct netxen_adapter *adapter, struct cmd_desc_type0 *desc, struct sk_buff *skb) { if (desc->mss) { - desc->total_hdr_length = sizeof(struct ethhdr) + - ((skb->nh.iph)->ihl * sizeof(u32)) + - ((skb->h.th)->doff * sizeof(u32)); + desc->total_hdr_length = (sizeof(struct ethhdr) + + ip_hdrlen(skb) + tcp_hdrlen(skb)); netxen_set_cmd_desc_opcode(desc, TX_TCP_LSO); } else if (skb->ip_summed == CHECKSUM_PARTIAL) { - if (skb->nh.iph->protocol == IPPROTO_TCP) { + if (ip_hdr(skb)->protocol == IPPROTO_TCP) { netxen_set_cmd_desc_opcode(desc, TX_TCP_PKT); - } else if (skb->nh.iph->protocol == IPPROTO_UDP) { + } else if (ip_hdr(skb)->protocol == IPPROTO_UDP) { netxen_set_cmd_desc_opcode(desc, TX_UDP_PKT); } else { return; } } - adapter->stats.xmitcsummed++; - desc->tcp_hdr_offset = skb->h.raw - skb->data; - desc->ip_hdr_offset = skb->nh.raw - skb->data; + desc->tcp_hdr_offset = skb_transport_offset(skb); + desc->ip_hdr_offset = skb_network_offset(skb); } int netxen_is_flash_supported(struct netxen_adapter *adapter) @@ -420,6 +590,7 @@ static int netxen_get_flash_block(struct netxen_adapter *adapter, int base, for (i = 0; i < size / sizeof(u32); i++) { if (netxen_rom_fast_read(adapter, addr, ptr32) == -1) return -1; + *ptr32 = cpu_to_le32(*ptr32); ptr32++; addr += sizeof(u32); } @@ -428,6 +599,7 @@ static int netxen_get_flash_block(struct netxen_adapter *adapter, int base, if (netxen_rom_fast_read(adapter, addr, &local) == -1) return -1; + local = cpu_to_le32(local); memcpy(ptr32, &local, (char *)buf + size - (char *)ptr32); } @@ -470,7 +642,30 @@ void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) if (adapter->curr_window == wndw) return; - + switch(adapter->ahw.pci_func) { + case 0: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); + break; + case 1: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F1)); + break; + case 2: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F2)); + break; + case 3: + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW_F3)); + break; + default: + printk(KERN_INFO "Changing the window for PCI function" + "%d\n", adapter->ahw.pci_func); + offset = PCI_OFFSET_SECOND_RANGE(adapter, + NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); + break; + } /* * Move the CRB window. * We need to write to the "direct access" region of PCI @@ -479,9 +674,6 @@ void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) * register address is received by PCI. The direct region bypasses * the CRB bus. */ - offset = - PCI_OFFSET_SECOND_RANGE(adapter, - NETXEN_PCIX_PH_REG(PCIX_CRB_WINDOW)); if (wndw & 0x1) wndw = NETXEN_WINDOW_ONE; @@ -499,14 +691,17 @@ void netxen_nic_pci_change_crbwindow(struct netxen_adapter *adapter, u32 wndw) count++; } - adapter->curr_window = wndw; + if (wndw == NETXEN_WINDOW_ONE) + adapter->curr_window = 1; + else + adapter->curr_window = 0; } void netxen_load_firmware(struct netxen_adapter *adapter) { int i; - long data, size = 0; - long flashaddr = NETXEN_FLASH_BASE, memaddr = NETXEN_PHANTOM_MEM_BASE; + u32 data, size = 0; + u32 flashaddr = NETXEN_FLASH_BASE, memaddr = NETXEN_PHANTOM_MEM_BASE; u64 off; void __iomem *addr; @@ -744,6 +939,17 @@ netxen_nic_pci_set_window(struct netxen_adapter *adapter, return addr; } +int +netxen_nic_erase_pxe(struct netxen_adapter *adapter) +{ + if (netxen_rom_fast_write(adapter, PXE_START, 0) == -1) { + printk(KERN_ERR "%s: erase pxe failed\n", + netxen_nic_driver_name); + return -1; + } + return 0; +} + int netxen_nic_get_board_info(struct netxen_adapter *adapter) { int rv = 0; @@ -805,40 +1011,29 @@ int netxen_nic_get_board_info(struct netxen_adapter *adapter) /* NIU access sections */ -int netxen_nic_set_mtu_gb(struct netxen_port *port, int new_mtu) +int netxen_nic_set_mtu_gb(struct netxen_adapter *adapter, int new_mtu) { - struct netxen_adapter *adapter = port->adapter; netxen_nic_write_w0(adapter, - NETXEN_NIU_GB_MAX_FRAME_SIZE(port->portnum), - new_mtu); + NETXEN_NIU_GB_MAX_FRAME_SIZE( + physical_port[adapter->portnum]), new_mtu); return 0; } -int netxen_nic_set_mtu_xgb(struct netxen_port *port, int new_mtu) +int netxen_nic_set_mtu_xgb(struct netxen_adapter *adapter, int new_mtu) { - struct netxen_adapter *adapter = port->adapter; new_mtu += NETXEN_NIU_HDRSIZE + NETXEN_NIU_TLRSIZE; - netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, new_mtu); + if (physical_port[adapter->portnum] == 0) + netxen_nic_write_w0(adapter, NETXEN_NIU_XGE_MAX_FRAME_SIZE, + new_mtu); + else + netxen_nic_write_w0(adapter, NETXEN_NIU_XG1_MAX_FRAME_SIZE, + new_mtu); return 0; } void netxen_nic_init_niu_gb(struct netxen_adapter *adapter) { - int portno; - for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) - netxen_niu_gbe_init_port(adapter, portno); -} - -void netxen_nic_stop_all_ports(struct netxen_adapter *adapter) -{ - int port_nr; - struct netxen_port *port; - - for (port_nr = 0; port_nr < adapter->ahw.max_ports; port_nr++) { - port = adapter->port[port_nr]; - if (adapter->stop_port) - adapter->stop_port(adapter, port->portnum); - } + netxen_niu_gbe_init_port(adapter, physical_port[adapter->portnum]); } void @@ -857,9 +1052,8 @@ netxen_crb_writelit_adapter(struct netxen_adapter *adapter, unsigned long off, } } -void netxen_nic_set_link_parameters(struct netxen_port *port) +void netxen_nic_set_link_parameters(struct netxen_adapter *adapter) { - struct netxen_adapter *adapter = port->adapter; __u32 status; __u32 autoneg; __u32 mode; @@ -868,47 +1062,47 @@ void netxen_nic_set_link_parameters(struct netxen_port *port) if (netxen_get_niu_enable_ge(mode)) { /* Gb 10/100/1000 Mbps mode */ if (adapter->phy_read && adapter-> - phy_read(adapter, port->portnum, + phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_link(status)) { switch (netxen_get_phy_speed(status)) { case 0: - port->link_speed = SPEED_10; + adapter->link_speed = SPEED_10; break; case 1: - port->link_speed = SPEED_100; + adapter->link_speed = SPEED_100; break; case 2: - port->link_speed = SPEED_1000; + adapter->link_speed = SPEED_1000; break; default: - port->link_speed = -1; + adapter->link_speed = -1; break; } switch (netxen_get_phy_duplex(status)) { case 0: - port->link_duplex = DUPLEX_HALF; + adapter->link_duplex = DUPLEX_HALF; break; case 1: - port->link_duplex = DUPLEX_FULL; + adapter->link_duplex = DUPLEX_FULL; break; default: - port->link_duplex = -1; + adapter->link_duplex = -1; break; } if (adapter->phy_read && adapter-> - phy_read(adapter, port->portnum, + phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_AUTONEG, &autoneg) != 0) - port->link_autoneg = autoneg; + adapter->link_autoneg = autoneg; } else goto link_down; } else { link_down: - port->link_speed = -1; - port->link_duplex = -1; + adapter->link_speed = -1; + adapter->link_duplex = -1; } } } @@ -922,7 +1116,7 @@ void netxen_nic_flash_print(struct netxen_adapter *adapter) char brd_name[NETXEN_MAX_SHORT_NAME]; struct netxen_new_user_info user_info; int i, addr = USER_START; - u32 *ptr32; + __le32 *ptr32; struct netxen_board_info *board_info = &(adapter->ahw.boardcfg); if (board_info->magic != NETXEN_BDINFO_MAGIC) { diff --git a/drivers/net/netxen/netxen_nic_hw.h b/drivers/net/netxen/netxen_nic_hw.h index ab1112eb..245bf13 100644 --- a/drivers/net/netxen/netxen_nic_hw.h +++ b/drivers/net/netxen/netxen_nic_hw.h @@ -6,12 +6,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, @@ -87,7 +87,7 @@ struct netxen_adapter; *(u32 *)Y = readl((void __iomem*) addr); struct netxen_port; -void netxen_nic_set_link_parameters(struct netxen_port *port); +void netxen_nic_set_link_parameters(struct netxen_adapter *adapter); void netxen_nic_flash_print(struct netxen_adapter *adapter); int netxen_nic_hw_write_wx(struct netxen_adapter *adapter, u64 off, void *data, int len); @@ -220,6 +220,69 @@ typedef enum { _netxen_crb_get_bit(config_word, 1) #define netxen_get_gb_mii_mgmt_notvalid(config_word) \ _netxen_crb_get_bit(config_word, 2) +/* + * NIU XG Pause Ctl Register + * + * Bit 0 : xg0_mask => 1:disable tx pause frames + * Bit 1 : xg0_request => 1:request single pause frame + * Bit 2 : xg0_on_off => 1:request is pause on, 0:off + * Bit 3 : xg1_mask => 1:disable tx pause frames + * Bit 4 : xg1_request => 1:request single pause frame + * Bit 5 : xg1_on_off => 1:request is pause on, 0:off + */ + +#define netxen_xg_set_xg0_mask(config_word) \ + ((config_word) |= 1 << 0) +#define netxen_xg_set_xg1_mask(config_word) \ + ((config_word) |= 1 << 3) + +#define netxen_xg_get_xg0_mask(config_word) \ + _netxen_crb_get_bit((config_word), 0) +#define netxen_xg_get_xg1_mask(config_word) \ + _netxen_crb_get_bit((config_word), 3) + +#define netxen_xg_unset_xg0_mask(config_word) \ + ((config_word) &= ~(1 << 0)) +#define netxen_xg_unset_xg1_mask(config_word) \ + ((config_word) &= ~(1 << 3)) + +/* + * NIU XG Pause Ctl Register + * + * Bit 0 : xg0_mask => 1:disable tx pause frames + * Bit 1 : xg0_request => 1:request single pause frame + * Bit 2 : xg0_on_off => 1:request is pause on, 0:off + * Bit 3 : xg1_mask => 1:disable tx pause frames + * Bit 4 : xg1_request => 1:request single pause frame + * Bit 5 : xg1_on_off => 1:request is pause on, 0:off + */ +#define netxen_gb_set_gb0_mask(config_word) \ + ((config_word) |= 1 << 0) +#define netxen_gb_set_gb1_mask(config_word) \ + ((config_word) |= 1 << 2) +#define netxen_gb_set_gb2_mask(config_word) \ + ((config_word) |= 1 << 4) +#define netxen_gb_set_gb3_mask(config_word) \ + ((config_word) |= 1 << 6) + +#define netxen_gb_get_gb0_mask(config_word) \ + _netxen_crb_get_bit((config_word), 0) +#define netxen_gb_get_gb1_mask(config_word) \ + _netxen_crb_get_bit((config_word), 2) +#define netxen_gb_get_gb2_mask(config_word) \ + _netxen_crb_get_bit((config_word), 4) +#define netxen_gb_get_gb3_mask(config_word) \ + _netxen_crb_get_bit((config_word), 6) + +#define netxen_gb_unset_gb0_mask(config_word) \ + ((config_word) &= ~(1 << 0)) +#define netxen_gb_unset_gb1_mask(config_word) \ + ((config_word) &= ~(1 << 2)) +#define netxen_gb_unset_gb2_mask(config_word) \ + ((config_word) &= ~(1 << 4)) +#define netxen_gb_unset_gb3_mask(config_word) \ + ((config_word) &= ~(1 << 6)) + /* * PHY-Specific MII control/status registers. @@ -452,21 +515,21 @@ typedef enum { ((config) |= (((val) & 0x0f) << 28)) /* Set promiscuous mode for a GbE interface */ -int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port, +int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode); int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, - int port, netxen_niu_prom_mode_t mode); + netxen_niu_prom_mode_t mode); /* get/set the MAC address for a given MAC */ -int netxen_niu_macaddr_get(struct netxen_adapter *adapter, int port, +int netxen_niu_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr); -int netxen_niu_macaddr_set(struct netxen_port *port, +int netxen_niu_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr); /* XG versons */ -int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int port, +int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr); -int netxen_niu_xg_macaddr_set(struct netxen_port *port, +int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr); /* Generic enable for GbE ports. Will detect the speed of the link. */ @@ -475,8 +538,8 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port); int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port); /* Disable a GbE interface */ -int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port); +int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter); -int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port); +int netxen_niu_disable_xg_port(struct netxen_adapter *adapter); #endif /* __NETXEN_NIC_HW_H_ */ diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index f7bb8c9..cf0e96a 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -38,13 +38,15 @@ #include "netxen_nic_phan_reg.h" struct crb_addr_pair { - long addr; - long data; + u32 addr; + u32 data; }; +unsigned long last_schedule_time; + #define NETXEN_MAX_CRB_XFORM 60 static unsigned int crb_addr_xform[NETXEN_MAX_CRB_XFORM]; -#define NETXEN_ADDR_ERROR ((unsigned long ) 0xffffffff ) +#define NETXEN_ADDR_ERROR (0xffffffff) #define crb_addr_transform(name) \ crb_addr_xform[NETXEN_HW_PX_MAP_CRB_##name] = \ @@ -137,7 +139,7 @@ int netxen_init_firmware(struct netxen_adapter *adapter) return err; } /* Window 1 call */ - writel(MPORT_SINGLE_FUNCTION_MODE, + writel(MPORT_MULTI_FUNCTION_MODE, NETXEN_CRB_NORMALIZE(adapter, CRB_MPORT_MODE)); writel(PHAN_INITIALIZE_ACK, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); @@ -224,7 +226,6 @@ void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) adapter->unset_promisc = netxen_niu_set_promiscuous_mode; adapter->phy_read = netxen_niu_gbe_phy_read; adapter->phy_write = netxen_niu_gbe_phy_write; - adapter->init_port = netxen_niu_gbe_init_port; adapter->init_niu = netxen_nic_init_niu_gb; adapter->stop_port = netxen_niu_disable_gbe_port; break; @@ -252,10 +253,10 @@ void netxen_initialize_adapter_ops(struct netxen_adapter *adapter) * netxen_decode_crb_addr(0 - utility to translate from internal Phantom CRB * address to external PCI CRB address. */ -unsigned long netxen_decode_crb_addr(unsigned long addr) +u32 netxen_decode_crb_addr(u32 addr) { int i; - unsigned long base_addr, offset, pci_base; + u32 base_addr, offset, pci_base; crb_addr_transform_setup(); @@ -275,8 +276,8 @@ unsigned long netxen_decode_crb_addr(unsigned long addr) return (pci_base + offset); } -static long rom_max_timeout = 10000; -static long rom_lock_timeout = 1000000; +static long rom_max_timeout = 100; +static long rom_lock_timeout = 10000; static long rom_write_timeout = 700; static inline int rom_lock(struct netxen_adapter *adapter) @@ -404,9 +405,14 @@ static inline int do_rom_fast_write(struct netxen_adapter *adapter, int addr, static inline int do_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp) { + if (jiffies > (last_schedule_time + (8 * HZ))) { + last_schedule_time = jiffies; + schedule(); + } + netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_ADDRESS, addr); netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_ABYTE_CNT, 3); - udelay(70); /* prevent bursting on CRB */ + udelay(100); /* prevent bursting on CRB */ netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_INSTR_OPCODE, 0xb); if (netxen_wait_rom_done(adapter)) { @@ -415,7 +421,7 @@ do_rom_fast_read(struct netxen_adapter *adapter, int addr, int *valp) } /* reset abyte_cnt and dummy_byte_cnt */ netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_ABYTE_CNT, 0); - udelay(70); /* prevent bursting on CRB */ + udelay(100); /* prevent bursting on CRB */ netxen_nic_reg_write(adapter, NETXEN_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); *valp = netxen_nic_reg_read(adapter, NETXEN_ROMUSB_ROM_RDATA); @@ -433,6 +439,7 @@ do_rom_fast_read_words(struct netxen_adapter *adapter, int addr, ret = do_rom_fast_read(adapter, addridx, (int *)bytes); if (ret != 0) break; + *(int *)bytes = cpu_to_le32(*(int *)bytes); bytes += 4; } @@ -490,8 +497,7 @@ static inline int do_rom_fast_write_words(struct netxen_adapter *adapter, int timeout = 0; int data; - data = *(u32*)bytes; - + data = le32_to_cpu((*(u32*)bytes)); ret = do_rom_fast_write(adapter, addridx, data); if (ret < 0) return ret; @@ -499,7 +505,10 @@ static inline int do_rom_fast_write_words(struct netxen_adapter *adapter, while(1) { int data1; - do_rom_fast_read(adapter, addridx, &data1); + ret = do_rom_fast_read(adapter, addridx, &data1); + if (ret < 0) + return ret; + if (data1 == data) break; @@ -717,6 +726,14 @@ netxen_flash_erase_primary(struct netxen_adapter *adapter) return ret; } +void netxen_halt_pegs(struct netxen_adapter *adapter) +{ + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_0 + 0x3c, 1); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_1 + 0x3c, 1); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_2 + 0x3c, 1); + netxen_crb_writelit_adapter(adapter, NETXEN_CRB_PEG_NET_3 + 0x3c, 1); +} + int netxen_flash_unlock(struct netxen_adapter *adapter) { int ret = 0; @@ -745,7 +762,7 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) int n, i; int init_delay = 0; struct crb_addr_pair *buf; - unsigned long off; + u32 off; /* resetall */ status = netxen_nic_get_board_info(adapter); @@ -802,14 +819,13 @@ int netxen_pinit_from_rom(struct netxen_adapter *adapter, int verbose) if (verbose) printk("%s: PCI: 0x%08x == 0x%08x\n", netxen_nic_driver_name, (unsigned int) - netxen_decode_crb_addr((unsigned long) - addr), val); + netxen_decode_crb_addr(addr), val); } for (i = 0; i < n; i++) { - off = netxen_decode_crb_addr((unsigned long)buf[i].addr); + off = netxen_decode_crb_addr(buf[i].addr); if (off == NETXEN_ADDR_ERROR) { - printk(KERN_ERR"CRB init value out of range %lx\n", + printk(KERN_ERR"CRB init value out of range %x\n", buf[i].addr); continue; } @@ -916,6 +932,10 @@ int netxen_initialize_adapter_offload(struct netxen_adapter *adapter) void netxen_free_adapter_offload(struct netxen_adapter *adapter) { if (adapter->dummy_dma.addr) { + writel(0, NETXEN_CRB_NORMALIZE(adapter, + CRB_HOST_DUMMY_BUF_ADDR_HI)); + writel(0, NETXEN_CRB_NORMALIZE(adapter, + CRB_HOST_DUMMY_BUF_ADDR_LO)); pci_free_consistent(adapter->ahw.pdev, NETXEN_HOST_DUMMY_DMA_SIZE, adapter->dummy_dma.addr, @@ -931,7 +951,8 @@ void netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val) if (!pegtune_val) { val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - while (val != PHAN_INITIALIZE_COMPLETE && loops < 200000) { + while (val != PHAN_INITIALIZE_COMPLETE && + val != PHAN_INITIALIZE_ACK && loops < 200000) { udelay(100); schedule(); val = @@ -968,9 +989,7 @@ int netxen_nic_rx_has_work(struct netxen_adapter *adapter) static inline int netxen_nic_check_temp(struct netxen_adapter *adapter) { - int port_num; - struct netxen_port *port; - struct net_device *netdev; + struct net_device *netdev = adapter->netdev; uint32_t temp, temp_state, temp_val; int rv = 0; @@ -984,14 +1003,9 @@ static inline int netxen_nic_check_temp(struct netxen_adapter *adapter) "%s: Device temperature %d degrees C exceeds" " maximum allowed. Hardware has been shut down.\n", netxen_nic_driver_name, temp_val); - for (port_num = 0; port_num < adapter->ahw.max_ports; - port_num++) { - port = adapter->port[port_num]; - netdev = port->netdev; - netif_carrier_off(netdev); - netif_stop_queue(netdev); - } + netif_carrier_off(netdev); + netif_stop_queue(netdev); rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { @@ -1015,29 +1029,23 @@ static inline int netxen_nic_check_temp(struct netxen_adapter *adapter) void netxen_watchdog_task(struct work_struct *work) { - int port_num; - struct netxen_port *port; struct net_device *netdev; struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, watchdog_task); - if (netxen_nic_check_temp(adapter)) + if ((adapter->portnum == 0) && netxen_nic_check_temp(adapter)) return; - for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) { - port = adapter->port[port_num]; - netdev = port->netdev; - - if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) { - printk(KERN_INFO "%s port %d, %s carrier is now ok\n", - netxen_nic_driver_name, port_num, netdev->name); - netif_carrier_on(netdev); - } - - if (netif_queue_stopped(netdev)) - netif_wake_queue(netdev); + netdev = adapter->netdev; + if ((netif_running(netdev)) && !netif_carrier_ok(netdev)) { + printk(KERN_INFO "%s port %d, %s carrier is now ok\n", + netxen_nic_driver_name, adapter->portnum, netdev->name); + netif_carrier_on(netdev); } + if (netif_queue_stopped(netdev)) + netif_wake_queue(netdev); + if (adapter->handle_phy_intr) adapter->handle_phy_intr(adapter); mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); @@ -1052,9 +1060,8 @@ void netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, struct status_desc *desc) { - struct netxen_port *port = adapter->port[netxen_get_sts_port(desc)]; - struct pci_dev *pdev = port->pdev; - struct net_device *netdev = port->netdev; + struct pci_dev *pdev = adapter->pdev; + struct net_device *netdev = adapter->netdev; int index = netxen_get_sts_refhandle(desc); struct netxen_recv_context *recv_ctx = &(adapter->recv_ctx[ctxid]); struct netxen_rx_buffer *buffer; @@ -1104,10 +1111,9 @@ netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, skb = (struct sk_buff *)buffer->skb; if (likely(netxen_get_sts_status(desc) == STATUS_CKSUM_OK)) { - port->stats.csummed++; + adapter->stats.csummed++; skb->ip_summed = CHECKSUM_UNNECESSARY; } - skb->dev = netdev; if (desc_ctx == RCV_DESC_LRO_CTXID) { /* True length was only available on the last pkt */ skb_put(skb, buffer->lro_length); @@ -1125,27 +1131,27 @@ netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, */ switch (ret) { case NET_RX_SUCCESS: - port->stats.uphappy++; + adapter->stats.uphappy++; break; case NET_RX_CN_LOW: - port->stats.uplcong++; + adapter->stats.uplcong++; break; case NET_RX_CN_MOD: - port->stats.upmcong++; + adapter->stats.upmcong++; break; case NET_RX_CN_HIGH: - port->stats.uphcong++; + adapter->stats.uphcong++; break; case NET_RX_DROP: - port->stats.updropped++; + adapter->stats.updropped++; break; default: - port->stats.updunno++; + adapter->stats.updunno++; break; } @@ -1157,14 +1163,13 @@ netxen_process_rcv(struct netxen_adapter *adapter, int ctxid, /* * We just consumed one buffer so post a buffer. */ - adapter->stats.post_called++; buffer->skb = NULL; buffer->state = NETXEN_BUFFER_FREE; buffer->lro_current_frags = 0; buffer->lro_expected_frags = 0; - port->stats.no_rcv++; - port->stats.rxbytes += length; + adapter->stats.no_rcv++; + adapter->stats.rxbytes += length; } /* Process Receive status ring */ @@ -1205,7 +1210,6 @@ u32 netxen_process_rcv_ring(struct netxen_adapter *adapter, int ctxid, int max) /* update the consumer index in phantom */ if (count) { - adapter->stats.process_rcv++; recv_ctx->status_rx_consumer = consumer; recv_ctx->status_rx_producer = producer; @@ -1228,13 +1232,10 @@ int netxen_process_cmd_ring(unsigned long data) int count1 = 0; int count2 = 0; struct netxen_cmd_buffer *buffer; - struct netxen_port *port; /* port #1 */ - struct netxen_port *nport; struct pci_dev *pdev; struct netxen_skb_frag *frag; u32 i; struct sk_buff *skb = NULL; - int p; int done; spin_lock(&adapter->tx_lock); @@ -1246,7 +1247,7 @@ int netxen_process_cmd_ring(unsigned long data) * the netdev which is associated with that device. */ - consumer = *(adapter->cmd_consumer); + consumer = le32_to_cpu(*(adapter->cmd_consumer)); if (last_consumer == consumer) { /* Ring is empty */ DPRINTK(INFO, "last_consumer %d == consumer %d\n", last_consumer, consumer); @@ -1255,7 +1256,6 @@ int netxen_process_cmd_ring(unsigned long data) } adapter->proc_cmd_buf_counter++; - adapter->stats.process_xmit++; /* * Not needed - does not seem to be used anywhere. * adapter->cmd_consumer = consumer; @@ -1264,8 +1264,7 @@ int netxen_process_cmd_ring(unsigned long data) while ((last_consumer != consumer) && (count1 < MAX_STATUS_HANDLE)) { buffer = &adapter->cmd_buf_arr[last_consumer]; - port = adapter->port[buffer->port]; - pdev = port->pdev; + pdev = adapter->pdev; frag = &buffer->frag_array[0]; skb = buffer->skb; if (skb && (cmpxchg(&buffer->skb, skb, 0) == skb)) { @@ -1278,24 +1277,23 @@ int netxen_process_cmd_ring(unsigned long data) PCI_DMA_TODEVICE); } - port->stats.skbfreed++; + adapter->stats.skbfreed++; dev_kfree_skb_any(skb); skb = NULL; } else if (adapter->proc_cmd_buf_counter == 1) { - port->stats.txnullskb++; + adapter->stats.txnullskb++; } - if (unlikely(netif_queue_stopped(port->netdev) - && netif_carrier_ok(port->netdev)) - && ((jiffies - port->netdev->trans_start) > - port->netdev->watchdog_timeo)) { - SCHEDULE_WORK(&port->tx_timeout_task); + if (unlikely(netif_queue_stopped(adapter->netdev) + && netif_carrier_ok(adapter->netdev)) + && ((jiffies - adapter->netdev->trans_start) > + adapter->netdev->watchdog_timeo)) { + SCHEDULE_WORK(&adapter->tx_timeout_task); } last_consumer = get_next_index(last_consumer, adapter->max_tx_desc_count); count1++; } - adapter->stats.noxmitdone += count1; count2 = 0; spin_lock(&adapter->tx_lock); @@ -1315,13 +1313,10 @@ int netxen_process_cmd_ring(unsigned long data) } } if (count1 || count2) { - for (p = 0; p < adapter->ahw.max_ports; p++) { - nport = adapter->port[p]; - if (netif_queue_stopped(nport->netdev) - && (nport->flags & NETXEN_NETDEV_STATUS)) { - netif_wake_queue(nport->netdev); - nport->flags &= ~NETXEN_NETDEV_STATUS; - } + if (netif_queue_stopped(adapter->netdev) + && (adapter->flags & NETXEN_NETDEV_STATUS)) { + netif_wake_queue(adapter->netdev); + adapter->flags &= ~NETXEN_NETDEV_STATUS; } } /* @@ -1340,7 +1335,7 @@ int netxen_process_cmd_ring(unsigned long data) if (adapter->last_cmd_consumer == consumer && (((adapter->cmd_producer + 1) % adapter->max_tx_desc_count) == adapter->last_cmd_consumer)) { - consumer = *(adapter->cmd_consumer); + consumer = le32_to_cpu(*(adapter->cmd_consumer)); } done = (adapter->last_cmd_consumer == consumer); @@ -1367,7 +1362,6 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) netxen_ctx_msg msg = 0; dma_addr_t dma; - adapter->stats.post_called++; rcv_desc = &recv_ctx->rcv_desc[ringid]; producer = rcv_desc->producer; @@ -1420,8 +1414,6 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) if (count) { rcv_desc->begin_alloc = index; rcv_desc->rcv_pending += count; - adapter->stats.lastposted = count; - adapter->stats.posted += count; rcv_desc->producer = producer; if (rcv_desc->rcv_free >= 32) { rcv_desc->rcv_free = 0; @@ -1429,7 +1421,8 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) writel((producer - 1) & (rcv_desc->max_rx_desc_count - 1), NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[0]. + recv_crb_registers[ + adapter->portnum]. rcv_desc_crb[ringid]. crb_rcv_producer_offset)); /* @@ -1442,7 +1435,7 @@ void netxen_post_rx_buffers(struct netxen_adapter *adapter, u32 ctx, u32 ringid) ((producer - 1) & (rcv_desc-> max_rx_desc_count - 1))); - netxen_set_msg_ctxid(msg, 0); + netxen_set_msg_ctxid(msg, adapter->portnum); netxen_set_msg_opcode(msg, NETXEN_RCV_PRODUCER(ringid)); writel(msg, DB_NORMALIZE(adapter, @@ -1464,7 +1457,6 @@ void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, uint32_t ctx, int count = 0; int index = 0; - adapter->stats.post_called++; rcv_desc = &recv_ctx->rcv_desc[ringid]; producer = rcv_desc->producer; @@ -1511,8 +1503,6 @@ void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, uint32_t ctx, if (count) { rcv_desc->begin_alloc = index; rcv_desc->rcv_pending += count; - adapter->stats.lastposted = count; - adapter->stats.posted += count; rcv_desc->producer = producer; if (rcv_desc->rcv_free >= 32) { rcv_desc->rcv_free = 0; @@ -1520,7 +1510,8 @@ void netxen_post_rx_buffers_nodb(struct netxen_adapter *adapter, uint32_t ctx, writel((producer - 1) & (rcv_desc->max_rx_desc_count - 1), NETXEN_CRB_NORMALIZE(adapter, - recv_crb_registers[0]. + recv_crb_registers[ + adapter->portnum]. rcv_desc_crb[ringid]. crb_rcv_producer_offset)); wmb(); @@ -1541,13 +1532,7 @@ int netxen_nic_tx_has_work(struct netxen_adapter *adapter) void netxen_nic_clear_stats(struct netxen_adapter *adapter) { - struct netxen_port *port; - int port_num; - memset(&adapter->stats, 0, sizeof(adapter->stats)); - for (port_num = 0; port_num < adapter->ahw.max_ports; port_num++) { - port = adapter->port[port_num]; - memset(&port->stats, 0, sizeof(port->stats)); - } + return; } diff --git a/drivers/net/netxen/netxen_nic_isr.c b/drivers/net/netxen/netxen_nic_isr.c index be366e4..b213b06 100644 --- a/drivers/net/netxen/netxen_nic_isr.c +++ b/drivers/net/netxen/netxen_nic_isr.c @@ -6,12 +6,12 @@ * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, @@ -40,35 +40,35 @@ */ struct net_device_stats *netxen_nic_get_stats(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct net_device_stats *stats = &port->net_stats; + struct netxen_adapter *adapter = netdev_priv(netdev); + struct net_device_stats *stats = &adapter->net_stats; memset(stats, 0, sizeof(*stats)); /* total packets received */ - stats->rx_packets = port->stats.no_rcv; + stats->rx_packets = adapter->stats.no_rcv; /* total packets transmitted */ - stats->tx_packets = port->stats.xmitedframes + port->stats.xmitfinished; + stats->tx_packets = adapter->stats.xmitedframes + + adapter->stats.xmitfinished; /* total bytes received */ - stats->rx_bytes = port->stats.rxbytes; + stats->rx_bytes = adapter->stats.rxbytes; /* total bytes transmitted */ - stats->tx_bytes = port->stats.txbytes; + stats->tx_bytes = adapter->stats.txbytes; /* bad packets received */ - stats->rx_errors = port->stats.rcvdbadskb; + stats->rx_errors = adapter->stats.rcvdbadskb; /* packet transmit problems */ - stats->tx_errors = port->stats.nocmddescriptor; + stats->tx_errors = adapter->stats.nocmddescriptor; /* no space in linux buffers */ - stats->rx_dropped = port->stats.updropped; + stats->rx_dropped = adapter->stats.updropped; /* no space available in linux */ - stats->tx_dropped = port->stats.txdropped; + stats->tx_dropped = adapter->stats.txdropped; return stats; } -void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno, - u32 link) +void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 link) { - struct net_device *netdev = (adapter->port[portno])->netdev; + struct net_device *netdev = adapter->netdev; if (link) netif_carrier_on(netdev); @@ -76,15 +76,13 @@ void netxen_indicate_link_status(struct netxen_adapter *adapter, u32 portno, netif_carrier_off(netdev); } -void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno, - u32 enable) +void netxen_handle_port_int(struct netxen_adapter *adapter, u32 enable) { __u32 int_src; - struct netxen_port *port; /* This should clear the interrupt source */ if (adapter->phy_read) - adapter->phy_read(adapter, portno, + adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, &int_src); if (int_src == 0) { @@ -92,9 +90,7 @@ void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno, return; } if (adapter->disable_phy_interrupts) - adapter->disable_phy_interrupts(adapter, portno); - - port = adapter->port[portno]; + adapter->disable_phy_interrupts(adapter); if (netxen_get_phy_int_jabber(int_src)) DPRINTK(INFO, "Jabber interrupt \n"); @@ -115,64 +111,57 @@ void netxen_handle_port_int(struct netxen_adapter *adapter, u32 portno, DPRINTK(INFO, "SPEED CHANGED OR LINK STATUS CHANGED \n"); if (adapter->phy_read - && adapter->phy_read(adapter, portno, + && adapter->phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_int_link_status_changed(int_src)) { if (netxen_get_phy_link(status)) { - netxen_niu_gbe_init_port(adapter, - portno); - printk("%s: %s Link UP\n", + printk(KERN_INFO "%s: %s Link UP\n", netxen_nic_driver_name, - port->netdev->name); + adapter->netdev->name); } else { - printk("%s: %s Link DOWN\n", + printk(KERN_INFO "%s: %s Link DOWN\n", netxen_nic_driver_name, - port->netdev->name); + adapter->netdev->name); } - netxen_indicate_link_status(adapter, portno, + netxen_indicate_link_status(adapter, netxen_get_phy_link (status)); } } } if (adapter->enable_phy_interrupts) - adapter->enable_phy_interrupts(adapter, portno); + adapter->enable_phy_interrupts(adapter); } void netxen_nic_isr_other(struct netxen_adapter *adapter) { - u32 portno; + int portno = adapter->portnum; u32 val, linkup, qg_linksup; /* verify the offset */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + val = val >> physical_port[adapter->portnum]; if (val == adapter->ahw.qg_linksup) return; qg_linksup = adapter->ahw.qg_linksup; adapter->ahw.qg_linksup = val; DPRINTK(INFO, "link update 0x%08x\n", val); - for (portno = 0; portno < NETXEN_NIU_MAX_GBE_PORTS; portno++) { - linkup = val & 1; - if (linkup != (qg_linksup & 1)) { - printk(KERN_INFO "%s: %s PORT %d link %s\n", - adapter->port[portno]->netdev->name, - netxen_nic_driver_name, portno, - ((linkup == 0) ? "down" : "up")); - netxen_indicate_link_status(adapter, portno, linkup); - if (linkup) - netxen_nic_set_link_parameters(adapter-> - port[portno]); - } - val = val >> 1; - qg_linksup = qg_linksup >> 1; - } + linkup = val & 1; - adapter->stats.otherints++; + if (linkup != (qg_linksup & 1)) { + printk(KERN_INFO "%s: %s PORT %d link %s\n", + adapter->netdev->name, + netxen_nic_driver_name, portno, + ((linkup == 0) ? "down" : "up")); + netxen_indicate_link_status(adapter, linkup); + if (linkup) + netxen_nic_set_link_parameters(adapter); + } } void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter) @@ -182,26 +171,28 @@ void netxen_nic_gbe_handle_phy_intr(struct netxen_adapter *adapter) void netxen_nic_xgbe_handle_phy_intr(struct netxen_adapter *adapter) { - struct net_device *netdev = adapter->port[0]->netdev; - u32 val; + struct net_device *netdev = adapter->netdev; + u32 val, val1; /* WINDOW = 1 */ val = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_XG_STATE)); + val >>= (physical_port[adapter->portnum] * 8); + val1 = val & 0xff; - if (adapter->ahw.xg_linkup == 1 && val != XG_LINK_UP) { + if (adapter->ahw.xg_linkup == 1 && val1 != XG_LINK_UP) { printk(KERN_INFO "%s: %s NIC Link is down\n", netxen_nic_driver_name, netdev->name); adapter->ahw.xg_linkup = 0; /* read twice to clear sticky bits */ /* WINDOW = 0 */ - netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val); - netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val); + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1); + netxen_nic_read_w0(adapter, NETXEN_NIU_XG_STATUS, &val1); if ((val & 0xffb) != 0xffb) { printk(KERN_INFO "%s ISR: Sync/Align BAD: 0x%08x\n", - netxen_nic_driver_name, val); + netxen_nic_driver_name, val1); } - } else if (adapter->ahw.xg_linkup == 0 && val == XG_LINK_UP) { + } else if (adapter->ahw.xg_linkup == 0 && val1 == XG_LINK_UP) { printk(KERN_INFO "%s: %s NIC Link is up\n", netxen_nic_driver_name, netdev->name); adapter->ahw.xg_linkup = 1; diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 36ba6a1..4e32bb6 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -36,13 +36,11 @@ #include "netxen_nic_hw.h" #include "netxen_nic.h" -#define DEFINE_GLOBAL_RECV_CRB #include "netxen_nic_phan_reg.h" #include <linux/dma-mapping.h> #include <linux/vmalloc.h> - -#define PHAN_VENDOR_ID 0x4040 +#include <net/ip.h> MODULE_DESCRIPTION("NetXen Multi port (1/10) Gigabit Network Driver"); MODULE_LICENSE("GPL"); @@ -78,6 +76,8 @@ static void netxen_nic_poll_controller(struct net_device *netdev); #endif static irqreturn_t netxen_intr(int irq, void *data); +int physical_port[] = {0, 1, 2, 3}; + /* PCI Device ID Table */ static struct pci_device_id netxen_pci_tbl[] __devinitdata = { {PCI_DEVICE(0x4040, 0x0001)}, @@ -95,6 +95,67 @@ MODULE_DEVICE_TABLE(pci, netxen_pci_tbl); struct workqueue_struct *netxen_workq; static void netxen_watchdog(unsigned long); +static inline void netxen_nic_update_cmd_producer(struct netxen_adapter *adapter, + uint32_t crb_producer) +{ + switch (adapter->portnum) { + case 0: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET)); + return; + case 1: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_1)); + return; + case 2: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_2)); + return; + case 3: + writel(crb_producer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_PRODUCER_OFFSET_3)); + return; + default: + printk(KERN_WARNING "We tried to update " + "CRB_CMD_PRODUCER_OFFSET for invalid " + "PCI function id %d\n", + adapter->portnum); + return; + } +} + +static inline void netxen_nic_update_cmd_consumer(struct netxen_adapter *adapter, + u32 crb_consumer) +{ + switch (adapter->portnum) { + case 0: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET)); + return; + case 1: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_1)); + return; + case 2: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_2)); + return; + case 3: + writel(crb_consumer, NETXEN_CRB_NORMALIZE + (adapter, CRB_CMD_CONSUMER_OFFSET_3)); + return; + default: + printk(KERN_WARNING "We tried to update " + "CRB_CMD_PRODUCER_OFFSET for invalid " + "PCI function id %d\n", + adapter->portnum); + return; + } +} + +#define ADAPTER_LIST_SIZE 12 +int netxen_cards_found; + /* * netxen_nic_probe() * @@ -112,26 +173,30 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *netdev = NULL; struct netxen_adapter *adapter = NULL; - struct netxen_port *port = NULL; void __iomem *mem_ptr0 = NULL; void __iomem *mem_ptr1 = NULL; void __iomem *mem_ptr2 = NULL; + unsigned long first_page_group_end; + unsigned long first_page_group_start; + u8 __iomem *db_ptr = NULL; unsigned long mem_base, mem_len, db_base, db_len; - int pci_using_dac, i, err; + int pci_using_dac, i = 0, err; int ring; struct netxen_recv_context *recv_ctx = NULL; struct netxen_rcv_desc_ctx *rcv_desc = NULL; struct netxen_cmd_buffer *cmd_buf_arr = NULL; u64 mac_addr[FLASH_NUM_PORTS + 1]; int valid_mac = 0; + u32 val; + int pci_func_id = PCI_FUNC(pdev->devfn); printk(KERN_INFO "%s \n", netxen_nic_driver_string); - /* In current scheme, we use only PCI function 0 */ - if (PCI_FUNC(pdev->devfn) != 0) { - DPRINTK(ERR, "NetXen function %d will not be enabled.\n", - PCI_FUNC(pdev->devfn)); + + if (pdev->class != 0x020000) { + printk(KERN_ERR"NetXen function %d, class %x will not" + "be enabled.\n",pci_func_id, pdev->class); return -ENODEV; } if ((err = pci_enable_device(pdev))) @@ -158,18 +223,52 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_using_dac = 0; } + + netdev = alloc_etherdev(sizeof(struct netxen_adapter)); + if(!netdev) { + printk(KERN_ERR"%s: Failed to allocate memory for the " + "device block.Check system memory resource" + " usage.\n", netxen_nic_driver_name); + goto err_out_free_res; + } + + SET_MODULE_OWNER(netdev); + SET_NETDEV_DEV(netdev, &pdev->dev); + + adapter = netdev->priv; + memset(adapter, 0 , sizeof(struct netxen_adapter)); + + adapter->ahw.pdev = pdev; + adapter->ahw.pci_func = pci_func_id; + spin_lock_init(&adapter->tx_lock); + spin_lock_init(&adapter->lock); + /* remap phys address */ mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */ mem_len = pci_resource_len(pdev, 0); /* 128 Meg of memory */ - mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE); - mem_ptr1 = - ioremap(mem_base + SECOND_PAGE_GROUP_START, SECOND_PAGE_GROUP_SIZE); - mem_ptr2 = - ioremap(mem_base + THIRD_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); + if (mem_len == NETXEN_PCI_128MB_SIZE) { + mem_ptr0 = ioremap(mem_base, FIRST_PAGE_GROUP_SIZE); + mem_ptr1 = ioremap(mem_base + SECOND_PAGE_GROUP_START, + SECOND_PAGE_GROUP_SIZE); + mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START, + THIRD_PAGE_GROUP_SIZE); + first_page_group_start = FIRST_PAGE_GROUP_START; + first_page_group_end = FIRST_PAGE_GROUP_END; + } else if (mem_len == NETXEN_PCI_32MB_SIZE) { + mem_ptr1 = ioremap(mem_base, SECOND_PAGE_GROUP_SIZE); + mem_ptr2 = ioremap(mem_base + THIRD_PAGE_GROUP_START - + SECOND_PAGE_GROUP_START, THIRD_PAGE_GROUP_SIZE); + first_page_group_start = 0; + first_page_group_end = 0; + } else { + err = -EIO; + goto err_out_free_netdev; + } - if ((mem_ptr0 == 0UL) || (mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) { + if (((mem_ptr0 == 0UL) && (mem_len == NETXEN_PCI_128MB_SIZE)) || + (mem_ptr1 == 0UL) || (mem_ptr2 == 0UL)) { DPRINTK(ERR, "Cannot remap adapter memory aborting.:" "0 -> %p, 1 -> %p, 2 -> %p\n", @@ -199,30 +298,87 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } DPRINTK(INFO, "doorbell ioremaped at %p\n", db_ptr); -/* - * Allocate a adapter structure which will manage all the initialization - * as well as the common resources for all ports... - * all the ports will have pointer to this adapter as well as Adapter - * will have pointers of all the ports structures. - */ + adapter->ahw.pci_base0 = mem_ptr0; + adapter->ahw.first_page_group_start = first_page_group_start; + adapter->ahw.first_page_group_end = first_page_group_end; + adapter->ahw.pci_base1 = mem_ptr1; + adapter->ahw.pci_base2 = mem_ptr2; + adapter->ahw.db_base = db_ptr; + adapter->ahw.db_len = db_len; - /* One adapter structure for all 4 ports.... */ - adapter = kzalloc(sizeof(struct netxen_adapter), GFP_KERNEL); - if (adapter == NULL) { - printk(KERN_ERR "%s: Could not allocate adapter memory:%d\n", - netxen_nic_driver_name, - (int)sizeof(struct netxen_adapter)); - err = -ENOMEM; - goto err_out_dbunmap; - } + adapter->netdev = netdev; + adapter->pdev = pdev; + adapter->portnum = pci_func_id; + + netdev->open = netxen_nic_open; + netdev->stop = netxen_nic_close; + netdev->hard_start_xmit = netxen_nic_xmit_frame; + netdev->get_stats = netxen_nic_get_stats; + netdev->set_multicast_list = netxen_nic_set_multi; + netdev->set_mac_address = netxen_nic_set_mac; + netdev->change_mtu = netxen_nic_change_mtu; + netdev->tx_timeout = netxen_tx_timeout; + netdev->watchdog_timeo = HZ; + + netxen_nic_change_mtu(netdev, netdev->mtu); + + SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); + netdev->poll = netxen_nic_poll; + netdev->weight = NETXEN_NETDEV_WEIGHT; +#ifdef CONFIG_NET_POLL_CONTROLLER + netdev->poll_controller = netxen_nic_poll_controller; +#endif + /* ScatterGather support */ + netdev->features = NETIF_F_SG; + netdev->features |= NETIF_F_IP_CSUM; + netdev->features |= NETIF_F_TSO; + + if (pci_using_dac) + netdev->features |= NETIF_F_HIGHDMA; + + if (pci_enable_msi(pdev)) { + adapter->flags &= ~NETXEN_NIC_MSI_ENABLED; + printk(KERN_WARNING "%s: unable to allocate MSI interrupt" + " error\n", netxen_nic_driver_name); + } else + adapter->flags |= NETXEN_NIC_MSI_ENABLED; - adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS; - adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS; + netdev->irq = pdev->irq; + INIT_WORK(&adapter->tx_timeout_task, netxen_tx_timeout_task); + + /* + * Set the CRB window to invalid. If any register in window 0 is + * accessed it should set the window to 0 and then reset it to 1. + */ + adapter->curr_window = 255; + + /* initialize the adapter */ + netxen_initialize_adapter_hw(adapter); + +#ifdef CONFIG_PPC + if ((adapter->ahw.boardcfg.board_type == + NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) && + (pci_func_id == 2)) + goto err_out_free_adapter; +#endif /* CONFIG_PPC */ + + /* + * Adapter in our case is quad port so initialize it before + * initializing the ports + */ + + netxen_initialize_adapter_ops(adapter); + + adapter->max_tx_desc_count = MAX_CMD_DESCRIPTORS_HOST; + if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB35_4G) || + (adapter->ahw.boardcfg.board_type == + NETXEN_BRDTYPE_P2_SB31_2G)) + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS_1G; + else + adapter->max_rx_desc_count = MAX_RCV_DESCRIPTORS; adapter->max_jumbo_rx_desc_count = MAX_JUMBO_RCV_DESCRIPTORS; adapter->max_lro_rx_desc_count = MAX_LRO_RCV_DESCRIPTORS; - pci_set_drvdata(pdev, adapter); - cmd_buf_arr = (struct netxen_cmd_buffer *)vmalloc(TX_RINGSIZE); if (cmd_buf_arr == NULL) { printk(KERN_ERR @@ -232,6 +388,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_free_adapter; } memset(cmd_buf_arr, 0, TX_RINGSIZE); + adapter->cmd_buf_arr = cmd_buf_arr; for (i = 0; i < MAX_RCV_CTX; ++i) { recv_ctx = &adapter->recv_ctx[i]; @@ -279,33 +436,20 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } - adapter->cmd_buf_arr = cmd_buf_arr; - adapter->ahw.pci_base0 = mem_ptr0; - adapter->ahw.pci_base1 = mem_ptr1; - adapter->ahw.pci_base2 = mem_ptr2; - adapter->ahw.db_base = db_ptr; - adapter->ahw.db_len = db_len; - spin_lock_init(&adapter->tx_lock); - spin_lock_init(&adapter->lock); netxen_initialize_adapter_sw(adapter); /* initialize the buffers in adapter */ -#ifdef CONFIG_IA64 - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); -#endif - /* - * Set the CRB window to invalid. If any register in window 0 is - * accessed it should set the window to 0 and then reset it to 1. - */ - adapter->curr_window = 255; - /* - * Adapter in our case is quad port so initialize it before - * initializing the ports - */ - netxen_initialize_adapter_hw(adapter); /* initialize the adapter */ + /* Mezz cards have PCI function 0,2,3 enabled */ + if ((adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) + && (pci_func_id >= 2)) + adapter->portnum = pci_func_id - 2; - netxen_initialize_adapter_ops(adapter); +#ifdef CONFIG_IA64 + if(adapter->portnum == 0) { + netxen_pinit_from_rom(adapter, 0); + udelay(500); + netxen_load_firmware(adapter); + } +#endif init_timer(&adapter->watchdog_timer); adapter->ahw.xg_linkup = 0; @@ -316,12 +460,12 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) adapter->proc_cmd_buf_counter = 0; adapter->ahw.revision_id = nx_p2_id; - if (pci_enable_msi(pdev)) { - adapter->flags &= ~NETXEN_NIC_MSI_ENABLED; - printk(KERN_WARNING "%s: unable to allocate MSI interrupt" - " error\n", netxen_nic_driver_name); - } else - adapter->flags |= NETXEN_NIC_MSI_ENABLED; + /* make sure Window == 1 */ + netxen_nic_pci_change_crbwindow(adapter, 1); + + netxen_nic_update_cmd_producer(adapter, 0); + netxen_nic_update_cmd_consumer(adapter, 0); + writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO)); if (netxen_is_flash_supported(adapter) == 0 && netxen_get_flash_mac_addr(adapter, mac_addr) == 0) @@ -329,152 +473,118 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) else valid_mac = 0; - /* - * Initialize all the CRB registers here. - */ - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET)); - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_CONSUMER_OFFSET)); - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_HOST_CMD_ADDR_LO)); - - /* do this before waking up pegs so that we have valid dummy dma addr */ - err = netxen_initialize_adapter_offload(adapter); - if (err) { - goto err_out_free_dev; + if (valid_mac) { + unsigned char *p = (unsigned char *)&mac_addr[adapter->portnum]; + netdev->dev_addr[0] = *(p + 5); + netdev->dev_addr[1] = *(p + 4); + netdev->dev_addr[2] = *(p + 3); + netdev->dev_addr[3] = *(p + 2); + netdev->dev_addr[4] = *(p + 1); + netdev->dev_addr[5] = *(p + 0); + + memcpy(netdev->perm_addr, netdev->dev_addr, + netdev->addr_len); + if (!is_valid_ether_addr(netdev->perm_addr)) { + printk(KERN_ERR "%s: Bad MAC address " + "%02x:%02x:%02x:%02x:%02x:%02x.\n", + netxen_nic_driver_name, + netdev->dev_addr[0], + netdev->dev_addr[1], + netdev->dev_addr[2], + netdev->dev_addr[3], + netdev->dev_addr[4], + netdev->dev_addr[5]); + } else { + if (adapter->macaddr_set) + adapter->macaddr_set(adapter, + netdev->dev_addr); + } } - /* Unlock the HW, prompting the boot sequence */ - writel(1, - NETXEN_CRB_NORMALIZE(adapter, NETXEN_ROMUSB_GLB_PEGTUNE_DONE)); - - /* Handshake with the card before we register the devices. */ - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); - - /* initialize the all the ports */ - adapter->active_ports = 0; - - for (i = 0; i < adapter->ahw.max_ports; i++) { - netdev = alloc_etherdev(sizeof(struct netxen_port)); - if (!netdev) { - printk(KERN_ERR "%s: could not allocate netdev for port" - " %d\n", netxen_nic_driver_name, i + 1); + if (adapter->portnum == 0) { + err = netxen_initialize_adapter_offload(adapter); + if (err) + goto err_out_free_rx_buffer; + val = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); + if (val == 0x55555555) { + /* This is the first boot after power up */ + val = readl(NETXEN_CRB_NORMALIZE(adapter, + NETXEN_ROMUSB_GLB_SW_RESET)); + printk(KERN_INFO"NetXen: read 0x%08x for reset reg.\n",val); + if (val != 0x80000f) { + /* clear the register for future unloads/loads */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); + printk(KERN_ERR "ERROR in NetXen HW init sequence.\n"); + err = -ENODEV; goto err_out_free_dev; - } + } - SET_MODULE_OWNER(netdev); - SET_NETDEV_DEV(netdev, &pdev->dev); - - port = netdev_priv(netdev); - port->netdev = netdev; - port->pdev = pdev; - port->adapter = adapter; - port->portnum = i; /* Gigabit port number from 0-3 */ - - netdev->open = netxen_nic_open; - netdev->stop = netxen_nic_close; - netdev->hard_start_xmit = netxen_nic_xmit_frame; - netdev->get_stats = netxen_nic_get_stats; - netdev->set_multicast_list = netxen_nic_set_multi; - netdev->set_mac_address = netxen_nic_set_mac; - netdev->change_mtu = netxen_nic_change_mtu; - netdev->tx_timeout = netxen_tx_timeout; - netdev->watchdog_timeo = HZ; - - SET_ETHTOOL_OPS(netdev, &netxen_nic_ethtool_ops); - netdev->poll = netxen_nic_poll; - netdev->weight = NETXEN_NETDEV_WEIGHT; -#ifdef CONFIG_NET_POLL_CONTROLLER - netdev->poll_controller = netxen_nic_poll_controller; -#endif - /* ScatterGather support */ - netdev->features = NETIF_F_SG; - netdev->features |= NETIF_F_IP_CSUM; - netdev->features |= NETIF_F_TSO; - - if (pci_using_dac) - netdev->features |= NETIF_F_HIGHDMA; - - if (valid_mac) { - unsigned char *p = (unsigned char *)&mac_addr[i]; - netdev->dev_addr[0] = *(p + 5); - netdev->dev_addr[1] = *(p + 4); - netdev->dev_addr[2] = *(p + 3); - netdev->dev_addr[3] = *(p + 2); - netdev->dev_addr[4] = *(p + 1); - netdev->dev_addr[5] = *(p + 0); - - memcpy(netdev->perm_addr, netdev->dev_addr, - netdev->addr_len); - if (!is_valid_ether_addr(netdev->perm_addr)) { - printk(KERN_ERR "%s: Bad MAC address " - "%02x:%02x:%02x:%02x:%02x:%02x.\n", - netxen_nic_driver_name, - netdev->dev_addr[0], - netdev->dev_addr[1], - netdev->dev_addr[2], - netdev->dev_addr[3], - netdev->dev_addr[4], - netdev->dev_addr[5]); - } else { - if (adapter->macaddr_set) - adapter->macaddr_set(port, - netdev->dev_addr); - } + /* clear the register for future unloads/loads */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, + NETXEN_CAM_RAM(0x1fc))); } - INIT_WORK(&port->tx_timeout_task, netxen_tx_timeout_task); - netif_carrier_off(netdev); - netif_stop_queue(netdev); + printk(KERN_INFO "State: 0x%0x\n", + readl(NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE))); - if ((err = register_netdev(netdev))) { - printk(KERN_ERR "%s: register_netdev failed port #%d" - " aborting\n", netxen_nic_driver_name, i + 1); - err = -EIO; - free_netdev(netdev); - goto err_out_free_dev; - } - adapter->port_count++; - adapter->port[i] = port; + /* + * Tell the hardware our version number. + */ + i = (_NETXEN_NIC_LINUX_MAJOR << 16) + | ((_NETXEN_NIC_LINUX_MINOR << 8)) + | (_NETXEN_NIC_LINUX_SUBVERSION); + writel(i, NETXEN_CRB_NORMALIZE(adapter, CRB_DRIVER_VERSION)); + + /* Unlock the HW, prompting the boot sequence */ + writel(1, + NETXEN_CRB_NORMALIZE(adapter, + NETXEN_ROMUSB_GLB_PEGTUNE_DONE)); + /* Handshake with the card before we register the devices. */ + netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); } - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - netxen_pinit_from_rom(adapter, 0); - udelay(500); - netxen_load_firmware(adapter); - netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); /* - * delay a while to ensure that the Pegs are up & running. - * Otherwise, we might see some flaky behaviour. + * See if the firmware gave us a virtual-physical port mapping. */ - udelay(100); + i = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_V2P(adapter->portnum))); + if (i != 0x55555555) + physical_port[adapter->portnum] = i; + + netif_carrier_off(netdev); + netif_stop_queue(netdev); + + if ((err = register_netdev(netdev))) { + printk(KERN_ERR "%s: register_netdev failed port #%d" + " aborting\n", netxen_nic_driver_name, + adapter->portnum); + err = -EIO; + goto err_out_free_dev; + } + + pci_set_drvdata(pdev, adapter); switch (adapter->ahw.board_type) { - case NETXEN_NIC_GBE: - printk("%s: QUAD GbE board initialized\n", - netxen_nic_driver_name); - break; + case NETXEN_NIC_GBE: + printk(KERN_INFO "%s: QUAD GbE board initialized\n", + netxen_nic_driver_name); + break; - case NETXEN_NIC_XGBE: - printk("%s: XGbE board initialized\n", netxen_nic_driver_name); - break; + case NETXEN_NIC_XGBE: + printk(KERN_INFO "%s: XGbE board initialized\n", + netxen_nic_driver_name); + break; } adapter->driver_mismatch = 0; return 0; - err_out_free_dev: - if (adapter->flags & NETXEN_NIC_MSI_ENABLED) - pci_disable_msi(pdev); - for (i = 0; i < adapter->port_count; i++) { - port = adapter->port[i]; - if ((port) && (port->netdev)) { - unregister_netdev(port->netdev); - free_netdev(port->netdev); - } - } - - netxen_free_adapter_offload(adapter); +err_out_free_dev: + if (adapter->portnum == 0) + netxen_free_adapter_offload(adapter); - err_out_free_rx_buffer: +err_out_free_rx_buffer: for (i = 0; i < MAX_RCV_CTX; ++i) { recv_ctx = &adapter->recv_ctx[i]; for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) { @@ -487,15 +597,16 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } vfree(cmd_buf_arr); - err_out_free_adapter: +err_out_free_adapter: + if (adapter->flags & NETXEN_NIC_MSI_ENABLED) + pci_disable_msi(pdev); + pci_set_drvdata(pdev, NULL); - kfree(adapter); - err_out_dbunmap: if (db_ptr) iounmap(db_ptr); - err_out_iounmap: +err_out_iounmap: if (mem_ptr0) iounmap(mem_ptr0); if (mem_ptr1) @@ -503,9 +614,13 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (mem_ptr2) iounmap(mem_ptr2); - err_out_free_res: +err_out_free_netdev: + free_netdev(netdev); + +err_out_free_res: pci_release_regions(pdev); - err_out_disable_pdev: + +err_out_disable_pdev: pci_disable_device(pdev); return err; } @@ -513,7 +628,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) static void __devexit netxen_nic_remove(struct pci_dev *pdev) { struct netxen_adapter *adapter; - struct netxen_port *port; + struct net_device *netdev; struct netxen_rx_buffer *buffer; struct netxen_recv_context *recv_ctx; struct netxen_rcv_desc_ctx *rcv_desc; @@ -524,35 +639,34 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) if (adapter == NULL) return; - netxen_nic_stop_all_ports(adapter); - /* leave the hw in the same state as reboot */ - netxen_pinit_from_rom(adapter, 0); - writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); - netxen_load_firmware(adapter); - netxen_free_adapter_offload(adapter); - - udelay(500); /* Delay for a while to drain the DMA engines */ - for (i = 0; i < adapter->port_count; i++) { - port = adapter->port[i]; - if ((port) && (port->netdev)) { - unregister_netdev(port->netdev); - free_netdev(port->netdev); - } - } + netdev = adapter->netdev; + + netxen_nic_disable_int(adapter); + if (adapter->irq) + free_irq(adapter->irq, adapter); + + if (adapter->stop_port) + adapter->stop_port(adapter); if ((adapter->flags & NETXEN_NIC_MSI_ENABLED)) pci_disable_msi(pdev); - pci_set_drvdata(pdev, NULL); - if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) - netxen_free_hw_resources(adapter); - iounmap(adapter->ahw.db_base); - iounmap(adapter->ahw.pci_base0); - iounmap(adapter->ahw.pci_base1); - iounmap(adapter->ahw.pci_base2); + if (adapter->portnum == 0) + netxen_free_adapter_offload(adapter); + + if (adapter->irq) + free_irq(adapter->irq, adapter); + if(adapter->portnum == 0) { + /* leave the hw in the same state as reboot */ + writel(0, NETXEN_CRB_NORMALIZE(adapter, CRB_CMDPEG_STATE)); + netxen_pinit_from_rom(adapter, 0); + udelay(500); + netxen_load_firmware(adapter); + netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); + } - pci_release_regions(pdev); - pci_disable_device(pdev); + if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) + netxen_free_hw_resources(adapter); for (ctxid = 0; ctxid < MAX_RCV_CTX; ++ctxid) { recv_ctx = &adapter->recv_ctx[ctxid]; @@ -572,8 +686,20 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) } } + unregister_netdev(netdev); + vfree(adapter->cmd_buf_arr); - kfree(adapter); + + iounmap(adapter->ahw.db_base); + iounmap(adapter->ahw.pci_base0); + iounmap(adapter->ahw.pci_base1); + iounmap(adapter->ahw.pci_base2); + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + free_netdev(netdev); } /* @@ -582,8 +708,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) */ static int netxen_nic_open(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = (struct netxen_adapter *)netdev->priv; int err = 0; int ctx, ring; @@ -594,8 +719,6 @@ static int netxen_nic_open(struct net_device *netdev) return -EIO; } netxen_nic_flash_print(adapter); - if (adapter->init_niu) - adapter->init_niu(adapter); /* setup all the resources for the Phantom... */ /* this include the descriptors for rcv, tx, and status */ @@ -606,21 +729,14 @@ static int netxen_nic_open(struct net_device *netdev) err); return err; } - if (adapter->init_port - && adapter->init_port(adapter, port->portnum) != 0) { - printk(KERN_ERR "%s: Failed to initialize port %d\n", - netxen_nic_driver_name, port->portnum); - netxen_free_hw_resources(adapter); - return -EIO; - } for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { for (ring = 0; ring < NUM_RCV_DESC_RINGS; ring++) netxen_post_rx_buffers(adapter, ctx, ring); } adapter->irq = adapter->ahw.pdev->irq; - err = request_irq(adapter->ahw.pdev->irq, &netxen_intr, - IRQF_SHARED | IRQF_SAMPLE_RANDOM, - netdev->name, adapter); + err = request_irq(adapter->ahw.pdev->irq, netxen_intr, + SA_SHIRQ | SA_SAMPLE_RANDOM, netdev->name, + adapter); if (err) { printk(KERN_ERR "request_irq failed with: %d\n", err); netxen_free_hw_resources(adapter); @@ -629,23 +745,28 @@ static int netxen_nic_open(struct net_device *netdev) adapter->is_up = NETXEN_ADAPTER_UP_MAGIC; } - adapter->active_ports++; - if (adapter->active_ports == 1) { - if (!adapter->driver_mismatch) - mod_timer(&adapter->watchdog_timer, jiffies); + if (!adapter->driver_mismatch) + mod_timer(&adapter->watchdog_timer, jiffies); - netxen_nic_enable_int(adapter); - } + netxen_nic_enable_int(adapter); /* Done here again so that even if phantom sw overwrote it, * we set it */ if (adapter->macaddr_set) - adapter->macaddr_set(port, netdev->dev_addr); - netxen_nic_set_link_parameters(port); + adapter->macaddr_set(adapter, netdev->dev_addr); + if (adapter->init_port + && adapter->init_port(adapter, adapter->portnum) != 0) { + del_timer_sync(&adapter->watchdog_timer); + printk(KERN_ERR "%s: Failed to initialize port %d\n", + netxen_nic_driver_name, adapter->portnum); + return -EIO; + } + + netxen_nic_set_link_parameters(adapter); netxen_nic_set_multi(netdev); if (adapter->set_mtu) - adapter->set_mtu(port, netdev->mtu); + adapter->set_mtu(adapter, netdev->mtu); if (!adapter->driver_mismatch) netif_start_queue(netdev); @@ -658,8 +779,7 @@ static int netxen_nic_open(struct net_device *netdev) */ static int netxen_nic_close(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int i, j; struct netxen_cmd_buffer *cmd_buff; struct netxen_skb_frag *buffrag; @@ -667,49 +787,39 @@ static int netxen_nic_close(struct net_device *netdev) netif_carrier_off(netdev); netif_stop_queue(netdev); - adapter->active_ports--; - - if (!adapter->active_ports) { - netxen_nic_disable_int(adapter); - if (adapter->irq) - free_irq(adapter->irq, adapter); - cmd_buff = adapter->cmd_buf_arr; - for (i = 0; i < adapter->max_tx_desc_count; i++) { - buffrag = cmd_buff->frag_array; + cmd_buff = adapter->cmd_buf_arr; + for (i = 0; i < adapter->max_tx_desc_count; i++) { + buffrag = cmd_buff->frag_array; + if (buffrag->dma) { + pci_unmap_single(adapter->pdev, buffrag->dma, + buffrag->length, PCI_DMA_TODEVICE); + buffrag->dma = (u64) NULL; + } + for (j = 0; j < cmd_buff->frag_count; j++) { + buffrag++; if (buffrag->dma) { - pci_unmap_single(port->pdev, buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); + pci_unmap_page(adapter->pdev, buffrag->dma, + buffrag->length, + PCI_DMA_TODEVICE); buffrag->dma = (u64) NULL; } - for (j = 0; j < cmd_buff->frag_count; j++) { - buffrag++; - if (buffrag->dma) { - pci_unmap_page(port->pdev, - buffrag->dma, - buffrag->length, - PCI_DMA_TODEVICE); - buffrag->dma = (u64) NULL; - } - } - /* Free the skb we received in netxen_nic_xmit_frame */ - if (cmd_buff->skb) { - dev_kfree_skb_any(cmd_buff->skb); - cmd_buff->skb = NULL; - } - cmd_buff++; } - FLUSH_SCHEDULED_WORK(); - del_timer_sync(&adapter->watchdog_timer); + /* Free the skb we received in netxen_nic_xmit_frame */ + if (cmd_buff->skb) { + dev_kfree_skb_any(cmd_buff->skb); + cmd_buff->skb = NULL; + } + cmd_buff++; } + FLUSH_SCHEDULED_WORK(); + del_timer_sync(&adapter->watchdog_timer); return 0; } static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); struct netxen_hardware_context *hw = &adapter->ahw; unsigned int first_seg_len = skb->len - skb->data_len; struct netxen_skb_frag *buffrag; @@ -727,12 +837,12 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) u32 last_cmd_consumer = 0; int no_of_desc; - port->stats.xmitcalled++; + adapter->stats.xmitcalled++; frag_count = skb_shinfo(skb)->nr_frags + 1; if (unlikely(skb->len <= 0)) { dev_kfree_skb_any(skb); - port->stats.badskblen++; + adapter->stats.badskblen++; return NETDEV_TX_OK; } @@ -741,7 +851,7 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) "too large, can handle only %d frags\n", netxen_nic_driver_name, netdev->name, frag_count, MAX_BUFFERS_PER_CMD); - port->stats.txdropped++; + adapter->stats.txdropped++; if ((++dropped_packet & 0xff) == 0xff) printk("%s: %s droppped packets = %d\n", netxen_nic_driver_name, netdev->name, @@ -758,7 +868,7 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) */ retry_getting_window: spin_lock_bh(&adapter->tx_lock); - if (adapter->total_threads == MAX_XMIT_PRODUCERS) { + if (adapter->total_threads >= MAX_XMIT_PRODUCERS) { spin_unlock_bh(&adapter->tx_lock); /* * Yield CPU @@ -778,9 +888,8 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if (skb_shinfo(skb)->gso_size > 0) { no_of_desc++; - if (((skb->nh.iph)->ihl * sizeof(u32)) + - ((skb->h.th)->doff * sizeof(u32)) + - sizeof(struct ethhdr) > + if ((ip_hdrlen(skb) + tcp_hdrlen(skb) + + sizeof(struct ethhdr)) > (sizeof(struct cmd_desc_type0) - 2)) { no_of_desc++; } @@ -792,15 +901,8 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) if ((k + no_of_desc) >= ((last_cmd_consumer <= k) ? last_cmd_consumer + max_tx_desc_count : last_cmd_consumer)) { - port->stats.nocmddescriptor++; - DPRINTK(ERR, "No command descriptors available," - " producer = %d, consumer = %d count=%llu," - " dropping packet\n", producer, - adapter->last_cmd_consumer, - port->stats.nocmddescriptor); - netif_stop_queue(netdev); - port->flags |= NETXEN_NETDEV_STATUS; + adapter->flags |= NETXEN_NETDEV_STATUS; spin_unlock_bh(&adapter->tx_lock); return NETDEV_TX_BUSY; } @@ -828,16 +930,17 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) pbuf->skb = skb; pbuf->cmd = TX_ETHER_PKT; pbuf->frag_count = frag_count; - pbuf->port = port->portnum; + pbuf->port = adapter->portnum; buffrag = &pbuf->frag_array[0]; - buffrag->dma = pci_map_single(port->pdev, skb->data, first_seg_len, + buffrag->dma = pci_map_single(adapter->pdev, skb->data, first_seg_len, PCI_DMA_TODEVICE); buffrag->length = first_seg_len; netxen_set_cmd_desc_totallength(hwdesc, skb->len); netxen_set_cmd_desc_num_of_buff(hwdesc, frag_count); netxen_set_cmd_desc_opcode(hwdesc, TX_ETHER_PKT); - netxen_set_cmd_desc_port(hwdesc, port->portnum); + netxen_set_cmd_desc_port(hwdesc, adapter->portnum); + netxen_set_cmd_desc_ctxid(hwdesc, adapter->portnum); hwdesc->buffer1_length = cpu_to_le16(first_seg_len); hwdesc->addr_buffer1 = cpu_to_le64(buffrag->dma); @@ -860,7 +963,7 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) offset = frag->page_offset; temp_len = len; - temp_dma = pci_map_page(port->pdev, frag->page, offset, + temp_dma = pci_map_page(adapter->pdev, frag->page, offset, len, PCI_DMA_TODEVICE); buffrag++; @@ -920,26 +1023,36 @@ static int netxen_nic_xmit_frame(struct sk_buff *skb, struct net_device *netdev) /* copy the next 64 bytes - should be enough except * for pathological case */ - memcpy((void *)hwdesc, (void *)(skb->data) + - first_hdr_len, hdr_len - first_hdr_len); + skb_copy_from_linear_data_offset(skb, first_hdr_len, + hwdesc, + (hdr_len - + first_hdr_len)); producer = get_next_index(producer, max_tx_desc_count); } } + + i = netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]); + + hw->cmd_desc_head[saved_producer].flags_opcode = + cpu_to_le16(hw->cmd_desc_head[saved_producer].flags_opcode); + hw->cmd_desc_head[saved_producer].num_of_buffers_total_length = + cpu_to_le32(hw->cmd_desc_head[saved_producer]. + num_of_buffers_total_length); + spin_lock_bh(&adapter->tx_lock); - port->stats.txbytes += - netxen_get_cmd_desc_totallength(&hw->cmd_desc_head[saved_producer]); + adapter->stats.txbytes += i; + /* Code to update the adapter considering how many producer threads are currently working */ if ((--adapter->num_threads) == 0) { /* This is the last thread */ u32 crb_producer = adapter->cmd_producer; - writel(crb_producer, - NETXEN_CRB_NORMALIZE(adapter, CRB_CMD_PRODUCER_OFFSET)); + netxen_nic_update_cmd_producer(adapter, crb_producer); wmb(); adapter->total_threads = 0; } - port->stats.xmitfinished++; + adapter->stats.xmitfinished++; spin_unlock_bh(&adapter->tx_lock); netdev->trans_start = jiffies; @@ -959,27 +1072,26 @@ static void netxen_watchdog(unsigned long v) static void netxen_tx_timeout(struct net_device *netdev) { - struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev); - - SCHEDULE_WORK(&port->tx_timeout_task); + struct netxen_adapter *adapter = (struct netxen_adapter *) + netdev_priv(netdev); + SCHEDULE_WORK(&adapter->tx_timeout_task); } static void netxen_tx_timeout_task(struct work_struct *work) { - struct netxen_port *port = - container_of(work, struct netxen_port, tx_timeout_task); - struct net_device *netdev = port->netdev; + struct netxen_adapter *adapter = + container_of(work, struct netxen_adapter, tx_timeout_task); unsigned long flags; printk(KERN_ERR "%s %s: transmit timeout, resetting.\n", - netxen_nic_driver_name, netdev->name); - - spin_lock_irqsave(&port->adapter->lock, flags); - netxen_nic_close(netdev); - netxen_nic_open(netdev); - spin_unlock_irqrestore(&port->adapter->lock, flags); - netdev->trans_start = jiffies; - netif_wake_queue(netdev); + netxen_nic_driver_name, adapter->netdev->name); + + spin_lock_irqsave(&adapter->lock, flags); + netxen_nic_close(adapter->netdev); + netxen_nic_open(adapter->netdev); + spin_unlock_irqrestore(&adapter->lock, flags); + adapter->netdev->trans_start = jiffies; + netif_wake_queue(adapter->netdev); } static int @@ -988,17 +1100,16 @@ netxen_handle_int(struct netxen_adapter *adapter, struct net_device *netdev) u32 ret = 0; DPRINTK(INFO, "Entered handle ISR\n"); - adapter->stats.ints++; if (!(adapter->flags & NETXEN_NIC_MSI_ENABLED)) { int count = 0; u32 mask; - mask = readl(pci_base_offset(adapter, ISR_INT_VECTOR)); - if ((mask & 0x80) == 0) { - /* not our interrupt */ + u32 our_int = 0; + our_int = readl(NETXEN_CRB_NORMALIZE(adapter, CRB_INT_VECTOR)); + /* not our interrupt */ + if ((our_int & (0x80 << adapter->portnum)) == 0) return ret; - } netxen_nic_disable_int(adapter); /* Window = 0 or 1 */ do { @@ -1010,7 +1121,6 @@ netxen_handle_int(struct netxen_adapter *adapter, struct net_device *netdev) printk("Could not disable interrupt completely\n"); } - adapter->stats.hostints++; if (netxen_nic_rx_has_work(adapter) || netxen_nic_tx_has_work(adapter)) { if (netif_rx_schedule_prep(netdev)) { @@ -1044,33 +1154,24 @@ netxen_handle_int(struct netxen_adapter *adapter, struct net_device *netdev) irqreturn_t netxen_intr(int irq, void *data) { struct netxen_adapter *adapter; - struct netxen_port *port; struct net_device *netdev; - int i; if (unlikely(!irq)) { return IRQ_NONE; /* Not our interrupt */ } adapter = (struct netxen_adapter *)data; - for (i = 0; i < adapter->ahw.max_ports; i++) { - port = adapter->port[i]; - netdev = port->netdev; - - /* process our status queue (for all 4 ports) */ - if (netif_running(netdev)) { - netxen_handle_int(adapter, netdev); - break; - } - } + netdev = adapter->netdev; + /* process our status queue (for all 4 ports) */ + if (netif_running(netdev)) + netxen_handle_int(adapter, netdev); return IRQ_HANDLED; } static int netxen_nic_poll(struct net_device *netdev, int *budget) { - struct netxen_port *port = (struct netxen_port *)netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); int work_to_do = min(*budget, netdev->quota); int done = 1; int ctx; @@ -1078,7 +1179,6 @@ static int netxen_nic_poll(struct net_device *netdev, int *budget) int work_done = 0; DPRINTK(INFO, "polling for %d descriptors\n", *budget); - port->stats.polled++; work_done = 0; for (ctx = 0; ctx < MAX_RCV_CTX; ++ctx) { @@ -1122,8 +1222,7 @@ static int netxen_nic_poll(struct net_device *netdev, int *budget) #ifdef CONFIG_NET_POLL_CONTROLLER static void netxen_nic_poll_controller(struct net_device *netdev) { - struct netxen_port *port = netdev_priv(netdev); - struct netxen_adapter *adapter = port->adapter; + struct netxen_adapter *adapter = netdev_priv(netdev); disable_irq(adapter->irq); netxen_intr(adapter->irq, adapter); enable_irq(adapter->irq); @@ -1154,8 +1253,8 @@ static void __exit netxen_exit_module(void) /* * Wait for some time to allow the dma to drain, if any. */ - destroy_workqueue(netxen_workq); pci_unregister_driver(&netxen_driver); + destroy_workqueue(netxen_workq); } module_exit(netxen_exit_module); diff --git a/drivers/net/netxen/netxen_nic_niu.c b/drivers/net/netxen/netxen_nic_niu.c index 40d7003..cef90a7 100644 --- a/drivers/net/netxen/netxen_nic_niu.c +++ b/drivers/net/netxen/netxen_nic_niu.c @@ -88,12 +88,13 @@ static inline int phy_unlock(struct netxen_adapter *adapter) * -1 on error * */ -int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, - long reg, __u32 * readval) +int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long reg, + __u32 * readval) { long timeout = 0; long result = 0; long restore = 0; + long phy = physical_port[adapter->portnum]; __u32 address; __u32 command; __u32 status; @@ -183,12 +184,13 @@ int netxen_niu_gbe_phy_read(struct netxen_adapter *adapter, long phy, * -1 on error * */ -int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, - long phy, long reg, __u32 val) +int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, long reg, + __u32 val) { long timeout = 0; long result = 0; long restore = 0; + long phy = physical_port[adapter->portnum]; __u32 address; __u32 command; __u32 status; @@ -258,15 +260,13 @@ int netxen_niu_gbe_phy_write(struct netxen_adapter *adapter, return result; } -int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_enable_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x3f); return 0; } -int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; __u32 enable = 0; @@ -275,7 +275,7 @@ int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, netxen_set_phy_int_speed_changed(enable); if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE, enable)) result = -EIO; @@ -283,38 +283,34 @@ int netxen_niu_gbe_enable_phy_interrupts(struct netxen_adapter *adapter, return result; } -int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_disable_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_INT_MASK, 0x7f); return 0; } -int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_disable_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_ENABLE, 0)) result = -EIO; return result; } -int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_xgbe_clear_phy_interrupts(struct netxen_adapter *adapter) { netxen_crb_writelit_adapter(adapter, NETXEN_NIU_ACTIVE_INT, -1); return 0; } -int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter, - int port) +int netxen_niu_gbe_clear_phy_interrupts(struct netxen_adapter *adapter) { int result = 0; if (0 != - netxen_niu_gbe_phy_write(adapter, port, + netxen_niu_gbe_phy_write(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, -EIO)) result = -EIO; @@ -355,9 +351,9 @@ void netxen_niu_gbe_set_mii_mode(struct netxen_adapter *adapter, 0x5); } - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); } @@ -393,9 +389,9 @@ void netxen_niu_gbe_set_gmii_mode(struct netxen_adapter *adapter, 0x5); } - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); } @@ -404,11 +400,11 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) int result = 0; __u32 status; if (adapter->disable_phy_interrupts) - adapter->disable_phy_interrupts(adapter, port); + adapter->disable_phy_interrupts(adapter); mdelay(2); if (0 == - netxen_niu_gbe_phy_read(adapter, port, + netxen_niu_gbe_phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status)) { if (netxen_get_phy_link(status)) { @@ -439,13 +435,13 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) | NETXEN_GB_MAC_ENABLE_TX_RX | NETXEN_GB_MAC_PAUSED_FRMS); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); - if (netxen_niu_gbe_enable_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_enable_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR enabling PHY interrupts\n"); - if (netxen_niu_gbe_clear_phy_interrupts(adapter, port)) + if (netxen_niu_gbe_clear_phy_interrupts(adapter)) printk(KERN_ERR PFX "ERROR clearing PHY interrupts\n"); result = -1; @@ -458,26 +454,18 @@ int netxen_niu_gbe_init_port(struct netxen_adapter *adapter, int port) int netxen_niu_xg_init_port(struct netxen_adapter *adapter, int port) { - long reg = 0, ret = 0; + u32 reg; + u32 portnum = physical_port[adapter->portnum]; - if (adapter->ahw.boardcfg.board_type == NETXEN_BRDTYPE_P2_SB31_10G_IMEZ) { - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XG1_CONFIG_0, 0x5); - /* XXX hack for Mez cards: both ports in promisc mode */ - netxen_nic_hw_read_wx(adapter, - NETXEN_NIU_XGE_CONFIG_1, ®, 4); - reg = (reg | 0x2000UL); - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XGE_CONFIG_1, reg); - reg = 0; - netxen_nic_hw_read_wx(adapter, - NETXEN_NIU_XG1_CONFIG_1, ®, 4); - reg = (reg | 0x2000UL); - netxen_crb_writelit_adapter(adapter, - NETXEN_NIU_XG1_CONFIG_1, reg); - } + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_0+(0x10000*portnum), 0x5); + netxen_nic_hw_read_wx(adapter, + NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), ®, 4); + reg = (reg & ~0x2000UL); + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_1+(0x10000*portnum), reg); - return ret; + return 0; } /* @@ -498,7 +486,7 @@ int netxen_niu_gbe_handle_phy_interrupt(struct netxen_adapter *adapter, * The read of the PHY INT status will clear the pending * interrupt status */ - if (netxen_niu_gbe_phy_read(adapter, port, + if (netxen_niu_gbe_phy_read(adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_INT_STATUS, &int_src) != 0) result = -EINVAL; @@ -535,7 +523,7 @@ int netxen_niu_gbe_handle_phy_interrupt(struct netxen_adapter *adapter, printk(KERN_INFO PFX "speed_changed or link status changed"); if (netxen_niu_gbe_phy_read - (adapter, port, + (adapter, NETXEN_NIU_GB_MII_MGMT_ADDR_PHY_STATUS, &status) == 0) { if (netxen_get_phy_speed(status) == 2) { @@ -581,10 +569,11 @@ int netxen_niu_gbe_handle_phy_interrupt(struct netxen_adapter *adapter, * Note that the passed-in value must already be in network byte order. */ int netxen_niu_macaddr_get(struct netxen_adapter *adapter, - int phy, netxen_ethernet_macaddr_t * addr) + netxen_ethernet_macaddr_t * addr) { u32 stationhigh; u32 stationlow; + int phy = physical_port[adapter->portnum]; u8 val[8]; if (addr == NULL) @@ -610,13 +599,12 @@ int netxen_niu_macaddr_get(struct netxen_adapter *adapter, * Set the station MAC address. * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_macaddr_set(struct netxen_port *port, +int netxen_niu_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr) { u8 temp[4]; u32 val; - struct netxen_adapter *adapter = port->adapter; - int phy = port->portnum; + int phy = physical_port[adapter->portnum]; unsigned char mac_addr[6]; int i; @@ -634,7 +622,7 @@ int netxen_niu_macaddr_set(struct netxen_port *port, (adapter, NETXEN_NIU_GB_STATION_ADDR_0(phy), &val, 4)) return -2; - netxen_niu_macaddr_get(adapter, phy, + netxen_niu_macaddr_get(adapter, (netxen_ethernet_macaddr_t *) mac_addr); if (memcmp(mac_addr, addr, 6) == 0) break; @@ -642,7 +630,7 @@ int netxen_niu_macaddr_set(struct netxen_port *port, if (i == 10) { printk(KERN_ERR "%s: cannot set Mac addr for %s\n", - netxen_nic_driver_name, port->netdev->name); + netxen_nic_driver_name, adapter->netdev->name); printk(KERN_ERR "MAC address set: " "%02x:%02x:%02x:%02x:%02x:%02x.\n", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); @@ -735,13 +723,13 @@ int netxen_niu_enable_gbe_port(struct netxen_adapter *adapter, } /* Disable a GbE interface */ -int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port) +int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter) { __u32 mac_cfg0; + u32 port = physical_port[adapter->portnum]; if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) return -EINVAL; - mac_cfg0 = 0; netxen_gb_soft_reset(mac_cfg0); if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_GB_MAC_CONFIG_0(port), @@ -751,13 +739,13 @@ int netxen_niu_disable_gbe_port(struct netxen_adapter *adapter, int port) } /* Disable an XG interface */ -int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port) +int netxen_niu_disable_xg_port(struct netxen_adapter *adapter) { __u32 mac_cfg; + u32 port = physical_port[adapter->portnum]; if (port != 0) return -EINVAL; - mac_cfg = 0; netxen_xg_soft_reset(mac_cfg); if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_CONFIG_0, @@ -767,10 +755,11 @@ int netxen_niu_disable_xg_port(struct netxen_adapter *adapter, int port) } /* Set promiscuous mode for a GbE interface */ -int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port, +int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, netxen_niu_prom_mode_t mode) { __u32 reg; + u32 port = physical_port[adapter->portnum]; if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) return -EINVAL; @@ -824,25 +813,50 @@ int netxen_niu_set_promiscuous_mode(struct netxen_adapter *adapter, int port, * Set the MAC address for an XG port * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_xg_macaddr_set(struct netxen_port *port, +int netxen_niu_xg_macaddr_set(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t addr) { + int phy = physical_port[adapter->portnum]; u8 temp[4]; u32 val; - struct netxen_adapter *adapter = port->adapter; + + if ((phy < 0) || (phy > NETXEN_NIU_MAX_XG_PORTS)) + return -EIO; temp[0] = temp[1] = 0; - memcpy(temp + 2, addr, 2); - val = le32_to_cpu(*(__le32 *)temp); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, - &val, 4)) + switch (phy) { + case 0: + memcpy(temp + 2, addr, 2); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_1, + &val, 4)) return -EIO; - memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); - val = le32_to_cpu(*(__le32 *)temp); - if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, - &val, 4)) + memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XGE_STATION_ADDR_0_HI, + &val, 4)) + return -EIO; + break; + + case 1: + memcpy(temp + 2, addr, 2); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_1, + &val, 4)) + return -EIO; + + memcpy(&temp, ((u8 *) addr) + 2, sizeof(__le32)); + val = le32_to_cpu(*(__le32 *)temp); + if (netxen_nic_hw_write_wx(adapter, NETXEN_NIU_XG1_STATION_ADDR_0_HI, + &val, 4)) return -EIO; + break; + + default: + printk(KERN_ERR "Unknown port %d\n", phy); + break; + } return 0; } @@ -851,9 +865,10 @@ int netxen_niu_xg_macaddr_set(struct netxen_port *port, * Return the current station MAC address. * Note that the passed-in value must already be in network byte order. */ -int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int phy, +int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, netxen_ethernet_macaddr_t * addr) { + int phy = physical_port[adapter->portnum]; u32 stationhigh; u32 stationlow; u8 val[8]; @@ -878,21 +893,24 @@ int netxen_niu_xg_macaddr_get(struct netxen_adapter *adapter, int phy, } int netxen_niu_xg_set_promiscuous_mode(struct netxen_adapter *adapter, - int port, netxen_niu_prom_mode_t mode) + netxen_niu_prom_mode_t mode) { __u32 reg; + u32 port = physical_port[adapter->portnum]; - if ((port < 0) || (port > NETXEN_NIU_MAX_GBE_PORTS)) + if ((port < 0) || (port > NETXEN_NIU_MAX_XG_PORTS)) return -EINVAL; - if (netxen_nic_hw_read_wx(adapter, NETXEN_NIU_XGE_CONFIG_1, ®, 4)) - return -EIO; + if (netxen_nic_hw_read_wx(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), ®, 4)) + return -EIO; if (mode == NETXEN_NIU_PROMISC_MODE) reg = (reg | 0x2000UL); else reg = (reg & ~0x2000UL); - netxen_crb_writelit_adapter(adapter, NETXEN_NIU_XGE_CONFIG_1, reg); + netxen_crb_writelit_adapter(adapter, + NETXEN_NIU_XGE_CONFIG_1 + (0x10000 * port), reg); return 0; } diff --git a/drivers/net/netxen/netxen_nic_phan_reg.h b/drivers/net/netxen/netxen_nic_phan_reg.h index 7879f85..9457fc7 100644 --- a/drivers/net/netxen/netxen_nic_phan_reg.h +++ b/drivers/net/netxen/netxen_nic_phan_reg.h @@ -100,8 +100,24 @@ #define CRB_CMD_PRODUCER_OFFSET_1 NETXEN_NIC_REG(0x1ac) #define CRB_CMD_CONSUMER_OFFSET_1 NETXEN_NIC_REG(0x1b0) +#define CRB_CMD_PRODUCER_OFFSET_2 NETXEN_NIC_REG(0x1b8) +#define CRB_CMD_CONSUMER_OFFSET_2 NETXEN_NIC_REG(0x1bc) + +// 1c0 to 1cc used for signature reg +#define CRB_CMD_PRODUCER_OFFSET_3 NETXEN_NIC_REG(0x1d0) +#define CRB_CMD_CONSUMER_OFFSET_3 NETXEN_NIC_REG(0x1d4) #define CRB_TEMP_STATE NETXEN_NIC_REG(0x1b4) +#define CRB_V2P_0 NETXEN_NIC_REG(0x290) +#define CRB_V2P_1 NETXEN_NIC_REG(0x294) +#define CRB_V2P_2 NETXEN_NIC_REG(0x298) +#define CRB_V2P_3 NETXEN_NIC_REG(0x29c) +#define CRB_V2P(port) (CRB_V2P_0+((port)*4)) +#define CRB_DRIVER_VERSION NETXEN_NIC_REG(0x2a0) + +/* used for ethtool tests */ +#define CRB_SCRATCHPAD_TEST NETXEN_NIC_REG(0x280) + /* * CrbPortPhanCntrHi/Lo is used to pass the address of HostPhantomIndex address * which can be read by the Phantom host to get producer/consumer indexes from @@ -136,128 +152,13 @@ struct netxen_recv_crb { }; #if defined(DEFINE_GLOBAL_RECV_CRB) -struct netxen_recv_crb recv_crb_registers[] = { - /* - * Instance 0. - */ - { - /* rcv_desc_crb: */ - { - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x100), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x104), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x108), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x10c), - - }, - /* Jumbo frames */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x110), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x114), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x118), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x11c), - }, - /* LRO */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x120), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x124), - /* crb_gloablrcv_ring: */ - NETXEN_NIC_REG(0x128), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x12c), - } - }, - /* crb_rcvstatus_ring: */ - NETXEN_NIC_REG(0x130), - /* crb_rcv_status_producer: */ - NETXEN_NIC_REG(0x134), - /* crb_rcv_status_consumer: */ - NETXEN_NIC_REG(0x138), - /* crb_rcvpeg_state: */ - NETXEN_NIC_REG(0x13c), - /* crb_status_ring_size */ - NETXEN_NIC_REG(0x140), - - }, - /* - * Instance 1, - */ - { - /* rcv_desc_crb: */ - { - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x144), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x148), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x14c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x150), - - }, - /* Jumbo frames */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x154), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x158), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x15c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x160), - }, - /* LRO */ - { - /* crb_rcv_producer_offset: */ - NETXEN_NIC_REG(0x164), - /* crb_rcv_consumer_offset: */ - NETXEN_NIC_REG(0x168), - /* crb_globalrcv_ring: */ - NETXEN_NIC_REG(0x16c), - /* crb_rcv_ring_size */ - NETXEN_NIC_REG(0x170), - } - - }, - /* crb_rcvstatus_ring: */ - NETXEN_NIC_REG(0x174), - /* crb_rcv_status_producer: */ - NETXEN_NIC_REG(0x178), - /* crb_rcv_status_consumer: */ - NETXEN_NIC_REG(0x17c), - /* crb_rcvpeg_state: */ - NETXEN_NIC_REG(0x180), - /* crb_status_ring_size */ - NETXEN_NIC_REG(0x184), - - }, -}; - -u64 ctx_addr_sig_regs[][3] = { - {NETXEN_NIC_REG(0x188), NETXEN_NIC_REG(0x18c), NETXEN_NIC_REG(0x1c0)}, - {NETXEN_NIC_REG(0x190), NETXEN_NIC_REG(0x194), NETXEN_NIC_REG(0x1c4)}, - {NETXEN_NIC_REG(0x198), NETXEN_NIC_REG(0x19c), NETXEN_NIC_REG(0x1c8)}, - {NETXEN_NIC_REG(0x1a0), NETXEN_NIC_REG(0x1a4), NETXEN_NIC_REG(0x1cc)} -}; - #else extern struct netxen_recv_crb recv_crb_registers[]; extern u64 ctx_addr_sig_regs[][3]; -#define CRB_CTX_ADDR_REG_LO (ctx_addr_sig_regs[0][0]) -#define CRB_CTX_ADDR_REG_HI (ctx_addr_sig_regs[0][2]) -#define CRB_CTX_SIGNATURE_REG (ctx_addr_sig_regs[0][1]) #endif /* DEFINE_GLOBAL_RECEIVE_CRB */ +#define CRB_CTX_ADDR_REG_LO(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][0]) +#define CRB_CTX_ADDR_REG_HI(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][2]) +#define CRB_CTX_SIGNATURE_REG(FUNC_ID) (ctx_addr_sig_regs[FUNC_ID][1]) /* * Temperature control. diff --git a/drivers/net/ni5010.c b/drivers/net/ni5010.c index 8be0d03..3d5b423 100644 --- a/drivers/net/ni5010.c +++ b/drivers/net/ni5010.c @@ -562,7 +562,6 @@ static void ni5010_rx(struct net_device *dev) return; } - skb->dev = dev; skb_reserve(skb, 2); /* Read packet into buffer */ diff --git a/drivers/net/ni52.c b/drivers/net/ni52.c index 196993a..8dbd6d1 100644 --- a/drivers/net/ni52.c +++ b/drivers/net/ni52.c @@ -104,8 +104,6 @@ static int automatic_resume; /* experimental .. better should be zero */ static int rfdadd; /* rfdadd=1 may be better for 8K MEM cards */ static int fifo=0x8; /* don't change */ -/* #define REALLY_SLOW_IO */ - #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -936,7 +934,6 @@ static void ni52_rcv_int(struct net_device *dev) skb = (struct sk_buff *) dev_alloc_skb(totlen+2); if(skb != NULL) { - skb->dev = dev; skb_reserve(skb,2); skb_put(skb,totlen); eth_copy_and_sum(skb,(char *) p->base+(unsigned long) rbd->buffer,totlen,0); @@ -1185,7 +1182,7 @@ static int ni52_send_packet(struct sk_buff *skb, struct net_device *dev) else #endif { - memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); + skb_copy_from_linear_data(skb, (char *) p->xmit_cbuffs[p->xmit_count], skb->len); len = skb->len; if (len < ETH_ZLEN) { len = ETH_ZLEN; diff --git a/drivers/net/ni65.c b/drivers/net/ni65.c index 1578f4d..3818edf 100644 --- a/drivers/net/ni65.c +++ b/drivers/net/ni65.c @@ -610,7 +610,6 @@ static void *ni65_alloc_mem(struct net_device *dev,char *what,int size,int type) printk(KERN_WARNING "%s: unable to allocate %s memory.\n",dev->name,what); return NULL; } - skb->dev = dev; skb_reserve(skb,2+16); skb_put(skb,R_BUF_SIZE); /* grab the whole space .. (not necessary) */ ptr = skb->data; @@ -1094,7 +1093,6 @@ static void ni65_recv_intr(struct net_device *dev,int csr0) if(skb) { skb_reserve(skb,2); - skb->dev = dev; #ifdef RCV_VIA_SKB if( (unsigned long) (skb->data + R_BUF_SIZE) > 0x1000000) { skb_put(skb,len); @@ -1178,8 +1176,9 @@ static int ni65_send_packet(struct sk_buff *skb, struct net_device *dev) if( (unsigned long) (skb->data + skb->len) > 0x1000000) { #endif - memcpy((char *) p->tmdbounce[p->tmdbouncenum] ,(char *)skb->data, - (skb->len > T_BUF_SIZE) ? T_BUF_SIZE : skb->len); + skb_copy_from_linear_data(skb, p->tmdbounce[p->tmdbouncenum], + skb->len > T_BUF_SIZE ? T_BUF_SIZE : + skb->len); if (len > skb->len) memset((char *)p->tmdbounce[p->tmdbouncenum]+skb->len, 0, len-skb->len); dev_kfree_skb (skb); diff --git a/drivers/net/ns83820.c b/drivers/net/ns83820.c index 568daeb..6a32338 100644 --- a/drivers/net/ns83820.c +++ b/drivers/net/ns83820.c @@ -514,8 +514,7 @@ static void ns83820_vlan_rx_kill_vid(struct net_device *ndev, unsigned short vid spin_lock_irq(&dev->misc_lock); spin_lock(&dev->tx_lock); - if (dev->vlgrp) - dev->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(dev->vlgrp, vid, NULL); spin_unlock(&dev->tx_lock); spin_unlock_irq(&dev->misc_lock); } @@ -608,7 +607,6 @@ static inline int rx_refill(struct net_device *ndev, gfp_t gfp) res &= 0xf; skb_reserve(skb, res); - skb->dev = ndev; if (gfp != GFP_ATOMIC) spin_lock_irqsave(&dev->rx_info.lock, flags); res = ns83820_add_rx_skb(dev, skb); @@ -1158,9 +1156,9 @@ again: extsts = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { extsts |= EXTSTS_IPPKT; - if (IPPROTO_TCP == skb->nh.iph->protocol) + if (IPPROTO_TCP == ip_hdr(skb)->protocol) extsts |= EXTSTS_TCPPKT; - else if (IPPROTO_UDP == skb->nh.iph->protocol) + else if (IPPROTO_UDP == ip_hdr(skb)->protocol) extsts |= EXTSTS_UDPPKT; } diff --git a/drivers/net/pasemi_mac.c b/drivers/net/pasemi_mac.c index d670ac7..76fe9dd 100644 --- a/drivers/net/pasemi_mac.c +++ b/drivers/net/pasemi_mac.c @@ -334,8 +334,6 @@ static void pasemi_mac_replenish_rx_ring(struct net_device *dev) break; } - skb->dev = dev; - dma = pci_map_single(mac->dma_pdev, skb->data, skb->len, PCI_DMA_FROMDEVICE); @@ -731,16 +729,18 @@ static int pasemi_mac_start_tx(struct sk_buff *skb, struct net_device *dev) dflags = XCT_MACTX_O | XCT_MACTX_ST | XCT_MACTX_SS | XCT_MACTX_CRC_PAD; if (skb->ip_summed == CHECKSUM_PARTIAL) { - switch (skb->nh.iph->protocol) { + const unsigned char *nh = skb_network_header(skb); + + switch (ip_hdr(skb)->protocol) { case IPPROTO_TCP: dflags |= XCT_MACTX_CSUM_TCP; - dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2); - dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data); + dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); + dflags |= XCT_MACTX_IPO(nh - skb->data); break; case IPPROTO_UDP: dflags |= XCT_MACTX_CSUM_UDP; - dflags |= XCT_MACTX_IPH((skb->h.raw - skb->nh.raw) >> 2); - dflags |= XCT_MACTX_IPO(skb->nh.raw - skb->data); + dflags |= XCT_MACTX_IPH(skb_network_header_len(skb) >> 2); + dflags |= XCT_MACTX_IPO(nh - skb->data); break; } } diff --git a/drivers/net/pci-skeleton.c b/drivers/net/pci-skeleton.c index 00ca0fd..df8998b 100644 --- a/drivers/net/pci-skeleton.c +++ b/drivers/net/pci-skeleton.c @@ -710,8 +710,8 @@ match: tp->chipset, rtl_chip_info[tp->chipset].name); - i = register_netdev (dev); - if (i) + rc = register_netdev (dev); + if (rc) goto err_out_unmap; DPRINTK ("EXIT, returning 0\n"); @@ -1344,7 +1344,7 @@ static int netdrv_start_xmit (struct sk_buff *skb, struct net_device *dev) tp->tx_info[entry].skb = skb; /* tp->tx_info[entry].mapping = 0; */ - memcpy (tp->tx_buf[entry], skb->data, skb->len); + skb_copy_from_linear_data(skb, tp->tx_buf[entry], skb->len); /* Note: the chip doesn't have auto-pad! */ NETDRV_W32 (TxStatus0 + (entry * sizeof(u32)), @@ -1565,7 +1565,6 @@ static void netdrv_rx_interrupt (struct net_device *dev, skb = dev_alloc_skb (pkt_size + 2); if (skb) { - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align the IP fields. */ eth_copy_and_sum (skb, &rx_ring[ring_offset + 4], pkt_size, 0); diff --git a/drivers/net/pcmcia/3c574_cs.c b/drivers/net/pcmcia/3c574_cs.c index 448bf4a..2b395ee 100644 --- a/drivers/net/pcmcia/3c574_cs.c +++ b/drivers/net/pcmcia/3c574_cs.c @@ -915,7 +915,7 @@ static void media_check(unsigned long arg) if ((inw(ioaddr + EL3_STATUS) & IntLatch) && (inb(ioaddr + Timer) == 0xff)) { if (!lp->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); - el3_interrupt(dev->irq, lp); + el3_interrupt(dev->irq, dev); lp->fast_poll = HZ; } if (lp->fast_poll) { @@ -1056,7 +1056,6 @@ static int el3_rx(struct net_device *dev, int worklimit) DEBUG(3, " Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), ((pkt_len+3)>>2)); diff --git a/drivers/net/pcmcia/3c589_cs.c b/drivers/net/pcmcia/3c589_cs.c index 461e827..143ae2f 100644 --- a/drivers/net/pcmcia/3c589_cs.c +++ b/drivers/net/pcmcia/3c589_cs.c @@ -883,7 +883,6 @@ static int el3_rx(struct net_device *dev) DEBUG(3, " Receiving packet size %d status %4.4x.\n", pkt_len, rx_status); if (skb != NULL) { - skb->dev = dev; skb_reserve(skb, 2); insl(ioaddr+RX_FIFO, skb_put(skb, pkt_len), (pkt_len+3)>>2); diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c index 6139048..808fae1 100644 --- a/drivers/net/pcmcia/axnet_cs.c +++ b/drivers/net/pcmcia/axnet_cs.c @@ -1136,7 +1136,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev) ei_block_output(dev, length, skb->data, output_page); else { memset(packet, 0, ETH_ZLEN); - memcpy(packet, skb->data, skb->len); + skb_copy_from_linear_data(skb, packet, skb->len); ei_block_output(dev, length, packet, output_page); } @@ -1496,7 +1496,6 @@ static void ei_receive(struct net_device *dev) else { skb_reserve(skb,2); /* IP headers on 16 byte boundaries */ - skb->dev = dev; skb_put(skb, pkt_len); /* Make room */ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame)); skb->protocol=eth_type_trans(skb,dev); diff --git a/drivers/net/pcmcia/fmvj18x_cs.c b/drivers/net/pcmcia/fmvj18x_cs.c index 0d7de61..3f93d49 100644 --- a/drivers/net/pcmcia/fmvj18x_cs.c +++ b/drivers/net/pcmcia/fmvj18x_cs.c @@ -999,7 +999,6 @@ static void fjn_rx(struct net_device *dev) lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), diff --git a/drivers/net/pcmcia/ibmtr_cs.c b/drivers/net/pcmcia/ibmtr_cs.c index a956a51..1060154 100644 --- a/drivers/net/pcmcia/ibmtr_cs.c +++ b/drivers/net/pcmcia/ibmtr_cs.c @@ -138,7 +138,7 @@ static const struct ethtool_ops netdev_ethtool_ops = { ======================================================================*/ -static int ibmtr_attach(struct pcmcia_device *link) +static int __devinit ibmtr_attach(struct pcmcia_device *link) { ibmtr_dev_t *info; struct net_device *dev; @@ -217,7 +217,7 @@ static void ibmtr_detach(struct pcmcia_device *link) #define CS_CHECK(fn, ret) \ do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) -static int ibmtr_config(struct pcmcia_device *link) +static int __devinit ibmtr_config(struct pcmcia_device *link) { ibmtr_dev_t *info = link->priv; struct net_device *dev = info->dev; diff --git a/drivers/net/pcmcia/nmclan_cs.c b/drivers/net/pcmcia/nmclan_cs.c index 3b70774..73da611 100644 --- a/drivers/net/pcmcia/nmclan_cs.c +++ b/drivers/net/pcmcia/nmclan_cs.c @@ -1182,12 +1182,10 @@ static int mace_rx(struct net_device *dev, unsigned char RxCnt) skb = dev_alloc_skb(pkt_len+2); if (skb != NULL) { - skb->dev = dev; - skb_reserve(skb, 2); insw(ioaddr + AM2150_RCV, skb_put(skb, pkt_len), pkt_len>>1); if (pkt_len & 1) - *(skb->tail-1) = inb(ioaddr + AM2150_RCV); + *(skb_tail_pointer(skb) - 1) = inb(ioaddr + AM2150_RCV); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); /* Send the packet to the upper (protocol) layers. */ diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c index 530df88..7912dbd 100644 --- a/drivers/net/pcmcia/smc91c92_cs.c +++ b/drivers/net/pcmcia/smc91c92_cs.c @@ -1669,7 +1669,6 @@ static void smc_rx(struct net_device *dev) (packet_length+1)>>1); skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; netif_rx(skb); dev->last_rx = jiffies; smc->stats.rx_packets++; @@ -1927,7 +1926,7 @@ static void media_check(u_long arg) if (smc->watchdog++ && ((i>>8) & i)) { if (!smc->fast_poll) printk(KERN_INFO "%s: interrupt(s) dropped!\n", dev->name); - smc_interrupt(dev->irq, smc); + smc_interrupt(dev->irq, dev); smc->fast_poll = HZ; } if (smc->fast_poll) { diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c index 5879e7c..809ec44 100644 --- a/drivers/net/pcmcia/xirc2ps_cs.c +++ b/drivers/net/pcmcia/xirc2ps_cs.c @@ -1226,7 +1226,6 @@ xirc2ps_interrupt(int irq, void *dev_id) (pktlen+1)>>1); } skb->protocol = eth_type_trans(skb, dev); - skb->dev = dev; netif_rx(skb); dev->last_rx = jiffies; lp->stats.rx_packets++; diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index 36f9d98..9c171a7 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -253,12 +253,12 @@ struct pcnet32_access { * so the structure should be allocated using pci_alloc_consistent(). */ struct pcnet32_private { - struct pcnet32_init_block init_block; + struct pcnet32_init_block *init_block; /* The Tx and Rx ring entries must be aligned on 16-byte boundaries in 32bit mode. */ struct pcnet32_rx_head *rx_ring; struct pcnet32_tx_head *tx_ring; - dma_addr_t dma_addr;/* DMA address of beginning of this - object, returned by pci_alloc_consistent */ + dma_addr_t init_dma_addr;/* DMA address of beginning of the init block, + returned by pci_alloc_consistent */ struct pci_dev *pci_dev; const char *name; /* The saved address of a sent-in-place packet/buffer, for skfree(). */ @@ -653,7 +653,7 @@ static void pcnet32_realloc_rx_ring(struct net_device *dev, static void pcnet32_purge_rx_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; /* free all allocated skbuffs */ @@ -681,7 +681,7 @@ static void pcnet32_poll_controller(struct net_device *dev) static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -696,7 +696,7 @@ static int pcnet32_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -711,7 +711,7 @@ static int pcnet32_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) static void pcnet32_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); @@ -723,7 +723,7 @@ static void pcnet32_get_drvinfo(struct net_device *dev, static u32 pcnet32_get_link(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r; @@ -743,19 +743,19 @@ static u32 pcnet32_get_link(struct net_device *dev) static u32 pcnet32_get_msglevel(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); return lp->msg_enable; } static void pcnet32_set_msglevel(struct net_device *dev, u32 value) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); lp->msg_enable = value; } static int pcnet32_nway_reset(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; int r = -EOPNOTSUPP; @@ -770,7 +770,7 @@ static int pcnet32_nway_reset(struct net_device *dev) static void pcnet32_get_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); ering->tx_max_pending = TX_MAX_RING_SIZE; ering->tx_pending = lp->tx_ring_size; @@ -781,7 +781,7 @@ static void pcnet32_get_ringparam(struct net_device *dev, static int pcnet32_set_ringparam(struct net_device *dev, struct ethtool_ringparam *ering) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; unsigned int size; ulong ioaddr = dev->base_addr; @@ -847,7 +847,7 @@ static int pcnet32_self_test_count(struct net_device *dev) static void pcnet32_ethtool_test(struct net_device *dev, struct ethtool_test *test, u64 * data) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int rc; if (test->flags == ETH_TEST_FL_OFFLINE) { @@ -868,7 +868,7 @@ static void pcnet32_ethtool_test(struct net_device *dev, static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; /* access to registers */ ulong ioaddr = dev->base_addr; /* card base I/O address */ struct sk_buff *skb; /* sk buff */ @@ -1047,7 +1047,7 @@ static int pcnet32_loopback_test(struct net_device *dev, uint64_t * data1) static void pcnet32_led_blink_callback(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1064,7 +1064,7 @@ static void pcnet32_led_blink_callback(struct net_device *dev) static int pcnet32_phys_id(struct net_device *dev, u32 data) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1109,7 +1109,7 @@ static int pcnet32_suspend(struct net_device *dev, unsigned long *flags, int can_sleep) { int csr5; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; int ticks; @@ -1206,7 +1206,6 @@ static void pcnet32_rx_entry(struct net_device *dev, PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len); lp->rx_skbuff[entry] = newskb; - newskb->dev = dev; lp->rx_dma_addr[entry] = pci_map_single(lp->pci_dev, newskb->data, @@ -1234,14 +1233,14 @@ static void pcnet32_rx_entry(struct net_device *dev, skb_put(skb, pkt_len); /* Make room */ pci_dma_sync_single_for_cpu(lp->pci_dev, lp->rx_dma_addr[entry], - PKT_BUF_SZ - 2, + pkt_len, PCI_DMA_FROMDEVICE); eth_copy_and_sum(skb, (unsigned char *)(lp->rx_skbuff[entry]->data), pkt_len, 0); pci_dma_sync_single_for_device(lp->pci_dev, lp->rx_dma_addr[entry], - PKT_BUF_SZ - 2, + pkt_len, PCI_DMA_FROMDEVICE); } lp->stats.rx_bytes += skb->len; @@ -1258,7 +1257,7 @@ static void pcnet32_rx_entry(struct net_device *dev, static int pcnet32_rx(struct net_device *dev, int quota) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int entry = lp->cur_rx & lp->rx_mod_mask; struct pcnet32_rx_head *rxp = &lp->rx_ring[entry]; int npackets = 0; @@ -1283,7 +1282,7 @@ static int pcnet32_rx(struct net_device *dev, int quota) static int pcnet32_tx(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned int dirty_tx = lp->dirty_tx; int delta; int must_restart = 0; @@ -1382,7 +1381,7 @@ static int pcnet32_tx(struct net_device *dev) #ifdef CONFIG_PCNET32_NAPI static int pcnet32_poll(struct net_device *dev, int *budget) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int quota = min(dev->quota, *budget); unsigned long ioaddr = dev->base_addr; unsigned long flags; @@ -1429,7 +1428,7 @@ static int pcnet32_poll(struct net_device *dev, int *budget) #define PCNET32_MAX_PHYS 32 static int pcnet32_get_regs_len(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int j = lp->phycount * PCNET32_REGS_PER_PHY; return ((PCNET32_NUM_REGS + j) * sizeof(u16)); @@ -1440,7 +1439,7 @@ static void pcnet32_get_regs(struct net_device *dev, struct ethtool_regs *regs, { int i, csr0; u16 *buff = ptr; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct pcnet32_access *a = &lp->a; ulong ioaddr = dev->base_addr; unsigned long flags; @@ -1593,7 +1592,6 @@ static int __devinit pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) { struct pcnet32_private *lp; - dma_addr_t lp_dma_addr; int i, media; int fdx, mii, fset, dxsuflo; int chip_version; @@ -1715,7 +1713,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) dxsuflo = 1; } - dev = alloc_etherdev(0); + dev = alloc_etherdev(sizeof(*lp)); if (!dev) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Memory allocation failed.\n"); @@ -1806,25 +1804,22 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) } dev->base_addr = ioaddr; + lp = netdev_priv(dev); /* pci_alloc_consistent returns page-aligned memory, so we do not have to check the alignment */ - if ((lp = - pci_alloc_consistent(pdev, sizeof(*lp), &lp_dma_addr)) == NULL) { + if ((lp->init_block = + pci_alloc_consistent(pdev, sizeof(*lp->init_block), &lp->init_dma_addr)) == NULL) { if (pcnet32_debug & NETIF_MSG_PROBE) printk(KERN_ERR PFX "Consistent memory allocation failed.\n"); ret = -ENOMEM; goto err_free_netdev; } - - memset(lp, 0, sizeof(*lp)); - lp->dma_addr = lp_dma_addr; lp->pci_dev = pdev; spin_lock_init(&lp->lock); SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); - dev->priv = lp; lp->name = chipname; lp->shared_irq = shared; lp->tx_ring_size = TX_RING_SIZE; /* default tx ring size */ @@ -1871,23 +1866,21 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) && dev->dev_addr[2] == 0x75) lp->options = PCNET32_PORT_FD | PCNET32_PORT_GPSI; - lp->init_block.mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ - lp->init_block.tlen_rlen = + lp->init_block->mode = le16_to_cpu(0x0003); /* Disable Rx and Tx. */ + lp->init_block->tlen_rlen = le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits); for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.filter[0] = 0x00000000; - lp->init_block.filter[1] = 0x00000000; - lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); - lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); + lp->init_block->phys_addr[i] = dev->dev_addr[i]; + lp->init_block->filter[0] = 0x00000000; + lp->init_block->filter[1] = 0x00000000; + lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); + lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); /* switch pcnet32 to 32bit mode */ a->write_bcr(ioaddr, 20, 2); - a->write_csr(ioaddr, 1, (lp->dma_addr + offsetof(struct pcnet32_private, - init_block)) & 0xffff); - a->write_csr(ioaddr, 2, (lp->dma_addr + offsetof(struct pcnet32_private, - init_block)) >> 16); + a->write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); + a->write_csr(ioaddr, 2, (lp->init_dma_addr >> 16)); if (pdev) { /* use the IRQ provided by PCI */ dev->irq = pdev->irq; @@ -1993,7 +1986,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) err_free_ring: pcnet32_free_ring(dev); err_free_consistent: - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); err_free_netdev: free_netdev(dev); err_release_region: @@ -2004,7 +1998,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) /* if any allocation fails, caller must also call pcnet32_free_ring */ static int pcnet32_alloc_ring(struct net_device *dev, char *name) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); lp->tx_ring = pci_alloc_consistent(lp->pci_dev, sizeof(struct pcnet32_tx_head) * @@ -2071,7 +2065,7 @@ static int pcnet32_alloc_ring(struct net_device *dev, char *name) static void pcnet32_free_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); kfree(lp->tx_skbuff); lp->tx_skbuff = NULL; @@ -2104,7 +2098,7 @@ static void pcnet32_free_ring(struct net_device *dev) static int pcnet32_open(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 val; int i; @@ -2135,8 +2129,7 @@ static int pcnet32_open(struct net_device *dev) "%s: pcnet32_open() irq %d tx/rx rings %#x/%#x init %#x.\n", dev->name, dev->irq, (u32) (lp->tx_ring_dma_addr), (u32) (lp->rx_ring_dma_addr), - (u32) (lp->dma_addr + - offsetof(struct pcnet32_private, init_block))); + (u32) (lp->init_dma_addr)); /* set/reset autoselect bit */ val = lp->a.read_bcr(ioaddr, 2) & ~2; @@ -2275,7 +2268,7 @@ static int pcnet32_open(struct net_device *dev) } #endif - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); pcnet32_load_multicast(dev); @@ -2285,12 +2278,8 @@ static int pcnet32_open(struct net_device *dev) } /* Re-initialize the PCNET32, and start it when done. */ - lp->a.write_csr(ioaddr, 1, (lp->dma_addr + - offsetof(struct pcnet32_private, - init_block)) & 0xffff); - lp->a.write_csr(ioaddr, 2, - (lp->dma_addr + - offsetof(struct pcnet32_private, init_block)) >> 16); + lp->a.write_csr(ioaddr, 1, (lp->init_dma_addr & 0xffff)); + lp->a.write_csr(ioaddr, 2, (lp->init_dma_addr >> 16)); lp->a.write_csr(ioaddr, CSR4, 0x0915); /* auto tx pad */ lp->a.write_csr(ioaddr, CSR0, CSR0_INIT); @@ -2317,8 +2306,7 @@ static int pcnet32_open(struct net_device *dev) printk(KERN_DEBUG "%s: pcnet32 open after %d ticks, init block %#x csr0 %4.4x.\n", dev->name, i, - (u32) (lp->dma_addr + - offsetof(struct pcnet32_private, init_block)), + (u32) (lp->init_dma_addr), lp->a.read_csr(ioaddr, CSR0)); spin_unlock_irqrestore(&lp->lock, flags); @@ -2356,7 +2344,7 @@ static int pcnet32_open(struct net_device *dev) static void pcnet32_purge_tx_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; for (i = 0; i < lp->tx_ring_size; i++) { @@ -2376,7 +2364,7 @@ static void pcnet32_purge_tx_ring(struct net_device *dev) /* Initialize the PCNET32 Rx and Tx rings. */ static int pcnet32_init_ring(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int i; lp->tx_full = 0; @@ -2418,12 +2406,12 @@ static int pcnet32_init_ring(struct net_device *dev) lp->tx_dma_addr[i] = 0; } - lp->init_block.tlen_rlen = + lp->init_block->tlen_rlen = le16_to_cpu(lp->tx_len_bits | lp->rx_len_bits); for (i = 0; i < 6; i++) - lp->init_block.phys_addr[i] = dev->dev_addr[i]; - lp->init_block.rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); - lp->init_block.tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); + lp->init_block->phys_addr[i] = dev->dev_addr[i]; + lp->init_block->rx_ring = (u32) le32_to_cpu(lp->rx_ring_dma_addr); + lp->init_block->tx_ring = (u32) le32_to_cpu(lp->tx_ring_dma_addr); wmb(); /* Make sure all changes are visible */ return 0; } @@ -2434,7 +2422,7 @@ static int pcnet32_init_ring(struct net_device *dev) */ static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; int i; @@ -2464,7 +2452,7 @@ static void pcnet32_restart(struct net_device *dev, unsigned int csr0_bits) static void pcnet32_tx_timeout(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr, flags; spin_lock_irqsave(&lp->lock, flags); @@ -2505,7 +2493,7 @@ static void pcnet32_tx_timeout(struct net_device *dev) static int pcnet32_start_xmit(struct sk_buff *skb, struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 status; int entry; @@ -2570,7 +2558,7 @@ pcnet32_interrupt(int irq, void *dev_id) int boguscnt = max_interrupt_work; ioaddr = dev->base_addr; - lp = dev->priv; + lp = netdev_priv(dev); spin_lock(&lp->lock); @@ -2652,7 +2640,7 @@ pcnet32_interrupt(int irq, void *dev_id) static int pcnet32_close(struct net_device *dev) { unsigned long ioaddr = dev->base_addr; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; del_timer_sync(&lp->watchdog_timer); @@ -2693,7 +2681,7 @@ static int pcnet32_close(struct net_device *dev) static struct net_device_stats *pcnet32_get_stats(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; unsigned long flags; @@ -2707,8 +2695,8 @@ static struct net_device_stats *pcnet32_get_stats(struct net_device *dev) /* taken from the sunlance driver, which it took from the depca driver */ static void pcnet32_load_multicast(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; - volatile struct pcnet32_init_block *ib = &lp->init_block; + struct pcnet32_private *lp = netdev_priv(dev); + volatile struct pcnet32_init_block *ib = lp->init_block; volatile u16 *mcast_table = (u16 *) & ib->filter; struct dev_mc_list *dmi = dev->mc_list; unsigned long ioaddr = dev->base_addr; @@ -2757,7 +2745,7 @@ static void pcnet32_load_multicast(struct net_device *dev) static void pcnet32_set_multicast_list(struct net_device *dev) { unsigned long ioaddr = dev->base_addr, flags; - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int csr15, suspended; spin_lock_irqsave(&lp->lock, flags); @@ -2768,12 +2756,12 @@ static void pcnet32_set_multicast_list(struct net_device *dev) if (netif_msg_hw(lp)) printk(KERN_INFO "%s: Promiscuous mode enabled.\n", dev->name); - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu(0x8000 | (lp->options & PCNET32_PORT_PORTSEL) << 7); lp->a.write_csr(ioaddr, CSR15, csr15 | 0x8000); } else { - lp->init_block.mode = + lp->init_block->mode = le16_to_cpu((lp->options & PCNET32_PORT_PORTSEL) << 7); lp->a.write_csr(ioaddr, CSR15, csr15 & 0x7fff); pcnet32_load_multicast(dev); @@ -2796,7 +2784,7 @@ static void pcnet32_set_multicast_list(struct net_device *dev) /* This routine assumes that the lp->lock is held */ static int mdio_read(struct net_device *dev, int phy_id, int reg_num) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; u16 val_out; @@ -2812,7 +2800,7 @@ static int mdio_read(struct net_device *dev, int phy_id, int reg_num) /* This routine assumes that the lp->lock is held */ static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long ioaddr = dev->base_addr; if (!lp->mii) @@ -2824,7 +2812,7 @@ static void mdio_write(struct net_device *dev, int phy_id, int reg_num, int val) static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int rc; unsigned long flags; @@ -2842,7 +2830,7 @@ static int pcnet32_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) static int pcnet32_check_otherphy(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); struct mii_if_info mii = lp->mii_if; u16 bmcr; int i; @@ -2889,7 +2877,7 @@ static int pcnet32_check_otherphy(struct net_device *dev) static void pcnet32_check_media(struct net_device *dev, int verbose) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); int curr_link; int prev_link = netif_carrier_ok(dev) ? 1 : 0; u32 bcr9; @@ -2945,7 +2933,7 @@ static void pcnet32_check_media(struct net_device *dev, int verbose) static void pcnet32_watchdog(struct net_device *dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unsigned long flags; /* Print the link status if it has changed */ @@ -2961,12 +2949,13 @@ static void __devexit pcnet32_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); if (dev) { - struct pcnet32_private *lp = dev->priv; + struct pcnet32_private *lp = netdev_priv(dev); unregister_netdev(dev); pcnet32_free_ring(dev); release_region(dev->base_addr, PCNET32_TOTAL_SIZE); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); free_netdev(dev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -3041,12 +3030,13 @@ static void __exit pcnet32_cleanup_module(void) struct net_device *next_dev; while (pcnet32_dev) { - struct pcnet32_private *lp = pcnet32_dev->priv; + struct pcnet32_private *lp = netdev_priv(pcnet32_dev); next_dev = lp->next; unregister_netdev(pcnet32_dev); pcnet32_free_ring(pcnet32_dev); release_region(pcnet32_dev->base_addr, PCNET32_TOTAL_SIZE); - pci_free_consistent(lp->pci_dev, sizeof(*lp), lp, lp->dma_addr); + pci_free_consistent(lp->pci_dev, sizeof(*lp->init_block), + lp->init_block, lp->init_dma_addr); free_netdev(pcnet32_dev); pcnet32_dev = next_dev; } diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c index 66da91b..68c99b4c 100644 --- a/drivers/net/phy/fixed.c +++ b/drivers/net/phy/fixed.c @@ -276,21 +276,15 @@ static int fixed_mdio_register_device(int number, int speed, int duplex) artificially, we are binding the driver here by hand; it will be the same for all the fixed phys anyway. */ - down_write(&phydev->dev.bus->subsys.rwsem); - phydev->dev.driver = &fixed_mdio_driver.driver; err = phydev->dev.driver->probe(&phydev->dev); if(err < 0) { printk(KERN_ERR "Phy %s: problems with fixed driver\n",phydev->dev.bus_id); - up_write(&phydev->dev.bus->subsys.rwsem); goto probe_fail; } err = device_bind_driver(&phydev->dev); - - up_write(&phydev->dev.bus->subsys.rwsem); - if (err) goto probe_fail; diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index f4d4eb6..22aec5c 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -42,6 +42,19 @@ #define MII_M1011_IMASK_INIT 0x6400 #define MII_M1011_IMASK_CLEAR 0x0000 +#define MII_M1011_PHY_SCR 0x10 +#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060 + +#define MII_M1145_PHY_EXT_CR 0x14 +#define MII_M1145_RGMII_RX_DELAY 0x0080 +#define MII_M1145_RGMII_TX_DELAY 0x0002 + +#define M1145_DEV_FLAGS_RESISTANCE 0x00000001 + +#define MII_M1111_PHY_LED_CONTROL 0x18 +#define MII_M1111_PHY_LED_DIRECT 0x4100 +#define MII_M1111_PHY_LED_COMBINE 0x411c + MODULE_DESCRIPTION("Marvell PHY driver"); MODULE_AUTHOR("Andy Fleming"); MODULE_LICENSE("GPL"); @@ -63,7 +76,7 @@ static int marvell_config_intr(struct phy_device *phydev) { int err; - if(phydev->interrupts == PHY_INTERRUPT_ENABLED) + if (phydev->interrupts == PHY_INTERRUPT_ENABLED) err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_INIT); else err = phy_write(phydev, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); @@ -103,34 +116,153 @@ static int marvell_config_aneg(struct phy_device *phydev) if (err < 0) return err; + err = phy_write(phydev, MII_M1011_PHY_SCR, + MII_M1011_PHY_SCR_AUTO_CROSS); + if (err < 0) + return err; + + err = phy_write(phydev, MII_M1111_PHY_LED_CONTROL, + MII_M1111_PHY_LED_DIRECT); + if (err < 0) + return err; err = genphy_config_aneg(phydev); return err; } +static int m88e1145_config_init(struct phy_device *phydev) +{ + int err; + + /* Take care of errata E0 & E1 */ + err = phy_write(phydev, 0x1d, 0x001b); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0x418f); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1d, 0x0016); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0xa2da); + if (err < 0) + return err; + + if (phydev->interface == PHY_INTERFACE_MODE_RGMII) { + int temp = phy_read(phydev, MII_M1145_PHY_EXT_CR); + if (temp < 0) + return temp; + + temp |= (MII_M1145_RGMII_RX_DELAY | MII_M1145_RGMII_TX_DELAY); + + err = phy_write(phydev, MII_M1145_PHY_EXT_CR, temp); + if (err < 0) + return err; + + if (phydev->dev_flags & M1145_DEV_FLAGS_RESISTANCE) { + err = phy_write(phydev, 0x1d, 0x0012); + if (err < 0) + return err; + + temp = phy_read(phydev, 0x1e); + if (temp < 0) + return temp; + + temp &= 0xf03f; + temp |= 2 << 9; /* 36 ohm */ + temp |= 2 << 6; /* 39 ohm */ + + err = phy_write(phydev, 0x1e, temp); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1d, 0x3); + if (err < 0) + return err; + + err = phy_write(phydev, 0x1e, 0x8000); + if (err < 0) + return err; + } + } + + return 0; +} static struct phy_driver m88e1101_driver = { - .phy_id = 0x01410c00, - .phy_id_mask = 0xffffff00, - .name = "Marvell 88E1101", - .features = PHY_GBIT_FEATURES, - .flags = PHY_HAS_INTERRUPT, - .config_aneg = &marvell_config_aneg, - .read_status = &genphy_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, - .driver = { .owner = THIS_MODULE,}, + .phy_id = 0x01410c60, + .phy_id_mask = 0xfffffff0, + .name = "Marvell 88E1101", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &marvell_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .driver = {.owner = THIS_MODULE,}, +}; + +static struct phy_driver m88e1111s_driver = { + .phy_id = 0x01410cc0, + .phy_id_mask = 0xfffffff0, + .name = "Marvell 88E1111", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_aneg = &marvell_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .driver = {.owner = THIS_MODULE,}, +}; + +static struct phy_driver m88e1145_driver = { + .phy_id = 0x01410cd0, + .phy_id_mask = 0xfffffff0, + .name = "Marvell 88E1145", + .features = PHY_GBIT_FEATURES, + .flags = PHY_HAS_INTERRUPT, + .config_init = &m88e1145_config_init, + .config_aneg = &marvell_config_aneg, + .read_status = &genphy_read_status, + .ack_interrupt = &marvell_ack_interrupt, + .config_intr = &marvell_config_intr, + .driver = {.owner = THIS_MODULE,}, }; static int __init marvell_init(void) { - return phy_driver_register(&m88e1101_driver); + int ret; + + ret = phy_driver_register(&m88e1101_driver); + if (ret) + return ret; + + ret = phy_driver_register(&m88e1111s_driver); + if (ret) + goto err1111s; + + ret = phy_driver_register(&m88e1145_driver); + if (ret) + goto err1145; + + return 0; + + err1145: + phy_driver_unregister(&m88e1111s_driver); + err1111s: + phy_driver_unregister(&m88e1101_driver); + return ret; } static void __exit marvell_exit(void) { phy_driver_unregister(&m88e1101_driver); + phy_driver_unregister(&m88e1111s_driver); + phy_driver_unregister(&m88e1145_driver); } module_init(marvell_init); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b31ce27..fc4aee9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -35,10 +35,14 @@ #include <asm/irq.h> #include <asm/uaccess.h> -/* mdiobus_register +/** + * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus + * @bus: target mii_bus * - * description: Called by a bus driver to bring up all the PHYs - * on a given bus, and attach them to the bus + * Description: Called by a bus driver to bring up all the PHYs + * on a given bus, and attach them to the bus. + * + * Returns 0 on success or < 0 on error. */ int mdiobus_register(struct mii_bus *bus) { @@ -114,10 +118,13 @@ void mdiobus_unregister(struct mii_bus *bus) } EXPORT_SYMBOL(mdiobus_unregister); -/* mdio_bus_match +/** + * mdio_bus_match - determine if given PHY driver supports the given PHY device + * @dev: target PHY device + * @drv: given PHY driver * - * description: Given a PHY device, and a PHY driver, return 1 if - * the driver supports the device. Otherwise, return 0 + * Description: Given a PHY device, and a PHY driver, return 1 if + * the driver supports the device. Otherwise, return 0. */ static int mdio_bus_match(struct device *dev, struct device_driver *drv) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c94a1fb..eed433d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -39,7 +39,9 @@ #include <asm/irq.h> #include <asm/uaccess.h> -/* Convenience function to print out the current phy status +/** + * phy_print_status - Convenience function to print out the current phy status + * @phydev: the phy_device struct */ void phy_print_status(struct phy_device *phydev) { @@ -55,10 +57,15 @@ void phy_print_status(struct phy_device *phydev) EXPORT_SYMBOL(phy_print_status); -/* Convenience functions for reading/writing a given PHY - * register. They MUST NOT be called from interrupt context, +/** + * phy_read - Convenience function for reading a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to read + * + * NOTE: MUST NOT be called from interrupt context, * because the bus read/write functions may wait for an interrupt - * to conclude the operation. */ + * to conclude the operation. + */ int phy_read(struct phy_device *phydev, u16 regnum) { int retval; @@ -72,6 +79,16 @@ int phy_read(struct phy_device *phydev, u16 regnum) } EXPORT_SYMBOL(phy_read); +/** + * phy_write - Convenience function for writing a given PHY register + * @phydev: the phy_device struct + * @regnum: register number to write + * @val: value to write to @regnum + * + * NOTE: MUST NOT be called from interrupt context, + * because the bus read/write functions may wait for an interrupt + * to conclude the operation. + */ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) { int err; @@ -85,7 +102,15 @@ int phy_write(struct phy_device *phydev, u16 regnum, u16 val) } EXPORT_SYMBOL(phy_write); - +/** + * phy_clear_interrupt - Ack the phy device's interrupt + * @phydev: the phy_device struct + * + * If the @phydev driver has an ack_interrupt function, call it to + * ack and clear the phy device's interrupt. + * + * Returns 0 on success on < 0 on error. + */ int phy_clear_interrupt(struct phy_device *phydev) { int err = 0; @@ -96,7 +121,13 @@ int phy_clear_interrupt(struct phy_device *phydev) return err; } - +/** + * phy_config_interrupt - configure the PHY device for the requested interrupts + * @phydev: the phy_device struct + * @interrupts: interrupt flags to configure for this @phydev + * + * Returns 0 on success on < 0 on error. + */ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) { int err = 0; @@ -109,9 +140,11 @@ int phy_config_interrupt(struct phy_device *phydev, u32 interrupts) } -/* phy_aneg_done +/** + * phy_aneg_done - return auto-negotiation status + * @phydev: target phy_device struct * - * description: Reads the status register and returns 0 either if + * Description: Reads the status register and returns 0 either if * auto-negotiation is incomplete, or if there was an error. * Returns BMSR_ANEGCOMPLETE if auto-negotiation is done. */ @@ -173,9 +206,12 @@ static const struct phy_setting settings[] = { #define MAX_NUM_SETTINGS (sizeof(settings)/sizeof(struct phy_setting)) -/* phy_find_setting +/** + * phy_find_setting - find a PHY settings array entry that matches speed & duplex + * @speed: speed to match + * @duplex: duplex to match * - * description: Searches the settings array for the setting which + * Description: Searches the settings array for the setting which * matches the desired speed and duplex, and returns the index * of that setting. Returns the index of the last setting if * none of the others match. @@ -192,11 +228,12 @@ static inline int phy_find_setting(int speed, int duplex) return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_find_valid - * idx: The first index in settings[] to search - * features: A mask of the valid settings +/** + * phy_find_valid - find a PHY setting that matches the requested features mask + * @idx: The first index in settings[] to search + * @features: A mask of the valid settings * - * description: Returns the index of the first valid setting less + * Description: Returns the index of the first valid setting less * than or equal to the one pointed to by idx, as determined by * the mask in features. Returns the index of the last setting * if nothing else matches. @@ -209,11 +246,13 @@ static inline int phy_find_valid(int idx, u32 features) return idx < MAX_NUM_SETTINGS ? idx : MAX_NUM_SETTINGS - 1; } -/* phy_sanitize_settings +/** + * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex + * @phydev: the target phy_device struct * - * description: Make sure the PHY is set to supported speeds and + * Description: Make sure the PHY is set to supported speeds and * duplexes. Drop down by one in this order: 1000/FULL, - * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF + * 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. */ void phy_sanitize_settings(struct phy_device *phydev) { @@ -232,16 +271,17 @@ void phy_sanitize_settings(struct phy_device *phydev) } EXPORT_SYMBOL(phy_sanitize_settings); -/* phy_ethtool_sset: - * A generic ethtool sset function. Handles all the details +/** + * phy_ethtool_sset - generic ethtool sset function, handles all the details + * @phydev: target phy_device struct + * @cmd: ethtool_cmd * * A few notes about parameter checking: * - We don't set port or transceiver, so we don't care what they * were set to. * - phy_start_aneg() will make sure forced settings are sane, and * choose the next best ones from the ones selected, so we don't - * care if ethtool tries to give us bad values - * + * care if ethtool tries to give us bad values. */ int phy_ethtool_sset(struct phy_device *phydev, struct ethtool_cmd *cmd) { @@ -304,9 +344,15 @@ int phy_ethtool_gset(struct phy_device *phydev, struct ethtool_cmd *cmd) } EXPORT_SYMBOL(phy_ethtool_gset); -/* Note that this function is currently incompatible with the +/** + * phy_mii_ioctl - generic PHY MII ioctl interface + * @phydev: the phy_device struct + * @mii_data: MII ioctl data + * @cmd: ioctl cmd to execute + * + * Note that this function is currently incompatible with the * PHYCONTROL layer. It changes registers without regard to - * current state. Use at own risk + * current state. Use at own risk. */ int phy_mii_ioctl(struct phy_device *phydev, struct mii_ioctl_data *mii_data, int cmd) @@ -336,6 +382,12 @@ int phy_mii_ioctl(struct phy_device *phydev, phydev->duplex = DUPLEX_FULL; else phydev->duplex = DUPLEX_HALF; + if ((!phydev->autoneg) && + (val & BMCR_SPEED1000)) + phydev->speed = SPEED_1000; + else if ((!phydev->autoneg) && + (val & BMCR_SPEED100)) + phydev->speed = SPEED_100; break; case MII_ADVERTISE: phydev->advertising = val; @@ -358,13 +410,14 @@ int phy_mii_ioctl(struct phy_device *phydev, return 0; } -/* phy_start_aneg +/** + * phy_start_aneg - start auto-negotiation for this PHY device + * @phydev: the phy_device struct * - * description: Sanitizes the settings (if we're not - * autonegotiating them), and then calls the driver's - * config_aneg function. If the PHYCONTROL Layer is operating, - * we change the state to reflect the beginning of - * Auto-negotiation or forcing. + * Description: Sanitizes the settings (if we're not autonegotiating + * them), and then calls the driver's config_aneg function. + * If the PHYCONTROL Layer is operating, we change the state to + * reflect the beginning of Auto-negotiation or forcing. */ int phy_start_aneg(struct phy_device *phydev) { @@ -400,15 +453,19 @@ EXPORT_SYMBOL(phy_start_aneg); static void phy_change(struct work_struct *work); static void phy_timer(unsigned long data); -/* phy_start_machine: +/** + * phy_start_machine - start PHY state machine tracking + * @phydev: the phy_device struct + * @handler: callback function for state change notifications * - * description: The PHY infrastructure can run a state machine + * Description: The PHY infrastructure can run a state machine * which tracks whether the PHY is starting up, negotiating, * etc. This function starts the timer which tracks the state - * of the PHY. If you want to be notified when the state - * changes, pass in the callback, otherwise, pass NULL. If you + * of the PHY. If you want to be notified when the state changes, + * pass in the callback @handler, otherwise, pass NULL. If you * want to maintain your own state machine, do not call this - * function. */ + * function. + */ void phy_start_machine(struct phy_device *phydev, void (*handler)(struct net_device *)) { @@ -420,9 +477,11 @@ void phy_start_machine(struct phy_device *phydev, mod_timer(&phydev->phy_timer, jiffies + HZ); } -/* phy_stop_machine +/** + * phy_stop_machine - stop the PHY state machine tracking + * @phydev: target phy_device struct * - * description: Stops the state machine timer, sets the state to UP + * Description: Stops the state machine timer, sets the state to UP * (unless it wasn't up yet). This function must be called BEFORE * phy_detach. */ @@ -438,12 +497,14 @@ void phy_stop_machine(struct phy_device *phydev) phydev->adjust_state = NULL; } -/* phy_force_reduction +/** + * phy_force_reduction - reduce PHY speed/duplex settings by one step + * @phydev: target phy_device struct * - * description: Reduces the speed/duplex settings by - * one notch. The order is so: - * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, - * 10/FULL, 10/HALF. The function bottoms out at 10/HALF. + * Description: Reduces the speed/duplex settings by one notch, + * in this order-- + * 1000/FULL, 1000/HALF, 100/FULL, 100/HALF, 10/FULL, 10/HALF. + * The function bottoms out at 10/HALF. */ static void phy_force_reduction(struct phy_device *phydev) { @@ -464,7 +525,9 @@ static void phy_force_reduction(struct phy_device *phydev) } -/* phy_error: +/** + * phy_error - enter HALTED state for this PHY device + * @phydev: target phy_device struct * * Moves the PHY to the HALTED state in response to a read * or write error, and tells the controller the link is down. @@ -478,9 +541,12 @@ void phy_error(struct phy_device *phydev) spin_unlock(&phydev->lock); } -/* phy_interrupt +/** + * phy_interrupt - PHY interrupt handler + * @irq: interrupt line + * @phy_dat: phy_device pointer * - * description: When a PHY interrupt occurs, the handler disables + * Description: When a PHY interrupt occurs, the handler disables * interrupts, and schedules a work task to clear the interrupt. */ static irqreturn_t phy_interrupt(int irq, void *phy_dat) @@ -501,7 +567,10 @@ static irqreturn_t phy_interrupt(int irq, void *phy_dat) return IRQ_HANDLED; } -/* Enable the interrupts from the PHY side */ +/** + * phy_enable_interrupts - Enable the interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_enable_interrupts(struct phy_device *phydev) { int err; @@ -517,7 +586,10 @@ int phy_enable_interrupts(struct phy_device *phydev) } EXPORT_SYMBOL(phy_enable_interrupts); -/* Disable the PHY interrupts from the PHY side */ +/** + * phy_disable_interrupts - Disable the PHY interrupts from the PHY side + * @phydev: target phy_device struct + */ int phy_disable_interrupts(struct phy_device *phydev) { int err; @@ -543,13 +615,15 @@ phy_err: } EXPORT_SYMBOL(phy_disable_interrupts); -/* phy_start_interrupts +/** + * phy_start_interrupts - request and enable interrupts for a PHY device + * @phydev: target phy_device struct * - * description: Request the interrupt for the given PHY. If - * this fails, then we set irq to PHY_POLL. + * Description: Request the interrupt for the given PHY. + * If this fails, then we set irq to PHY_POLL. * Otherwise, we enable the interrupts in the PHY. - * Returns 0 on success. * This should only be called with a valid IRQ number. + * Returns 0 on success or < 0 on error. */ int phy_start_interrupts(struct phy_device *phydev) { @@ -574,6 +648,10 @@ int phy_start_interrupts(struct phy_device *phydev) } EXPORT_SYMBOL(phy_start_interrupts); +/** + * phy_stop_interrupts - disable interrupts from a PHY device + * @phydev: target phy_device struct + */ int phy_stop_interrupts(struct phy_device *phydev) { int err; @@ -596,7 +674,10 @@ int phy_stop_interrupts(struct phy_device *phydev) EXPORT_SYMBOL(phy_stop_interrupts); -/* Scheduled by the phy_interrupt/timer to handle PHY changes */ +/** + * phy_change - Scheduled by the phy_interrupt/timer to handle PHY changes + * @work: work_struct that describes the work to be done + */ static void phy_change(struct work_struct *work) { int err; @@ -630,7 +711,10 @@ phy_err: phy_error(phydev); } -/* Bring down the PHY link, and stop checking the status. */ +/** + * phy_stop - Bring down the PHY link, and stop checking the status + * @phydev: target phy_device struct + */ void phy_stop(struct phy_device *phydev) { spin_lock(&phydev->lock); @@ -659,9 +743,11 @@ out_unlock: } -/* phy_start +/** + * phy_start - start or restart a PHY device + * @phydev: target phy_device struct * - * description: Indicates the attached device's readiness to + * Description: Indicates the attached device's readiness to * handle PHY-related work. Used during startup to start the * PHY, and after a call to phy_stop() to resume operation. * Also used to indicate the MDIO bus has cleared an error diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index fdf45fd..a8b74cd 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -74,11 +74,13 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id) } EXPORT_SYMBOL(phy_device_create); -/* get_phy_device +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus * - * description: Reads the ID registers of the PHY at addr on the - * bus, then allocates and returns the phy_device to - * represent it. + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. */ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) { @@ -112,23 +114,33 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) return dev; } -/* phy_prepare_link: +/** + * phy_prepare_link - prepares the PHY layer to monitor link status + * @phydev: target phy_device struct + * @handler: callback function for link status change notifications * - * description: Tells the PHY infrastructure to handle the + * Description: Tells the PHY infrastructure to handle the * gory details on monitoring link status (whether through * polling or an interrupt), and to call back to the * connected device driver when the link status changes. * If you want to monitor your own link state, don't call - * this function */ + * this function. + */ void phy_prepare_link(struct phy_device *phydev, void (*handler)(struct net_device *)) { phydev->adjust_link = handler; } -/* phy_connect: +/** + * phy_connect - connect an ethernet device to a PHY device + * @dev: the network device to connect + * @phy_id: the PHY device to connect + * @handler: callback function for state change notifications + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Convenience function for connecting ethernet + * Description: Convenience function for connecting ethernet * devices to PHY devices. The default behavior is for * the PHY infrastructure to handle everything, and only notify * the connected driver when the link status changes. If you @@ -138,7 +150,7 @@ void phy_prepare_link(struct phy_device *phydev, */ struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, void (*handler)(struct net_device *), u32 flags, - u32 interface) + phy_interface_t interface) { struct phy_device *phydev; @@ -158,6 +170,10 @@ struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, } EXPORT_SYMBOL(phy_connect); +/** + * phy_disconnect - disable interrupts, stop state machine, and detach a PHY device + * @phydev: target phy_device struct + */ void phy_disconnect(struct phy_device *phydev) { if (phydev->irq > 0) @@ -171,23 +187,27 @@ void phy_disconnect(struct phy_device *phydev) } EXPORT_SYMBOL(phy_disconnect); -/* phy_attach: +static int phy_compare_id(struct device *dev, void *data) +{ + return strcmp((char *)data, dev->bus_id) ? 0 : 1; +} + +/** + * phy_attach - attach a network device to a particular PHY device + * @dev: network device to attach + * @phy_id: PHY device to attach + * @flags: PHY device's dev_flags + * @interface: PHY device's interface * - * description: Called by drivers to attach to a particular PHY + * Description: Called by drivers to attach to a particular PHY * device. The phy_device is found, and properly hooked up * to the phy_driver. If no driver is attached, then the * genphy_driver is used. The phy_device is given a ptr to * the attaching device, and given a callback for link status - * change. The phy_device is returned to the attaching - * driver. + * change. The phy_device is returned to the attaching driver. */ -static int phy_compare_id(struct device *dev, void *data) -{ - return strcmp((char *)data, dev->bus_id) ? 0 : 1; -} - struct phy_device *phy_attach(struct net_device *dev, - const char *phy_id, u32 flags, u32 interface) + const char *phy_id, u32 flags, phy_interface_t interface) { struct bus_type *bus = &mdio_bus_type; struct phy_device *phydev; @@ -208,16 +228,12 @@ struct phy_device *phy_attach(struct net_device *dev, * exist, and we should use the genphy driver. */ if (NULL == d->driver) { int err; - down_write(&d->bus->subsys.rwsem); d->driver = &genphy_driver.driver; err = d->driver->probe(d); - if (err >= 0) err = device_bind_driver(d); - up_write(&d->bus->subsys.rwsem); - if (err) return ERR_PTR(err); } @@ -250,6 +266,10 @@ struct phy_device *phy_attach(struct net_device *dev, } EXPORT_SYMBOL(phy_attach); +/** + * phy_detach - detach a PHY device from its network device + * @phydev: target phy_device struct + */ void phy_detach(struct phy_device *phydev) { phydev->attached_dev = NULL; @@ -258,22 +278,21 @@ void phy_detach(struct phy_device *phydev) * was using the generic driver), we unbind the device * from the generic driver so that there's a chance a * real driver could be loaded */ - if (phydev->dev.driver == &genphy_driver.driver) { - down_write(&phydev->dev.bus->subsys.rwsem); + if (phydev->dev.driver == &genphy_driver.driver) device_release_driver(&phydev->dev); - up_write(&phydev->dev.bus->subsys.rwsem); - } } EXPORT_SYMBOL(phy_detach); /* Generic PHY support and helper functions */ -/* genphy_config_advert +/** + * genphy_config_advert - sanitize and advertise auto-negotation parameters + * @phydev: target phy_device struct * - * description: Writes MII_ADVERTISE with the appropriate values, + * Description: Writes MII_ADVERTISE with the appropriate values, * after sanitizing the values to make sure we only advertise - * what is supported + * what is supported. */ int genphy_config_advert(struct phy_device *phydev) { @@ -335,11 +354,14 @@ int genphy_config_advert(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_advert); -/* genphy_setup_forced +/** + * genphy_setup_forced - configures/forces speed/duplex from @phydev + * @phydev: target phy_device struct * - * description: Configures MII_BMCR to force speed/duplex + * Description: Configures MII_BMCR to force speed/duplex * to the values in phydev. Assumes that the values are valid. - * Please see phy_sanitize_settings() */ + * Please see phy_sanitize_settings(). + */ int genphy_setup_forced(struct phy_device *phydev) { int ctl = BMCR_RESET; @@ -368,7 +390,10 @@ int genphy_setup_forced(struct phy_device *phydev) } -/* Enable and Restart Autonegotiation */ +/** + * genphy_restart_aneg - Enable and Restart Autonegotiation + * @phydev: target phy_device struct + */ int genphy_restart_aneg(struct phy_device *phydev) { int ctl; @@ -389,11 +414,13 @@ int genphy_restart_aneg(struct phy_device *phydev) } -/* genphy_config_aneg +/** + * genphy_config_aneg - restart auto-negotiation or write BMCR + * @phydev: target phy_device struct * - * description: If auto-negotiation is enabled, we configure the + * Description: If auto-negotiation is enabled, we configure the * advertising, and then restart auto-negotiation. If it is not - * enabled, then we write the BMCR + * enabled, then we write the BMCR. */ int genphy_config_aneg(struct phy_device *phydev) { @@ -413,11 +440,13 @@ int genphy_config_aneg(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_config_aneg); -/* genphy_update_link +/** + * genphy_update_link - update link status in @phydev + * @phydev: target phy_device struct * - * description: Update the value in phydev->link to reflect the + * Description: Update the value in phydev->link to reflect the * current link value. In order to do this, we need to read - * the status register twice, keeping the second value + * the status register twice, keeping the second value. */ int genphy_update_link(struct phy_device *phydev) { @@ -444,9 +473,11 @@ int genphy_update_link(struct phy_device *phydev) } EXPORT_SYMBOL(genphy_update_link); -/* genphy_read_status +/** + * genphy_read_status - check the link status and update current link state + * @phydev: target phy_device struct * - * description: Check the link, then figure out the current state + * Description: Check the link, then figure out the current state * by comparing what we advertise with what the link partner * advertises. Start by checking the gigabit possibilities, * then move on to 10/100. @@ -586,9 +617,11 @@ static int genphy_config_init(struct phy_device *phydev) } -/* phy_probe +/** + * phy_probe - probe and init a PHY device + * @dev: device to probe and init * - * description: Take care of setting up the phy_device structure, + * Description: Take care of setting up the phy_device structure, * set the state to READY (the driver's init function should * set it to STARTING if needed). */ @@ -650,6 +683,10 @@ static int phy_remove(struct device *dev) return 0; } +/** + * phy_driver_register - register a phy_driver with the PHY layer + * @new_driver: new phy_driver to register + */ int phy_driver_register(struct phy_driver *new_driver) { int retval; diff --git a/drivers/net/plip.c b/drivers/net/plip.c index 6bb085f..8754cf3 100644 --- a/drivers/net/plip.c +++ b/drivers/net/plip.c @@ -546,7 +546,7 @@ static __be16 plip_type_trans(struct sk_buff *skb, struct net_device *dev) struct ethhdr *eth; unsigned char *rawp; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb_pull(skb,dev->hard_header_len); eth = eth_hdr(skb); diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 933e2f3..caabbc4 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -802,9 +802,9 @@ process_input_packet(struct asyncppp *ap) /* check for address/control and protocol compression */ p = skb->data; - if (p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) { + if (p[0] == PPP_ALLSTATIONS) { /* chop off address/control */ - if (skb->len < 3) + if (p[1] != PPP_UI || skb->len < 3) goto err; p = skb_pull(skb, 2); } diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c index 11b575f..6d596ca 100644 --- a/drivers/net/ppp_generic.c +++ b/drivers/net/ppp_generic.c @@ -88,8 +88,6 @@ struct ppp_file { #define PF_TO_PPP(pf) PF_TO_X(pf, struct ppp) #define PF_TO_CHANNEL(pf) PF_TO_X(pf, struct channel) -#define ROUNDUP(n, x) (((n) + (x) - 1) / (x)) - /* * Data structure describing one ppp unit. * A ppp unit corresponds to a ppp network interface device @@ -1297,7 +1295,7 @@ static int ppp_mp_explode(struct ppp *ppp, struct sk_buff *skb) */ fragsize = len; if (nfree > 1) - fragsize = ROUNDUP(fragsize, nfree); + fragsize = DIV_ROUND_UP(fragsize, nfree); /* nbigger channels get fragsize bytes, the rest get fragsize-1, except if nbigger==0, then they all get fragsize. */ nbigger = len % nfree; @@ -1685,7 +1683,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb) skb_pull_rcsum(skb, 2); skb->dev = ppp->dev; skb->protocol = htons(npindex_to_ethertype[npi]); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); ppp->dev->last_rx = jiffies; } @@ -2544,6 +2542,9 @@ static void ppp_destroy_interface(struct ppp *ppp) ppp->active_filter = NULL; #endif /* CONFIG_PPP_FILTER */ + if (ppp->xmit_pending) + kfree_skb(ppp->xmit_pending); + kfree(ppp); } diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index b6f0e9a..5918fab 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -594,7 +594,8 @@ ppp_sync_txmunge(struct syncppp *ap, struct sk_buff *skb) return NULL; } skb_reserve(npkt,2); - memcpy(skb_put(npkt,skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, + skb_put(npkt, skb->len), skb->len); kfree_skb(skb); skb = npkt; } diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index 860bb0f..6f98834 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c @@ -7,6 +7,12 @@ * * Version: 0.7.0 * + * 070228 : Fix to allow multiple sessions with same remote MAC and same + * session id by including the local device ifindex in the + * tuple identifying a session. This also ensures packets can't + * be injected into a session from interfaces other than the one + * specified by userspace. Florian Zumbiehl <florz@florz.de> + * (Oh, BTW, this one is YYMMDD, in case you were wondering ...) * 220102 : Fix module use count on failure in pppoe_create, pppox_sk -acme * 030700 : Fixed connect logic to allow for disconnect. * 270700 : Fixed potential SMP problems; we must protect against @@ -127,14 +133,14 @@ static struct pppox_sock *item_hash_table[PPPOE_HASH_SIZE]; * Set/get/delete/rehash items (internal versions) * **********************************************************************/ -static struct pppox_sock *__get_item(unsigned long sid, unsigned char *addr) +static struct pppox_sock *__get_item(unsigned long sid, unsigned char *addr, int ifindex) { int hash = hash_item(sid, addr); struct pppox_sock *ret; ret = item_hash_table[hash]; - while (ret && !cmp_addr(&ret->pppoe_pa, sid, addr)) + while (ret && !(cmp_addr(&ret->pppoe_pa, sid, addr) && ret->pppoe_ifindex == ifindex)) ret = ret->next; return ret; @@ -147,21 +153,19 @@ static int __set_item(struct pppox_sock *po) ret = item_hash_table[hash]; while (ret) { - if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa)) + if (cmp_2_addr(&ret->pppoe_pa, &po->pppoe_pa) && ret->pppoe_ifindex == po->pppoe_ifindex) return -EALREADY; ret = ret->next; } - if (!ret) { - po->next = item_hash_table[hash]; - item_hash_table[hash] = po; - } + po->next = item_hash_table[hash]; + item_hash_table[hash] = po; return 0; } -static struct pppox_sock *__delete_item(unsigned long sid, char *addr) +static struct pppox_sock *__delete_item(unsigned long sid, char *addr, int ifindex) { int hash = hash_item(sid, addr); struct pppox_sock *ret, **src; @@ -170,7 +174,7 @@ static struct pppox_sock *__delete_item(unsigned long sid, char *addr) src = &item_hash_table[hash]; while (ret) { - if (cmp_addr(&ret->pppoe_pa, sid, addr)) { + if (cmp_addr(&ret->pppoe_pa, sid, addr) && ret->pppoe_ifindex == ifindex) { *src = ret->next; break; } @@ -188,12 +192,12 @@ static struct pppox_sock *__delete_item(unsigned long sid, char *addr) * **********************************************************************/ static inline struct pppox_sock *get_item(unsigned long sid, - unsigned char *addr) + unsigned char *addr, int ifindex) { struct pppox_sock *po; read_lock_bh(&pppoe_hash_lock); - po = __get_item(sid, addr); + po = __get_item(sid, addr, ifindex); if (po) sock_hold(sk_pppox(po)); read_unlock_bh(&pppoe_hash_lock); @@ -203,29 +207,23 @@ static inline struct pppox_sock *get_item(unsigned long sid, static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp) { - return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote); -} - -static inline int set_item(struct pppox_sock *po) -{ - int i; - - if (!po) - return -EINVAL; - - write_lock_bh(&pppoe_hash_lock); - i = __set_item(po); - write_unlock_bh(&pppoe_hash_lock); - - return i; + struct net_device *dev; + int ifindex; + + dev = dev_get_by_name(sp->sa_addr.pppoe.dev); + if(!dev) + return NULL; + ifindex = dev->ifindex; + dev_put(dev); + return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); } -static inline struct pppox_sock *delete_item(unsigned long sid, char *addr) +static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex) { struct pppox_sock *ret; write_lock_bh(&pppoe_hash_lock); - ret = __delete_item(sid, addr); + ret = __delete_item(sid, addr, ifindex); write_unlock_bh(&pppoe_hash_lock); return ret; @@ -243,54 +241,53 @@ static inline struct pppox_sock *delete_item(unsigned long sid, char *addr) static void pppoe_flush_dev(struct net_device *dev) { int hash; - BUG_ON(dev == NULL); - read_lock_bh(&pppoe_hash_lock); + write_lock_bh(&pppoe_hash_lock); for (hash = 0; hash < PPPOE_HASH_SIZE; hash++) { struct pppox_sock *po = item_hash_table[hash]; while (po != NULL) { - if (po->pppoe_dev == dev) { - struct sock *sk = sk_pppox(po); - - sock_hold(sk); - po->pppoe_dev = NULL; + struct sock *sk = sk_pppox(po); + if (po->pppoe_dev != dev) { + po = po->next; + continue; + } + po->pppoe_dev = NULL; + dev_put(dev); - /* We hold a reference to SK, now drop the - * hash table lock so that we may attempt - * to lock the socket (which can sleep). - */ - read_unlock_bh(&pppoe_hash_lock); - lock_sock(sk); + /* We always grab the socket lock, followed by the + * pppoe_hash_lock, in that order. Since we should + * hold the sock lock while doing any unbinding, + * we need to release the lock we're holding. + * Hold a reference to the sock so it doesn't disappear + * as we're jumping between locks. + */ - if (sk->sk_state & - (PPPOX_CONNECTED | PPPOX_BOUND)) { - pppox_unbind_sock(sk); - dev_put(dev); - sk->sk_state = PPPOX_ZOMBIE; - sk->sk_state_change(sk); - } + sock_hold(sk); - release_sock(sk); + write_unlock_bh(&pppoe_hash_lock); + lock_sock(sk); - sock_put(sk); + if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) { + pppox_unbind_sock(sk); + sk->sk_state = PPPOX_ZOMBIE; + sk->sk_state_change(sk); + } - read_lock_bh(&pppoe_hash_lock); + release_sock(sk); + sock_put(sk); - /* Now restart from the beginning of this - * hash chain. We always NULL out pppoe_dev - * so we are guaranteed to make forward - * progress. - */ - po = item_hash_table[hash]; - continue; - } - po = po->next; + /* Restart scan at the beginning of this hash chain. + * While the lock was dropped the chain contents may + * have changed. + */ + write_lock_bh(&pppoe_hash_lock); + po = item_hash_table[hash]; } } - read_unlock_bh(&pppoe_hash_lock); + write_unlock_bh(&pppoe_hash_lock); } static int pppoe_device_event(struct notifier_block *this, @@ -332,10 +329,10 @@ static struct notifier_block pppoe_notifier = { static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb) { struct pppox_sock *po = pppox_sk(sk); - struct pppox_sock *relay_po = NULL; + struct pppox_sock *relay_po; if (sk->sk_state & PPPOX_BOUND) { - struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw; + struct pppoe_hdr *ph = pppoe_hdr(skb); int len = ntohs(ph->length); skb_pull_rcsum(skb, sizeof(struct pppoe_hdr)); if (pskb_trim_rcsum(skb, len)) @@ -389,9 +386,9 @@ static int pppoe_rcv(struct sk_buff *skb, if (!(skb = skb_share_check(skb, GFP_ATOMIC))) goto out; - ph = (struct pppoe_hdr *) skb->nh.raw; + ph = pppoe_hdr(skb); - po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source); + po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex); if (po != NULL) return sk_receive_skb(sk_pppox(po), skb, 0); drop: @@ -421,11 +418,11 @@ static int pppoe_disc_rcv(struct sk_buff *skb, if (!(skb = skb_share_check(skb, GFP_ATOMIC))) goto out; - ph = (struct pppoe_hdr *) skb->nh.raw; + ph = pppoe_hdr(skb); if (ph->code != PADT_CODE) goto abort; - po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source); + po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source, dev->ifindex); if (po) { struct sock *sk = sk_pppox(po); @@ -502,36 +499,49 @@ static int pppoe_release(struct socket *sock) { struct sock *sk = sock->sk; struct pppox_sock *po; - int error = 0; if (!sk) return 0; - if (sock_flag(sk, SOCK_DEAD)) + lock_sock(sk); + if (sock_flag(sk, SOCK_DEAD)){ + release_sock(sk); return -EBADF; + } pppox_unbind_sock(sk); /* Signal the death of the socket. */ sk->sk_state = PPPOX_DEAD; + + /* Write lock on hash lock protects the entire "po" struct from + * concurrent updates via pppoe_flush_dev. The "po" struct should + * be considered part of the hash table contents, thus protected + * by the hash table lock */ + write_lock_bh(&pppoe_hash_lock); + po = pppox_sk(sk); if (po->pppoe_pa.sid) { - delete_item(po->pppoe_pa.sid, po->pppoe_pa.remote); + __delete_item(po->pppoe_pa.sid, + po->pppoe_pa.remote, po->pppoe_ifindex); } - if (po->pppoe_dev) + if (po->pppoe_dev) { dev_put(po->pppoe_dev); + po->pppoe_dev = NULL; + } - po->pppoe_dev = NULL; + write_unlock_bh(&pppoe_hash_lock); sock_orphan(sk); sock->sk = NULL; skb_queue_purge(&sk->sk_receive_queue); + release_sock(sk); sock_put(sk); - return error; + return 0; } @@ -539,7 +549,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, int sockaddr_len, int flags) { struct sock *sk = sock->sk; - struct net_device *dev = NULL; + struct net_device *dev; struct sockaddr_pppox *sp = (struct sockaddr_pppox *) uservaddr; struct pppox_sock *po = pppox_sk(sk); int error; @@ -565,7 +575,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, pppox_unbind_sock(sk); /* Delete the old binding */ - delete_item(po->pppoe_pa.sid,po->pppoe_pa.remote); + delete_item(po->pppoe_pa.sid,po->pppoe_pa.remote,po->pppoe_ifindex); if(po->pppoe_dev) dev_put(po->pppoe_dev); @@ -585,15 +595,20 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, goto end; po->pppoe_dev = dev; + po->pppoe_ifindex = dev->ifindex; - if (!(dev->flags & IFF_UP)) + write_lock_bh(&pppoe_hash_lock); + if (!(dev->flags & IFF_UP)){ + write_unlock_bh(&pppoe_hash_lock); goto err_put; + } memcpy(&po->pppoe_pa, &sp->sa_addr.pppoe, sizeof(struct pppoe_addr)); - error = set_item(po); + error = __set_item(po); + write_unlock_bh(&pppoe_hash_lock); if (error < 0) goto err_put; @@ -705,7 +720,7 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, break; /* PPPoE address from the user specifies an outbound - PPPoE address to which frames are forwarded to */ + PPPoE address which frames are forwarded to */ err = -EFAULT; if (copy_from_user(&po->pppoe_relay, (void __user *)arg, @@ -749,10 +764,10 @@ static int pppoe_ioctl(struct socket *sock, unsigned int cmd, static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len) { - struct sk_buff *skb = NULL; + struct sk_buff *skb; struct sock *sk = sock->sk; struct pppox_sock *po = pppox_sk(sk); - int error = 0; + int error; struct pppoe_hdr hdr; struct pppoe_hdr *ph; struct net_device *dev; @@ -786,7 +801,7 @@ static int pppoe_sendmsg(struct kiocb *iocb, struct socket *sock, /* Reserve space for headers. */ skb_reserve(skb, dev->hard_header_len); - skb->nh.raw = skb->data; + skb_reset_network_header(skb); skb->dev = dev; @@ -856,7 +871,8 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) goto abort; skb_reserve(skb2, dev->hard_header_len + sizeof(struct pppoe_hdr)); - memcpy(skb_put(skb2, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, skb_put(skb2, skb->len), + skb->len); } else { /* Make a clone so as to not disturb the original skb, * give dev_queue_xmit something it can free. @@ -871,7 +887,7 @@ static int __pppoe_xmit(struct sock *sk, struct sk_buff *skb) memcpy(ph, &hdr, sizeof(struct pppoe_hdr)); skb2->protocol = __constant_htons(ETH_P_PPP_SES); - skb2->nh.raw = skb2->data; + skb_reset_network_header(skb2); skb2->dev = dev; @@ -916,10 +932,8 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *m, size_t total_len, int flags) { struct sock *sk = sock->sk; - struct sk_buff *skb = NULL; + struct sk_buff *skb; int error = 0; - int len; - struct pppoe_hdr *ph = NULL; if (sk->sk_state & PPPOX_BOUND) { error = -EIO; @@ -929,26 +943,21 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock, skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &error); - if (error < 0) { + if (error < 0) goto end; - } m->msg_namelen = 0; if (skb) { - error = 0; - ph = (struct pppoe_hdr *) skb->nh.raw; - len = ntohs(ph->length); + struct pppoe_hdr *ph = pppoe_hdr(skb); + const int len = ntohs(ph->length); error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len); - if (error < 0) - goto do_skb_free; - error = len; + if (error == 0) + error = len; } -do_skb_free: - if (skb) - kfree_skb(skb); + kfree_skb(skb); end: return error; } @@ -978,7 +987,7 @@ out: static __inline__ struct pppox_sock *pppoe_get_idx(loff_t pos) { - struct pppox_sock *po = NULL; + struct pppox_sock *po; int i = 0; for (; i < PPPOE_HASH_SIZE; i++) { diff --git a/drivers/net/pppox.c b/drivers/net/pppox.c index 9315046..f3e47d0 100644 --- a/drivers/net/pppox.c +++ b/drivers/net/pppox.c @@ -31,6 +31,7 @@ #include <linux/ppp_defs.h> #include <linux/if_ppp.h> #include <linux/ppp_channel.h> +#include <linux/kmod.h> #include <net/sock.h> @@ -58,7 +59,7 @@ void pppox_unbind_sock(struct sock *sk) { /* Clear connection to ppp device, if attached. */ - if (sk->sk_state & (PPPOX_BOUND | PPPOX_ZOMBIE)) { + if (sk->sk_state & (PPPOX_BOUND | PPPOX_CONNECTED | PPPOX_ZOMBIE)) { ppp_unregister_channel(&pppox_sk(sk)->chan); sk->sk_state = PPPOX_DEAD; } @@ -114,6 +115,13 @@ static int pppox_create(struct socket *sock, int protocol) goto out; rc = -EPROTONOSUPPORT; +#ifdef CONFIG_KMOD + if (!pppox_protos[protocol]) { + char buffer[32]; + sprintf(buffer, "pppox-proto-%d", protocol); + request_module(buffer); + } +#endif if (!pppox_protos[protocol] || !try_module_get(pppox_protos[protocol]->owner)) goto out; diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index a142cdf..d8766c0 100755 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -39,7 +39,7 @@ #define DRV_NAME "qla3xxx" #define DRV_STRING "QLogic ISP3XXX Network Driver" -#define DRV_VERSION "v2.02.00-k36" +#define DRV_VERSION "v2.03.00-k4" #define PFX DRV_NAME " " static const char ql3xxx_driver_name[] = DRV_NAME; @@ -72,6 +72,30 @@ static struct pci_device_id ql3xxx_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci, ql3xxx_pci_tbl); /* + * These are the known PHY's which are used + */ +typedef enum { + PHY_TYPE_UNKNOWN = 0, + PHY_VITESSE_VSC8211, + PHY_AGERE_ET1011C, + MAX_PHY_DEV_TYPES +} PHY_DEVICE_et; + +typedef struct { + PHY_DEVICE_et phyDevice; + u32 phyIdOUI; + u16 phyIdModel; + char *name; +} PHY_DEVICE_INFO_t; + +static const PHY_DEVICE_INFO_t PHY_DEVICES[] = + {{PHY_TYPE_UNKNOWN, 0x000000, 0x0, "PHY_TYPE_UNKNOWN"}, + {PHY_VITESSE_VSC8211, 0x0003f1, 0xb, "PHY_VITESSE_VSC8211"}, + {PHY_AGERE_ET1011C, 0x00a0bc, 0x1, "PHY_AGERE_ET1011C"}, +}; + + +/* * Caller must take hw_lock. */ static int ql_sem_spinlock(struct ql3_adapter *qdev, @@ -276,7 +300,8 @@ static void ql_enable_interrupts(struct ql3_adapter *qdev) static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, struct ql_rcv_buf_cb *lrg_buf_cb) { - u64 map; + dma_addr_t map; + int err; lrg_buf_cb->next = NULL; if (qdev->lrg_buf_free_tail == NULL) { /* The list is empty */ @@ -287,9 +312,10 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, } if (!lrg_buf_cb->skb) { - lrg_buf_cb->skb = dev_alloc_skb(qdev->lrg_buffer_len); + lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev, + qdev->lrg_buffer_len); if (unlikely(!lrg_buf_cb->skb)) { - printk(KERN_ERR PFX "%s: failed dev_alloc_skb().\n", + printk(KERN_ERR PFX "%s: failed netdev_alloc_skb().\n", qdev->ndev->name); qdev->lrg_buf_skb_check++; } else { @@ -303,6 +329,17 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev, qdev->lrg_buffer_len - QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); + err = pci_dma_mapping_error(map); + if(err) { + printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", + qdev->ndev->name, err); + dev_kfree_skb(lrg_buf_cb->skb); + lrg_buf_cb->skb = NULL; + + qdev->lrg_buf_skb_check++; + return; + } + lrg_buf_cb->buf_phy_addr_low = cpu_to_le32(LS_64BITS(map)); lrg_buf_cb->buf_phy_addr_high = @@ -649,7 +686,7 @@ static u8 ql_mii_disable_scan_mode(struct ql3_adapter *qdev) } static int ql_mii_write_reg_ex(struct ql3_adapter *qdev, - u16 regAddr, u16 value, u32 mac_index) + u16 regAddr, u16 value, u32 phyAddr) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; @@ -667,7 +704,7 @@ static int ql_mii_write_reg_ex(struct ql3_adapter *qdev, } ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, - PHYAddr[mac_index] | regAddr); + phyAddr | regAddr); ql_write_page0_reg(qdev, &port_regs->macMIIMgmtDataReg, value); @@ -688,7 +725,7 @@ static int ql_mii_write_reg_ex(struct ql3_adapter *qdev, } static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr, - u16 * value, u32 mac_index) + u16 * value, u32 phyAddr) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; @@ -707,7 +744,7 @@ static int ql_mii_read_reg_ex(struct ql3_adapter *qdev, u16 regAddr, } ql_write_page0_reg(qdev, &port_regs->macMIIMgmtAddrReg, - PHYAddr[mac_index] | regAddr); + phyAddr | regAddr); ql_write_page0_reg(qdev, &port_regs->macMIIMgmtControlReg, (MAC_MII_CONTROL_RC << 16)); @@ -837,28 +874,31 @@ static void ql_petbi_start_neg(struct ql3_adapter *qdev) } -static void ql_petbi_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_reset_ex(struct ql3_adapter *qdev) { ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_SOFT_RESET, - mac_index); + PHYAddr[qdev->mac_index]); } -static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_start_neg_ex(struct ql3_adapter *qdev) { u16 reg; /* Enable Auto-negotiation sense */ - ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®, mac_index); + ql_mii_read_reg_ex(qdev, PETBI_TBI_CTRL, ®, + PHYAddr[qdev->mac_index]); reg |= PETBI_TBI_AUTO_SENSE; - ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, mac_index); + ql_mii_write_reg_ex(qdev, PETBI_TBI_CTRL, reg, + PHYAddr[qdev->mac_index]); ql_mii_write_reg_ex(qdev, PETBI_NEG_ADVER, - PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, mac_index); + PETBI_NEG_PAUSE | PETBI_NEG_DUPLEX, + PHYAddr[qdev->mac_index]); ql_mii_write_reg_ex(qdev, PETBI_CONTROL_REG, PETBI_CTRL_AUTO_NEG | PETBI_CTRL_RESTART_NEG | PETBI_CTRL_FULL_DUPLEX | PETBI_CTRL_SPEED_1000, - mac_index); + PHYAddr[qdev->mac_index]); } static void ql_petbi_init(struct ql3_adapter *qdev) @@ -867,10 +907,10 @@ static void ql_petbi_init(struct ql3_adapter *qdev) ql_petbi_start_neg(qdev); } -static void ql_petbi_init_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_petbi_init_ex(struct ql3_adapter *qdev) { - ql_petbi_reset_ex(qdev, mac_index); - ql_petbi_start_neg_ex(qdev, mac_index); + ql_petbi_reset_ex(qdev); + ql_petbi_start_neg_ex(qdev); } static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev) @@ -883,33 +923,128 @@ static int ql_is_petbi_neg_pause(struct ql3_adapter *qdev) return (reg & PETBI_NEG_PAUSE_MASK) == PETBI_NEG_PAUSE; } +static void phyAgereSpecificInit(struct ql3_adapter *qdev, u32 miiAddr) +{ + printk(KERN_INFO "%s: enabling Agere specific PHY\n", qdev->ndev->name); + /* power down device bit 11 = 1 */ + ql_mii_write_reg_ex(qdev, 0x00, 0x1940, miiAddr); + /* enable diagnostic mode bit 2 = 1 */ + ql_mii_write_reg_ex(qdev, 0x12, 0x840e, miiAddr); + /* 1000MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8805, miiAddr); + /* 1000MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0xf03e, miiAddr); + /* 100MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8806, miiAddr); + /* 100MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0x003e, miiAddr); + /* 10MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x10, 0x8807, miiAddr); + /* 10MB amplitude adjust (see Agere errata) */ + ql_mii_write_reg_ex(qdev, 0x11, 0x1f00, miiAddr); + /* point to hidden reg 0x2806 */ + ql_mii_write_reg_ex(qdev, 0x10, 0x2806, miiAddr); + /* Write new PHYAD w/bit 5 set */ + ql_mii_write_reg_ex(qdev, 0x11, 0x0020 | (PHYAddr[qdev->mac_index] >> 8), miiAddr); + /* + * Disable diagnostic mode bit 2 = 0 + * Power up device bit 11 = 0 + * Link up (on) and activity (blink) + */ + ql_mii_write_reg(qdev, 0x12, 0x840a); + ql_mii_write_reg(qdev, 0x00, 0x1140); + ql_mii_write_reg(qdev, 0x1c, 0xfaf0); +} + +static PHY_DEVICE_et getPhyType (struct ql3_adapter *qdev, + u16 phyIdReg0, u16 phyIdReg1) +{ + PHY_DEVICE_et result = PHY_TYPE_UNKNOWN; + u32 oui; + u16 model; + int i; + + if (phyIdReg0 == 0xffff) { + return result; + } + + if (phyIdReg1 == 0xffff) { + return result; + } + + /* oui is split between two registers */ + oui = (phyIdReg0 << 6) | ((phyIdReg1 & PHY_OUI_1_MASK) >> 10); + + model = (phyIdReg1 & PHY_MODEL_MASK) >> 4; + + /* Scan table for this PHY */ + for(i = 0; i < MAX_PHY_DEV_TYPES; i++) { + if ((oui == PHY_DEVICES[i].phyIdOUI) && (model == PHY_DEVICES[i].phyIdModel)) + { + result = PHY_DEVICES[i].phyDevice; + + printk(KERN_INFO "%s: Phy: %s\n", + qdev->ndev->name, PHY_DEVICES[i].name); + + break; + } + } + + return result; +} + static int ql_phy_get_speed(struct ql3_adapter *qdev) { u16 reg; + switch(qdev->phyType) { + case PHY_AGERE_ET1011C: + { + if (ql_mii_read_reg(qdev, 0x1A, ®) < 0) + return 0; + + reg = (reg >> 8) & 3; + break; + } + default: if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) return 0; reg = (((reg & 0x18) >> 3) & 3); + } - if (reg == 2) + switch(reg) { + case 2: return SPEED_1000; - else if (reg == 1) + case 1: return SPEED_100; - else if (reg == 0) + case 0: return SPEED_10; - else + default: return -1; + } } static int ql_is_full_dup(struct ql3_adapter *qdev) { u16 reg; - if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) - return 0; - - return (reg & PHY_AUX_DUPLEX_STAT) != 0; + switch(qdev->phyType) { + case PHY_AGERE_ET1011C: + { + if (ql_mii_read_reg(qdev, 0x1A, ®)) + return 0; + + return ((reg & 0x0080) && (reg & 0x1000)) != 0; + } + case PHY_VITESSE_VSC8211: + default: + { + if (ql_mii_read_reg(qdev, AUX_CONTROL_STATUS, ®) < 0) + return 0; + return (reg & PHY_AUX_DUPLEX_STAT) != 0; + } + } } static int ql_is_phy_neg_pause(struct ql3_adapter *qdev) @@ -922,6 +1057,73 @@ static int ql_is_phy_neg_pause(struct ql3_adapter *qdev) return (reg & PHY_NEG_PAUSE) != 0; } +static int PHY_Setup(struct ql3_adapter *qdev) +{ + u16 reg1; + u16 reg2; + bool agereAddrChangeNeeded = false; + u32 miiAddr = 0; + int err; + + /* Determine the PHY we are using by reading the ID's */ + err = ql_mii_read_reg(qdev, PHY_ID_0_REG, ®1); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n", + qdev->ndev->name); + return err; + } + + err = ql_mii_read_reg(qdev, PHY_ID_1_REG, ®2); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG\n", + qdev->ndev->name); + return err; + } + + /* Check if we have a Agere PHY */ + if ((reg1 == 0xffff) || (reg2 == 0xffff)) { + + /* Determine which MII address we should be using + determined by the index of the card */ + if (qdev->mac_index == 0) { + miiAddr = MII_AGERE_ADDR_1; + } else { + miiAddr = MII_AGERE_ADDR_2; + } + + err =ql_mii_read_reg_ex(qdev, PHY_ID_0_REG, ®1, miiAddr); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n", + qdev->ndev->name); + return err; + } + + err = ql_mii_read_reg_ex(qdev, PHY_ID_1_REG, ®2, miiAddr); + if(err != 0) { + printk(KERN_ERR "%s: Could not read from reg PHY_ID_0_REG after Agere detected\n", + qdev->ndev->name); + return err; + } + + /* We need to remember to initialize the Agere PHY */ + agereAddrChangeNeeded = true; + } + + /* Determine the particular PHY we have on board to apply + PHY specific initializations */ + qdev->phyType = getPhyType(qdev, reg1, reg2); + + if ((qdev->phyType == PHY_AGERE_ET1011C) && agereAddrChangeNeeded) { + /* need this here so address gets changed */ + phyAgereSpecificInit(qdev, miiAddr); + } else if (qdev->phyType == PHY_TYPE_UNKNOWN) { + printk(KERN_ERR "%s: PHY is unknown\n", qdev->ndev->name); + return -EIO; + } + + return 0; +} + /* * Caller holds hw_lock. */ @@ -1192,15 +1394,14 @@ static int ql_link_down_detect_clear(struct ql3_adapter *qdev) /* * Caller holds hw_lock. */ -static int ql_this_adapter_controls_port(struct ql3_adapter *qdev, - u32 mac_index) +static int ql_this_adapter_controls_port(struct ql3_adapter *qdev) { struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; u32 bitToCheck = 0; u32 temp; - switch (mac_index) { + switch (qdev->mac_index) { case 0: bitToCheck = PORT_STATUS_F1_ENABLED; break; @@ -1225,27 +1426,96 @@ static int ql_this_adapter_controls_port(struct ql3_adapter *qdev, } } -static void ql_phy_reset_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_reset_ex(struct ql3_adapter *qdev) { - ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, mac_index); + ql_mii_write_reg_ex(qdev, CONTROL_REG, PHY_CTRL_SOFT_RESET, + PHYAddr[qdev->mac_index]); } -static void ql_phy_start_neg_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_start_neg_ex(struct ql3_adapter *qdev) { u16 reg; + u16 portConfiguration; + + if(qdev->phyType == PHY_AGERE_ET1011C) { + /* turn off external loopback */ + ql_mii_write_reg(qdev, 0x13, 0x0000); + } + + if(qdev->mac_index == 0) + portConfiguration = qdev->nvram_data.macCfg_port0.portConfiguration; + else + portConfiguration = qdev->nvram_data.macCfg_port1.portConfiguration; + + /* Some HBA's in the field are set to 0 and they need to + be reinterpreted with a default value */ + if(portConfiguration == 0) + portConfiguration = PORT_CONFIG_DEFAULT; + + /* Set the 1000 advertisements */ + ql_mii_read_reg_ex(qdev, PHY_GIG_CONTROL, ®, + PHYAddr[qdev->mac_index]); + reg &= ~PHY_GIG_ALL_PARAMS; + + if(portConfiguration & + PORT_CONFIG_FULL_DUPLEX_ENABLED & + PORT_CONFIG_1000MB_SPEED) { + reg |= PHY_GIG_ADV_1000F; + } + + if(portConfiguration & + PORT_CONFIG_HALF_DUPLEX_ENABLED & + PORT_CONFIG_1000MB_SPEED) { + reg |= PHY_GIG_ADV_1000H; + } + + ql_mii_write_reg_ex(qdev, PHY_GIG_CONTROL, reg, + PHYAddr[qdev->mac_index]); - ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, - PHY_NEG_PAUSE | PHY_NEG_ADV_SPEED | 1, mac_index); + /* Set the 10/100 & pause negotiation advertisements */ + ql_mii_read_reg_ex(qdev, PHY_NEG_ADVER, ®, + PHYAddr[qdev->mac_index]); + reg &= ~PHY_NEG_ALL_PARAMS; - ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, mac_index); - ql_mii_write_reg_ex(qdev, CONTROL_REG, reg | PHY_CTRL_RESTART_NEG, - mac_index); + if(portConfiguration & PORT_CONFIG_SYM_PAUSE_ENABLED) + reg |= PHY_NEG_ASY_PAUSE | PHY_NEG_SYM_PAUSE; + + if(portConfiguration & PORT_CONFIG_FULL_DUPLEX_ENABLED) { + if(portConfiguration & PORT_CONFIG_100MB_SPEED) + reg |= PHY_NEG_ADV_100F; + + if(portConfiguration & PORT_CONFIG_10MB_SPEED) + reg |= PHY_NEG_ADV_10F; + } + + if(portConfiguration & PORT_CONFIG_HALF_DUPLEX_ENABLED) { + if(portConfiguration & PORT_CONFIG_100MB_SPEED) + reg |= PHY_NEG_ADV_100H; + + if(portConfiguration & PORT_CONFIG_10MB_SPEED) + reg |= PHY_NEG_ADV_10H; + } + + if(portConfiguration & + PORT_CONFIG_1000MB_SPEED) { + reg |= 1; + } + + ql_mii_write_reg_ex(qdev, PHY_NEG_ADVER, reg, + PHYAddr[qdev->mac_index]); + + ql_mii_read_reg_ex(qdev, CONTROL_REG, ®, PHYAddr[qdev->mac_index]); + + ql_mii_write_reg_ex(qdev, CONTROL_REG, + reg | PHY_CTRL_RESTART_NEG | PHY_CTRL_AUTO_NEG, + PHYAddr[qdev->mac_index]); } -static void ql_phy_init_ex(struct ql3_adapter *qdev, u32 mac_index) +static void ql_phy_init_ex(struct ql3_adapter *qdev) { - ql_phy_reset_ex(qdev, mac_index); - ql_phy_start_neg_ex(qdev, mac_index); + ql_phy_reset_ex(qdev); + PHY_Setup(qdev); + ql_phy_start_neg_ex(qdev); } /* @@ -1282,14 +1552,17 @@ static int ql_port_start(struct ql3_adapter *qdev) { if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * - 2) << 7)) + 2) << 7)) { + printk(KERN_ERR "%s: Could not get hw lock for GIO\n", + qdev->ndev->name); return -1; + } if (ql_is_fiber(qdev)) { ql_petbi_init(qdev); } else { /* Copper port */ - ql_phy_init_ex(qdev, qdev->mac_index); + ql_phy_init_ex(qdev); } ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); @@ -1387,6 +1660,8 @@ static void ql_link_state_machine(struct ql3_adapter *qdev) printk(KERN_INFO PFX "%s: Reset in progress, skip processing link " "state.\n", qdev->ndev->name); + + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); return; } @@ -1438,7 +1713,7 @@ static void ql_link_state_machine(struct ql3_adapter *qdev) */ static void ql_get_phy_owner(struct ql3_adapter *qdev) { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) + if (ql_this_adapter_controls_port(qdev)) set_bit(QL_LINK_MASTER,&qdev->flags); else clear_bit(QL_LINK_MASTER,&qdev->flags); @@ -1452,11 +1727,11 @@ static void ql_init_scan_mode(struct ql3_adapter *qdev) ql_mii_enable_scan_mode(qdev); if (test_bit(QL_LINK_OPTICAL,&qdev->flags)) { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) - ql_petbi_init_ex(qdev, qdev->mac_index); + if (ql_this_adapter_controls_port(qdev)) + ql_petbi_init_ex(qdev); } else { - if (ql_this_adapter_controls_port(qdev, qdev->mac_index)) - ql_phy_init_ex(qdev, qdev->mac_index); + if (ql_this_adapter_controls_port(qdev)) + ql_phy_init_ex(qdev); } } @@ -1518,8 +1793,10 @@ static int ql_get_auto_cfg_status(struct ql3_adapter *qdev) spin_lock_irqsave(&qdev->hw_lock, hw_flags); if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * - 2) << 7)) + 2) << 7)) { + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); return 0; + } status = ql_is_auto_cfg(qdev); ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); @@ -1533,8 +1810,10 @@ static u32 ql_get_speed(struct ql3_adapter *qdev) spin_lock_irqsave(&qdev->hw_lock, hw_flags); if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * - 2) << 7)) + 2) << 7)) { + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); return 0; + } status = ql_get_link_speed(qdev); ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); @@ -1548,8 +1827,10 @@ static int ql_get_full_dup(struct ql3_adapter *qdev) spin_lock_irqsave(&qdev->hw_lock, hw_flags); if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * - 2) << 7)) + 2) << 7)) { + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); return 0; + } status = ql_is_link_full_dup(qdev); ql_sem_unlock(qdev, QL_PHY_GIO_SEM_MASK); spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); @@ -1603,6 +1884,23 @@ static void ql_set_msglevel(struct net_device *ndev, u32 value) qdev->msg_enable = value; } +static void ql_get_pauseparam(struct net_device *ndev, + struct ethtool_pauseparam *pause) +{ + struct ql3_adapter *qdev = netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + + u32 reg; + if(qdev->mac_index == 0) + reg = ql_read_page0_reg(qdev, &port_regs->mac0ConfigReg); + else + reg = ql_read_page0_reg(qdev, &port_regs->mac1ConfigReg); + + pause->autoneg = ql_get_auto_cfg_status(qdev); + pause->rx_pause = (reg & MAC_CONFIG_REG_RF) >> 2; + pause->tx_pause = (reg & MAC_CONFIG_REG_TF) >> 1; +} + static const struct ethtool_ops ql3xxx_ethtool_ops = { .get_settings = ql_get_settings, .get_drvinfo = ql_get_drvinfo, @@ -1610,19 +1908,22 @@ static const struct ethtool_ops ql3xxx_ethtool_ops = { .get_link = ethtool_op_get_link, .get_msglevel = ql_get_msglevel, .set_msglevel = ql_set_msglevel, + .get_pauseparam = ql_get_pauseparam, }; static int ql_populate_free_queue(struct ql3_adapter *qdev) { struct ql_rcv_buf_cb *lrg_buf_cb = qdev->lrg_buf_free_head; - u64 map; + dma_addr_t map; + int err; while (lrg_buf_cb) { if (!lrg_buf_cb->skb) { - lrg_buf_cb->skb = dev_alloc_skb(qdev->lrg_buffer_len); + lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev, + qdev->lrg_buffer_len); if (unlikely(!lrg_buf_cb->skb)) { printk(KERN_DEBUG PFX - "%s: Failed dev_alloc_skb().\n", + "%s: Failed netdev_alloc_skb().\n", qdev->ndev->name); break; } else { @@ -1636,6 +1937,17 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) qdev->lrg_buffer_len - QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); + + err = pci_dma_mapping_error(map); + if(err) { + printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", + qdev->ndev->name, err); + dev_kfree_skb(lrg_buf_cb->skb); + lrg_buf_cb->skb = NULL; + break; + } + + lrg_buf_cb->buf_phy_addr_low = cpu_to_le32(LS_64BITS(map)); lrg_buf_cb->buf_phy_addr_high = @@ -1657,6 +1969,27 @@ static int ql_populate_free_queue(struct ql3_adapter *qdev) /* * Caller holds hw_lock. */ +static void ql_update_small_bufq_prod_index(struct ql3_adapter *qdev) +{ + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + if (qdev->small_buf_release_cnt >= 16) { + while (qdev->small_buf_release_cnt >= 16) { + qdev->small_buf_q_producer_index++; + + if (qdev->small_buf_q_producer_index == + NUM_SBUFQ_ENTRIES) + qdev->small_buf_q_producer_index = 0; + qdev->small_buf_release_cnt -= 8; + } + wmb(); + writel(qdev->small_buf_q_producer_index, + &port_regs->CommonRegs.rxSmallQProducerIndex); + } +} + +/* + * Caller holds hw_lock. + */ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev) { struct bufq_addr_element *lrg_buf_q_ele; @@ -1690,21 +2023,18 @@ static void ql_update_lrg_bufq_prod_index(struct ql3_adapter *qdev) qdev->lrg_buf_q_producer_index++; - if (qdev->lrg_buf_q_producer_index == NUM_LBUFQ_ENTRIES) + if (qdev->lrg_buf_q_producer_index == qdev->num_lbufq_entries) qdev->lrg_buf_q_producer_index = 0; if (qdev->lrg_buf_q_producer_index == - (NUM_LBUFQ_ENTRIES - 1)) { + (qdev->num_lbufq_entries - 1)) { lrg_buf_q_ele = qdev->lrg_buf_q_virt_addr; } } - + wmb(); qdev->lrg_buf_next_free = lrg_buf_q_ele; - - ql_write_common_reg(qdev, - &port_regs->CommonRegs. - rxLargeQProducerIndex, - qdev->lrg_buf_q_producer_index); + writel(qdev->lrg_buf_q_producer_index, + &port_regs->CommonRegs.rxLargeQProducerIndex); } } @@ -1713,8 +2043,31 @@ static void ql_process_mac_tx_intr(struct ql3_adapter *qdev, { struct ql_tx_buf_cb *tx_cb; int i; + int retval = 0; + if(mac_rsp->flags & OB_MAC_IOCB_RSP_S) { + printk(KERN_WARNING "Frame short but, frame was padded and sent.\n"); + } + tx_cb = &qdev->tx_buf[mac_rsp->transaction_id]; + + /* Check the transmit response flags for any errors */ + if(mac_rsp->flags & OB_MAC_IOCB_RSP_S) { + printk(KERN_ERR "Frame too short to be legal, frame not sent.\n"); + + qdev->stats.tx_errors++; + retval = -EIO; + goto frame_not_sent; + } + + if(tx_cb->seg_count == 0) { + printk(KERN_ERR "tx_cb->seg_count == 0: %d\n", mac_rsp->transaction_id); + + qdev->stats.tx_errors++; + retval = -EIO; + goto invalid_seg_count; + } + pci_unmap_single(qdev->pdev, pci_unmap_addr(&tx_cb->map[0], mapaddr), pci_unmap_len(&tx_cb->map[0], maplen), @@ -1731,11 +2084,32 @@ static void ql_process_mac_tx_intr(struct ql3_adapter *qdev, } qdev->stats.tx_packets++; qdev->stats.tx_bytes += tx_cb->skb->len; + +frame_not_sent: dev_kfree_skb_irq(tx_cb->skb); tx_cb->skb = NULL; + +invalid_seg_count: atomic_inc(&qdev->tx_count); } +static void ql_get_sbuf(struct ql3_adapter *qdev) +{ + if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) + qdev->small_buf_index = 0; + qdev->small_buf_release_cnt++; +} + +static struct ql_rcv_buf_cb *ql_get_lbuf(struct ql3_adapter *qdev) +{ + struct ql_rcv_buf_cb *lrg_buf_cb = NULL; + lrg_buf_cb = &qdev->lrg_buf[qdev->lrg_buf_index]; + qdev->lrg_buf_release_cnt++; + if (++qdev->lrg_buf_index == qdev->num_large_buffers) + qdev->lrg_buf_index = 0; + return(lrg_buf_cb); +} + /* * The difference between 3022 and 3032 for inbound completions: * 3022 uses two buffers per completion. The first buffer contains @@ -1751,47 +2125,21 @@ static void ql_process_mac_tx_intr(struct ql3_adapter *qdev, static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, struct ib_mac_iocb_rsp *ib_mac_rsp_ptr) { - long int offset; - u32 lrg_buf_phy_addr_low = 0; struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL; struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL; - u32 *curr_ial_ptr; struct sk_buff *skb; u16 length = le16_to_cpu(ib_mac_rsp_ptr->length); /* * Get the inbound address list (small buffer). */ - offset = qdev->small_buf_index * QL_SMALL_BUFFER_SIZE; - if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) - qdev->small_buf_index = 0; + ql_get_sbuf(qdev); - curr_ial_ptr = (u32 *) (qdev->small_buf_virt_addr + offset); - qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; - qdev->small_buf_release_cnt++; - - if (qdev->device_id == QL3022_DEVICE_ID) { - /* start of first buffer (3022 only) */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) { - qdev->lrg_buf_index = 0; - } - curr_ial_ptr++; /* 64-bit pointers require two incs. */ - curr_ial_ptr++; - } + if (qdev->device_id == QL3022_DEVICE_ID) + lrg_buf_cb1 = ql_get_lbuf(qdev); /* start of second buffer */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb2 = &qdev->lrg_buf[qdev->lrg_buf_index]; - - /* - * Second buffer gets sent up the stack. - */ - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) - qdev->lrg_buf_index = 0; + lrg_buf_cb2 = ql_get_lbuf(qdev); skb = lrg_buf_cb2->skb; qdev->stats.rx_packets++; @@ -1803,7 +2151,6 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, pci_unmap_len(lrg_buf_cb2, maplen), PCI_DMA_FROMDEVICE); prefetch(skb->data); - skb->dev = qdev->ndev; skb->ip_summed = CHECKSUM_NONE; skb->protocol = eth_type_trans(skb, qdev->ndev); @@ -1819,11 +2166,8 @@ static void ql_process_mac_rx_intr(struct ql3_adapter *qdev, static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, struct ib_ip_iocb_rsp *ib_ip_rsp_ptr) { - long int offset; - u32 lrg_buf_phy_addr_low = 0; struct ql_rcv_buf_cb *lrg_buf_cb1 = NULL; struct ql_rcv_buf_cb *lrg_buf_cb2 = NULL; - u32 *curr_ial_ptr; struct sk_buff *skb1 = NULL, *skb2; struct net_device *ndev = qdev->ndev; u16 length = le16_to_cpu(ib_ip_rsp_ptr->length); @@ -1833,35 +2177,20 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, * Get the inbound address list (small buffer). */ - offset = qdev->small_buf_index * QL_SMALL_BUFFER_SIZE; - if (++qdev->small_buf_index == NUM_SMALL_BUFFERS) - qdev->small_buf_index = 0; - curr_ial_ptr = (u32 *) (qdev->small_buf_virt_addr + offset); - qdev->last_rsp_offset = qdev->small_buf_phy_addr_low + offset; - qdev->small_buf_release_cnt++; + ql_get_sbuf(qdev); if (qdev->device_id == QL3022_DEVICE_ID) { /* start of first buffer on 3022 */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb1 = &qdev->lrg_buf[qdev->lrg_buf_index]; - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) - qdev->lrg_buf_index = 0; + lrg_buf_cb1 = ql_get_lbuf(qdev); skb1 = lrg_buf_cb1->skb; - curr_ial_ptr++; /* 64-bit pointers require two incs. */ - curr_ial_ptr++; size = ETH_HLEN; if (*((u16 *) skb1->data) != 0xFFFF) size += VLAN_ETH_HLEN - ETH_HLEN; } /* start of second buffer */ - lrg_buf_phy_addr_low = le32_to_cpu(*curr_ial_ptr); - lrg_buf_cb2 = &qdev->lrg_buf[qdev->lrg_buf_index]; + lrg_buf_cb2 = ql_get_lbuf(qdev); skb2 = lrg_buf_cb2->skb; - qdev->lrg_buf_release_cnt++; - if (++qdev->lrg_buf_index == NUM_LARGE_BUFFERS) - qdev->lrg_buf_index = 0; skb_put(skb2, length); /* Just the second buffer length here. */ pci_unmap_single(qdev->pdev, @@ -1876,24 +2205,25 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, * Copy the ethhdr from first buffer to second. This * is necessary for 3022 IP completions. */ - memcpy(skb_push(skb2, size), skb1->data + VLAN_ID_LEN, size); + skb_copy_from_linear_data_offset(skb1, VLAN_ID_LEN, + skb_push(skb2, size), size); } else { u16 checksum = le16_to_cpu(ib_ip_rsp_ptr->checksum); if (checksum & (IB_IP_IOCB_RSP_3032_ICE | - IB_IP_IOCB_RSP_3032_CE | - IB_IP_IOCB_RSP_3032_NUC)) { + IB_IP_IOCB_RSP_3032_CE)) { printk(KERN_ERR "%s: Bad checksum for this %s packet, checksum = %x.\n", __func__, ((checksum & IB_IP_IOCB_RSP_3032_TCP) ? "TCP" : "UDP"),checksum); - } else if (checksum & IB_IP_IOCB_RSP_3032_TCP) { + } else if ((checksum & IB_IP_IOCB_RSP_3032_TCP) || + (checksum & IB_IP_IOCB_RSP_3032_UDP && + !(checksum & IB_IP_IOCB_RSP_3032_NUC))) { skb2->ip_summed = CHECKSUM_UNNECESSARY; - } + } } - skb2->dev = qdev->ndev; skb2->protocol = eth_type_trans(skb2, qdev->ndev); netif_receive_skb(skb2); @@ -1910,14 +2240,13 @@ static void ql_process_macip_rx_intr(struct ql3_adapter *qdev, static int ql_tx_rx_clean(struct ql3_adapter *qdev, int *tx_cleaned, int *rx_cleaned, int work_to_do) { - struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; struct net_rsp_iocb *net_rsp; struct net_device *ndev = qdev->ndev; - unsigned long hw_flags; + int work_done = 0; /* While there are entries in the completion queue. */ - while ((cpu_to_le32(*(qdev->prsp_producer_index)) != - qdev->rsp_consumer_index) && (*rx_cleaned < work_to_do)) { + while ((le32_to_cpu(*(qdev->prsp_producer_index)) != + qdev->rsp_consumer_index) && (work_done < work_to_do)) { net_rsp = qdev->rsp_current; switch (net_rsp->opcode) { @@ -1968,40 +2297,11 @@ static int ql_tx_rx_clean(struct ql3_adapter *qdev, } else { qdev->rsp_current++; } - } - - spin_lock_irqsave(&qdev->hw_lock, hw_flags); - - ql_update_lrg_bufq_prod_index(qdev); - - if (qdev->small_buf_release_cnt >= 16) { - while (qdev->small_buf_release_cnt >= 16) { - qdev->small_buf_q_producer_index++; - - if (qdev->small_buf_q_producer_index == - NUM_SBUFQ_ENTRIES) - qdev->small_buf_q_producer_index = 0; - qdev->small_buf_release_cnt -= 8; - } - ql_write_common_reg(qdev, - &port_regs->CommonRegs. - rxSmallQProducerIndex, - qdev->small_buf_q_producer_index); + work_done = *tx_cleaned + *rx_cleaned; } - ql_write_common_reg(qdev, - &port_regs->CommonRegs.rspQConsumerIndex, - qdev->rsp_consumer_index); - spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); - - if (unlikely(netif_queue_stopped(qdev->ndev))) { - if (netif_queue_stopped(qdev->ndev) && - (atomic_read(&qdev->tx_count) > (NUM_REQ_Q_ENTRIES / 4))) - netif_wake_queue(qdev->ndev); - } - - return *tx_cleaned + *rx_cleaned; + return work_done; } static int ql_poll(struct net_device *ndev, int *budget) @@ -2009,6 +2309,8 @@ static int ql_poll(struct net_device *ndev, int *budget) struct ql3_adapter *qdev = netdev_priv(ndev); int work_to_do = min(*budget, ndev->quota); int rx_cleaned = 0, tx_cleaned = 0; + unsigned long hw_flags; + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; if (!netif_carrier_ok(ndev)) goto quit_polling; @@ -2017,9 +2319,18 @@ static int ql_poll(struct net_device *ndev, int *budget) *budget -= rx_cleaned; ndev->quota -= rx_cleaned; - if ((!tx_cleaned && !rx_cleaned) || !netif_running(ndev)) { + if( tx_cleaned + rx_cleaned != work_to_do || + !netif_running(ndev)) { quit_polling: netif_rx_complete(ndev); + + spin_lock_irqsave(&qdev->hw_lock, hw_flags); + ql_update_small_bufq_prod_index(qdev); + ql_update_lrg_bufq_prod_index(qdev); + writel(qdev->rsp_consumer_index, + &port_regs->CommonRegs.rspQConsumerIndex); + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); + ql_enable_interrupts(qdev); return 0; } @@ -2073,10 +2384,9 @@ static irqreturn_t ql3xxx_isr(int irq, void *dev_id) spin_unlock(&qdev->adapter_lock); } else if (value & ISP_IMR_DISABLE_CMPL_INT) { ql_disable_interrupts(qdev); - if (likely(netif_rx_schedule_prep(ndev))) + if (likely(netif_rx_schedule_prep(ndev))) { __netif_rx_schedule(ndev); - else - ql_enable_interrupts(qdev); + } } else { return IRQ_NONE; } @@ -2093,8 +2403,12 @@ static irqreturn_t ql3xxx_isr(int irq, void *dev_id) * the next AOL if more frags are coming. * That is why the frags:segment count ratio is not linear. */ -static int ql_get_seg_count(unsigned short frags) +static int ql_get_seg_count(struct ql3_adapter *qdev, + unsigned short frags) { + if (qdev->device_id == QL3022_DEVICE_ID) + return 1; + switch(frags) { case 0: return 1; /* just the skb->data seg */ case 1: return 2; /* skb->data + 1 frag */ @@ -2139,11 +2453,13 @@ static void ql_hw_csum_setup(struct sk_buff *skb, if (ip) { if (ip->protocol == IPPROTO_TCP) { - mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_TC; + mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_TC | + OB_3032MAC_IOCB_REQ_IC; mac_iocb_ptr->ip_hdr_off = offset; mac_iocb_ptr->ip_hdr_len = ip->ihl; } else if (ip->protocol == IPPROTO_UDP) { - mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_UC; + mac_iocb_ptr->flags1 |= OB_3032MAC_IOCB_REQ_UC | + OB_3032MAC_IOCB_REQ_IC; mac_iocb_ptr->ip_hdr_off = offset; mac_iocb_ptr->ip_hdr_len = ip->ihl; } @@ -2151,53 +2467,37 @@ static void ql_hw_csum_setup(struct sk_buff *skb, } /* - * The difference between 3022 and 3032 sends: - * 3022 only supports a simple single segment transmission. - * 3032 supports checksumming and scatter/gather lists (fragments). - * The 3032 supports sglists by using the 3 addr/len pairs (ALP) - * in the IOCB plus a chain of outbound address lists (OAL) that - * each contain 5 ALPs. The last ALP of the IOCB (3rd) or OAL (5th) - * will used to point to an OAL when more ALP entries are required. - * The IOCB is always the top of the chain followed by one or more - * OALs (when necessary). + * Map the buffers for this transmit. This will return + * NETDEV_TX_BUSY or NETDEV_TX_OK based on success. */ -static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) +static int ql_send_map(struct ql3_adapter *qdev, + struct ob_mac_iocb_req *mac_iocb_ptr, + struct ql_tx_buf_cb *tx_cb, + struct sk_buff *skb) { - struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); - struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; - struct ql_tx_buf_cb *tx_cb; - u32 tot_len = skb->len; struct oal *oal; struct oal_entry *oal_entry; - int len; - struct ob_mac_iocb_req *mac_iocb_ptr; - u64 map; + int len = skb_headlen(skb); + dma_addr_t map; + int err; + int completed_segs, i; int seg_cnt, seg = 0; int frag_cnt = (int)skb_shinfo(skb)->nr_frags; - if (unlikely(atomic_read(&qdev->tx_count) < 2)) { - if (!netif_queue_stopped(ndev)) - netif_stop_queue(ndev); - return NETDEV_TX_BUSY; - } - tx_cb = &qdev->tx_buf[qdev->req_producer_index] ; - seg_cnt = tx_cb->seg_count = ql_get_seg_count((skb_shinfo(skb)->nr_frags)); - if(seg_cnt == -1) { - printk(KERN_ERR PFX"%s: invalid segment count!\n",__func__); - return NETDEV_TX_OK; + seg_cnt = tx_cb->seg_count; + /* + * Map the skb buffer first. + */ + map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); + + err = pci_dma_mapping_error(map); + if(err) { + printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", + qdev->ndev->name, err); + return NETDEV_TX_BUSY; } - mac_iocb_ptr = tx_cb->queue_entry; - memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req)); - mac_iocb_ptr->opcode = qdev->mac_ob_opcode; - mac_iocb_ptr->flags |= qdev->mb_bit_mask; - mac_iocb_ptr->transaction_id = qdev->req_producer_index; - mac_iocb_ptr->data_len = cpu_to_le16((u16) tot_len); - tx_cb->skb = skb; - if (skb->ip_summed == CHECKSUM_PARTIAL) - ql_hw_csum_setup(skb, mac_iocb_ptr); - len = skb_headlen(skb); - map = pci_map_single(qdev->pdev, skb->data, len, PCI_DMA_TODEVICE); + oal_entry = (struct oal_entry *)&mac_iocb_ptr->buf_addr0_low; oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); @@ -2206,15 +2506,14 @@ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) pci_unmap_len_set(&tx_cb->map[seg], maplen, len); seg++; - if (!skb_shinfo(skb)->nr_frags) { + if (seg_cnt == 1) { /* Terminate the last segment. */ oal_entry->len = cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY); } else { - int i; oal = tx_cb->oal; - for (i=0; i<frag_cnt; i++,seg++) { - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + for (completed_segs=0; completed_segs<frag_cnt; completed_segs++,seg++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[completed_segs]; oal_entry++; if ((seg == 2 && seg_cnt > 3) || /* Check for continuation */ (seg == 7 && seg_cnt > 8) || /* requirements. It's strange */ @@ -2224,6 +2523,15 @@ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) map = pci_map_single(qdev->pdev, oal, sizeof(struct oal), PCI_DMA_TODEVICE); + + err = pci_dma_mapping_error(map); + if(err) { + + printk(KERN_ERR "%s: PCI mapping outbound address list with error: %d\n", + qdev->ndev->name, err); + goto map_error; + } + oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); oal_entry->len = @@ -2232,7 +2540,7 @@ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) pci_unmap_addr_set(&tx_cb->map[seg], mapaddr, map); pci_unmap_len_set(&tx_cb->map[seg], maplen, - len); + sizeof(struct oal)); oal_entry = (struct oal_entry *)oal; oal++; seg++; @@ -2242,6 +2550,14 @@ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) pci_map_page(qdev->pdev, frag->page, frag->page_offset, frag->size, PCI_DMA_TODEVICE); + + err = pci_dma_mapping_error(map); + if(err) { + printk(KERN_ERR "%s: PCI mapping frags failed with error: %d\n", + qdev->ndev->name, err); + goto map_error; + } + oal_entry->dma_lo = cpu_to_le32(LS_64BITS(map)); oal_entry->dma_hi = cpu_to_le32(MS_64BITS(map)); oal_entry->len = cpu_to_le32(frag->size); @@ -2253,6 +2569,95 @@ static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) oal_entry->len = cpu_to_le32(le32_to_cpu(oal_entry->len) | OAL_LAST_ENTRY); } + + return NETDEV_TX_OK; + +map_error: + /* A PCI mapping failed and now we will need to back out + * We need to traverse through the oal's and associated pages which + * have been mapped and now we must unmap them to clean up properly + */ + + seg = 1; + oal_entry = (struct oal_entry *)&mac_iocb_ptr->buf_addr0_low; + oal = tx_cb->oal; + for (i=0; i<completed_segs; i++,seg++) { + oal_entry++; + + if((seg == 2 && seg_cnt > 3) || /* Check for continuation */ + (seg == 7 && seg_cnt > 8) || /* requirements. It's strange */ + (seg == 12 && seg_cnt > 13) || /* but necessary. */ + (seg == 17 && seg_cnt > 18)) { + pci_unmap_single(qdev->pdev, + pci_unmap_addr(&tx_cb->map[seg], mapaddr), + pci_unmap_len(&tx_cb->map[seg], maplen), + PCI_DMA_TODEVICE); + oal++; + seg++; + } + + pci_unmap_page(qdev->pdev, + pci_unmap_addr(&tx_cb->map[seg], mapaddr), + pci_unmap_len(&tx_cb->map[seg], maplen), + PCI_DMA_TODEVICE); + } + + pci_unmap_single(qdev->pdev, + pci_unmap_addr(&tx_cb->map[0], mapaddr), + pci_unmap_addr(&tx_cb->map[0], maplen), + PCI_DMA_TODEVICE); + + return NETDEV_TX_BUSY; + +} + +/* + * The difference between 3022 and 3032 sends: + * 3022 only supports a simple single segment transmission. + * 3032 supports checksumming and scatter/gather lists (fragments). + * The 3032 supports sglists by using the 3 addr/len pairs (ALP) + * in the IOCB plus a chain of outbound address lists (OAL) that + * each contain 5 ALPs. The last ALP of the IOCB (3rd) or OAL (5th) + * will used to point to an OAL when more ALP entries are required. + * The IOCB is always the top of the chain followed by one or more + * OALs (when necessary). + */ +static int ql3xxx_send(struct sk_buff *skb, struct net_device *ndev) +{ + struct ql3_adapter *qdev = (struct ql3_adapter *)netdev_priv(ndev); + struct ql3xxx_port_registers __iomem *port_regs = qdev->mem_map_registers; + struct ql_tx_buf_cb *tx_cb; + u32 tot_len = skb->len; + struct ob_mac_iocb_req *mac_iocb_ptr; + + if (unlikely(atomic_read(&qdev->tx_count) < 2)) { + return NETDEV_TX_BUSY; + } + + tx_cb = &qdev->tx_buf[qdev->req_producer_index] ; + if((tx_cb->seg_count = ql_get_seg_count(qdev, + (skb_shinfo(skb)->nr_frags))) == -1) { + printk(KERN_ERR PFX"%s: invalid segment count!\n",__func__); + return NETDEV_TX_OK; + } + + mac_iocb_ptr = tx_cb->queue_entry; + memset((void *)mac_iocb_ptr, 0, sizeof(struct ob_mac_iocb_req)); + mac_iocb_ptr->opcode = qdev->mac_ob_opcode; + mac_iocb_ptr->flags = OB_MAC_IOCB_REQ_X; + mac_iocb_ptr->flags |= qdev->mb_bit_mask; + mac_iocb_ptr->transaction_id = qdev->req_producer_index; + mac_iocb_ptr->data_len = cpu_to_le16((u16) tot_len); + tx_cb->skb = skb; + if (qdev->device_id == QL3032_DEVICE_ID && + skb->ip_summed == CHECKSUM_PARTIAL) + ql_hw_csum_setup(skb, mac_iocb_ptr); + + if(ql_send_map(qdev,mac_iocb_ptr,tx_cb,skb) != NETDEV_TX_OK) { + printk(KERN_ERR PFX"%s: Could not map the segments!\n",__func__); + return NETDEV_TX_BUSY; + } + wmb(); qdev->req_producer_index++; if (qdev->req_producer_index == NUM_REQ_Q_ENTRIES) @@ -2338,12 +2743,19 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev) { /* Create Large Buffer Queue */ qdev->lrg_buf_q_size = - NUM_LBUFQ_ENTRIES * sizeof(struct lrg_buf_q_entry); + qdev->num_lbufq_entries * sizeof(struct lrg_buf_q_entry); if (qdev->lrg_buf_q_size < PAGE_SIZE) qdev->lrg_buf_q_alloc_size = PAGE_SIZE; else qdev->lrg_buf_q_alloc_size = qdev->lrg_buf_q_size * 2; + qdev->lrg_buf = kmalloc(qdev->num_large_buffers * sizeof(struct ql_rcv_buf_cb),GFP_KERNEL); + if (qdev->lrg_buf == NULL) { + printk(KERN_ERR PFX + "%s: qdev->lrg_buf alloc failed.\n", qdev->ndev->name); + return -ENOMEM; + } + qdev->lrg_buf_q_alloc_virt_addr = pci_alloc_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size, @@ -2393,7 +2805,7 @@ static void ql_free_buffer_queues(struct ql3_adapter *qdev) "%s: Already done.\n", qdev->ndev->name); return; } - + if(qdev->lrg_buf) kfree(qdev->lrg_buf); pci_free_consistent(qdev->pdev, qdev->lrg_buf_q_alloc_size, qdev->lrg_buf_q_alloc_virt_addr, @@ -2438,8 +2850,6 @@ static int ql_alloc_small_buffers(struct ql3_adapter *qdev) small_buf_q_entry = qdev->small_buf_q_virt_addr; - qdev->last_rsp_offset = qdev->small_buf_phy_addr_low; - /* Initialize the small buffer queue. */ for (i = 0; i < (QL_ADDR_ELE_PER_BUFQ_ENTRY * NUM_SBUFQ_ENTRIES); i++) { small_buf_q_entry->addr_high = @@ -2476,7 +2886,7 @@ static void ql_free_large_buffers(struct ql3_adapter *qdev) int i = 0; struct ql_rcv_buf_cb *lrg_buf_cb; - for (i = 0; i < NUM_LARGE_BUFFERS; i++) { + for (i = 0; i < qdev->num_large_buffers; i++) { lrg_buf_cb = &qdev->lrg_buf[i]; if (lrg_buf_cb->skb) { dev_kfree_skb(lrg_buf_cb->skb); @@ -2497,7 +2907,7 @@ static void ql_init_large_buffers(struct ql3_adapter *qdev) struct ql_rcv_buf_cb *lrg_buf_cb; struct bufq_addr_element *buf_addr_ele = qdev->lrg_buf_q_virt_addr; - for (i = 0; i < NUM_LARGE_BUFFERS; i++) { + for (i = 0; i < qdev->num_large_buffers; i++) { lrg_buf_cb = &qdev->lrg_buf[i]; buf_addr_ele->addr_high = lrg_buf_cb->buf_phy_addr_high; buf_addr_ele->addr_low = lrg_buf_cb->buf_phy_addr_low; @@ -2512,10 +2922,12 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) int i; struct ql_rcv_buf_cb *lrg_buf_cb; struct sk_buff *skb; - u64 map; + dma_addr_t map; + int err; - for (i = 0; i < NUM_LARGE_BUFFERS; i++) { - skb = dev_alloc_skb(qdev->lrg_buffer_len); + for (i = 0; i < qdev->num_large_buffers; i++) { + skb = netdev_alloc_skb(qdev->ndev, + qdev->lrg_buffer_len); if (unlikely(!skb)) { /* Better luck next round */ printk(KERN_ERR PFX @@ -2541,6 +2953,15 @@ static int ql_alloc_large_buffers(struct ql3_adapter *qdev) qdev->lrg_buffer_len - QL_HEADER_SPACE, PCI_DMA_FROMDEVICE); + + err = pci_dma_mapping_error(map); + if(err) { + printk(KERN_ERR "%s: PCI mapping failed with error: %d\n", + qdev->ndev->name, err); + ql_free_large_buffers(qdev); + return -ENOMEM; + } + pci_unmap_addr_set(lrg_buf_cb, mapaddr, map); pci_unmap_len_set(lrg_buf_cb, maplen, qdev->lrg_buffer_len - @@ -2592,9 +3013,15 @@ static int ql_create_send_free_list(struct ql3_adapter *qdev) static int ql_alloc_mem_resources(struct ql3_adapter *qdev) { - if (qdev->ndev->mtu == NORMAL_MTU_SIZE) + if (qdev->ndev->mtu == NORMAL_MTU_SIZE) { + qdev->num_lbufq_entries = NUM_LBUFQ_ENTRIES; qdev->lrg_buffer_len = NORMAL_MTU_SIZE; + } else if (qdev->ndev->mtu == JUMBO_MTU_SIZE) { + /* + * Bigger buffers, so less of them. + */ + qdev->num_lbufq_entries = JUMBO_NUM_LBUFQ_ENTRIES; qdev->lrg_buffer_len = JUMBO_MTU_SIZE; } else { printk(KERN_ERR PFX @@ -2602,6 +3029,7 @@ static int ql_alloc_mem_resources(struct ql3_adapter *qdev) qdev->ndev->name); return -ENOMEM; } + qdev->num_large_buffers = qdev->num_lbufq_entries * QL_ADDR_ELE_PER_BUFQ_ENTRY; qdev->lrg_buffer_len += VLAN_ETH_HLEN + VLAN_ID_LEN + QL_HEADER_SPACE; qdev->max_frame_size = (qdev->lrg_buffer_len - QL_HEADER_SPACE) + ETHERNET_CRC_SIZE; @@ -2834,7 +3262,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) &hmem_regs->rxLargeQBaseAddrLow, LS_64BITS(qdev->lrg_buf_q_phy_addr)); - ql_write_page1_reg(qdev, &hmem_regs->rxLargeQLength, NUM_LBUFQ_ENTRIES); + ql_write_page1_reg(qdev, &hmem_regs->rxLargeQLength, qdev->num_lbufq_entries); ql_write_page1_reg(qdev, &hmem_regs->rxLargeBufferLength, @@ -2856,7 +3284,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) qdev->small_buf_q_producer_index = NUM_SBUFQ_ENTRIES - 1; qdev->small_buf_release_cnt = 8; - qdev->lrg_buf_q_producer_index = NUM_LBUFQ_ENTRIES - 1; + qdev->lrg_buf_q_producer_index = qdev->num_lbufq_entries - 1; qdev->lrg_buf_release_cnt = 8; qdev->lrg_buf_next_free = (struct bufq_addr_element *)qdev->lrg_buf_q_virt_addr; @@ -2889,15 +3317,6 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) goto out; } - if (qdev->mac_index) - ql_write_page0_reg(qdev, - &port_regs->mac1MaxFrameLengthReg, - qdev->max_frame_size); - else - ql_write_page0_reg(qdev, - &port_regs->mac0MaxFrameLengthReg, - qdev->max_frame_size); - value = qdev->nvram_data.tcpMaxWindowSize; ql_write_page0_reg(qdev, &port_regs->tcpMaxWindow, value); @@ -2917,6 +3336,14 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) ql_sem_unlock(qdev, QL_FLASH_SEM_MASK); } + if (qdev->mac_index) + ql_write_page0_reg(qdev, + &port_regs->mac1MaxFrameLengthReg, + qdev->max_frame_size); + else + ql_write_page0_reg(qdev, + &port_regs->mac0MaxFrameLengthReg, + qdev->max_frame_size); if(ql_sem_spinlock(qdev, QL_PHY_GIO_SEM_MASK, (QL_RESOURCE_BITS_BASE_CODE | (qdev->mac_index) * @@ -2925,6 +3352,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) goto out; } + PHY_Setup(qdev); ql_init_scan_mode(qdev); ql_get_phy_owner(qdev); @@ -2987,7 +3415,8 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) if (qdev->device_id == QL3032_DEVICE_ID) { value = (QL3032_PORT_CONTROL_EF | QL3032_PORT_CONTROL_KIE | - QL3032_PORT_CONTROL_EIv6 | QL3032_PORT_CONTROL_EIv4); + QL3032_PORT_CONTROL_EIv6 | QL3032_PORT_CONTROL_EIv4 | + QL3032_PORT_CONTROL_ET); ql_write_page0_reg(qdev, &port_regs->functionControl, ((value << 16) | value)); } else { @@ -3292,6 +3721,7 @@ static int ql_adapter_up(struct ql3_adapter *qdev) err_init: ql_sem_unlock(qdev, QL_DRVR_SEM_MASK); err_lock: + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); free_irq(qdev->pdev->irq, ndev); err_irq: if (qdev->msi && test_bit(QL_MSI_ENABLED,&qdev->flags)) { @@ -3343,27 +3773,6 @@ static struct net_device_stats *ql3xxx_get_stats(struct net_device *dev) return &qdev->stats; } -static int ql3xxx_change_mtu(struct net_device *ndev, int new_mtu) -{ - struct ql3_adapter *qdev = netdev_priv(ndev); - printk(KERN_ERR PFX "%s: new mtu size = %d.\n", ndev->name, new_mtu); - if (new_mtu != NORMAL_MTU_SIZE && new_mtu != JUMBO_MTU_SIZE) { - printk(KERN_ERR PFX - "%s: mtu size of %d is not valid. Use exactly %d or " - "%d.\n", ndev->name, new_mtu, NORMAL_MTU_SIZE, - JUMBO_MTU_SIZE); - return -EINVAL; - } - - if (!netif_running(ndev)) { - ndev->mtu = new_mtu; - return 0; - } - - ndev->mtu = new_mtu; - return ql_cycle_adapter(qdev,QL_DO_RESET); -} - static void ql3xxx_set_multicast_list(struct net_device *ndev) { /* @@ -3609,8 +4018,12 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev, } ndev = alloc_etherdev(sizeof(struct ql3_adapter)); - if (!ndev) + if (!ndev) { + printk(KERN_ERR PFX "%s could not alloc etherdev\n", + pci_name(pdev)); + err = -ENOMEM; goto err_out_free_regions; + } SET_MODULE_OWNER(ndev); SET_NETDEV_DEV(ndev, &pdev->dev); @@ -3639,6 +4052,7 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev, if (!qdev->mem_map_registers) { printk(KERN_ERR PFX "%s: cannot map device registers\n", pci_name(pdev)); + err = -EIO; goto err_out_free_ndev; } @@ -3650,7 +4064,6 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev, ndev->hard_start_xmit = ql3xxx_send; ndev->stop = ql3xxx_close; ndev->get_stats = ql3xxx_get_stats; - ndev->change_mtu = ql3xxx_change_mtu; ndev->set_multicast_list = ql3xxx_set_multicast_list; SET_ETHTOOL_OPS(ndev, &ql3xxx_ethtool_ops); ndev->set_mac_address = ql3xxx_set_mac_address; @@ -3667,6 +4080,7 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev, printk(KERN_ALERT PFX "ql3xxx_probe: Adapter #%d, Invalid NVRAM parameters.\n", qdev->index); + err = -EIO; goto err_out_iounmap; } @@ -3674,9 +4088,11 @@ static int __devinit ql3xxx_probe(struct pci_dev *pdev, /* Validate and set parameters */ if (qdev->mac_index) { + ndev->mtu = qdev->nvram_data.macCfg_port1.etherMtu_mac ; memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn2.macAddress, ETH_ALEN); } else { + ndev->mtu = qdev->nvram_data.macCfg_port0.etherMtu_mac ; memcpy(ndev->dev_addr, &qdev->nvram_data.funcCfg_fn0.macAddress, ETH_ALEN); } diff --git a/drivers/net/qla3xxx.h b/drivers/net/qla3xxx.h index b2d76ea..4a832c4 100755 --- a/drivers/net/qla3xxx.h +++ b/drivers/net/qla3xxx.h @@ -293,6 +293,16 @@ struct net_rsp_iocb { #define MII_SCAN_REGISTER 0x00000001 +#define PHY_ID_0_REG 2 +#define PHY_ID_1_REG 3 + +#define PHY_OUI_1_MASK 0xfc00 +#define PHY_MODEL_MASK 0x03f0 + +/* Address for the Agere Phy */ +#define MII_AGERE_ADDR_1 0x00001000 +#define MII_AGERE_ADDR_2 0x00001100 + /* 32-bit ispControlStatus */ enum { ISP_CONTROL_NP_MASK = 0x0003, @@ -789,6 +799,7 @@ enum { PHY_CTRL_LOOPBACK = 0x4000, PETBI_CONTROL_REG = 0x00, + PETBI_CTRL_ALL_PARAMS = 0x7140, PETBI_CTRL_SOFT_RESET = 0x8000, PETBI_CTRL_AUTO_NEG = 0x1000, PETBI_CTRL_RESTART_NEG = 0x0200, @@ -811,6 +822,23 @@ enum { PETBI_EXPANSION_REG = 0x06, PETBI_EXP_PAGE_RX = 0x0002, + PHY_GIG_CONTROL = 9, + PHY_GIG_ENABLE_MAN = 0x1000, /* Enable Master/Slave Manual Config*/ + PHY_GIG_SET_MASTER = 0x0800, /* Set Master (slave if clear)*/ + PHY_GIG_ALL_PARAMS = 0x0300, + PHY_GIG_ADV_1000F = 0x0200, + PHY_GIG_ADV_1000H = 0x0100, + + PHY_NEG_ADVER = 4, + PHY_NEG_ALL_PARAMS = 0x0fe0, + PHY_NEG_ASY_PAUSE = 0x0800, + PHY_NEG_SYM_PAUSE = 0x0400, + PHY_NEG_ADV_SPEED = 0x01e0, + PHY_NEG_ADV_100F = 0x0100, + PHY_NEG_ADV_100H = 0x0080, + PHY_NEG_ADV_10F = 0x0040, + PHY_NEG_ADV_10H = 0x0020, + PETBI_TBI_CTRL = 0x11, PETBI_TBI_RESET = 0x8000, PETBI_TBI_AUTO_SENSE = 0x0100, @@ -826,8 +854,7 @@ enum { PHY_AUX_RESET_STICK = 0x0002, PHY_NEG_PAUSE = 0x0400, PHY_CTRL_SOFT_RESET = 0x8000, - PHY_NEG_ADVER = 4, - PHY_NEG_ADV_SPEED = 0x01e0, + PHY_CTRL_AUTO_NEG = 0x1000, PHY_CTRL_RESTART_NEG = 0x0200, }; enum { @@ -892,6 +919,7 @@ enum {EEPROM_SIZE = FM93C86A_SIZE_16, u16 pauseThreshold_mac; u16 resumeThreshold_mac; u16 portConfiguration; +#define PORT_CONFIG_DEFAULT 0xf700 #define PORT_CONFIG_AUTO_NEG_ENABLED 0x8000 #define PORT_CONFIG_SYM_PAUSE_ENABLED 0x4000 #define PORT_CONFIG_FULL_DUPLEX_ENABLED 0x2000 @@ -1014,13 +1042,14 @@ struct eeprom_data { /* Transmit and Receive Buffers */ #define NUM_LBUFQ_ENTRIES 128 +#define JUMBO_NUM_LBUFQ_ENTRIES 32 #define NUM_SBUFQ_ENTRIES 64 #define QL_SMALL_BUFFER_SIZE 32 #define QL_ADDR_ELE_PER_BUFQ_ENTRY \ (sizeof(struct lrg_buf_q_entry) / sizeof(struct bufq_addr_element)) /* Each send has at least control block. This is how many we keep. */ #define NUM_SMALL_BUFFERS NUM_SBUFQ_ENTRIES * QL_ADDR_ELE_PER_BUFQ_ENTRY -#define NUM_LARGE_BUFFERS NUM_LBUFQ_ENTRIES * QL_ADDR_ELE_PER_BUFQ_ENTRY + #define QL_HEADER_SPACE 32 /* make header space at top of skb. */ /* * Large & Small Buffers for Receives @@ -1092,7 +1121,6 @@ struct oal_entry { u32 len; #define OAL_LAST_ENTRY 0x80000000 /* Last valid buffer in list. */ #define OAL_CONT_ENTRY 0x40000000 /* points to an OAL. (continuation) */ - u32 reserved; }; struct oal { @@ -1193,7 +1221,7 @@ struct ql3_adapter { struct net_rsp_iocb *rsp_current; u16 rsp_consumer_index; u16 reserved_06; - u32 *prsp_producer_index; + volatile u32 *prsp_producer_index; u32 rsp_producer_index_phy_addr_high; u32 rsp_producer_index_phy_addr_low; @@ -1207,9 +1235,11 @@ struct ql3_adapter { u32 lrg_buf_q_producer_index; u32 lrg_buf_release_cnt; struct bufq_addr_element *lrg_buf_next_free; + u32 num_large_buffers; + u32 num_lbufq_entries; /* Large (Receive) Buffers */ - struct ql_rcv_buf_cb lrg_buf[NUM_LARGE_BUFFERS]; + struct ql_rcv_buf_cb *lrg_buf; struct ql_rcv_buf_cb *lrg_buf_free_head; struct ql_rcv_buf_cb *lrg_buf_free_tail; u32 lrg_buf_free_count; @@ -1257,6 +1287,7 @@ struct ql3_adapter { struct delayed_work tx_timeout_work; u32 max_frame_size; u32 device_id; + u16 phyType; }; #endif /* _QLA3XXX_H_ */ diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 5598d86..45876a8 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -66,6 +66,7 @@ VERSION 2.2LK <2005/01/25> #include <linux/init.h> #include <linux/dma-mapping.h> +#include <asm/system.h> #include <asm/io.h> #include <asm/irq.h> @@ -486,6 +487,7 @@ static int rtl8169_rx_interrupt(struct net_device *, struct rtl8169_private *, void __iomem *); static int rtl8169_change_mtu(struct net_device *dev, int new_mtu); static void rtl8169_down(struct net_device *dev); +static void rtl8169_rx_clear(struct rtl8169_private *tp); #ifdef CONFIG_R8169_NAPI static int rtl8169_poll(struct net_device *dev, int *budget); @@ -572,8 +574,8 @@ static void rtl8169_xmii_reset_enable(void __iomem *ioaddr) { unsigned int val; - mdio_write(ioaddr, MII_BMCR, BMCR_RESET); - val = mdio_read(ioaddr, MII_BMCR); + val = mdio_read(ioaddr, MII_BMCR) | BMCR_RESET; + mdio_write(ioaddr, MII_BMCR, val & 0xffff); } static void rtl8169_check_link_status(struct net_device *dev, @@ -890,8 +892,7 @@ static void rtl8169_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) unsigned long flags; spin_lock_irqsave(&tp->lock, flags); - if (tp->vlgrp) - tp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(tp->vlgrp, vid, NULL); spin_unlock_irqrestore(&tp->lock, flags); } @@ -1369,11 +1370,7 @@ static inline void rtl8169_request_timer(struct net_device *dev) (tp->phy_version >= RTL_GIGA_PHY_VER_H)) return; - init_timer(timer); - timer->expires = jiffies + RTL8169_PHY_TIMEOUT; - timer->data = (unsigned long)(dev); - timer->function = rtl8169_phy_timer; - add_timer(timer); + mod_timer(timer, jiffies + RTL8169_PHY_TIMEOUT); } #ifdef CONFIG_NET_POLL_CONTROLLER @@ -1686,6 +1683,10 @@ rtl8169_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) tp->mmio_addr = ioaddr; tp->align = rtl_cfg_info[ent->driver_data].align; + init_timer(&tp->timer); + tp->timer.data = (unsigned long) dev; + tp->timer.function = rtl8169_phy_timer; + spin_lock_init(&tp->lock); rc = register_netdev(dev); @@ -1733,6 +1734,8 @@ rtl8169_remove_one(struct pci_dev *pdev) assert(dev != NULL); assert(tp != NULL); + flush_scheduled_work(); + unregister_netdev(dev); rtl8169_release_board(pdev, dev, tp->mmio_addr); pci_set_drvdata(pdev, NULL); @@ -1750,16 +1753,10 @@ static int rtl8169_open(struct net_device *dev) { struct rtl8169_private *tp = netdev_priv(dev); struct pci_dev *pdev = tp->pci_dev; - int retval; + int retval = -ENOMEM; - rtl8169_set_rxbufsize(tp, dev); - retval = - request_irq(dev->irq, rtl8169_interrupt, IRQF_SHARED, dev->name, dev); - if (retval < 0) - goto out; - - retval = -ENOMEM; + rtl8169_set_rxbufsize(tp, dev); /* * Rx and Tx desscriptors needs 256 bytes alignment. @@ -1768,19 +1765,26 @@ static int rtl8169_open(struct net_device *dev) tp->TxDescArray = pci_alloc_consistent(pdev, R8169_TX_RING_BYTES, &tp->TxPhyAddr); if (!tp->TxDescArray) - goto err_free_irq; + goto out; tp->RxDescArray = pci_alloc_consistent(pdev, R8169_RX_RING_BYTES, &tp->RxPhyAddr); if (!tp->RxDescArray) - goto err_free_tx; + goto err_free_tx_0; retval = rtl8169_init_ring(dev); if (retval < 0) - goto err_free_rx; + goto err_free_rx_1; INIT_DELAYED_WORK(&tp->task, NULL); + smp_mb(); + + retval = request_irq(dev->irq, rtl8169_interrupt, IRQF_SHARED, + dev->name, dev); + if (retval < 0) + goto err_release_ring_2; + rtl8169_hw_start(dev); rtl8169_request_timer(dev); @@ -1789,14 +1793,14 @@ static int rtl8169_open(struct net_device *dev) out: return retval; -err_free_rx: +err_release_ring_2: + rtl8169_rx_clear(tp); +err_free_rx_1: pci_free_consistent(pdev, R8169_RX_RING_BYTES, tp->RxDescArray, tp->RxPhyAddr); -err_free_tx: +err_free_tx_0: pci_free_consistent(pdev, R8169_TX_RING_BYTES, tp->TxDescArray, tp->TxPhyAddr); -err_free_irq: - free_irq(dev->irq, dev); goto out; } @@ -2161,10 +2165,13 @@ static void rtl8169_reinit_task(struct work_struct *work) struct net_device *dev = tp->dev; int ret; - if (netif_running(dev)) { - rtl8169_wait_for_quiescence(dev); - rtl8169_close(dev); - } + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + + rtl8169_wait_for_quiescence(dev); + rtl8169_close(dev); ret = rtl8169_open(dev); if (unlikely(ret < 0)) { @@ -2179,6 +2186,9 @@ static void rtl8169_reinit_task(struct work_struct *work) } rtl8169_schedule_work(dev, rtl8169_reinit_task); } + +out_unlock: + rtnl_unlock(); } static void rtl8169_reset_task(struct work_struct *work) @@ -2187,8 +2197,10 @@ static void rtl8169_reset_task(struct work_struct *work) container_of(work, struct rtl8169_private, task.work); struct net_device *dev = tp->dev; + rtnl_lock(); + if (!netif_running(dev)) - return; + goto out_unlock; rtl8169_wait_for_quiescence(dev); @@ -2210,6 +2222,9 @@ static void rtl8169_reset_task(struct work_struct *work) } rtl8169_schedule_work(dev, rtl8169_reset_task); } + +out_unlock: + rtnl_unlock(); } static void rtl8169_tx_timeout(struct net_device *dev) @@ -2269,7 +2284,7 @@ static inline u32 rtl8169_tso_csum(struct sk_buff *skb, struct net_device *dev) return LargeSend | ((mss & MSSMask) << MSSShift); } if (skb->ip_summed == CHECKSUM_PARTIAL) { - const struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) return IPCS | TCPCS; @@ -2571,7 +2586,6 @@ rtl8169_rx_interrupt(struct net_device *dev, struct rtl8169_private *tp, pci_action(tp->pci_dev, le64_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); @@ -2722,8 +2736,6 @@ static void rtl8169_down(struct net_device *dev) netif_stop_queue(dev); - flush_scheduled_work(); - core_down: spin_lock_irq(&tp->lock); @@ -2877,7 +2889,7 @@ static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) void __iomem *ioaddr = tp->mmio_addr; if (!netif_running(dev)) - goto out; + goto out_pci_suspend; netif_device_detach(dev); netif_stop_queue(dev); @@ -2891,10 +2903,11 @@ static int rtl8169_suspend(struct pci_dev *pdev, pm_message_t state) spin_unlock_irq(&tp->lock); +out_pci_suspend: pci_save_state(pdev); pci_enable_wake(pdev, pci_choose_state(pdev, state), tp->wol_enabled); pci_set_power_state(pdev, pci_choose_state(pdev, state)); -out: + return 0; } @@ -2902,15 +2915,15 @@ static int rtl8169_resume(struct pci_dev *pdev) { struct net_device *dev = pci_get_drvdata(pdev); + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + pci_enable_wake(pdev, PCI_D0, 0); + if (!netif_running(dev)) goto out; netif_device_attach(dev); - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - pci_enable_wake(pdev, PCI_D0, 0); - rtl8169_schedule_work(dev, rtl8169_reset_task); out: return 0; diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index b7ff484..df6b738 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -115,7 +115,6 @@ static int rionet_rx_clean(struct net_device *ndev) rnet->rx_skb[i]->data = data; skb_put(rnet->rx_skb[i], RIO_MAX_MSG_SIZE); - rnet->rx_skb[i]->dev = ndev; rnet->rx_skb[i]->protocol = eth_type_trans(rnet->rx_skb[i], ndev); error = netif_rx(rnet->rx_skb[i]); diff --git a/drivers/net/rrunner.c b/drivers/net/rrunner.c index d81536f..25c73d4 100644 --- a/drivers/net/rrunner.c +++ b/drivers/net/rrunner.c @@ -1029,7 +1029,6 @@ static void rx_int(struct net_device *dev, u32 rxlimit, u32 index) goto defer; } } - skb->dev = dev; skb->protocol = hippi_type_trans(skb, dev); netif_rx(skb); /* send it up */ @@ -1452,7 +1451,7 @@ static int rr_start_xmit(struct sk_buff *skb, struct net_device *dev) } skb_reserve(new_skb, 8); skb_put(new_skb, len); - memcpy(new_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, new_skb->data, len); dev_kfree_skb(skb); skb = new_skb; } diff --git a/drivers/net/s2io-regs.h b/drivers/net/s2io-regs.h index 0e345cb..4cb710b 100644 --- a/drivers/net/s2io-regs.h +++ b/drivers/net/s2io-regs.h @@ -1,6 +1,6 @@ /************************************************************************ * regs.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. @@ -430,6 +430,7 @@ struct XENA_dev_config { #define TX_PA_CFG_IGNORE_SNAP_OUI BIT(2) #define TX_PA_CFG_IGNORE_LLC_CTRL BIT(3) #define TX_PA_CFG_IGNORE_L2_ERR BIT(6) +#define RX_PA_CFG_STRIP_VLAN_TAG BIT(15) /* Recent add, used only debug purposes. */ u64 pcc_enable; diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c index e8e0d94..290e1c1 100644 --- a/drivers/net/s2io.c +++ b/drivers/net/s2io.c @@ -1,6 +1,6 @@ /************************************************************************ * s2io.c: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. @@ -42,6 +42,14 @@ * Possible values '1' for enable '0' for disable. Default is '0' * lro_max_pkts: This parameter defines maximum number of packets can be * aggregated as a single large packet + * napi: This parameter used to enable/disable NAPI (polling Rx) + * Possible values '1' for enable and '0' for disable. Default is '1' + * ufo: This parameter used to enable/disable UDP Fragmentation Offload(UFO) + * Possible values '1' for enable and '0' for disable. Default is '0' + * vlan_tag_strip: This can be used to enable or disable vlan stripping. + * Possible values '1' for enable , '0' for disable. + * Default is '2' - which means disable in promisc mode + * and enable in non-promiscuous mode. ************************************************************************/ #include <linux/module.h> @@ -76,7 +84,7 @@ #include "s2io.h" #include "s2io-regs.h" -#define DRV_VERSION "2.0.16.1" +#define DRV_VERSION "2.0.22.1" /* S2io Driver name & version. */ static char s2io_driver_name[] = "Neterion"; @@ -131,7 +139,7 @@ static char s2io_gstrings[][ETH_GSTRING_LEN] = { "BIST Test\t(offline)" }; -static char ethtool_stats_keys[][ETH_GSTRING_LEN] = { +static char ethtool_xena_stats_keys[][ETH_GSTRING_LEN] = { {"tmac_frms"}, {"tmac_data_octets"}, {"tmac_drop_frms"}, @@ -225,7 +233,10 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = { {"rxd_rd_cnt"}, {"rxd_wr_cnt"}, {"txf_rd_cnt"}, - {"rxf_wr_cnt"}, + {"rxf_wr_cnt"} +}; + +static char ethtool_enhanced_stats_keys[][ETH_GSTRING_LEN] = { {"rmac_ttl_1519_4095_frms"}, {"rmac_ttl_4096_8191_frms"}, {"rmac_ttl_8192_max_frms"}, @@ -241,7 +252,10 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = { {"rmac_red_discard"}, {"rmac_rts_discard"}, {"rmac_ingm_full_discard"}, - {"link_fault_cnt"}, + {"link_fault_cnt"} +}; + +static char ethtool_driver_stats_keys[][ETH_GSTRING_LEN] = { {"\n DRIVER STATISTICS"}, {"single_bit_ecc_errs"}, {"double_bit_ecc_errs"}, @@ -269,8 +283,16 @@ static char ethtool_stats_keys[][ETH_GSTRING_LEN] = { ("lro_avg_aggr_pkts"), }; -#define S2IO_STAT_LEN sizeof(ethtool_stats_keys)/ ETH_GSTRING_LEN -#define S2IO_STAT_STRINGS_LEN S2IO_STAT_LEN * ETH_GSTRING_LEN +#define S2IO_XENA_STAT_LEN sizeof(ethtool_xena_stats_keys)/ ETH_GSTRING_LEN +#define S2IO_ENHANCED_STAT_LEN sizeof(ethtool_enhanced_stats_keys)/ \ + ETH_GSTRING_LEN +#define S2IO_DRIVER_STAT_LEN sizeof(ethtool_driver_stats_keys)/ ETH_GSTRING_LEN + +#define XFRAME_I_STAT_LEN (S2IO_XENA_STAT_LEN + S2IO_DRIVER_STAT_LEN ) +#define XFRAME_II_STAT_LEN (XFRAME_I_STAT_LEN + S2IO_ENHANCED_STAT_LEN ) + +#define XFRAME_I_STAT_STRINGS_LEN ( XFRAME_I_STAT_LEN * ETH_GSTRING_LEN ) +#define XFRAME_II_STAT_STRINGS_LEN ( XFRAME_II_STAT_LEN * ETH_GSTRING_LEN ) #define S2IO_TEST_LEN sizeof(s2io_gstrings) / ETH_GSTRING_LEN #define S2IO_STRINGS_LEN S2IO_TEST_LEN * ETH_GSTRING_LEN @@ -293,6 +315,9 @@ static void s2io_vlan_rx_register(struct net_device *dev, spin_unlock_irqrestore(&nic->tx_lock, flags); } +/* A flag indicating whether 'RX_PA_CFG_STRIP_VLAN_TAG' bit is set or not */ +static int vlan_strip_flag; + /* Unregister the vlan */ static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid) { @@ -300,8 +325,7 @@ static void s2io_vlan_rx_kill_vid(struct net_device *dev, unsigned long vid) unsigned long flags; spin_lock_irqsave(&nic->tx_lock, flags); - if (nic->vlgrp) - nic->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(nic->vlgrp, vid, NULL); spin_unlock_irqrestore(&nic->tx_lock, flags); } @@ -370,7 +394,6 @@ static const u64 fix_mac[] = { END_SIGN }; -MODULE_AUTHOR("Raghavendra Koushik <raghavendra.koushik@neterion.com>"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); @@ -404,6 +427,7 @@ S2IO_PARM_INT(indicate_max_pkts, 0); S2IO_PARM_INT(napi, 1); S2IO_PARM_INT(ufo, 0); +S2IO_PARM_INT(vlan_tag_strip, NO_STRIP_IN_PROMISC); static unsigned int tx_fifo_len[MAX_TX_FIFOS] = {DEFAULT_FIFO_0_LEN, [1 ...(MAX_TX_FIFOS - 1)] = DEFAULT_FIFO_1_7_LEN}; @@ -491,7 +515,7 @@ static int init_shared_mem(struct s2io_nic *nic) mac_control->fifos[i].list_info = kmalloc(list_holder_size, GFP_KERNEL); if (!mac_control->fifos[i].list_info) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "Malloc failed for list_info\n"); return -ENOMEM; } @@ -517,9 +541,9 @@ static int init_shared_mem(struct s2io_nic *nic) tmp_v = pci_alloc_consistent(nic->pdev, PAGE_SIZE, &tmp_p); if (!tmp_v) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "pci_alloc_consistent "); - DBG_PRINT(ERR_DBG, "failed for TxDL\n"); + DBG_PRINT(INFO_DBG, "failed for TxDL\n"); return -ENOMEM; } /* If we got a zero DMA address(can happen on @@ -536,9 +560,9 @@ static int init_shared_mem(struct s2io_nic *nic) tmp_v = pci_alloc_consistent(nic->pdev, PAGE_SIZE, &tmp_p); if (!tmp_v) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "pci_alloc_consistent "); - DBG_PRINT(ERR_DBG, "failed for TxDL\n"); + DBG_PRINT(INFO_DBG, "failed for TxDL\n"); return -ENOMEM; } } @@ -1371,6 +1395,16 @@ static int init_nic(struct s2io_nic *nic) &bar0->rts_frm_len_n[i]); } } + + /* Disable differentiated services steering logic */ + for (i = 0; i < 64; i++) { + if (rts_ds_steer(nic, i, 0) == FAILURE) { + DBG_PRINT(ERR_DBG, "%s: failed rts ds steering", + dev->name); + DBG_PRINT(ERR_DBG, "set on codepoint %d\n", i); + return FAILURE; + } + } /* Program statistics memory */ writeq(mac_control->stats_mem_phy, &bar0->stat_addr); @@ -1943,6 +1977,13 @@ static int start_nic(struct s2io_nic *nic) writeq(val64, &bar0->rx_pa_cfg); } + if (vlan_tag_strip == 0) { + val64 = readq(&bar0->rx_pa_cfg); + val64 &= ~RX_PA_CFG_STRIP_VLAN_TAG; + writeq(val64, &bar0->rx_pa_cfg); + vlan_strip_flag = 0; + } + /* * Enabling MC-RLDRAM. After enabling the device, we timeout * for around 100ms, which is approximately the time required @@ -2145,7 +2186,7 @@ static int fill_rxd_3buf(struct s2io_nic *nic, struct RxD_t *rxdp, struct \ /* skb_shinfo(skb)->frag_list will have L4 data payload */ skb_shinfo(skb)->frag_list = dev_alloc_skb(dev->mtu + ALIGN_SIZE); if (skb_shinfo(skb)->frag_list == NULL) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n ", dev->name); + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n ", dev->name); return -ENOMEM ; } frag_list = skb_shinfo(skb)->frag_list; @@ -2153,7 +2194,7 @@ static int fill_rxd_3buf(struct s2io_nic *nic, struct RxD_t *rxdp, struct \ frag_list->next = NULL; tmp = (void *)ALIGN((long)frag_list->data, ALIGN_SIZE + 1); frag_list->data = tmp; - frag_list->tail = tmp; + skb_reset_tail_pointer(frag_list); /* Buffer-2 receives L4 data payload */ ((struct RxD3*)rxdp)->Buffer2_ptr = pci_map_single(nic->pdev, @@ -2200,6 +2241,7 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) struct buffAdd *ba; unsigned long flags; struct RxD_t *first_rxdp = NULL; + u64 Buffer0_ptr = 0, Buffer1_ptr = 0; mac_control = &nic->mac_control; config = &nic->config; @@ -2271,8 +2313,8 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) /* allocate skb */ skb = dev_alloc_skb(size); if(!skb) { - DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name); - DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n"); + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); + DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n"); if (first_rxdp) { wmb(); first_rxdp->Control_1 |= RXD_OWN_XENA; @@ -2300,14 +2342,21 @@ static int fill_rx_buffers(struct s2io_nic *nic, int ring_no) * payload */ + /* save the buffer pointers to avoid frequent dma mapping */ + Buffer0_ptr = ((struct RxD3*)rxdp)->Buffer0_ptr; + Buffer1_ptr = ((struct RxD3*)rxdp)->Buffer1_ptr; memset(rxdp, 0, sizeof(struct RxD3)); + /* restore the buffer pointers for dma sync*/ + ((struct RxD3*)rxdp)->Buffer0_ptr = Buffer0_ptr; + ((struct RxD3*)rxdp)->Buffer1_ptr = Buffer1_ptr; + ba = &mac_control->rings[ring_no].ba[block_no][off]; skb_reserve(skb, BUF0_LEN); tmp = (u64)(unsigned long) skb->data; tmp += ALIGN_SIZE; tmp &= ~ALIGN_SIZE; skb->data = (void *) (unsigned long)tmp; - skb->tail = (void *) (unsigned long)tmp; + skb_reset_tail_pointer(skb); if (!(((struct RxD3*)rxdp)->Buffer0_ptr)) ((struct RxD3*)rxdp)->Buffer0_ptr = @@ -2531,8 +2580,8 @@ static int s2io_poll(struct net_device *dev, int *budget) for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Poll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; } } @@ -2548,8 +2597,8 @@ no_rx: for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Poll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Poll!!\n"); break; } } @@ -2598,8 +2647,8 @@ static void s2io_netpoll(struct net_device *dev) for (i = 0; i < config->rx_ring_num; i++) { if (fill_rx_buffers(nic, i) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", dev->name); - DBG_PRINT(ERR_DBG, " in Rx Netpoll!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", dev->name); + DBG_PRINT(INFO_DBG, " in Rx Netpoll!!\n"); break; } } @@ -3195,26 +3244,37 @@ static void alarm_intr_handler(struct s2io_nic *nic) * SUCCESS on success and FAILURE on failure. */ -static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit) +static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit, + int bit_state) { - int ret = FAILURE, cnt = 0; + int ret = FAILURE, cnt = 0, delay = 1; u64 val64; - while (TRUE) { + if ((bit_state != S2IO_BIT_RESET) && (bit_state != S2IO_BIT_SET)) + return FAILURE; + + do { val64 = readq(addr); - if (!(val64 & busy_bit)) { - ret = SUCCESS; - break; + if (bit_state == S2IO_BIT_RESET) { + if (!(val64 & busy_bit)) { + ret = SUCCESS; + break; + } + } else { + if (!(val64 & busy_bit)) { + ret = SUCCESS; + break; + } } if(in_interrupt()) - mdelay(50); + mdelay(delay); else - msleep(50); + msleep(delay); - if (cnt++ > 10) - break; - } + if (++cnt >= 10) + delay = 50; + } while (cnt < 20); return ret; } /* @@ -3254,6 +3314,7 @@ static void s2io_reset(struct s2io_nic * sp) u16 subid, pci_cmd; int i; u16 val16; + unsigned long long reset_cnt = 0; DBG_PRINT(INIT_DBG,"%s - Resetting XFrame card %s\n", __FUNCTION__, sp->dev->name); @@ -3319,6 +3380,11 @@ new_way: /* Reset device statistics maintained by OS */ memset(&sp->stats, 0, sizeof (struct net_device_stats)); + /* save reset count */ + reset_cnt = sp->mac_control.stats_info->sw_stat.soft_reset_cnt; + memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block)); + /* restore reset count */ + sp->mac_control.stats_info->sw_stat.soft_reset_cnt = reset_cnt; /* SXE-002: Configure link and activity LED to turn it off */ subid = sp->pdev->subsystem_device; @@ -3340,6 +3406,9 @@ new_way: writeq(val64, &bar0->pcc_err_reg); } + /* restore the previously assigned mac address */ + s2io_set_mac_addr(sp->dev, (u8 *)&sp->def_mac_addr[0].mac_addr); + sp->device_enabled_once = FALSE; } @@ -3603,7 +3672,7 @@ static int s2io_enable_msi_x(struct s2io_nic *nic) nic->entries = kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct msix_entry), GFP_KERNEL); if (nic->entries == NULL) { - DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__); + DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__); return -ENOMEM; } memset(nic->entries, 0, MAX_REQUESTED_MSI_X * sizeof(struct msix_entry)); @@ -3612,7 +3681,7 @@ static int s2io_enable_msi_x(struct s2io_nic *nic) kmalloc(MAX_REQUESTED_MSI_X * sizeof(struct s2io_msix_entry), GFP_KERNEL); if (nic->s2io_entries == NULL) { - DBG_PRINT(ERR_DBG, "%s: Memory allocation failed\n", __FUNCTION__); + DBG_PRINT(INFO_DBG, "%s: Memory allocation failed\n", __FUNCTION__); kfree(nic->entries); return -ENOMEM; } @@ -3758,7 +3827,6 @@ static int s2io_close(struct net_device *dev) { struct s2io_nic *sp = dev->priv; - flush_scheduled_work(); netif_stop_queue(dev); /* Reset card, kill tasklet and free Tx and Rx buffers. */ s2io_card_down(sp); @@ -3964,7 +4032,7 @@ static int s2io_chk_rx_buffers(struct s2io_nic *sp, int rng_n) DBG_PRINT(INTR_DBG, "%s: Rx BD hit ", __FUNCTION__); DBG_PRINT(INTR_DBG, "PANIC levels\n"); if ((ret = fill_rx_buffers(sp, rng_n)) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "Out of memory in %s", + DBG_PRINT(INFO_DBG, "Out of memory in %s", __FUNCTION__); clear_bit(0, (&sp->tasklet_status)); return -1; @@ -3974,8 +4042,8 @@ static int s2io_chk_rx_buffers(struct s2io_nic *sp, int rng_n) tasklet_schedule(&sp->task); } else if (fill_rx_buffers(sp, rng_n) == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s:Out of memory", sp->dev->name); - DBG_PRINT(ERR_DBG, " in Rx Intr!!\n"); + DBG_PRINT(INFO_DBG, "%s:Out of memory", sp->dev->name); + DBG_PRINT(INFO_DBG, " in Rx Intr!!\n"); } return 0; } @@ -4088,6 +4156,11 @@ static void s2io_txpic_intr_handle(struct s2io_nic *sp) val64 &= ~GPIO_INT_MASK_LINK_UP; val64 |= GPIO_INT_MASK_LINK_DOWN; writeq(val64, &bar0->gpio_int_mask); + + /* turn off LED */ + val64 = readq(&bar0->adapter_control); + val64 = val64 &(~ADAPTER_LED_ON); + writeq(val64, &bar0->adapter_control); } } val64 = readq(&bar0->gpio_int_mask); @@ -4219,9 +4292,7 @@ static void s2io_updt_stats(struct s2io_nic *sp) if (cnt == 5) break; /* Updt failed */ } while(1); - } else { - memset(sp->mac_control.stats_info, 0, sizeof(struct stat_block)); - } + } } /** @@ -4297,7 +4368,8 @@ static void s2io_set_multicast(struct net_device *dev) writeq(val64, &bar0->rmac_addr_cmd_mem); /* Wait till command completes */ wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING); + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, + S2IO_BIT_RESET); sp->m_cast_flg = 1; sp->all_multi_pos = MAC_MC_ALL_MC_ADDR_OFFSET; @@ -4313,7 +4385,8 @@ static void s2io_set_multicast(struct net_device *dev) writeq(val64, &bar0->rmac_addr_cmd_mem); /* Wait till command completes */ wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING); + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, + S2IO_BIT_RESET); sp->m_cast_flg = 0; sp->all_multi_pos = 0; @@ -4330,6 +4403,13 @@ static void s2io_set_multicast(struct net_device *dev) writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key); writel((u32) (val64 >> 32), (add + 4)); + if (vlan_tag_strip != 1) { + val64 = readq(&bar0->rx_pa_cfg); + val64 &= ~RX_PA_CFG_STRIP_VLAN_TAG; + writeq(val64, &bar0->rx_pa_cfg); + vlan_strip_flag = 0; + } + val64 = readq(&bar0->mac_cfg); sp->promisc_flg = 1; DBG_PRINT(INFO_DBG, "%s: entered promiscuous mode\n", @@ -4345,6 +4425,13 @@ static void s2io_set_multicast(struct net_device *dev) writeq(RMAC_CFG_KEY(0x4C0D), &bar0->rmac_cfg_key); writel((u32) (val64 >> 32), (add + 4)); + if (vlan_tag_strip != 0) { + val64 = readq(&bar0->rx_pa_cfg); + val64 |= RX_PA_CFG_STRIP_VLAN_TAG; + writeq(val64, &bar0->rx_pa_cfg); + vlan_strip_flag = 1; + } + val64 = readq(&bar0->mac_cfg); sp->promisc_flg = 0; DBG_PRINT(INFO_DBG, "%s: left promiscuous mode\n", @@ -4379,7 +4466,8 @@ static void s2io_set_multicast(struct net_device *dev) /* Wait for command completes */ if (wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING)) { + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, + S2IO_BIT_RESET)) { DBG_PRINT(ERR_DBG, "%s: Adding ", dev->name); DBG_PRINT(ERR_DBG, "Multicasts failed\n"); @@ -4410,7 +4498,8 @@ static void s2io_set_multicast(struct net_device *dev) /* Wait for command completes */ if (wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING)) { + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, + S2IO_BIT_RESET)) { DBG_PRINT(ERR_DBG, "%s: Adding ", dev->name); DBG_PRINT(ERR_DBG, "Multicasts failed\n"); @@ -4436,6 +4525,7 @@ static int s2io_set_mac_addr(struct net_device *dev, u8 * addr) struct XENA_dev_config __iomem *bar0 = sp->bar0; register u64 val64, mac_addr = 0; int i; + u64 old_mac_addr = 0; /* * Set the new MAC address as the new unicast filter and reflect this @@ -4445,6 +4535,22 @@ static int s2io_set_mac_addr(struct net_device *dev, u8 * addr) for (i = 0; i < ETH_ALEN; i++) { mac_addr <<= 8; mac_addr |= addr[i]; + old_mac_addr <<= 8; + old_mac_addr |= sp->def_mac_addr[0].mac_addr[i]; + } + + if(0 == mac_addr) + return SUCCESS; + + /* Update the internal structure with this new mac address */ + if(mac_addr != old_mac_addr) { + memset(sp->def_mac_addr[0].mac_addr, 0, sizeof(ETH_ALEN)); + sp->def_mac_addr[0].mac_addr[5] = (u8) (mac_addr); + sp->def_mac_addr[0].mac_addr[4] = (u8) (mac_addr >> 8); + sp->def_mac_addr[0].mac_addr[3] = (u8) (mac_addr >> 16); + sp->def_mac_addr[0].mac_addr[2] = (u8) (mac_addr >> 24); + sp->def_mac_addr[0].mac_addr[1] = (u8) (mac_addr >> 32); + sp->def_mac_addr[0].mac_addr[0] = (u8) (mac_addr >> 40); } writeq(RMAC_ADDR_DATA0_MEM_ADDR(mac_addr), @@ -4456,7 +4562,7 @@ static int s2io_set_mac_addr(struct net_device *dev, u8 * addr) writeq(val64, &bar0->rmac_addr_cmd_mem); /* Wait till command completes */ if (wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING)) { + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, S2IO_BIT_RESET)) { DBG_PRINT(ERR_DBG, "%s: set_mac_addr failed\n", dev->name); return FAILURE; } @@ -4547,7 +4653,11 @@ static void s2io_ethtool_gdrvinfo(struct net_device *dev, info->regdump_len = XENA_REG_SPACE; info->eedump_len = XENA_EEPROM_SPACE; info->testinfo_len = S2IO_TEST_LEN; - info->n_stats = S2IO_STAT_LEN; + + if (sp->device_type == XFRAME_I_DEVICE) + info->n_stats = XFRAME_I_STAT_LEN; + else + info->n_stats = XFRAME_II_STAT_LEN; } /** @@ -5569,22 +5679,30 @@ static void s2io_get_ethtool_stats(struct net_device *dev, tmp_stats[i++] = le32_to_cpu(stat_info->rxd_wr_cnt); tmp_stats[i++] = le32_to_cpu(stat_info->txf_rd_cnt); tmp_stats[i++] = le32_to_cpu(stat_info->rxf_wr_cnt); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_ttl_1519_4095_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_ttl_4096_8191_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_ttl_8192_max_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_ttl_gt_max_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_osized_alt_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_jabber_alt_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_gt_max_alt_frms); - tmp_stats[i++] = le64_to_cpu(stat_info->rmac_vlan_frms); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_len_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_fcs_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_pf_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_da_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_red_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_rts_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->rmac_ingm_full_discard); - tmp_stats[i++] = le32_to_cpu(stat_info->link_fault_cnt); + + /* Enhanced statistics exist only for Hercules */ + if(sp->device_type == XFRAME_II_DEVICE) { + tmp_stats[i++] = + le64_to_cpu(stat_info->rmac_ttl_1519_4095_frms); + tmp_stats[i++] = + le64_to_cpu(stat_info->rmac_ttl_4096_8191_frms); + tmp_stats[i++] = + le64_to_cpu(stat_info->rmac_ttl_8192_max_frms); + tmp_stats[i++] = le64_to_cpu(stat_info->rmac_ttl_gt_max_frms); + tmp_stats[i++] = le64_to_cpu(stat_info->rmac_osized_alt_frms); + tmp_stats[i++] = le64_to_cpu(stat_info->rmac_jabber_alt_frms); + tmp_stats[i++] = le64_to_cpu(stat_info->rmac_gt_max_alt_frms); + tmp_stats[i++] = le64_to_cpu(stat_info->rmac_vlan_frms); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_len_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_fcs_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_pf_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_da_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_red_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_rts_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->rmac_ingm_full_discard); + tmp_stats[i++] = le32_to_cpu(stat_info->link_fault_cnt); + } + tmp_stats[i++] = 0; tmp_stats[i++] = stat_info->sw_stat.single_ecc_errs; tmp_stats[i++] = stat_info->sw_stat.double_ecc_errs; @@ -5664,18 +5782,42 @@ static int s2io_ethtool_self_test_count(struct net_device *dev) static void s2io_ethtool_get_strings(struct net_device *dev, u32 stringset, u8 * data) { + int stat_size = 0; + struct s2io_nic *sp = dev->priv; + switch (stringset) { case ETH_SS_TEST: memcpy(data, s2io_gstrings, S2IO_STRINGS_LEN); break; case ETH_SS_STATS: - memcpy(data, ðtool_stats_keys, - sizeof(ethtool_stats_keys)); + stat_size = sizeof(ethtool_xena_stats_keys); + memcpy(data, ðtool_xena_stats_keys,stat_size); + if(sp->device_type == XFRAME_II_DEVICE) { + memcpy(data + stat_size, + ðtool_enhanced_stats_keys, + sizeof(ethtool_enhanced_stats_keys)); + stat_size += sizeof(ethtool_enhanced_stats_keys); + } + + memcpy(data + stat_size, ðtool_driver_stats_keys, + sizeof(ethtool_driver_stats_keys)); } } static int s2io_ethtool_get_stats_count(struct net_device *dev) { - return (S2IO_STAT_LEN); + struct s2io_nic *sp = dev->priv; + int stat_count = 0; + switch(sp->device_type) { + case XFRAME_I_DEVICE: + stat_count = XFRAME_I_STAT_LEN; + break; + + case XFRAME_II_DEVICE: + stat_count = XFRAME_II_STAT_LEN; + break; + } + + return stat_count; } static int s2io_ethtool_op_set_tx_csum(struct net_device *dev, u32 data) @@ -5818,12 +5960,12 @@ static void s2io_tasklet(unsigned long dev_addr) for (i = 0; i < config->rx_ring_num; i++) { ret = fill_rx_buffers(sp, i); if (ret == -ENOMEM) { - DBG_PRINT(ERR_DBG, "%s: Out of ", + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); DBG_PRINT(ERR_DBG, "memory in tasklet\n"); break; } else if (ret == -EFILL) { - DBG_PRINT(ERR_DBG, + DBG_PRINT(INFO_DBG, "%s: Rx Ring %d is full\n", dev->name, i); break; @@ -5847,9 +5989,14 @@ static void s2io_set_link(struct work_struct *work) register u64 val64; u16 subid; + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + if (test_and_set_bit(0, &(nic->link_state))) { /* The card is being reset, no point doing anything */ - return; + goto out_unlock; } subid = nic->pdev->subsystem_device; @@ -5903,6 +6050,9 @@ static void s2io_set_link(struct work_struct *work) s2io_link(nic, LINK_DOWN); } clear_bit(0, &(nic->link_state)); + +out_unlock: + rtnl_unlock(); } static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, @@ -5926,8 +6076,8 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: Out of ", dev->name); - DBG_PRINT(ERR_DBG, "memory to allocate SKBs\n"); + DBG_PRINT(INFO_DBG, "%s: Out of ", dev->name); + DBG_PRINT(INFO_DBG, "memory to allocate SKBs\n"); return -ENOMEM ; } /* storing the mapped addr in a temp variable @@ -5949,7 +6099,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n", + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n", dev->name); return -ENOMEM; } @@ -5976,7 +6126,7 @@ static int set_rxd_buffer_pointer(struct s2io_nic *sp, struct RxD_t *rxdp, } else { *skb = dev_alloc_skb(size); if (!(*skb)) { - DBG_PRINT(ERR_DBG, "%s: dev_alloc_skb failed\n", + DBG_PRINT(INFO_DBG, "%s: dev_alloc_skb failed\n", dev->name); return -ENOMEM; } @@ -6059,10 +6209,13 @@ static int rxd_owner_bit_reset(struct s2io_nic *sp) rx_blocks[j].rxds[k].virt_addr; if(sp->rxd_mode >= RXD_MODE_3A) ba = &mac_control->rings[i].ba[j][k]; - set_rxd_buffer_pointer(sp, rxdp, ba, + if (set_rxd_buffer_pointer(sp, rxdp, ba, &skb,(u64 *)&temp0_64, (u64 *)&temp1_64, - (u64 *)&temp2_64, size); + (u64 *)&temp2_64, + size) == ENOMEM) { + return 0; + } set_rxd_buffer_size(sp, rxdp, size); wmb(); @@ -6105,7 +6258,7 @@ static int s2io_add_isr(struct s2io_nic * sp) } } if (sp->intr_type == MSI_X) { - int i; + int i, msix_tx_cnt=0,msix_rx_cnt=0; for (i=1; (sp->s2io_entries[i].in_use == MSIX_FLG); i++) { if (sp->s2io_entries[i].type == MSIX_FIFO_TYPE) { @@ -6114,16 +6267,36 @@ static int s2io_add_isr(struct s2io_nic * sp) err = request_irq(sp->entries[i].vector, s2io_msix_fifo_handle, 0, sp->desc[i], sp->s2io_entries[i].arg); - DBG_PRINT(ERR_DBG, "%s @ 0x%llx\n", sp->desc[i], - (unsigned long long)sp->msix_info[i].addr); + /* If either data or addr is zero print it */ + if(!(sp->msix_info[i].addr && + sp->msix_info[i].data)) { + DBG_PRINT(ERR_DBG, "%s @ Addr:0x%llx" + "Data:0x%lx\n",sp->desc[i], + (unsigned long long) + sp->msix_info[i].addr, + (unsigned long) + ntohl(sp->msix_info[i].data)); + } else { + msix_tx_cnt++; + } } else { sprintf(sp->desc[i], "%s:MSI-X-%d-RX", dev->name, i); err = request_irq(sp->entries[i].vector, s2io_msix_ring_handle, 0, sp->desc[i], sp->s2io_entries[i].arg); - DBG_PRINT(ERR_DBG, "%s @ 0x%llx\n", sp->desc[i], - (unsigned long long)sp->msix_info[i].addr); + /* If either data or addr is zero print it */ + if(!(sp->msix_info[i].addr && + sp->msix_info[i].data)) { + DBG_PRINT(ERR_DBG, "%s @ Addr:0x%llx" + "Data:0x%lx\n",sp->desc[i], + (unsigned long long) + sp->msix_info[i].addr, + (unsigned long) + ntohl(sp->msix_info[i].data)); + } else { + msix_rx_cnt++; + } } if (err) { DBG_PRINT(ERR_DBG,"%s:MSI-X-%d registration " @@ -6133,6 +6306,8 @@ static int s2io_add_isr(struct s2io_nic * sp) } sp->s2io_entries[i].in_use = MSIX_REGISTERED_SUCCESS; } + printk("MSI-X-TX %d entries enabled\n",msix_tx_cnt); + printk("MSI-X-RX %d entries enabled\n",msix_rx_cnt); } if (sp->intr_type == INTA) { err = request_irq((int) sp->pdev->irq, s2io_isr, IRQF_SHARED, @@ -6356,6 +6531,11 @@ static void s2io_restart_nic(struct work_struct *work) struct s2io_nic *sp = container_of(work, struct s2io_nic, rst_timer_task); struct net_device *dev = sp->dev; + rtnl_lock(); + + if (!netif_running(dev)) + goto out_unlock; + s2io_card_down(sp); if (s2io_card_up(sp)) { DBG_PRINT(ERR_DBG, "%s: Device bring up failed\n", @@ -6364,7 +6544,8 @@ static void s2io_restart_nic(struct work_struct *work) netif_wake_queue(dev); DBG_PRINT(ERR_DBG, "%s: was reset by Tx watchdog timer\n", dev->name); - +out_unlock: + rtnl_unlock(); } /** @@ -6446,7 +6627,6 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) /* Updating statistics */ rxdp->Host_Control = 0; - sp->rx_pkt_count++; sp->stats.rx_packets++; if (sp->rxd_mode == RXD_MODE_1) { int len = RXD_GET_BUFFER0_SIZE_1(rxdp->Control_2); @@ -6554,7 +6734,8 @@ static int rx_osm_handler(struct ring_info *ring_data, struct RxD_t * rxdp) if (!sp->lro) { skb->protocol = eth_type_trans(skb, dev); - if (sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2)) { + if ((sp->vlgrp && RXD_GET_VLAN_TAG(rxdp->Control_2) && + vlan_strip_flag)) { /* Queueing the vlan frame to the upper layer */ if (napi) vlan_hwaccel_receive_skb(skb, sp->vlgrp, @@ -6691,8 +6872,7 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type) "Defaulting to INTA\n"); *dev_intr_type = INTA; } - if ( (rx_ring_num > 1) && (*dev_intr_type != INTA) ) - napi = 0; + if (rx_ring_mode > 3) { DBG_PRINT(ERR_DBG, "s2io: Requested ring mode not supported\n"); DBG_PRINT(ERR_DBG, "s2io: Defaulting to 3-buffer mode\n"); @@ -6702,6 +6882,37 @@ static int s2io_verify_parm(struct pci_dev *pdev, u8 *dev_intr_type) } /** + * rts_ds_steer - Receive traffic steering based on IPv4 or IPv6 TOS + * or Traffic class respectively. + * @nic: device peivate variable + * Description: The function configures the receive steering to + * desired receive ring. + * Return Value: SUCCESS on success and + * '-1' on failure (endian settings incorrect). + */ +static int rts_ds_steer(struct s2io_nic *nic, u8 ds_codepoint, u8 ring) +{ + struct XENA_dev_config __iomem *bar0 = nic->bar0; + register u64 val64 = 0; + + if (ds_codepoint > 63) + return FAILURE; + + val64 = RTS_DS_MEM_DATA(ring); + writeq(val64, &bar0->rts_ds_mem_data); + + val64 = RTS_DS_MEM_CTRL_WE | + RTS_DS_MEM_CTRL_STROBE_NEW_CMD | + RTS_DS_MEM_CTRL_OFFSET(ds_codepoint); + + writeq(val64, &bar0->rts_ds_mem_ctrl); + + return wait_for_cmd_complete(&bar0->rts_ds_mem_ctrl, + RTS_DS_MEM_CTRL_STROBE_CMD_BEING_EXECUTED, + S2IO_BIT_RESET); +} + +/** * s2io_init_nic - Initialization of the adapter . * @pdev : structure containing the PCI related information of the device. * @pre: List of PCI devices supported by the driver listed in s2io_tbl. @@ -6995,13 +7206,11 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) RMAC_ADDR_CMD_MEM_OFFSET(0 + MAC_MAC_ADDR_START_OFFSET); writeq(val64, &bar0->rmac_addr_cmd_mem); wait_for_cmd_complete(&bar0->rmac_addr_cmd_mem, - RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING); + RMAC_ADDR_CMD_MEM_STROBE_CMD_EXECUTING, S2IO_BIT_RESET); tmp64 = readq(&bar0->rmac_addr_data0_mem); mac_down = (u32) tmp64; mac_up = (u32) (tmp64 >> 32); - memset(sp->def_mac_addr[0].mac_addr, 0, sizeof(ETH_ALEN)); - sp->def_mac_addr[0].mac_addr[3] = (u8) (mac_up); sp->def_mac_addr[0].mac_addr[2] = (u8) (mac_up >> 8); sp->def_mac_addr[0].mac_addr[1] = (u8) (mac_up >> 16); @@ -7053,7 +7262,7 @@ s2io_init_nic(struct pci_dev *pdev, const struct pci_device_id *pre) goto register_failed; } s2io_vpd_read(sp); - DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2005 Neterion Inc.\n"); + DBG_PRINT(ERR_DBG, "Copyright(c) 2002-2007 Neterion Inc.\n"); DBG_PRINT(ERR_DBG, "%s: Neterion %s (rev %d)\n",dev->name, sp->product_name, get_xena_rev_id(sp->pdev)); DBG_PRINT(ERR_DBG, "%s: Driver version %s\n", dev->name, @@ -7173,6 +7382,8 @@ static void __devexit s2io_rem_nic(struct pci_dev *pdev) return; } + flush_scheduled_work(); + sp = dev->priv; unregister_netdev(dev); diff --git a/drivers/net/s2io.h b/drivers/net/s2io.h index 0de0c65..a656d18 100644 --- a/drivers/net/s2io.h +++ b/drivers/net/s2io.h @@ -1,6 +1,6 @@ /************************************************************************ * s2io.h: A Linux PCI-X Ethernet driver for Neterion 10GbE Server NIC - * Copyright(c) 2002-2005 Neterion Inc. + * Copyright(c) 2002-2007 Neterion Inc. * This software may be used and distributed according to the terms of * the GNU General Public License (GPL), incorporated herein by reference. @@ -32,7 +32,8 @@ #define FAILURE -1 #define S2IO_MINUS_ONE 0xFFFFFFFFFFFFFFFFULL #define S2IO_MAX_PCI_CONFIG_SPACE_REINIT 100 - +#define S2IO_BIT_RESET 1 +#define S2IO_BIT_SET 2 #define CHECKBIT(value, nbit) (value & (1 << nbit)) /* Maximum time to flicker LED when asked to identify NIC using ethtool */ @@ -296,6 +297,9 @@ struct stat_block { struct xpakStat xpak_stat; }; +/* Default value for 'vlan_strip_tag' configuration parameter */ +#define NO_STRIP_IN_PROMISC 2 + /* * Structures representing different init time configuration * parameters of the NIC. @@ -756,7 +760,6 @@ struct s2io_nic { #define MAX_SUPPORTED_MULTICASTS MAX_MAC_SUPPORTED struct mac_addr def_mac_addr[MAX_MAC_SUPPORTED]; - struct mac_addr pre_mac_addr[MAX_MAC_SUPPORTED]; struct net_device_stats stats; int high_dma_flag; @@ -790,11 +793,6 @@ struct s2io_nic { u16 all_multi_pos; u16 promisc_flg; - u16 tx_pkt_count; - u16 rx_pkt_count; - u16 tx_err_count; - u16 rx_err_count; - /* Id timer, used to blink NIC to physically identify NIC. */ struct timer_list id_timer; @@ -1005,7 +1003,8 @@ static int s2io_set_swapper(struct s2io_nic * sp); static void s2io_card_down(struct s2io_nic *nic); static int s2io_card_up(struct s2io_nic *nic); static int get_xena_rev_id(struct pci_dev *pdev); -static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit); +static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit, + int bit_state); static int s2io_add_isr(struct s2io_nic * sp); static void s2io_rem_isr(struct s2io_nic * sp); @@ -1019,6 +1018,7 @@ static void queue_rx_frame(struct sk_buff *skb); static void update_L3L4_header(struct s2io_nic *sp, struct lro *lro); static void lro_append_pkt(struct s2io_nic *sp, struct lro *lro, struct sk_buff *skb, u32 tcp_len); +static int rts_ds_steer(struct s2io_nic *nic, u8 ds_codepoint, u8 ring); #define s2io_tcp_mss(skb) skb_shinfo(skb)->gso_size #define s2io_udp_mss(skb) skb_shinfo(skb)->gso_size diff --git a/drivers/net/saa9730.c b/drivers/net/saa9730.c index b269513..ad94358 100644 --- a/drivers/net/saa9730.c +++ b/drivers/net/saa9730.c @@ -64,37 +64,37 @@ static unsigned int pci_irq_line; static void evm_saa9730_enable_lan_int(struct lan_saa9730_private *lp) { - outl(readl(&lp->evm_saa9730_regs->InterruptBlock1) | EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptBlock1); - outl(readl(&lp->evm_saa9730_regs->InterruptStatus1) | EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptStatus1); - outl(readl(&lp->evm_saa9730_regs->InterruptEnable1) | EVM_LAN_INT | - EVM_MASTER_EN, &lp->evm_saa9730_regs->InterruptEnable1); + writel(readl(&lp->evm_saa9730_regs->InterruptBlock1) | EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptBlock1); + writel(readl(&lp->evm_saa9730_regs->InterruptStatus1) | EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptStatus1); + writel(readl(&lp->evm_saa9730_regs->InterruptEnable1) | EVM_LAN_INT | + EVM_MASTER_EN, &lp->evm_saa9730_regs->InterruptEnable1); } static void evm_saa9730_disable_lan_int(struct lan_saa9730_private *lp) { - outl(readl(&lp->evm_saa9730_regs->InterruptBlock1) & ~EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptBlock1); - outl(readl(&lp->evm_saa9730_regs->InterruptEnable1) & ~EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptEnable1); + writel(readl(&lp->evm_saa9730_regs->InterruptBlock1) & ~EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptBlock1); + writel(readl(&lp->evm_saa9730_regs->InterruptEnable1) & ~EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptEnable1); } static void evm_saa9730_clear_lan_int(struct lan_saa9730_private *lp) { - outl(EVM_LAN_INT, &lp->evm_saa9730_regs->InterruptStatus1); + writel(EVM_LAN_INT, &lp->evm_saa9730_regs->InterruptStatus1); } static void evm_saa9730_block_lan_int(struct lan_saa9730_private *lp) { - outl(readl(&lp->evm_saa9730_regs->InterruptBlock1) & ~EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptBlock1); + writel(readl(&lp->evm_saa9730_regs->InterruptBlock1) & ~EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptBlock1); } static void evm_saa9730_unblock_lan_int(struct lan_saa9730_private *lp) { - outl(readl(&lp->evm_saa9730_regs->InterruptBlock1) | EVM_LAN_INT, - &lp->evm_saa9730_regs->InterruptBlock1); + writel(readl(&lp->evm_saa9730_regs->InterruptBlock1) | EVM_LAN_INT, + &lp->evm_saa9730_regs->InterruptBlock1); } static void __attribute_used__ show_saa9730_regs(struct lan_saa9730_private *lp) @@ -147,7 +147,7 @@ static void __attribute_used__ show_saa9730_regs(struct lan_saa9730_private *lp) printk("lp->lan_saa9730_regs->RxStatus = %x\n", readl(&lp->lan_saa9730_regs->RxStatus)); for (i = 0; i < LAN_SAA9730_CAM_DWORDS; i++) { - outl(i, &lp->lan_saa9730_regs->CamAddress); + writel(i, &lp->lan_saa9730_regs->CamAddress); printk("lp->lan_saa9730_regs->CamData = %x\n", readl(&lp->lan_saa9730_regs->CamData)); } @@ -288,28 +288,27 @@ static int lan_saa9730_allocate_buffers(struct pci_dev *pdev, * Set rx buffer A and rx buffer B to point to the first two buffer * spaces. */ - outl(lp->dma_addr + rxoffset, - &lp->lan_saa9730_regs->RxBuffA); - outl(lp->dma_addr + rxoffset + - LAN_SAA9730_PACKET_SIZE * LAN_SAA9730_RCV_Q_SIZE, - &lp->lan_saa9730_regs->RxBuffB); + writel(lp->dma_addr + rxoffset, &lp->lan_saa9730_regs->RxBuffA); + writel(lp->dma_addr + rxoffset + + LAN_SAA9730_PACKET_SIZE * LAN_SAA9730_RCV_Q_SIZE, + &lp->lan_saa9730_regs->RxBuffB); /* * Set txm_buf_a and txm_buf_b to point to the first two buffer * space */ - outl(lp->dma_addr + txoffset, - &lp->lan_saa9730_regs->TxBuffA); - outl(lp->dma_addr + txoffset + - LAN_SAA9730_PACKET_SIZE * LAN_SAA9730_TXM_Q_SIZE, - &lp->lan_saa9730_regs->TxBuffB); + writel(lp->dma_addr + txoffset, + &lp->lan_saa9730_regs->TxBuffA); + writel(lp->dma_addr + txoffset + + LAN_SAA9730_PACKET_SIZE * LAN_SAA9730_TXM_Q_SIZE, + &lp->lan_saa9730_regs->TxBuffB); /* Set packet number */ - outl((lp->DmaRcvPackets << PK_COUNT_RX_A_SHF) | - (lp->DmaRcvPackets << PK_COUNT_RX_B_SHF) | - (lp->DmaTxmPackets << PK_COUNT_TX_A_SHF) | - (lp->DmaTxmPackets << PK_COUNT_TX_B_SHF), - &lp->lan_saa9730_regs->PacketCount); + writel((lp->DmaRcvPackets << PK_COUNT_RX_A_SHF) | + (lp->DmaRcvPackets << PK_COUNT_RX_B_SHF) | + (lp->DmaTxmPackets << PK_COUNT_TX_A_SHF) | + (lp->DmaTxmPackets << PK_COUNT_TX_B_SHF), + &lp->lan_saa9730_regs->PacketCount); return 0; @@ -326,10 +325,10 @@ static int lan_saa9730_cam_load(struct lan_saa9730_private *lp) for (i = 0; i < LAN_SAA9730_CAM_DWORDS; i++) { /* First set address to where data is written */ - outl(i, &lp->lan_saa9730_regs->CamAddress); - outl((NetworkAddress[0] << 24) | (NetworkAddress[1] << 16) - | (NetworkAddress[2] << 8) | NetworkAddress[3], - &lp->lan_saa9730_regs->CamData); + writel(i, &lp->lan_saa9730_regs->CamAddress); + writel((NetworkAddress[0] << 24) | (NetworkAddress[1] << 16) | + (NetworkAddress[2] << 8) | NetworkAddress[3], + &lp->lan_saa9730_regs->CamData); NetworkAddress += 4; } return 0; @@ -365,8 +364,8 @@ static int lan_saa9730_mii_init(struct lan_saa9730_private *lp) } /* Now set the control and address register. */ - outl(MD_CA_BUSY | PHY_STATUS | PHY_ADDRESS << MD_CA_PHY_SHF, - &lp->lan_saa9730_regs->StationMgmtCtl); + writel(MD_CA_BUSY | PHY_STATUS | PHY_ADDRESS << MD_CA_PHY_SHF, + &lp->lan_saa9730_regs->StationMgmtCtl); /* check link status, spin here till station is not busy */ i = 0; @@ -391,23 +390,23 @@ static int lan_saa9730_mii_init(struct lan_saa9730_private *lp) /* Link is down, reset the PHY first. */ /* set PHY address = 'CONTROL' */ - outl(PHY_ADDRESS << MD_CA_PHY_SHF | MD_CA_WR | PHY_CONTROL, - &lp->lan_saa9730_regs->StationMgmtCtl); + writel(PHY_ADDRESS << MD_CA_PHY_SHF | MD_CA_WR | PHY_CONTROL, + &lp->lan_saa9730_regs->StationMgmtCtl); /* Wait for 1 ms. */ mdelay(1); /* set 'CONTROL' = force reset and renegotiate */ - outl(PHY_CONTROL_RESET | PHY_CONTROL_AUTO_NEG | - PHY_CONTROL_RESTART_AUTO_NEG, - &lp->lan_saa9730_regs->StationMgmtData); + writel(PHY_CONTROL_RESET | PHY_CONTROL_AUTO_NEG | + PHY_CONTROL_RESTART_AUTO_NEG, + &lp->lan_saa9730_regs->StationMgmtData); /* Wait for 50 ms. */ mdelay(50); /* set 'BUSY' to start operation */ - outl(MD_CA_BUSY | PHY_ADDRESS << MD_CA_PHY_SHF | MD_CA_WR | - PHY_CONTROL, &lp->lan_saa9730_regs->StationMgmtCtl); + writel(MD_CA_BUSY | PHY_ADDRESS << MD_CA_PHY_SHF | MD_CA_WR | + PHY_CONTROL, &lp->lan_saa9730_regs->StationMgmtCtl); /* await completion */ i = 0; @@ -427,9 +426,9 @@ static int lan_saa9730_mii_init(struct lan_saa9730_private *lp) for (l = 0; l < 2; l++) { /* set PHY address = 'STATUS' */ - outl(MD_CA_BUSY | PHY_ADDRESS << MD_CA_PHY_SHF | - PHY_STATUS, - &lp->lan_saa9730_regs->StationMgmtCtl); + writel(MD_CA_BUSY | PHY_ADDRESS << MD_CA_PHY_SHF | + PHY_STATUS, + &lp->lan_saa9730_regs->StationMgmtCtl); /* await completion */ i = 0; @@ -462,35 +461,35 @@ static int lan_saa9730_mii_init(struct lan_saa9730_private *lp) static int lan_saa9730_control_init(struct lan_saa9730_private *lp) { /* Initialize DMA control register. */ - outl((LANMB_ANY << DMA_CTL_MAX_XFER_SHF) | - (LANEND_LITTLE << DMA_CTL_ENDIAN_SHF) | - (LAN_SAA9730_RCV_Q_INT_THRESHOLD << DMA_CTL_RX_INT_COUNT_SHF) - | DMA_CTL_RX_INT_TO_EN | DMA_CTL_RX_INT_EN | - DMA_CTL_MAC_RX_INT_EN | DMA_CTL_MAC_TX_INT_EN, - &lp->lan_saa9730_regs->LanDmaCtl); + writel((LANMB_ANY << DMA_CTL_MAX_XFER_SHF) | + (LANEND_LITTLE << DMA_CTL_ENDIAN_SHF) | + (LAN_SAA9730_RCV_Q_INT_THRESHOLD << DMA_CTL_RX_INT_COUNT_SHF) + | DMA_CTL_RX_INT_TO_EN | DMA_CTL_RX_INT_EN | + DMA_CTL_MAC_RX_INT_EN | DMA_CTL_MAC_TX_INT_EN, + &lp->lan_saa9730_regs->LanDmaCtl); /* Initial MAC control register. */ - outl((MACCM_MII << MAC_CONTROL_CONN_SHF) | MAC_CONTROL_FULL_DUP, - &lp->lan_saa9730_regs->MacCtl); + writel((MACCM_MII << MAC_CONTROL_CONN_SHF) | MAC_CONTROL_FULL_DUP, + &lp->lan_saa9730_regs->MacCtl); /* Initialize CAM control register. */ - outl(CAM_CONTROL_COMP_EN | CAM_CONTROL_BROAD_ACC, - &lp->lan_saa9730_regs->CamCtl); + writel(CAM_CONTROL_COMP_EN | CAM_CONTROL_BROAD_ACC, + &lp->lan_saa9730_regs->CamCtl); /* * Initialize CAM enable register, only turn on first entry, should * contain own addr. */ - outl(0x0001, &lp->lan_saa9730_regs->CamEnable); + writel(0x0001, &lp->lan_saa9730_regs->CamEnable); /* Initialize Tx control register */ - outl(TX_CTL_EN_COMP, &lp->lan_saa9730_regs->TxCtl); + writel(TX_CTL_EN_COMP, &lp->lan_saa9730_regs->TxCtl); /* Initialize Rcv control register */ - outl(RX_CTL_STRIP_CRC, &lp->lan_saa9730_regs->RxCtl); + writel(RX_CTL_STRIP_CRC, &lp->lan_saa9730_regs->RxCtl); /* Reset DMA engine */ - outl(DMA_TEST_SW_RESET, &lp->lan_saa9730_regs->DmaTest); + writel(DMA_TEST_SW_RESET, &lp->lan_saa9730_regs->DmaTest); return 0; } @@ -500,14 +499,14 @@ static int lan_saa9730_stop(struct lan_saa9730_private *lp) int i; /* Stop DMA first */ - outl(readl(&lp->lan_saa9730_regs->LanDmaCtl) & - ~(DMA_CTL_EN_TX_DMA | DMA_CTL_EN_RX_DMA), - &lp->lan_saa9730_regs->LanDmaCtl); + writel(readl(&lp->lan_saa9730_regs->LanDmaCtl) & + ~(DMA_CTL_EN_TX_DMA | DMA_CTL_EN_RX_DMA), + &lp->lan_saa9730_regs->LanDmaCtl); /* Set the SW Reset bits in DMA and MAC control registers */ - outl(DMA_TEST_SW_RESET, &lp->lan_saa9730_regs->DmaTest); - outl(readl(&lp->lan_saa9730_regs->MacCtl) | MAC_CONTROL_RESET, - &lp->lan_saa9730_regs->MacCtl); + writel(DMA_TEST_SW_RESET, &lp->lan_saa9730_regs->DmaTest); + writel(readl(&lp->lan_saa9730_regs->MacCtl) | MAC_CONTROL_RESET, + &lp->lan_saa9730_regs->MacCtl); /* * Wait for MAC reset to have finished. The reset bit is auto cleared @@ -532,8 +531,8 @@ static int lan_saa9730_dma_init(struct lan_saa9730_private *lp) /* Stop lan controller. */ lan_saa9730_stop(lp); - outl(LAN_SAA9730_DEFAULT_TIME_OUT_CNT, - &lp->lan_saa9730_regs->Timeout); + writel(LAN_SAA9730_DEFAULT_TIME_OUT_CNT, + &lp->lan_saa9730_regs->Timeout); return 0; } @@ -552,19 +551,19 @@ static int lan_saa9730_start(struct lan_saa9730_private *lp) lp->PendingTxmPacketIndex = 0; lp->PendingTxmBufferIndex = 0; - outl(readl(&lp->lan_saa9730_regs->LanDmaCtl) | DMA_CTL_EN_TX_DMA | - DMA_CTL_EN_RX_DMA, &lp->lan_saa9730_regs->LanDmaCtl); + writel(readl(&lp->lan_saa9730_regs->LanDmaCtl) | DMA_CTL_EN_TX_DMA | + DMA_CTL_EN_RX_DMA, &lp->lan_saa9730_regs->LanDmaCtl); /* For Tx, turn on MAC then DMA */ - outl(readl(&lp->lan_saa9730_regs->TxCtl) | TX_CTL_TX_EN, - &lp->lan_saa9730_regs->TxCtl); + writel(readl(&lp->lan_saa9730_regs->TxCtl) | TX_CTL_TX_EN, + &lp->lan_saa9730_regs->TxCtl); /* For Rx, turn on DMA then MAC */ - outl(readl(&lp->lan_saa9730_regs->RxCtl) | RX_CTL_RX_EN, - &lp->lan_saa9730_regs->RxCtl); + writel(readl(&lp->lan_saa9730_regs->RxCtl) | RX_CTL_RX_EN, + &lp->lan_saa9730_regs->RxCtl); /* Set Ok2Use to let hardware own the buffers. */ - outl(OK2USE_RX_A | OK2USE_RX_B, &lp->lan_saa9730_regs->Ok2Use); + writel(OK2USE_RX_A | OK2USE_RX_B, &lp->lan_saa9730_regs->Ok2Use); return 0; } @@ -587,7 +586,7 @@ static int lan_saa9730_tx(struct net_device *dev) printk("lan_saa9730_tx interrupt\n"); /* Clear interrupt. */ - outl(DMA_STATUS_MAC_TX_INT, &lp->lan_saa9730_regs->DmaStatus); + writel(DMA_STATUS_MAC_TX_INT, &lp->lan_saa9730_regs->DmaStatus); while (1) { pPacket = lp->TxmBuffer[lp->PendingTxmBufferIndex] @@ -660,8 +659,8 @@ static int lan_saa9730_rx(struct net_device *dev) printk("lan_saa9730_rx interrupt\n"); /* Clear receive interrupts. */ - outl(DMA_STATUS_MAC_RX_INT | DMA_STATUS_RX_INT | - DMA_STATUS_RX_TO_INT, &lp->lan_saa9730_regs->DmaStatus); + writel(DMA_STATUS_MAC_RX_INT | DMA_STATUS_RX_INT | + DMA_STATUS_RX_TO_INT, &lp->lan_saa9730_regs->DmaStatus); /* Address next packet */ BufferIndex = lp->NextRcvBufferIndex; @@ -689,7 +688,6 @@ static int lan_saa9730_rx(struct net_device *dev) } else { lp->stats.rx_bytes += len; lp->stats.rx_packets++; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ eth_copy_and_sum(skb, @@ -725,8 +723,8 @@ static int lan_saa9730_rx(struct net_device *dev) *pPacket = cpu_to_le32(RXSF_READY << RX_STAT_CTL_OWNER_SHF); /* Make sure A or B is available to hardware as appropriate. */ - outl(BufferIndex ? OK2USE_RX_B : OK2USE_RX_A, - &lp->lan_saa9730_regs->Ok2Use); + writel(BufferIndex ? OK2USE_RX_B : OK2USE_RX_A, + &lp->lan_saa9730_regs->Ok2Use); /* Go to next packet in sequence. */ lp->NextRcvPacketIndex++; @@ -844,8 +842,8 @@ static int lan_saa9730_write(struct lan_saa9730_private *lp, (len << TX_STAT_CTL_LENGTH_SHF)); /* Make sure A or B is available to hardware as appropriate. */ - outl(BufferIndex ? OK2USE_TX_B : OK2USE_TX_A, - &lp->lan_saa9730_regs->Ok2Use); + writel(BufferIndex ? OK2USE_TX_B : OK2USE_TX_A, + &lp->lan_saa9730_regs->Ok2Use); return 0; } @@ -938,15 +936,15 @@ static void lan_saa9730_set_multicast(struct net_device *dev) if (dev->flags & IFF_PROMISC) { /* accept all packets */ - outl(CAM_CONTROL_COMP_EN | CAM_CONTROL_STATION_ACC | - CAM_CONTROL_GROUP_ACC | CAM_CONTROL_BROAD_ACC, - &lp->lan_saa9730_regs->CamCtl); + writel(CAM_CONTROL_COMP_EN | CAM_CONTROL_STATION_ACC | + CAM_CONTROL_GROUP_ACC | CAM_CONTROL_BROAD_ACC, + &lp->lan_saa9730_regs->CamCtl); } else { if (dev->flags & IFF_ALLMULTI) { /* accept all multicast packets */ - outl(CAM_CONTROL_COMP_EN | CAM_CONTROL_GROUP_ACC | - CAM_CONTROL_BROAD_ACC, - &lp->lan_saa9730_regs->CamCtl); + writel(CAM_CONTROL_COMP_EN | CAM_CONTROL_GROUP_ACC | + CAM_CONTROL_BROAD_ACC, + &lp->lan_saa9730_regs->CamCtl); } else { /* * Will handle the multicast stuff later. -carstenl diff --git a/drivers/net/sb1000.c b/drivers/net/sb1000.c index b9fa4fb..1de3eec 100644 --- a/drivers/net/sb1000.c +++ b/drivers/net/sb1000.c @@ -834,7 +834,7 @@ printk("cm0: IP identification: %02x%02x fragment offset: %02x%02x\n", buffer[3 goto dropped_frame; } skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = (unsigned short) buffer[NewDatagramHeaderSkip + 16]; insw(ioaddr, skb_put(skb, NewDatagramDataSize), NewDatagramDataSize / 2); diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 1eae16b..132e214 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -95,19 +95,28 @@ MODULE_PARM_DESC(full_duplex, "1-" __MODULE_STRING(MAX_UNITS)); #endif #ifdef CONFIG_SBMAC_COALESCE -static int int_pktcnt = 0; -module_param(int_pktcnt, int, S_IRUGO); -MODULE_PARM_DESC(int_pktcnt, "Packet count"); +static int int_pktcnt_tx = 255; +module_param(int_pktcnt_tx, int, S_IRUGO); +MODULE_PARM_DESC(int_pktcnt_tx, "TX packet count"); -static int int_timeout = 0; -module_param(int_timeout, int, S_IRUGO); -MODULE_PARM_DESC(int_timeout, "Timeout value"); +static int int_timeout_tx = 255; +module_param(int_timeout_tx, int, S_IRUGO); +MODULE_PARM_DESC(int_timeout_tx, "TX timeout value"); + +static int int_pktcnt_rx = 64; +module_param(int_pktcnt_rx, int, S_IRUGO); +MODULE_PARM_DESC(int_pktcnt_rx, "RX packet count"); + +static int int_timeout_rx = 64; +module_param(int_timeout_rx, int, S_IRUGO); +MODULE_PARM_DESC(int_timeout_rx, "RX timeout value"); #endif #include <asm/sibyte/sb1250.h> #if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80) #include <asm/sibyte/bcm1480_regs.h> #include <asm/sibyte/bcm1480_int.h> +#define R_MAC_DMA_OODPKTLOST_RX R_MAC_DMA_OODPKTLOST #elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X) #include <asm/sibyte/sb1250_regs.h> #include <asm/sibyte/sb1250_int.h> @@ -155,8 +164,8 @@ typedef enum { sbmac_state_uninit, sbmac_state_off, sbmac_state_on, #define NUMCACHEBLKS(x) (((x)+SMP_CACHE_BYTES-1)/SMP_CACHE_BYTES) -#define SBMAC_MAX_TXDESCR 32 -#define SBMAC_MAX_RXDESCR 32 +#define SBMAC_MAX_TXDESCR 256 +#define SBMAC_MAX_RXDESCR 256 #define ETHER_ALIGN 2 #define ETHER_ADDR_LEN 6 @@ -185,10 +194,10 @@ typedef struct sbmacdma_s { * associated with it. */ - struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */ - int sbdma_channel; /* channel number */ + struct sbmac_softc *sbdma_eth; /* back pointer to associated MAC */ + int sbdma_channel; /* channel number */ int sbdma_txdir; /* direction (1=transmit) */ - int sbdma_maxdescr; /* total # of descriptors in ring */ + int sbdma_maxdescr; /* total # of descriptors in ring */ #ifdef CONFIG_SBMAC_COALESCE int sbdma_int_pktcnt; /* # descriptors rx/tx before interrupt*/ int sbdma_int_timeout; /* # usec rx/tx interrupt */ @@ -197,13 +206,16 @@ typedef struct sbmacdma_s { volatile void __iomem *sbdma_config0; /* DMA config register 0 */ volatile void __iomem *sbdma_config1; /* DMA config register 1 */ volatile void __iomem *sbdma_dscrbase; /* Descriptor base address */ - volatile void __iomem *sbdma_dscrcnt; /* Descriptor count register */ + volatile void __iomem *sbdma_dscrcnt; /* Descriptor count register */ volatile void __iomem *sbdma_curdscr; /* current descriptor address */ + volatile void __iomem *sbdma_oodpktlost;/* pkt drop (rx only) */ + /* * This stuff is for maintenance of the ring */ + sbdmadscr_t *sbdma_dscrtable_unaligned; sbdmadscr_t *sbdma_dscrtable; /* base of descriptor table */ sbdmadscr_t *sbdma_dscrtable_end; /* end of descriptor table */ @@ -243,7 +255,7 @@ struct sbmac_softc { * Controller-specific things */ - volatile void __iomem *sbm_base; /* MAC's base address */ + void __iomem *sbm_base; /* MAC's base address */ sbmac_state_t sbm_state; /* current state */ volatile void __iomem *sbm_macenable; /* MAC Enable Register */ @@ -286,8 +298,8 @@ static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *m); static int sbdma_add_txbuffer(sbmacdma_t *d,struct sk_buff *m); static void sbdma_emptyring(sbmacdma_t *d); static void sbdma_fillring(sbmacdma_t *d); -static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d); -static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d); +static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, int work_to_do, int poll); +static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll); static int sbmac_initctx(struct sbmac_softc *s); static void sbmac_channel_start(struct sbmac_softc *s); static void sbmac_channel_stop(struct sbmac_softc *s); @@ -308,6 +320,8 @@ static struct net_device_stats *sbmac_get_stats(struct net_device *dev); static void sbmac_set_rx_mode(struct net_device *dev); static int sbmac_mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); static int sbmac_close(struct net_device *dev); +static int sbmac_poll(struct net_device *poll_dev, int *budget); + static int sbmac_mii_poll(struct sbmac_softc *s,int noisy); static int sbmac_mii_probe(struct net_device *dev); @@ -679,6 +693,10 @@ static void sbdma_initctx(sbmacdma_t *d, int txrx, int maxdescr) { +#ifdef CONFIG_SBMAC_COALESCE + int int_pktcnt, int_timeout; +#endif + /* * Save away interesting stuff in the structure */ @@ -728,6 +746,11 @@ static void sbdma_initctx(sbmacdma_t *d, s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_DSCR_CNT); d->sbdma_curdscr = s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_CUR_DSCRADDR); + if (d->sbdma_txdir) + d->sbdma_oodpktlost = NULL; + else + d->sbdma_oodpktlost = + s->sbm_base + R_MAC_DMA_REGISTER(txrx,chan,R_MAC_DMA_OODPKTLOST_RX); /* * Allocate memory for the ring @@ -735,6 +758,7 @@ static void sbdma_initctx(sbmacdma_t *d, d->sbdma_maxdescr = maxdescr; + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = (sbdmadscr_t *) kmalloc((d->sbdma_maxdescr+1)*sizeof(sbdmadscr_t), GFP_KERNEL); @@ -765,12 +789,14 @@ static void sbdma_initctx(sbmacdma_t *d, * Setup Rx/Tx DMA coalescing defaults */ + int_pktcnt = (txrx == DMA_TX) ? int_pktcnt_tx : int_pktcnt_rx; if ( int_pktcnt ) { d->sbdma_int_pktcnt = int_pktcnt; } else { d->sbdma_int_pktcnt = 1; } + int_timeout = (txrx == DMA_TX) ? int_timeout_tx : int_timeout_rx; if ( int_timeout ) { d->sbdma_int_timeout = int_timeout; } else { @@ -933,9 +959,6 @@ static int sbdma_add_rcvbuffer(sbmacdma_t *d,struct sk_buff *sb) } sbdma_align_skb(sb_new, SMP_CACHE_BYTES, ETHER_ALIGN); - - /* mark skbuff owned by our device */ - sb_new->dev = d->sbdma_eth->sbm_dev; } else { sb_new = sb; @@ -1128,32 +1151,63 @@ static void sbdma_fillring(sbmacdma_t *d) } } +#ifdef CONFIG_NET_POLL_CONTROLLER +static void sbmac_netpoll(struct net_device *netdev) +{ + struct sbmac_softc *sc = netdev_priv(netdev); + int irq = sc->sbm_dev->irq; + + __raw_writeq(0, sc->sbm_imr); + + sbmac_intr(irq, netdev, NULL); + +#ifdef CONFIG_SBMAC_COALESCE + __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | + ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), + sc->sbm_imr); +#else + __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | + (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr); +#endif +} +#endif /********************************************************************** - * SBDMA_RX_PROCESS(sc,d) + * SBDMA_RX_PROCESS(sc,d,work_to_do,poll) * * Process "completed" receive buffers on the specified DMA channel. - * Note that this isn't really ideal for priority channels, since - * it processes all of the packets on a given channel before - * returning. * * Input parameters: - * sc - softc structure - * d - DMA channel context + * sc - softc structure + * d - DMA channel context + * work_to_do - no. of packets to process before enabling interrupt + * again (for NAPI) + * poll - 1: using polling (for NAPI) * * Return value: * nothing ********************************************************************* */ -static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) +static int sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d, + int work_to_do, int poll) { int curidx; int hwidx; sbdmadscr_t *dsc; struct sk_buff *sb; int len; + int work_done = 0; + int dropped = 0; - for (;;) { + prefetch(d); + +again: + /* Check if the HW dropped any frames */ + sc->sbm_stats.rx_fifo_errors + += __raw_readq(sc->sbm_rxdma.sbdma_oodpktlost) & 0xffff; + __raw_writeq(0, sc->sbm_rxdma.sbdma_oodpktlost); + + while (work_to_do-- > 0) { /* * figure out where we are (as an index) and where * the hardware is (also as an index) @@ -1165,7 +1219,12 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) * (sbdma_remptr) and the physical address (sbdma_curdscr CSR) */ - curidx = d->sbdma_remptr - d->sbdma_dscrtable; + dsc = d->sbdma_remptr; + curidx = dsc - d->sbdma_dscrtable; + + prefetch(dsc); + prefetch(&d->sbdma_ctxtable[curidx]); + hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); @@ -1176,13 +1235,12 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) */ if (curidx == hwidx) - break; + goto done; /* * Otherwise, get the packet's sk_buff ptr back */ - dsc = &(d->sbdma_dscrtable[curidx]); sb = d->sbdma_ctxtable[curidx]; d->sbdma_ctxtable[curidx] = NULL; @@ -1194,7 +1252,7 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) * receive ring. */ - if (!(dsc->dscr_a & M_DMA_ETHRX_BAD)) { + if (likely (!(dsc->dscr_a & M_DMA_ETHRX_BAD))) { /* * Add a new buffer to replace the old one. If we fail @@ -1202,9 +1260,14 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) * packet and put it right back on the receive ring. */ - if (sbdma_add_rcvbuffer(d,NULL) == -ENOBUFS) { - sc->sbm_stats.rx_dropped++; + if (unlikely (sbdma_add_rcvbuffer(d,NULL) == + -ENOBUFS)) { + sc->sbm_stats.rx_dropped++; sbdma_add_rcvbuffer(d,sb); /* re-add old buffer */ + /* No point in continuing at the moment */ + printk(KERN_ERR "dropped packet (1)\n"); + d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + goto done; } else { /* * Set length into the packet @@ -1216,8 +1279,6 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) * receive ring. Pass the buffer to * the kernel */ - sc->sbm_stats.rx_bytes += len; - sc->sbm_stats.rx_packets++; sb->protocol = eth_type_trans(sb,d->sbdma_eth->sbm_dev); /* Check hw IPv4/TCP checksum if supported */ if (sc->rx_hw_checksum == ENABLE) { @@ -1229,8 +1290,22 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) sb->ip_summed = CHECKSUM_NONE; } } - - netif_rx(sb); + prefetch(sb->data); + prefetch((const void *)(((char *)sb->data)+32)); + if (poll) + dropped = netif_receive_skb(sb); + else + dropped = netif_rx(sb); + + if (dropped == NET_RX_DROP) { + sc->sbm_stats.rx_dropped++; + d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + goto done; + } + else { + sc->sbm_stats.rx_bytes += len; + sc->sbm_stats.rx_packets++; + } } } else { /* @@ -1247,12 +1322,16 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) */ d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); - + work_done++; + } + if (!poll) { + work_to_do = 32; + goto again; /* collect fifo drop statistics again */ } +done: + return work_done; } - - /********************************************************************** * SBDMA_TX_PROCESS(sc,d) * @@ -1264,22 +1343,30 @@ static void sbdma_rx_process(struct sbmac_softc *sc,sbmacdma_t *d) * * Input parameters: * sc - softc structure - * d - DMA channel context + * d - DMA channel context + * poll - 1: using polling (for NAPI) * * Return value: * nothing ********************************************************************* */ -static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d) +static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d, int poll) { int curidx; int hwidx; sbdmadscr_t *dsc; struct sk_buff *sb; unsigned long flags; + int packets_handled = 0; spin_lock_irqsave(&(sc->sbm_lock), flags); + if (d->sbdma_remptr == d->sbdma_addptr) + goto end_unlock; + + hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - + d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); + for (;;) { /* * figure out where we are (as an index) and where @@ -1293,8 +1380,6 @@ static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d) */ curidx = d->sbdma_remptr - d->sbdma_dscrtable; - hwidx = (int) (((__raw_readq(d->sbdma_curdscr) & M_DMA_CURDSCR_ADDR) - - d->sbdma_dscrtable_phys) / sizeof(sbdmadscr_t)); /* * If they're the same, that means we've processed all @@ -1332,6 +1417,8 @@ static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d) d->sbdma_remptr = SBDMA_NEXTBUF(d,sbdma_remptr); + packets_handled++; + } /* @@ -1340,8 +1427,10 @@ static void sbdma_tx_process(struct sbmac_softc *sc,sbmacdma_t *d) * watermark on the transmit queue. */ - netif_wake_queue(d->sbdma_eth->sbm_dev); + if (packets_handled) + netif_wake_queue(d->sbdma_eth->sbm_dev); +end_unlock: spin_unlock_irqrestore(&(sc->sbm_lock), flags); } @@ -1415,9 +1504,9 @@ static int sbmac_initctx(struct sbmac_softc *s) static void sbdma_uninitctx(struct sbmacdma_s *d) { - if (d->sbdma_dscrtable) { - kfree(d->sbdma_dscrtable); - d->sbdma_dscrtable = NULL; + if (d->sbdma_dscrtable_unaligned) { + kfree(d->sbdma_dscrtable_unaligned); + d->sbdma_dscrtable_unaligned = d->sbdma_dscrtable = NULL; } if (d->sbdma_ctxtable) { @@ -1615,15 +1704,9 @@ static void sbmac_channel_start(struct sbmac_softc *s) #endif #ifdef CONFIG_SBMAC_COALESCE - /* - * Accept any TX interrupt and EOP count/timer RX interrupts on ch 0 - */ __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), s->sbm_imr); #else - /* - * Accept any kind of interrupt on TX and RX DMA channel 0 - */ __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), s->sbm_imr); #endif @@ -2056,57 +2139,46 @@ static irqreturn_t sbmac_intr(int irq,void *dev_instance) uint64_t isr; int handled = 0; - for (;;) { - - /* - * Read the ISR (this clears the bits in the real - * register, except for counter addr) - */ + /* + * Read the ISR (this clears the bits in the real + * register, except for counter addr) + */ - isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR; + isr = __raw_readq(sc->sbm_isr) & ~M_MAC_COUNTER_ADDR; - if (isr == 0) - break; + if (isr == 0) + return IRQ_RETVAL(0); + handled = 1; - handled = 1; - - /* - * Transmits on channel 0 - */ + /* + * Transmits on channel 0 + */ - if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) { - sbdma_tx_process(sc,&(sc->sbm_txdma)); + if (isr & (M_MAC_INT_CHANNEL << S_MAC_TX_CH0)) { + sbdma_tx_process(sc,&(sc->sbm_txdma), 0); +#ifdef CONFIG_NETPOLL_TRAP + if (netpoll_trap()) { + if (test_and_clear_bit(__LINK_STATE_XOFF, &dev->state)) + __netif_schedule(dev); } +#endif + } - /* - * Receives on channel 0 - */ - - /* - * It's important to test all the bits (or at least the - * EOP_SEEN bit) when deciding to do the RX process - * particularly when coalescing, to make sure we - * take care of the following: - * - * If you have some packets waiting (have been received - * but no interrupt) and get a TX interrupt before - * the RX timer or counter expires, reading the ISR - * above will clear the timer and counter, and you - * won't get another interrupt until a packet shows - * up to start the timer again. Testing - * EOP_SEEN here takes care of this case. - * (EOP_SEEN is part of M_MAC_INT_CHANNEL << S_MAC_RX_CH0) - */ - - - if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) { - sbdma_rx_process(sc,&(sc->sbm_rxdma)); + if (isr & (M_MAC_INT_CHANNEL << S_MAC_RX_CH0)) { + if (netif_rx_schedule_prep(dev)) { + __raw_writeq(0, sc->sbm_imr); + __netif_rx_schedule(dev); + /* Depend on the exit from poll to reenable intr */ + } + else { + /* may leave some packets behind */ + sbdma_rx_process(sc,&(sc->sbm_rxdma), + SBMAC_MAX_RXDESCR * 2, 0); } } return IRQ_RETVAL(handled); } - /********************************************************************** * SBMAC_START_TX(skb,dev) * @@ -2236,8 +2308,6 @@ static void sbmac_setmulti(struct sbmac_softc *sc) } } - - #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR) /********************************************************************** * SBMAC_PARSE_XDIGIT(str) @@ -2400,8 +2470,13 @@ static int sbmac_init(struct net_device *dev, int idx) dev->do_ioctl = sbmac_mii_ioctl; dev->tx_timeout = sbmac_tx_timeout; dev->watchdog_timeo = TX_TIMEOUT; + dev->poll = sbmac_poll; + dev->weight = 16; dev->change_mtu = sb1250_change_mtu; +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = sbmac_netpoll; +#endif /* This is needed for PASS2 for Rx H/W checksum feature */ sbmac_set_iphdr_offset(sc); @@ -2799,7 +2874,39 @@ static int sbmac_close(struct net_device *dev) return 0; } +static int sbmac_poll(struct net_device *dev, int *budget) +{ + int work_to_do; + int work_done; + struct sbmac_softc *sc = netdev_priv(dev); + + work_to_do = min(*budget, dev->quota); + work_done = sbdma_rx_process(sc, &(sc->sbm_rxdma), work_to_do, 1); + if (work_done > work_to_do) + printk(KERN_ERR "%s exceeded work_to_do budget=%d quota=%d work-done=%d\n", + sc->sbm_dev->name, *budget, dev->quota, work_done); + + sbdma_tx_process(sc, &(sc->sbm_txdma), 1); + + *budget -= work_done; + dev->quota -= work_done; + + if (work_done < work_to_do) { + netif_rx_complete(dev); + +#ifdef CONFIG_SBMAC_COALESCE + __raw_writeq(((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_TX_CH0) | + ((M_MAC_INT_EOP_COUNT | M_MAC_INT_EOP_TIMER) << S_MAC_RX_CH0), + sc->sbm_imr); +#else + __raw_writeq((M_MAC_INT_CHANNEL << S_MAC_TX_CH0) | + (M_MAC_INT_CHANNEL << S_MAC_RX_CH0), sc->sbm_imr); +#endif + } + + return (work_done >= work_to_do); +} #if defined(SBMAC_ETH0_HWADDR) || defined(SBMAC_ETH1_HWADDR) || defined(SBMAC_ETH2_HWADDR) || defined(SBMAC_ETH3_HWADDR) static void @@ -2886,7 +2993,7 @@ sbmac_init_module(void) /* * The R_MAC_ETHERNET_ADDR register will be set to some nonzero - * value for us by the firmware if we're going to use this MAC. + * value for us by the firmware if we are going to use this MAC. * If we find a zero, skip this MAC. */ diff --git a/drivers/net/sc92031.c b/drivers/net/sc92031.c index 4a926f2..5b7284c 100644 --- a/drivers/net/sc92031.c +++ b/drivers/net/sc92031.c @@ -814,7 +814,6 @@ static void _sc92031_rx_tasklet(struct net_device *dev) memcpy(skb_put(skb, pkt_size), rx_ring + rx_ring_offset, pkt_size); } - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); dev->last_rx = jiffies; netif_rx(skb); @@ -964,7 +963,7 @@ static int sc92031_start_xmit(struct sk_buff *skb, struct net_device *dev) goto out; } - spin_lock_bh(&priv->lock); + spin_lock(&priv->lock); if (unlikely(!netif_carrier_ok(dev))) { err = -ENOLINK; @@ -1005,7 +1004,7 @@ static int sc92031_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); out_unlock: - spin_unlock_bh(&priv->lock); + spin_unlock(&priv->lock); out: dev_kfree_skb(skb); @@ -1042,12 +1041,12 @@ static int sc92031_open(struct net_device *dev) priv->pm_config = 0; /* Interrupts already disabled by sc92031_stop or sc92031_probe */ - spin_lock(&priv->lock); + spin_lock_bh(&priv->lock); _sc92031_reset(dev); mmiowb(); - spin_unlock(&priv->lock); + spin_unlock_bh(&priv->lock); sc92031_enable_interrupts(dev); if (netif_carrier_ok(dev)) @@ -1077,13 +1076,13 @@ static int sc92031_stop(struct net_device *dev) /* Disable interrupts, stop Tx and Rx. */ sc92031_disable_interrupts(dev); - spin_lock(&priv->lock); + spin_lock_bh(&priv->lock); _sc92031_disable_tx_rx(dev); _sc92031_tx_clear(dev); mmiowb(); - spin_unlock(&priv->lock); + spin_unlock_bh(&priv->lock); free_irq(pdev->irq, dev); pci_free_consistent(pdev, TX_BUF_TOT_LEN, priv->tx_bufs, @@ -1539,13 +1538,13 @@ static int sc92031_suspend(struct pci_dev *pdev, pm_message_t state) /* Disable interrupts, stop Tx and Rx. */ sc92031_disable_interrupts(dev); - spin_lock(&priv->lock); + spin_lock_bh(&priv->lock); _sc92031_disable_tx_rx(dev); _sc92031_tx_clear(dev); mmiowb(); - spin_unlock(&priv->lock); + spin_unlock_bh(&priv->lock); out: pci_set_power_state(pdev, pci_choose_state(pdev, state)); @@ -1565,12 +1564,12 @@ static int sc92031_resume(struct pci_dev *pdev) goto out; /* Interrupts already disabled by sc92031_suspend */ - spin_lock(&priv->lock); + spin_lock_bh(&priv->lock); _sc92031_reset(dev); mmiowb(); - spin_unlock(&priv->lock); + spin_unlock_bh(&priv->lock); sc92031_enable_interrupts(dev); netif_device_attach(dev); diff --git a/drivers/net/seeq8005.c b/drivers/net/seeq8005.c index 0d6c95c..4bce7c4 100644 --- a/drivers/net/seeq8005.c +++ b/drivers/net/seeq8005.c @@ -550,7 +550,6 @@ static void seeq8005_rx(struct net_device *dev) lp->stats.rx_dropped++; break; } - skb->dev = dev; skb_reserve(skb, 2); /* align data on 16 byte */ buf = skb_put(skb,pkt_len); diff --git a/drivers/net/sgiseeq.c b/drivers/net/sgiseeq.c index a833e7f..1fc7730 100644 --- a/drivers/net/sgiseeq.c +++ b/drivers/net/sgiseeq.c @@ -12,26 +12,15 @@ #include <linux/init.h> #include <linux/types.h> #include <linux/interrupt.h> -#include <linux/ioport.h> -#include <linux/socket.h> -#include <linux/in.h> -#include <linux/route.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/delay.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> -#include <linux/bitops.h> -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/system.h> -#include <asm/page.h> -#include <asm/pgtable.h> #include <asm/sgi/hpc3.h> #include <asm/sgi/ip22.h> -#include <asm/sgialib.h> #include "sgiseeq.h" @@ -329,7 +318,6 @@ static inline void sgiseeq_rx(struct net_device *dev, struct sgiseeq_private *sp skb = dev_alloc_skb(len + 2); if (skb) { - skb->dev = dev; skb_reserve(skb, 2); skb_put(skb, len); @@ -546,7 +534,7 @@ static int sgiseeq_start_xmit(struct sk_buff *skb, struct net_device *dev) * entry and the HPC got to the end of the chain before we * added this new entry and restarted it. */ - memcpy((char *)(long)td->buf_vaddr, skb->data, skblen); + skb_copy_from_linear_data(skb, (char *)(long)td->buf_vaddr, skblen); if (len != skblen) memset((char *)(long)td->buf_vaddr + skb->len, 0, len-skblen); td->tdma.cntinfo = (len & HPCDMA_BCNT) | @@ -636,7 +624,7 @@ static inline void setup_rx_ring(struct sgiseeq_rx_desc *buf, int nbufs) #define ALIGNED(x) ((((unsigned long)(x)) + 0xf) & ~(0xf)) -static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq) +static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq, int has_eeprom) { struct sgiseeq_init_block *sr; struct sgiseeq_private *sp; @@ -662,7 +650,9 @@ static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq) #define EADDR_NVOFS 250 for (i = 0; i < 3; i++) { - unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i); + unsigned short tmp = has_eeprom ? + ip22_eeprom_read(&hpcregs->eeprom, EADDR_NVOFS / 2+i) : + ip22_nvram_read(EADDR_NVOFS / 2+i); dev->dev_addr[2 * i] = tmp >> 8; dev->dev_addr[2 * i + 1] = tmp & 0xff; @@ -695,6 +685,11 @@ static int sgiseeq_init(struct hpc3_regs* hpcregs, int irq) sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; + /* Setup PIO and DMA transfer timing */ + sp->hregs->pconfig = 0x161; + sp->hregs->dconfig = HPC3_EDCFG_FIRQ | HPC3_EDCFG_FEOP | + HPC3_EDCFG_FRXDC | HPC3_EDCFG_PTO | 0x026; + /* Reset the chip. */ hpc3_eth_reset(sp->hregs); @@ -741,8 +736,23 @@ err_out: static int __init sgiseeq_probe(void) { + unsigned int tmp, ret1, ret2 = 0; + /* On board adapter on 1st HPC is always present */ - return sgiseeq_init(hpc3c0, SGI_ENET_IRQ); + ret1 = sgiseeq_init(hpc3c0, SGI_ENET_IRQ, 0); + /* Let's see if second HPC is there */ + if (!(ip22_is_fullhouse()) && + get_dbe(tmp, (unsigned int *)&hpc3c1->pbdma[1]) == 0) { + sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | + SGIMC_GIOPAR_EXP164 | + SGIMC_GIOPAR_HPC264; + hpc3c1->pbus_piocfg[0][0] = 0x3ffff; + /* interrupt/config register on Challenge S Mezz board */ + hpc3c1->pbus_extregs[0][0] = 0x30; + ret2 = sgiseeq_init(hpc3c1, SGI_GIO_0_IRQ, 1); + } + + return (ret1 & ret2) ? ret1 : 0; } static void __exit sgiseeq_exit(void) diff --git a/drivers/net/sis190.c b/drivers/net/sis190.c index 45d91b1..bc8de48 100644 --- a/drivers/net/sis190.c +++ b/drivers/net/sis190.c @@ -324,6 +324,7 @@ static struct mii_chip_info { u32 feature; } mii_chip_table[] = { { "Broadcom PHY BCM5461", { 0x0020, 0x60c0 }, LAN, F_PHY_BCM5461 }, + { "Broadcom PHY AC131", { 0x0143, 0xbc70 }, LAN, 0 }, { "Agere PHY ET1101B", { 0x0282, 0xf010 }, LAN, 0 }, { "Marvell PHY 88E1111", { 0x0141, 0x0cc0 }, LAN, F_PHY_88E1111 }, { "Realtek PHY RTL8201", { 0x0000, 0x8200 }, LAN, 0 }, @@ -631,7 +632,6 @@ static int sis190_rx_interrupt(struct net_device *dev, pci_action(tp->pci_dev, le32_to_cpu(desc->addr), tp->rx_buf_sz, PCI_DMA_FROMDEVICE); - skb->dev = dev; skb_put(skb, pkt_size); skb->protocol = eth_type_trans(skb, dev); @@ -909,6 +909,9 @@ static void sis190_phy_task(struct work_struct *work) rtnl_lock(); + if (!netif_running(dev)) + goto out_unlock; + val = mdio_read(ioaddr, phy_id, MII_BMCR); if (val & BMCR_RESET) { // FIXME: needlessly high ? -- FR 02/07/2005 @@ -981,6 +984,7 @@ static void sis190_phy_task(struct work_struct *work) netif_carrier_on(dev); } +out_unlock: rtnl_unlock(); } @@ -1102,8 +1106,6 @@ static void sis190_down(struct net_device *dev) netif_stop_queue(dev); - flush_scheduled_work(); - do { spin_lock_irq(&tp->lock); @@ -1857,6 +1859,7 @@ static void __devexit sis190_remove_one(struct pci_dev *pdev) struct net_device *dev = pci_get_drvdata(pdev); sis190_mii_remove(dev); + flush_scheduled_work(); unregister_netdev(dev); sis190_release_board(pdev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/net/sis900.c b/drivers/net/sis900.c index fb2b530..2cb2e15 100644 --- a/drivers/net/sis900.c +++ b/drivers/net/sis900.c @@ -968,10 +968,10 @@ static void mdio_write(struct net_device *net_dev, int phy_id, int location, static u16 sis900_reset_phy(struct net_device *net_dev, int phy_addr) { - int i = 0; + int i; u16 status; - while (i++ < 2) + for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); mdio_write( net_dev, phy_addr, MII_CONTROL, MII_CNTL_RESET ); @@ -1160,7 +1160,6 @@ sis900_init_rx_ring(struct net_device *net_dev) buffer */ break; } - skb->dev = net_dev; sis_priv->rx_skbuff[i] = skb; sis_priv->rx_ring[i].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[i].bufptr = pci_map_single(sis_priv->pci_dev, @@ -1430,7 +1429,7 @@ static void sis900_auto_negotiate(struct net_device *net_dev, int phy_addr) int i = 0; u32 status; - while (i++ < 2) + for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (!(status & MII_STAT_LINK)){ @@ -1466,9 +1465,9 @@ static void sis900_read_mode(struct net_device *net_dev, int *speed, int *duplex int phy_addr = sis_priv->cur_phy; u32 status; u16 autoadv, autorec; - int i = 0; + int i; - while (i++ < 2) + for (i = 0; i < 2; i++) status = mdio_read(net_dev, phy_addr, MII_STATUS); if (!(status & MII_STAT_LINK)) @@ -1754,6 +1753,25 @@ static int sis900_rx(struct net_device *net_dev) sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; } else { struct sk_buff * skb; + struct sk_buff * rx_skb; + + pci_unmap_single(sis_priv->pci_dev, + sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + + /* refill the Rx buffer, what if there is not enought + * memory for new socket buffer ?? */ + if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { + /* + * Not enough memory to refill the buffer + * so we need to recycle the old one so + * as to avoid creating a memory hole + * in the rx ring + */ + skb = sis_priv->rx_skbuff[entry]; + sis_priv->stats.rx_dropped++; + goto refill_rx_ring; + } /* This situation should never happen, but due to some unknow bugs, it is possible that @@ -1768,14 +1786,11 @@ static int sis900_rx(struct net_device *net_dev) break; } - pci_unmap_single(sis_priv->pci_dev, - sis_priv->rx_ring[entry].bufptr, RX_BUF_SIZE, - PCI_DMA_FROMDEVICE); /* give the socket buffer to upper layers */ - skb = sis_priv->rx_skbuff[entry]; - skb_put(skb, rx_size); - skb->protocol = eth_type_trans(skb, net_dev); - netif_rx(skb); + rx_skb = sis_priv->rx_skbuff[entry]; + skb_put(rx_skb, rx_size); + rx_skb->protocol = eth_type_trans(rx_skb, net_dev); + netif_rx(rx_skb); /* some network statistics */ if ((rx_status & BCAST) == MCAST) @@ -1783,33 +1798,13 @@ static int sis900_rx(struct net_device *net_dev) net_dev->last_rx = jiffies; sis_priv->stats.rx_bytes += rx_size; sis_priv->stats.rx_packets++; - - /* refill the Rx buffer, what if there is not enought - * memory for new socket buffer ?? */ - if ((skb = dev_alloc_skb(RX_BUF_SIZE)) == NULL) { - /* not enough memory for skbuff, this makes a - * "hole" on the buffer ring, it is not clear - * how the hardware will react to this kind - * of degenerated buffer */ - if (netif_msg_rx_status(sis_priv)) - printk(KERN_INFO "%s: Memory squeeze," - "deferring packet.\n", - net_dev->name); - sis_priv->rx_skbuff[entry] = NULL; - /* reset buffer descriptor state */ - sis_priv->rx_ring[entry].cmdsts = 0; - sis_priv->rx_ring[entry].bufptr = 0; - sis_priv->stats.rx_dropped++; - sis_priv->cur_rx++; - break; - } - skb->dev = net_dev; + sis_priv->dirty_rx++; +refill_rx_ring: sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = pci_map_single(sis_priv->pci_dev, skb->data, RX_BUF_SIZE, PCI_DMA_FROMDEVICE); - sis_priv->dirty_rx++; } sis_priv->cur_rx++; entry = sis_priv->cur_rx % NUM_RX_DESC; @@ -1836,7 +1831,6 @@ static int sis900_rx(struct net_device *net_dev) sis_priv->stats.rx_dropped++; break; } - skb->dev = net_dev; sis_priv->rx_skbuff[entry] = skb; sis_priv->rx_ring[entry].cmdsts = RX_BUF_SIZE; sis_priv->rx_ring[entry].bufptr = diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index 92d11b9..bf21862 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -1562,10 +1562,10 @@ struct sk_buff *pMessage) /* pointer to send-message */ pTxd->pMBuf = pMessage; if (pMessage->ip_summed == CHECKSUM_PARTIAL) { - u16 hdrlen = pMessage->h.raw - pMessage->data; + u16 hdrlen = skb_transport_offset(pMessage); u16 offset = hdrlen + pMessage->csum_offset; - if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) && + if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) && (pAC->GIni.GIChipRev == 0) && (pAC->GIni.GIChipId == CHIP_ID_YUKON)) { pTxd->TBControl = BMU_TCP_CHECK; @@ -1681,7 +1681,7 @@ struct sk_buff *pMessage) /* pointer to send-message */ ** Does the HW need to evaluate checksum for TCP or UDP packets? */ if (pMessage->ip_summed == CHECKSUM_PARTIAL) { - u16 hdrlen = pMessage->h.raw - pMessage->data; + u16 hdrlen = skb_transport_offset(pMessage); u16 offset = hdrlen + pMessage->csum_offset; Control = BMU_STFWD; @@ -1691,7 +1691,7 @@ struct sk_buff *pMessage) /* pointer to send-message */ ** opcode for udp is not working in the hardware yet ** (Revision 2.0) */ - if ((pMessage->h.ipiph->protocol == IPPROTO_UDP ) && + if ((ipip_hdr(pMessage)->protocol == IPPROTO_UDP) && (pAC->GIni.GIChipRev == 0) && (pAC->GIni.GIChipId == CHIP_ID_YUKON)) { Control |= BMU_TCP_CHECK; @@ -2127,7 +2127,7 @@ rx_start: (dma_addr_t) PhysAddr, FrameLength, PCI_DMA_FROMDEVICE); - memcpy(pNewMsg->data, pMsg, FrameLength); + skb_copy_to_linear_data(pNewMsg, pMsg, FrameLength); pci_dma_sync_single_for_device(pAC->PciDev, (dma_addr_t) PhysAddr, @@ -2193,7 +2193,6 @@ rx_start: SK_PNMI_CNT_RX_OCTETS_DELIVERED(pAC, FrameLength, pRxPort->PortIndex); - pMsg->dev = pAC->dev[pRxPort->PortIndex]; pMsg->protocol = eth_type_trans(pMsg, pAC->dev[pRxPort->PortIndex]); netif_rx(pMsg); @@ -2246,7 +2245,6 @@ rx_start: (IFF_PROMISC | IFF_ALLMULTI)) != 0 || (ForRlmt & SK_RLMT_RX_PROTOCOL) == SK_RLMT_RX_PROTOCOL) { - pMsg->dev = pAC->dev[pRxPort->PortIndex]; pMsg->protocol = eth_type_trans(pMsg, pAC->dev[pRxPort->PortIndex]); netif_rx(pMsg); @@ -5125,7 +5123,12 @@ static int skge_resume(struct pci_dev *pdev) pci_set_power_state(pdev, PCI_D0); pci_restore_state(pdev); - pci_enable_device(pdev); + ret = pci_enable_device(pdev); + if (ret) { + printk(KERN_WARNING "sk98lin: unable to enable device %s " + "in resume\n", dev->name); + goto err_out; + } pci_set_master(pdev); if (pAC->GIni.GIMacsFound == 2) ret = request_irq(dev->irq, SkGeIsr, IRQF_SHARED, "sk98lin", dev); @@ -5133,10 +5136,8 @@ static int skge_resume(struct pci_dev *pdev) ret = request_irq(dev->irq, SkGeIsrOnePort, IRQF_SHARED, "sk98lin", dev); if (ret) { printk(KERN_WARNING "sk98lin: unable to acquire IRQ %d\n", dev->irq); - pAC->AllocFlag &= ~SK_ALLOC_IRQ; - dev->irq = 0; - pci_disable_device(pdev); - return -EBUSY; + ret = -EBUSY; + goto err_out_disable_pdev; } netif_device_attach(dev); @@ -5153,6 +5154,13 @@ static int skge_resume(struct pci_dev *pdev) } return 0; + +err_out_disable_pdev: + pci_disable_device(pdev); +err_out: + pAC->AllocFlag &= ~SK_ALLOC_IRQ; + dev->irq = 0; + return ret; } #else #define skge_suspend NULL @@ -5188,6 +5196,9 @@ static struct pci_driver skge_driver = { static int __init skge_init(void) { + printk(KERN_NOTICE "sk98lin: driver has been replaced by the skge driver" + " and is scheduled for removal\n"); + return pci_register_driver(&skge_driver); } diff --git a/drivers/net/skfp/cfm.c b/drivers/net/skfp/cfm.c index 4c8aaa7..5310d39 100644 --- a/drivers/net/skfp/cfm.c +++ b/drivers/net/skfp/cfm.c @@ -73,7 +73,7 @@ static const char * const cfm_events[] = { /* * map from state to downstream port type */ -static const u_char cf_to_ptype[] = { +static const unsigned char cf_to_ptype[] = { TNONE,TNONE,TNONE,TNONE,TNONE, TNONE,TB,TB,TS, TA,TB,TS,TB diff --git a/drivers/net/skfp/h/lnkstat.h b/drivers/net/skfp/h/lnkstat.h deleted file mode 100644 index c73dcd9..0000000 --- a/drivers/net/skfp/h/lnkstat.h +++ /dev/null @@ -1,84 +0,0 @@ -/****************************************************************************** - * - * (C)Copyright 1998,1999 SysKonnect, - * a business unit of Schneider & Koch & Co. Datensysteme GmbH. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The information in this file is provided "AS IS" without warranty. - * - ******************************************************************************/ - -/* - * Definition of the Error Log Structure - * This structure will be copied into the Error Log buffer - * during the NDIS General Request ReadErrorLog by the MAC Driver - */ - -struct s_error_log { - - /* - * place holder for token ring adapter error log (zeros) - */ - u_char reserved_0 ; /* byte 0 inside Error Log */ - u_char reserved_1 ; /* byte 1 */ - u_char reserved_2 ; /* byte 2 */ - u_char reserved_3 ; /* byte 3 */ - u_char reserved_4 ; /* byte 4 */ - u_char reserved_5 ; /* byte 5 */ - u_char reserved_6 ; /* byte 6 */ - u_char reserved_7 ; /* byte 7 */ - u_char reserved_8 ; /* byte 8 */ - u_char reserved_9 ; /* byte 9 */ - u_char reserved_10 ; /* byte 10 */ - u_char reserved_11 ; /* byte 11 */ - u_char reserved_12 ; /* byte 12 */ - u_char reserved_13 ; /* byte 13 */ - - /* - * FDDI link statistics - */ -/* - * smt error low - */ -#define SMT_ERL_AEB (1<<15) /* A elast. buffer */ -#define SMT_ERL_BLC (1<<14) /* B link error condition */ -#define SMT_ERL_ALC (1<<13) /* A link error condition */ -#define SMT_ERL_NCC (1<<12) /* not copied condition */ -#define SMT_ERL_FEC (1<<11) /* frame error condition */ - -/* - * smt event low - */ -#define SMT_EVL_NCE (1<<5) - - u_short smt_error_low ; /* byte 14/15 */ - u_short smt_error_high ; /* byte 16/17 */ - u_short smt_event_low ; /* byte 18/19 */ - u_short smt_event_high ; /* byte 20/21 */ - u_short connection_policy_violation ; /* byte 22/23 */ - u_short port_event ; /* byte 24/25 */ - u_short set_count_low ; /* byte 26/27 */ - u_short set_count_high ; /* byte 28/29 */ - u_short aci_id_code ; /* byte 30/31 */ - u_short purge_frame_counter ; /* byte 32/33 */ - - /* - * CMT and RMT state machines - */ - u_short ecm_state ; /* byte 34/35 */ - u_short pcm_a_state ; /* byte 36/37 */ - u_short pcm_b_state ; /* byte 38/39 */ - u_short cfm_state ; /* byte 40/41 */ - u_short rmt_state ; /* byte 42/43 */ - - u_short not_used[30] ; /* byte 44-103 */ - - u_short ucode_version_level ; /* byte 104/105 */ - - u_short not_used_1 ; /* byte 106/107 */ - u_short not_used_2 ; /* byte 108/109 */ -} ; diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c index 9733a11..a7ef6c8 100644 --- a/drivers/net/skfp/skfddi.c +++ b/drivers/net/skfp/skfddi.c @@ -1680,7 +1680,6 @@ void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, rxd->rxd_os.skb = NULL; skb_trim(skb, len); skb->protocol = fddi_type_trans(skb, bp->dev); - skb->dev = bp->dev; /* pass up device pointer */ netif_rx(skb); bp->dev->last_rx = jiffies; @@ -1938,7 +1937,7 @@ int mac_drv_rx_init(struct s_smc *smc, int len, int fc, } skb_reserve(skb, 3); skb_put(skb, len); - memcpy(skb->data, look_ahead, len); + skb_copy_to_linear_data(skb, look_ahead, len); // deliver frame to system skb->protocol = fddi_type_trans(skb, smc->os.dev); diff --git a/drivers/net/skge.c b/drivers/net/skge.c index e482e7f..21afe10 100644 --- a/drivers/net/skge.c +++ b/drivers/net/skge.c @@ -42,7 +42,7 @@ #include "skge.h" #define DRV_NAME "skge" -#define DRV_VERSION "1.10" +#define DRV_VERSION "1.11" #define PFX DRV_NAME " " #define DEFAULT_TX_RING_SIZE 128 @@ -77,13 +77,13 @@ static const struct pci_device_id skge_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_3COM, PCI_DEVICE_ID_3COM_3C940B) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_GE) }, { PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_YU) }, - { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T), }, + { PCI_DEVICE(PCI_VENDOR_ID_DLINK, PCI_DEVICE_ID_DLINK_DGE510T) }, { PCI_DEVICE(PCI_VENDOR_ID_DLINK, 0x4b01) }, /* DGE-530T */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4320) }, { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x5005) }, /* Belkin */ { PCI_DEVICE(PCI_VENDOR_ID_CNET, PCI_DEVICE_ID_CNET_GIGACARD) }, { PCI_DEVICE(PCI_VENDOR_ID_LINKSYS, PCI_DEVICE_ID_LINKSYS_EG1064) }, - { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015, }, + { PCI_VENDOR_ID_LINKSYS, 0x1032, PCI_ANY_ID, 0x0015 }, { 0 } }; MODULE_DEVICE_TABLE(pci, skge_id_table); @@ -105,7 +105,8 @@ static const int txqaddr[] = { Q_XA1, Q_XA2 }; static const int rxqaddr[] = { Q_R1, Q_R2 }; static const u32 rxirqmask[] = { IS_R1_F, IS_R2_F }; static const u32 txirqmask[] = { IS_XA1_F, IS_XA2_F }; -static const u32 irqmask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F }; +static const u32 napimask[] = { IS_R1_F|IS_XA1_F, IS_R2_F|IS_XA2_F }; +static const u32 portmask[] = { IS_PORT_1, IS_PORT_2 }; static int skge_get_regs_len(struct net_device *dev) { @@ -162,27 +163,46 @@ static void skge_wol_init(struct skge_port *skge) { struct skge_hw *hw = skge->hw; int port = skge->port; - enum pause_control save_mode; - u32 ctrl; + u16 ctrl; - /* Bring hardware out of reset */ skge_write16(hw, B0_CTST, CS_RST_CLR); skge_write16(hw, SK_REG(port, GMAC_LINK_CTRL), GMLC_RST_CLR); - skge_write8(hw, SK_REG(port, GPHY_CTRL), GPC_RST_CLR); - skge_write8(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); + /* Turn on Vaux */ + skge_write8(hw, B0_POWER_CTRL, + PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF); - /* Force to 10/100 skge_reset will re-enable on resume */ - save_mode = skge->flow_control; - skge->flow_control = FLOW_MODE_SYMMETRIC; + /* WA code for COMA mode -- clear PHY reset */ + if (hw->chip_id == CHIP_ID_YUKON_LITE && + hw->chip_rev >= CHIP_REV_YU_LITE_A3) { + u32 reg = skge_read32(hw, B2_GP_IO); + reg |= GP_DIR_9; + reg &= ~GP_IO_9; + skge_write32(hw, B2_GP_IO, reg); + } - ctrl = skge->advertising; - skge->advertising &= ~(ADVERTISED_1000baseT_Half|ADVERTISED_1000baseT_Full); + skge_write32(hw, SK_REG(port, GPHY_CTRL), + GPC_DIS_SLEEP | + GPC_HWCFG_M_3 | GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0 | + GPC_ANEG_1 | GPC_RST_SET); - skge_phy_reset(skge); + skge_write32(hw, SK_REG(port, GPHY_CTRL), + GPC_DIS_SLEEP | + GPC_HWCFG_M_3 | GPC_HWCFG_M_2 | GPC_HWCFG_M_1 | GPC_HWCFG_M_0 | + GPC_ANEG_1 | GPC_RST_CLR); + + skge_write32(hw, SK_REG(port, GMAC_CTRL), GMC_RST_CLR); + + /* Force to 10/100 skge_reset will re-enable on resume */ + gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, + PHY_AN_100FULL | PHY_AN_100HALF | + PHY_AN_10FULL | PHY_AN_10HALF| PHY_AN_CSMA); + /* no 1000 HD/FD */ + gm_phy_write(hw, port, PHY_MARV_1000T_CTRL, 0); + gm_phy_write(hw, port, PHY_MARV_CTRL, + PHY_CT_RESET | PHY_CT_SPS_LSB | PHY_CT_ANE | + PHY_CT_RE_CFG | PHY_CT_DUP_MD); - skge->flow_control = save_mode; - skge->advertising = ctrl; /* Set GMAC to no flow control and auto update for speed/duplex */ gma_write16(hw, port, GM_GP_CTRL, @@ -226,12 +246,10 @@ static int skge_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; - if (wol->wolopts & wol_supported(hw)) + if (wol->wolopts & ~wol_supported(hw)) return -EOPNOTSUPP; skge->wol = wol->wolopts; - if (!netif_running(dev)) - skge_wol_init(skge); return 0; } @@ -671,7 +689,7 @@ static void skge_led(struct skge_port *skge, enum led_mode mode) struct skge_hw *hw = skge->hw; int port = skge->port; - mutex_lock(&hw->phy_mutex); + spin_lock_bh(&hw->phy_lock); if (hw->chip_id == CHIP_ID_GENESIS) { switch (mode) { case LED_MODE_OFF: @@ -742,7 +760,7 @@ static void skge_led(struct skge_port *skge, enum led_mode mode) PHY_M_LED_MO_RX(MO_LED_ON)); } } - mutex_unlock(&hw->phy_mutex); + spin_unlock_bh(&hw->phy_lock); } /* blink LED's for finding board */ @@ -1316,7 +1334,7 @@ static void xm_phy_init(struct skge_port *skge) xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl); /* Poll PHY for status changes */ - schedule_delayed_work(&skge->link_thread, LINK_HZ); + mod_timer(&skge->link_timer, jiffies + LINK_HZ); } static void xm_check_link(struct net_device *dev) @@ -1391,10 +1409,9 @@ static void xm_check_link(struct net_device *dev) * Since internal PHY is wired to a level triggered pin, can't * get an interrupt when carrier is detected. */ -static void xm_link_timer(struct work_struct *work) +static void xm_link_timer(unsigned long arg) { - struct skge_port *skge = - container_of(work, struct skge_port, link_thread.work); + struct skge_port *skge = (struct skge_port *) arg; struct net_device *dev = skge->netdev; struct skge_hw *hw = skge->hw; int port = skge->port; @@ -1414,12 +1431,13 @@ static void xm_link_timer(struct work_struct *work) goto nochange; } - mutex_lock(&hw->phy_mutex); + spin_lock(&hw->phy_lock); xm_check_link(dev); - mutex_unlock(&hw->phy_mutex); + spin_unlock(&hw->phy_lock); nochange: - schedule_delayed_work(&skge->link_thread, LINK_HZ); + if (netif_running(dev)) + mod_timer(&skge->link_timer, jiffies + LINK_HZ); } static void genesis_mac_init(struct skge_hw *hw, int port) @@ -2322,7 +2340,7 @@ static void skge_phy_reset(struct skge_port *skge) netif_stop_queue(skge->netdev); netif_carrier_off(skge->netdev); - mutex_lock(&hw->phy_mutex); + spin_lock_bh(&hw->phy_lock); if (hw->chip_id == CHIP_ID_GENESIS) { genesis_reset(hw, port); genesis_mac_init(hw, port); @@ -2330,7 +2348,7 @@ static void skge_phy_reset(struct skge_port *skge) yukon_reset(hw, port); yukon_init(hw, port); } - mutex_unlock(&hw->phy_mutex); + spin_unlock_bh(&hw->phy_lock); dev->set_multicast_list(dev); } @@ -2353,12 +2371,12 @@ static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) /* fallthru */ case SIOCGMIIREG: { u16 val = 0; - mutex_lock(&hw->phy_mutex); + spin_lock_bh(&hw->phy_lock); if (hw->chip_id == CHIP_ID_GENESIS) err = __xm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val); else err = __gm_phy_read(hw, skge->port, data->reg_num & 0x1f, &val); - mutex_unlock(&hw->phy_mutex); + spin_unlock_bh(&hw->phy_lock); data->val_out = val; break; } @@ -2367,14 +2385,14 @@ static int skge_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) if (!capable(CAP_NET_ADMIN)) return -EPERM; - mutex_lock(&hw->phy_mutex); + spin_lock_bh(&hw->phy_lock); if (hw->chip_id == CHIP_ID_GENESIS) err = xm_phy_write(hw, skge->port, data->reg_num & 0x1f, data->val_in); else err = gm_phy_write(hw, skge->port, data->reg_num & 0x1f, data->val_in); - mutex_unlock(&hw->phy_mutex); + spin_unlock_bh(&hw->phy_lock); break; } return err; @@ -2480,12 +2498,12 @@ static int skge_up(struct net_device *dev) goto free_rx_ring; /* Initialize MAC */ - mutex_lock(&hw->phy_mutex); + spin_lock_bh(&hw->phy_lock); if (hw->chip_id == CHIP_ID_GENESIS) genesis_mac_init(hw, port); else yukon_mac_init(hw, port); - mutex_unlock(&hw->phy_mutex); + spin_unlock_bh(&hw->phy_lock); /* Configure RAMbuffers */ chunk = hw->ram_size / ((hw->ports + 1)*2); @@ -2503,6 +2521,11 @@ static int skge_up(struct net_device *dev) skge_write8(hw, Q_ADDR(rxqaddr[port], Q_CSR), CSR_START | CSR_IRQ_CL_F); skge_led(skge, LED_MODE_ON); + spin_lock_irq(&hw->hw_lock); + hw->intr_mask |= portmask[port]; + skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); + netif_poll_enable(dev); return 0; @@ -2529,8 +2552,17 @@ static int skge_down(struct net_device *dev) printk(KERN_INFO PFX "%s: disabling interface\n", dev->name); netif_stop_queue(dev); + if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC) - cancel_rearming_delayed_work(&skge->link_thread); + del_timer_sync(&skge->link_timer); + + netif_poll_disable(dev); + netif_carrier_off(dev); + + spin_lock_irq(&hw->hw_lock); + hw->intr_mask &= ~portmask[port]; + skge_write32(hw, B0_IMSK, hw->intr_mask); + spin_unlock_irq(&hw->hw_lock); skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF); if (hw->chip_id == CHIP_ID_GENESIS) @@ -2574,8 +2606,10 @@ static int skge_down(struct net_device *dev) skge_led(skge, LED_MODE_OFF); - netif_poll_disable(dev); + netif_tx_lock_bh(dev); skge_tx_clean(dev); + netif_tx_unlock_bh(dev); + skge_rx_clean(skge); kfree(skge->rx_ring.start); @@ -2587,6 +2621,7 @@ static int skge_down(struct net_device *dev) static inline int skge_avail(const struct skge_ring *ring) { + smp_mb(); return ((ring->to_clean > ring->to_use) ? 0 : ring->count) + (ring->to_clean - ring->to_use) - 1; } @@ -2620,12 +2655,12 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev) td->dma_hi = map >> 32; if (skb->ip_summed == CHECKSUM_PARTIAL) { - int offset = skb->h.raw - skb->data; + const int offset = skb_transport_offset(skb); /* This seems backwards, but it is what the sk98lin * does. Looks like hardware is wrong? */ - if (skb->h.ipiph->protocol == IPPROTO_UDP + if (ipip_hdr(skb)->protocol == IPPROTO_UDP && hw->chip_rev == 0 && hw->chip_id == CHIP_ID_YUKON) control = BMU_TCP_CHECK; else @@ -2675,6 +2710,8 @@ static int skge_xmit_frame(struct sk_buff *skb, struct net_device *dev) dev->name, e - skge->tx_ring.start, skb->len); skge->tx_ring.to_use = e->next; + smp_wmb(); + if (skge_avail(&skge->tx_ring) <= TX_LOW_WATER) { pr_debug("%s: transmit queue full\n", dev->name); netif_stop_queue(dev); @@ -2692,8 +2729,6 @@ static void skge_tx_free(struct skge_port *skge, struct skge_element *e, { struct pci_dev *pdev = skge->hw->pdev; - BUG_ON(!e->skb); - /* skb header vs. fragment */ if (control & BMU_STF) pci_unmap_single(pdev, pci_unmap_addr(e, mapaddr), @@ -2711,7 +2746,6 @@ static void skge_tx_free(struct skge_port *skge, struct skge_element *e, dev_kfree_skb(e->skb); } - e->skb = NULL; } /* Free all buffers in transmit ring */ @@ -2720,7 +2754,6 @@ static void skge_tx_clean(struct net_device *dev) struct skge_port *skge = netdev_priv(dev); struct skge_element *e; - netif_tx_lock_bh(dev); for (e = skge->tx_ring.to_clean; e != skge->tx_ring.to_use; e = e->next) { struct skge_tx_desc *td = e->desc; skge_tx_free(skge, e, td->control); @@ -2729,7 +2762,6 @@ static void skge_tx_clean(struct net_device *dev) skge->tx_ring.to_clean = e; netif_wake_queue(dev); - netif_tx_unlock_bh(dev); } static void skge_tx_timeout(struct net_device *dev) @@ -2766,6 +2798,17 @@ static int skge_change_mtu(struct net_device *dev, int new_mtu) return err; } +static const u8 pause_mc_addr[ETH_ALEN] = { 0x1, 0x80, 0xc2, 0x0, 0x0, 0x1 }; + +static void genesis_add_filter(u8 filter[8], const u8 *addr) +{ + u32 crc, bit; + + crc = ether_crc_le(ETH_ALEN, addr); + bit = ~crc & 0x3f; + filter[bit/8] |= 1 << (bit%8); +} + static void genesis_set_multicast(struct net_device *dev) { struct skge_port *skge = netdev_priv(dev); @@ -2787,24 +2830,33 @@ static void genesis_set_multicast(struct net_device *dev) memset(filter, 0xff, sizeof(filter)); else { memset(filter, 0, sizeof(filter)); - for (i = 0; list && i < count; i++, list = list->next) { - u32 crc, bit; - crc = ether_crc_le(ETH_ALEN, list->dmi_addr); - bit = ~crc & 0x3f; - filter[bit/8] |= 1 << (bit%8); - } + + if (skge->flow_status == FLOW_STAT_REM_SEND + || skge->flow_status == FLOW_STAT_SYMMETRIC) + genesis_add_filter(filter, pause_mc_addr); + + for (i = 0; list && i < count; i++, list = list->next) + genesis_add_filter(filter, list->dmi_addr); } xm_write32(hw, port, XM_MODE, mode); xm_outhash(hw, port, XM_HSM, filter); } +static void yukon_add_filter(u8 filter[8], const u8 *addr) +{ + u32 bit = ether_crc(ETH_ALEN, addr) & 0x3f; + filter[bit/8] |= 1 << (bit%8); +} + static void yukon_set_multicast(struct net_device *dev) { struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; int port = skge->port; struct dev_mc_list *list = dev->mc_list; + int rx_pause = (skge->flow_status == FLOW_STAT_REM_SEND + || skge->flow_status == FLOW_STAT_SYMMETRIC); u16 reg; u8 filter[8]; @@ -2817,16 +2869,17 @@ static void yukon_set_multicast(struct net_device *dev) reg &= ~(GM_RXCR_UCF_ENA | GM_RXCR_MCF_ENA); else if (dev->flags & IFF_ALLMULTI) /* all multicast */ memset(filter, 0xff, sizeof(filter)); - else if (dev->mc_count == 0) /* no multicast */ + else if (dev->mc_count == 0 && !rx_pause)/* no multicast */ reg &= ~GM_RXCR_MCF_ENA; else { int i; reg |= GM_RXCR_MCF_ENA; - for (i = 0; list && i < dev->mc_count; i++, list = list->next) { - u32 bit = ether_crc(ETH_ALEN, list->dmi_addr) & 0x3f; - filter[bit/8] |= 1 << (bit%8); - } + if (rx_pause) + yukon_add_filter(filter, pause_mc_addr); + + for (i = 0; list && i < dev->mc_count; i++, list = list->next) + yukon_add_filter(filter, list->dmi_addr); } @@ -2897,7 +2950,7 @@ static struct sk_buff *skge_rx_get(struct net_device *dev, pci_dma_sync_single_for_cpu(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); - memcpy(skb->data, e->skb->data, len); + skb_copy_from_linear_data(e->skb, skb->data, len); pci_dma_sync_single_for_device(skge->hw->pdev, pci_unmap_addr(e, mapaddr), len, PCI_DMA_FROMDEVICE); @@ -2964,21 +3017,29 @@ static void skge_tx_done(struct net_device *dev) skge_write8(skge->hw, Q_ADDR(txqaddr[skge->port], Q_CSR), CSR_IRQ_CL_F); - netif_tx_lock(dev); for (e = ring->to_clean; e != ring->to_use; e = e->next) { - struct skge_tx_desc *td = e->desc; + u32 control = ((const struct skge_tx_desc *) e->desc)->control; - if (td->control & BMU_OWN) + if (control & BMU_OWN) break; - skge_tx_free(skge, e, td->control); + skge_tx_free(skge, e, control); } skge->tx_ring.to_clean = e; - if (skge_avail(&skge->tx_ring) > TX_LOW_WATER) - netif_wake_queue(dev); + /* Can run lockless until we need to synchronize to restart queue. */ + smp_mb(); + + if (unlikely(netif_queue_stopped(dev) && + skge_avail(&skge->tx_ring) > TX_LOW_WATER)) { + netif_tx_lock(dev); + if (unlikely(netif_queue_stopped(dev) && + skge_avail(&skge->tx_ring) > TX_LOW_WATER)) { + netif_wake_queue(dev); - netif_tx_unlock(dev); + } + netif_tx_unlock(dev); + } } static int skge_poll(struct net_device *dev, int *budget) @@ -3027,7 +3088,7 @@ static int skge_poll(struct net_device *dev, int *budget) spin_lock_irqsave(&hw->hw_lock, flags); __netif_rx_complete(dev); - hw->intr_mask |= irqmask[skge->port]; + hw->intr_mask |= napimask[skge->port]; skge_write32(hw, B0_IMSK, hw->intr_mask); skge_read32(hw, B0_IMSK); spin_unlock_irqrestore(&hw->hw_lock, flags); @@ -3138,28 +3199,29 @@ static void skge_error_irq(struct skge_hw *hw) } /* - * Interrupt from PHY are handled in work queue + * Interrupt from PHY are handled in tasklet (softirq) * because accessing phy registers requires spin wait which might * cause excess interrupt latency. */ -static void skge_extirq(struct work_struct *work) +static void skge_extirq(unsigned long arg) { - struct skge_hw *hw = container_of(work, struct skge_hw, phy_work); + struct skge_hw *hw = (struct skge_hw *) arg; int port; - mutex_lock(&hw->phy_mutex); for (port = 0; port < hw->ports; port++) { struct net_device *dev = hw->dev[port]; - struct skge_port *skge = netdev_priv(dev); if (netif_running(dev)) { + struct skge_port *skge = netdev_priv(dev); + + spin_lock(&hw->phy_lock); if (hw->chip_id != CHIP_ID_GENESIS) yukon_phy_intr(skge); else if (hw->phy_type == SK_PHY_BCOM) bcom_phy_intr(skge); + spin_unlock(&hw->phy_lock); } } - mutex_unlock(&hw->phy_mutex); spin_lock_irq(&hw->hw_lock); hw->intr_mask |= IS_EXT_REG; @@ -3184,7 +3246,7 @@ static irqreturn_t skge_intr(int irq, void *dev_id) status &= hw->intr_mask; if (status & IS_EXT_REG) { hw->intr_mask &= ~IS_EXT_REG; - schedule_work(&hw->phy_work); + tasklet_schedule(&hw->phy_task); } if (status & (IS_XA1_F|IS_R1_F)) { @@ -3253,24 +3315,35 @@ static int skge_set_mac_address(struct net_device *dev, void *p) struct skge_hw *hw = skge->hw; unsigned port = skge->port; const struct sockaddr *addr = p; + u16 ctrl; if (!is_valid_ether_addr(addr->sa_data)) return -EADDRNOTAVAIL; - mutex_lock(&hw->phy_mutex); memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); - memcpy_toio(hw->regs + B2_MAC_1 + port*8, - dev->dev_addr, ETH_ALEN); - memcpy_toio(hw->regs + B2_MAC_2 + port*8, - dev->dev_addr, ETH_ALEN); - if (hw->chip_id == CHIP_ID_GENESIS) - xm_outaddr(hw, port, XM_SA, dev->dev_addr); - else { - gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr); - gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr); + if (!netif_running(dev)) { + memcpy_toio(hw->regs + B2_MAC_1 + port*8, dev->dev_addr, ETH_ALEN); + memcpy_toio(hw->regs + B2_MAC_2 + port*8, dev->dev_addr, ETH_ALEN); + } else { + /* disable Rx */ + spin_lock_bh(&hw->phy_lock); + ctrl = gma_read16(hw, port, GM_GP_CTRL); + gma_write16(hw, port, GM_GP_CTRL, ctrl & ~GM_GPCR_RX_ENA); + + memcpy_toio(hw->regs + B2_MAC_1 + port*8, dev->dev_addr, ETH_ALEN); + memcpy_toio(hw->regs + B2_MAC_2 + port*8, dev->dev_addr, ETH_ALEN); + + if (hw->chip_id == CHIP_ID_GENESIS) + xm_outaddr(hw, port, XM_SA, dev->dev_addr); + else { + gma_set_addr(hw, port, GM_SRC_ADDR_1L, dev->dev_addr); + gma_set_addr(hw, port, GM_SRC_ADDR_2L, dev->dev_addr); + } + + gma_write16(hw, port, GM_GP_CTRL, ctrl); + spin_unlock_bh(&hw->phy_lock); } - mutex_unlock(&hw->phy_mutex); return 0; } @@ -3385,10 +3458,9 @@ static int skge_reset(struct skge_hw *hw) else hw->ram_size = t8 * 4096; - hw->intr_mask = IS_HW_ERR | IS_PORT_1; - if (hw->ports > 1) - hw->intr_mask |= IS_PORT_2; + hw->intr_mask = IS_HW_ERR; + /* Use PHY IRQ for all but fiber based Genesis board */ if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)) hw->intr_mask |= IS_EXT_REG; @@ -3456,14 +3528,12 @@ static int skge_reset(struct skge_hw *hw) skge_write32(hw, B0_IMSK, hw->intr_mask); - mutex_lock(&hw->phy_mutex); for (i = 0; i < hw->ports; i++) { if (hw->chip_id == CHIP_ID_GENESIS) genesis_reset(hw, i); else yukon_reset(hw, i); } - mutex_unlock(&hw->phy_mutex); return 0; } @@ -3511,6 +3581,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, skge->netdev = dev; skge->hw = hw; skge->msg_enable = netif_msg_init(debug, default_msg); + skge->tx_ring.count = DEFAULT_TX_RING_SIZE; skge->rx_ring.count = DEFAULT_RX_RING_SIZE; @@ -3527,7 +3598,7 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port, skge->port = port; /* Only used for Genesis XMAC */ - INIT_DELAYED_WORK(&skge->link_thread, xm_link_timer); + setup_timer(&skge->link_timer, xm_link_timer, (unsigned long) skge); if (hw->chip_id != CHIP_ID_GENESIS) { dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG; @@ -3609,9 +3680,9 @@ static int __devinit skge_probe(struct pci_dev *pdev, } hw->pdev = pdev; - mutex_init(&hw->phy_mutex); - INIT_WORK(&hw->phy_work, skge_extirq); spin_lock_init(&hw->hw_lock); + spin_lock_init(&hw->phy_lock); + tasklet_init(&hw->phy_task, &skge_extirq, (unsigned long) hw); hw->regs = ioremap_nocache(pci_resource_start(pdev, 0), 0x4000); if (!hw->regs) { @@ -3690,11 +3761,15 @@ static void __devexit skge_remove(struct pci_dev *pdev) if (!hw) return; + flush_scheduled_work(); + if ((dev1 = hw->dev[1])) unregister_netdev(dev1); dev0 = hw->dev[0]; unregister_netdev(dev0); + tasklet_disable(&hw->phy_task); + spin_lock_irq(&hw->hw_lock); hw->intr_mask = 0; skge_write32(hw, B0_IMSK, 0); @@ -3704,8 +3779,6 @@ static void __devexit skge_remove(struct pci_dev *pdev) skge_write16(hw, B0_LED, LED_STAT_OFF); skge_write8(hw, B0_CTST, CS_RST_SET); - flush_scheduled_work(); - free_irq(pdev->irq, hw); pci_release_regions(pdev); pci_disable_device(pdev); @@ -3719,21 +3792,6 @@ static void __devexit skge_remove(struct pci_dev *pdev) } #ifdef CONFIG_PM -static int vaux_avail(struct pci_dev *pdev) -{ - int pm_cap; - - pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM); - if (pm_cap) { - u16 ctl; - pci_read_config_word(pdev, pm_cap + PCI_PM_PMC, &ctl); - if (ctl & PCI_PM_CAP_AUX_POWER) - return 1; - } - return 0; -} - - static int skge_suspend(struct pci_dev *pdev, pm_message_t state) { struct skge_hw *hw = pci_get_drvdata(pdev); @@ -3755,10 +3813,6 @@ static int skge_suspend(struct pci_dev *pdev, pm_message_t state) wol |= skge->wol; } - if (wol && vaux_avail(pdev)) - skge_write8(hw, B0_POWER_CTRL, - PC_VAUX_ENA | PC_VCC_ENA | PC_VAUX_ON | PC_VCC_OFF); - skge_write32(hw, B0_IMSK, 0); pci_enable_wake(pdev, pci_choose_state(pdev, state), wol); pci_set_power_state(pdev, pci_choose_state(pdev, state)); @@ -3804,6 +3858,28 @@ out: } #endif +static void skge_shutdown(struct pci_dev *pdev) +{ + struct skge_hw *hw = pci_get_drvdata(pdev); + int i, wol = 0; + + for (i = 0; i < hw->ports; i++) { + struct net_device *dev = hw->dev[i]; + struct skge_port *skge = netdev_priv(dev); + + if (skge->wol) + skge_wol_init(skge); + wol |= skge->wol; + } + + pci_enable_wake(pdev, PCI_D3hot, wol); + pci_enable_wake(pdev, PCI_D3cold, wol); + + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + +} + static struct pci_driver skge_driver = { .name = DRV_NAME, .id_table = skge_id_table, @@ -3813,6 +3889,7 @@ static struct pci_driver skge_driver = { .suspend = skge_suspend, .resume = skge_resume, #endif + .shutdown = skge_shutdown, }; static int __init skge_init_module(void) diff --git a/drivers/net/skge.h b/drivers/net/skge.h index 17b1b47..edd7146 100644 --- a/drivers/net/skge.h +++ b/drivers/net/skge.h @@ -232,7 +232,6 @@ enum { IS_R2_PAR_ERR = 1<<0, /* Queue R2 Parity Error */ IS_ERR_MSK = IS_IRQ_MST_ERR | IS_IRQ_STAT - | IS_NO_STAT_M1 | IS_NO_STAT_M2 | IS_RAM_RD_PAR | IS_RAM_WR_PAR | IS_M1_PAR_ERR | IS_M2_PAR_ERR | IS_R1_PAR_ERR | IS_R2_PAR_ERR, @@ -1849,8 +1848,7 @@ enum { GMR_FS_JABBER, /* Rx GMAC FIFO Flush Mask (default) */ RX_FF_FL_DEF_MSK = GMR_FS_CRC_ERR | GMR_FS_RX_FF_OV |GMR_FS_MII_ERR | - GMR_FS_BAD_FC | GMR_FS_GOOD_FC | GMR_FS_UN_SIZE | - GMR_FS_JABBER, + GMR_FS_BAD_FC | GMR_FS_UN_SIZE | GMR_FS_JABBER, }; /* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */ @@ -2425,8 +2423,8 @@ struct skge_hw { u32 ram_size; u32 ram_offset; u16 phy_addr; - struct work_struct phy_work; - struct mutex phy_mutex; + spinlock_t phy_lock; + struct tasklet_struct phy_task; }; enum pause_control { @@ -2448,17 +2446,17 @@ enum pause_status { struct skge_port { - u32 msg_enable; struct skge_hw *hw; struct net_device *netdev; int port; + u32 msg_enable; struct skge_ring tx_ring; - struct skge_ring rx_ring; - struct net_device_stats net_stats; + struct skge_ring rx_ring ____cacheline_aligned_in_smp; + unsigned int rx_buf_size; - struct delayed_work link_thread; + struct timer_list link_timer; enum pause_control flow_control; enum pause_status flow_status; u8 rx_csum; @@ -2472,7 +2470,8 @@ struct skge_port { void *mem; /* PCI memory for rings */ dma_addr_t dma; unsigned long mem_size; - unsigned int rx_buf_size; + + struct net_device_stats net_stats; }; diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c index f2ab3d5..238c2ca 100644 --- a/drivers/net/sky2.c +++ b/drivers/net/sky2.c @@ -32,6 +32,7 @@ #include <linux/ethtool.h> #include <linux/pci.h> #include <linux/ip.h> +#include <net/ip.h> #include <linux/tcp.h> #include <linux/in.h> #include <linux/delay.h> @@ -49,7 +50,7 @@ #include "sky2.h" #define DRV_NAME "sky2" -#define DRV_VERSION "1.12" +#define DRV_VERSION "1.14" #define PFX DRV_NAME " " /* @@ -123,7 +124,10 @@ static const struct pci_device_id sky2_id_table[] = { { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4361) }, /* 88E8050 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4362) }, /* 88E8053 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4363) }, /* 88E8055 */ +#ifdef broken + /* This device causes data corruption problems that are not resolved */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4364) }, /* 88E8056 */ +#endif { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4366) }, /* 88EC036 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4367) }, /* 88EC032 */ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4368) }, /* 88EC034 */ @@ -510,9 +514,9 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) ledover &= ~PHY_M_LED_MO_RX; } - if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == CHIP_REV_YU_EC_A1) { + if (hw->chip_id == CHIP_ID_YUKON_EC_U && + hw->chip_rev == CHIP_REV_YU_EC_U_A1) { /* apply fixes in PHY AFE */ - pg = gm_phy_read(hw, port, PHY_MARV_EXT_ADR); gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 255); /* increase differential signal amplitude in 10BASE-T */ @@ -524,7 +528,7 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port) gm_phy_write(hw, port, 0x17, 0x2002); /* set page register to 0 */ - gm_phy_write(hw, port, PHY_MARV_EXT_ADR, pg); + gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0); } else if (hw->chip_id != CHIP_ID_YUKON_EX) { gm_phy_write(hw, port, PHY_MARV_LED_CTRL, ledctrl); @@ -740,12 +744,17 @@ static void sky2_mac_init(struct sky2_hw *hw, unsigned port) if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX) { sky2_write8(hw, SK_REG(port, RX_GMF_LP_THR), 768/8); sky2_write8(hw, SK_REG(port, RX_GMF_UP_THR), 1024/8); - if (hw->dev[port]->mtu > ETH_DATA_LEN) { - /* set Tx GMAC FIFO Almost Empty Threshold */ - sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR), 0x180); - /* Disable Store & Forward mode for TX */ - sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_STFW_DIS); - } + + /* set Tx GMAC FIFO Almost Empty Threshold */ + sky2_write32(hw, SK_REG(port, TX_GMF_AE_THR), + (ECU_JUMBO_WM << 16) | ECU_AE_THR); + + if (hw->dev[port]->mtu > ETH_DATA_LEN) + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_JUMBO_ENA | TX_STFW_DIS); + else + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_JUMBO_DIS | TX_STFW_ENA); } } @@ -1053,8 +1062,7 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF); sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF); - if (sky2->vlgrp) - sky2->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(sky2->vlgrp, vid, NULL); netif_tx_unlock_bh(dev); } @@ -1279,7 +1287,7 @@ static int sky2_up(struct net_device *dev) /* Set almost empty threshold */ if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == CHIP_REV_YU_EC_U_A0) - sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0); + sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), ECU_TXFF_LEV); sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map, TX_RING_SIZE - 1); @@ -1384,8 +1392,8 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) /* Check for TCP Segmentation Offload */ mss = skb_shinfo(skb)->gso_size; if (mss != 0) { - mss += ((skb->h.th->doff - 5) * 4); /* TCP options */ - mss += (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + mss += tcp_optlen(skb); /* TCP options */ + mss += ip_hdrlen(skb) + sizeof(struct tcphdr); mss += ETH_HLEN; if (mss != sky2->tx_last_mss) { @@ -1413,14 +1421,14 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev) /* Handle TCP checksum offload */ if (skb->ip_summed == CHECKSUM_PARTIAL) { - unsigned offset = skb->h.raw - skb->data; + const unsigned offset = skb_transport_offset(skb); u32 tcpsum; tcpsum = offset << 16; /* sum start */ tcpsum |= offset + skb->csum_offset; /* sum write */ ctrl = CALSUM | WR_SUM | INIT_SUM | LOCK_SUM; - if (skb->nh.iph->protocol == IPPROTO_UDP) + if (ip_hdr(skb)->protocol == IPPROTO_UDP) ctrl |= UDPTCP; if (tcpsum != sky2->tx_tcpsum) { @@ -1562,6 +1570,7 @@ static int sky2_down(struct net_device *dev) /* Stop more packets from being queued */ netif_stop_queue(dev); + netif_carrier_off(dev); /* Disable port IRQ */ imask = sky2_read32(hw, B0_IMSK); @@ -1584,13 +1593,6 @@ static int sky2_down(struct net_device *dev) sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL), RB_RST_SET | RB_DIS_OP_MD); - /* WA for dev. #4.209 */ - if (hw->chip_id == CHIP_ID_YUKON_EC_U - && (hw->chip_rev == CHIP_REV_YU_EC_U_A1 || hw->chip_rev == CHIP_REV_YU_EC_U_B0)) - sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), - sky2->speed != SPEED_1000 ? - TX_STFW_ENA : TX_STFW_DIS); - ctrl = gma_read16(hw, port, GM_GP_CTRL); ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA); gma_write16(hw, port, GM_GP_CTRL, ctrl); @@ -1742,13 +1744,6 @@ static void sky2_link_down(struct sky2_port *sky2) reg &= ~(GM_GPCR_RX_ENA | GM_GPCR_TX_ENA); gma_write16(hw, port, GM_GP_CTRL, reg); - if (sky2->flow_status == FC_RX) { - /* restore Asymmetric Pause bit */ - gm_phy_write(hw, port, PHY_MARV_AUNE_ADV, - gm_phy_read(hw, port, PHY_MARV_AUNE_ADV) - | PHY_M_AN_ASP); - } - netif_carrier_off(sky2->netdev); netif_stop_queue(sky2->netdev); @@ -1773,10 +1768,10 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) { struct sky2_hw *hw = sky2->hw; unsigned port = sky2->port; - u16 lpa; + u16 advert, lpa; + advert = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV); lpa = gm_phy_read(hw, port, PHY_MARV_AUNE_LP); - if (lpa & PHY_M_AN_RF) { printk(KERN_ERR PFX "%s: remote fault", sky2->netdev->name); return -1; @@ -1791,20 +1786,40 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux) sky2->speed = sky2_phy_speed(hw, aux); sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF; - /* Pause bits are offset (9..8) */ - if (hw->chip_id == CHIP_ID_YUKON_XL - || hw->chip_id == CHIP_ID_YUKON_EC_U - || hw->chip_id == CHIP_ID_YUKON_EX) - aux >>= 6; - - sky2->flow_status = sky2_flow(aux & PHY_M_PS_RX_P_EN, - aux & PHY_M_PS_TX_P_EN); + /* Since the pause result bits seem to in different positions on + * different chips. look at registers. + */ + if (!sky2_is_copper(hw)) { + /* Shift for bits in fiber PHY */ + advert &= ~(ADVERTISE_PAUSE_CAP|ADVERTISE_PAUSE_ASYM); + lpa &= ~(LPA_PAUSE_CAP|LPA_PAUSE_ASYM); + + if (advert & ADVERTISE_1000XPAUSE) + advert |= ADVERTISE_PAUSE_CAP; + if (advert & ADVERTISE_1000XPSE_ASYM) + advert |= ADVERTISE_PAUSE_ASYM; + if (lpa & LPA_1000XPAUSE) + lpa |= LPA_PAUSE_CAP; + if (lpa & LPA_1000XPAUSE_ASYM) + lpa |= LPA_PAUSE_ASYM; + } + + sky2->flow_status = FC_NONE; + if (advert & ADVERTISE_PAUSE_CAP) { + if (lpa & LPA_PAUSE_CAP) + sky2->flow_status = FC_BOTH; + else if (advert & ADVERTISE_PAUSE_ASYM) + sky2->flow_status = FC_RX; + } else if (advert & ADVERTISE_PAUSE_ASYM) { + if ((lpa & LPA_PAUSE_CAP) && (lpa & LPA_PAUSE_ASYM)) + sky2->flow_status = FC_TX; + } if (sky2->duplex == DUPLEX_HALF && sky2->speed < SPEED_1000 && !(hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX)) sky2->flow_status = FC_NONE; - if (aux & PHY_M_PS_RX_P_EN) + if (sky2->flow_status & FC_TX) sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_ON); else sky2_write8(hw, SK_REG(port, GMAC_CTRL), GMC_PAUSE_OFF); @@ -1853,16 +1868,13 @@ out: spin_unlock(&sky2->phy_lock); } - /* Transmit timeout is only called if we are running, carrier is up * and tx queue is full (stopped). - * Called with netif_tx_lock held. */ static void sky2_tx_timeout(struct net_device *dev) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; - u32 imask; if (netif_msg_timer(sky2)) printk(KERN_ERR PFX "%s: tx timeout\n", dev->name); @@ -1872,25 +1884,15 @@ static void sky2_tx_timeout(struct net_device *dev) sky2_read16(hw, sky2->port == 0 ? STAT_TXA1_RIDX : STAT_TXA2_RIDX), sky2_read16(hw, Q_ADDR(txqaddr[sky2->port], Q_DONE))); - imask = sky2_read32(hw, B0_IMSK); /* block IRQ in hw */ - sky2_write32(hw, B0_IMSK, 0); - sky2_read32(hw, B0_IMSK); - - netif_poll_disable(hw->dev[0]); /* stop NAPI poll */ - synchronize_irq(hw->pdev->irq); - - netif_start_queue(dev); /* don't wakeup during flush */ - sky2_tx_complete(sky2, sky2->tx_prod); /* Flush transmit queue */ - - sky2_write32(hw, B0_IMSK, imask); - - sky2_phy_reinit(sky2); /* this clears flow control etc */ + /* can't restart safely under softirq */ + schedule_work(&hw->restart_work); } static int sky2_change_mtu(struct net_device *dev, int new_mtu) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; + unsigned port = sky2->port; int err; u16 ctl, mode; u32 imask; @@ -1898,9 +1900,8 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) if (new_mtu < ETH_ZLEN || new_mtu > ETH_JUMBO_MTU) return -EINVAL; - /* TSO on Yukon Ultra and MTU > 1500 not supported */ - if (hw->chip_id == CHIP_ID_YUKON_EC_U && new_mtu > ETH_DATA_LEN) - dev->features &= ~NETIF_F_TSO; + if (new_mtu > ETH_DATA_LEN && hw->chip_id == CHIP_ID_YUKON_FE) + return -EINVAL; if (!netif_running(dev)) { dev->mtu = new_mtu; @@ -1916,8 +1917,18 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) synchronize_irq(hw->pdev->irq); - ctl = gma_read16(hw, sky2->port, GM_GP_CTRL); - gma_write16(hw, sky2->port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA); + if (hw->chip_id == CHIP_ID_YUKON_EC_U || hw->chip_id == CHIP_ID_YUKON_EX) { + if (new_mtu > ETH_DATA_LEN) { + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_JUMBO_ENA | TX_STFW_DIS); + dev->features &= NETIF_F_TSO | NETIF_F_SG | NETIF_F_IP_CSUM; + } else + sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), + TX_JUMBO_DIS | TX_STFW_ENA); + } + + ctl = gma_read16(hw, port, GM_GP_CTRL); + gma_write16(hw, port, GM_GP_CTRL, ctl & ~GM_GPCR_RX_ENA); sky2_rx_stop(sky2); sky2_rx_clean(sky2); @@ -1929,9 +1940,9 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) if (dev->mtu > ETH_DATA_LEN) mode |= GM_SMOD_JUMBO_ENA; - gma_write16(hw, sky2->port, GM_SERIAL_MODE, mode); + gma_write16(hw, port, GM_SERIAL_MODE, mode); - sky2_write8(hw, RB_ADDR(rxqaddr[sky2->port], RB_CTRL), RB_ENA_OP_MD); + sky2_write8(hw, RB_ADDR(rxqaddr[port], RB_CTRL), RB_ENA_OP_MD); err = sky2_rx_start(sky2); sky2_write32(hw, B0_IMSK, imask); @@ -1939,7 +1950,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu) if (err) dev_close(dev); else { - gma_write16(hw, sky2->port, GM_GP_CTRL, ctl); + gma_write16(hw, port, GM_GP_CTRL, ctl); netif_poll_enable(hw->dev[0]); netif_wake_queue(dev); @@ -1960,7 +1971,7 @@ static struct sk_buff *receive_copy(struct sky2_port *sky2, skb_reserve(skb, 2); pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr, length, PCI_DMA_FROMDEVICE); - memcpy(skb->data, re->skb->data, length); + skb_copy_from_linear_data(re->skb, skb->data, length); skb->ip_summed = re->skb->ip_summed; skb->csum = re->skb->csum; pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr, @@ -2057,9 +2068,6 @@ static struct sk_buff *sky2_receive(struct net_device *dev, if (!(status & GMR_FS_RX_OK)) goto resubmit; - if (length > dev->mtu + ETH_HLEN) - goto oversize; - if (length < copybreak) skb = receive_copy(sky2, re, length); else @@ -2069,14 +2077,10 @@ resubmit: return skb; -oversize: - ++sky2->net_stats.rx_over_errors; - goto resubmit; - error: ++sky2->net_stats.rx_errors; if (status & GMR_FS_RX_FF_OV) { - sky2->net_stats.rx_fifo_errors++; + sky2->net_stats.rx_over_errors++; goto resubmit; } @@ -2174,9 +2178,27 @@ force_update: /* fall through */ #endif case OP_RXCHKS: - skb = sky2->rx_ring[sky2->rx_next].skb; - skb->ip_summed = CHECKSUM_COMPLETE; - skb->csum = status & 0xffff; + if (!sky2->rx_csum) + break; + + /* Both checksum counters are programmed to start at + * the same offset, so unless there is a problem they + * should match. This failure is an early indication that + * hardware receive checksumming won't work. + */ + if (likely(status >> 16 == (status & 0xffff))) { + skb = sky2->rx_ring[sky2->rx_next].skb; + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = status & 0xffff; + } else { + printk(KERN_NOTICE PFX "%s: hardware receive " + "checksum problem (status = %#x)\n", + dev->name, status); + sky2->rx_csum = 0; + sky2_write32(sky2->hw, + Q_ADDR(rxqaddr[le->link], Q_CSR), + BMU_DIS_RX_CHKSUM); + } break; case OP_TXINDEXLE: @@ -2330,26 +2352,22 @@ static void sky2_mac_intr(struct sky2_hw *hw, unsigned port) } } -/* This should never happen it is a fatal situation */ -static void sky2_descriptor_error(struct sky2_hw *hw, unsigned port, - const char *rxtx, u32 mask) +/* This should never happen it is a bug. */ +static void sky2_le_error(struct sky2_hw *hw, unsigned port, + u16 q, unsigned ring_size) { struct net_device *dev = hw->dev[port]; struct sky2_port *sky2 = netdev_priv(dev); - u32 imask; + unsigned idx; + const u64 *le = (q == Q_R1 || q == Q_R2) + ? (u64 *) sky2->rx_le : (u64 *) sky2->tx_le; - printk(KERN_ERR PFX "%s: %s descriptor error (hardware problem)\n", - dev ? dev->name : "<not registered>", rxtx); + idx = sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_GET_IDX)); + printk(KERN_ERR PFX "%s: descriptor error q=%#x get=%u [%llx] put=%u\n", + dev->name, (unsigned) q, idx, (unsigned long long) le[idx], + (unsigned) sky2_read16(hw, Y2_QADDR(q, PREF_UNIT_PUT_IDX))); - imask = sky2_read32(hw, B0_IMSK); - imask &= ~mask; - sky2_write32(hw, B0_IMSK, imask); - - if (dev) { - spin_lock(&sky2->phy_lock); - sky2_link_down(sky2); - spin_unlock(&sky2->phy_lock); - } + sky2_write32(hw, Q_ADDR(q, Q_CSR), BMU_CLR_IRQ_CHK); } /* If idle then force a fake soft NAPI poll once a second @@ -2373,23 +2391,15 @@ static void sky2_idle(unsigned long arg) mod_timer(&hw->idle_timer, jiffies + msecs_to_jiffies(idle_timeout)); } - -static int sky2_poll(struct net_device *dev0, int *budget) +/* Hardware/software error handling */ +static void sky2_err_intr(struct sky2_hw *hw, u32 status) { - struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; - int work_limit = min(dev0->quota, *budget); - int work_done = 0; - u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + if (net_ratelimit()) + dev_warn(&hw->pdev->dev, "error interrupt status=%#x\n", status); if (status & Y2_IS_HW_ERR) sky2_hw_intr(hw); - if (status & Y2_IS_IRQ_PHY1) - sky2_phy_intr(hw, 0); - - if (status & Y2_IS_IRQ_PHY2) - sky2_phy_intr(hw, 1); - if (status & Y2_IS_IRQ_MAC1) sky2_mac_intr(hw, 0); @@ -2397,16 +2407,33 @@ static int sky2_poll(struct net_device *dev0, int *budget) sky2_mac_intr(hw, 1); if (status & Y2_IS_CHK_RX1) - sky2_descriptor_error(hw, 0, "receive", Y2_IS_CHK_RX1); + sky2_le_error(hw, 0, Q_R1, RX_LE_SIZE); if (status & Y2_IS_CHK_RX2) - sky2_descriptor_error(hw, 1, "receive", Y2_IS_CHK_RX2); + sky2_le_error(hw, 1, Q_R2, RX_LE_SIZE); if (status & Y2_IS_CHK_TXA1) - sky2_descriptor_error(hw, 0, "transmit", Y2_IS_CHK_TXA1); + sky2_le_error(hw, 0, Q_XA1, TX_RING_SIZE); if (status & Y2_IS_CHK_TXA2) - sky2_descriptor_error(hw, 1, "transmit", Y2_IS_CHK_TXA2); + sky2_le_error(hw, 1, Q_XA2, TX_RING_SIZE); +} + +static int sky2_poll(struct net_device *dev0, int *budget) +{ + struct sky2_hw *hw = ((struct sky2_port *) netdev_priv(dev0))->hw; + int work_limit = min(dev0->quota, *budget); + int work_done = 0; + u32 status = sky2_read32(hw, B0_Y2_SP_EISR); + + if (unlikely(status & Y2_IS_ERROR)) + sky2_err_intr(hw, status); + + if (status & Y2_IS_IRQ_PHY1) + sky2_phy_intr(hw, 0); + + if (status & Y2_IS_IRQ_PHY2) + sky2_phy_intr(hw, 1); work_done = sky2_status_intr(hw, work_limit); if (work_done < work_limit) { @@ -2524,16 +2551,14 @@ static void sky2_reset(struct sky2_hw *hw) int i; /* disable ASF */ - if (hw->chip_id <= CHIP_ID_YUKON_EC) { - if (hw->chip_id == CHIP_ID_YUKON_EX) { - status = sky2_read16(hw, HCU_CCSR); - status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE | - HCU_CCSR_UC_STATE_MSK); - sky2_write16(hw, HCU_CCSR, status); - } else - sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); - sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE); - } + if (hw->chip_id == CHIP_ID_YUKON_EX) { + status = sky2_read16(hw, HCU_CCSR); + status &= ~(HCU_CCSR_AHB_RST | HCU_CCSR_CPU_RST_MODE | + HCU_CCSR_UC_STATE_MSK); + sky2_write16(hw, HCU_CCSR, status); + } else + sky2_write8(hw, B28_Y2_ASF_STAT_CMD, Y2_ASF_RESET); + sky2_write16(hw, B0_CTST, Y2_ASF_DISABLE); /* do a SW reset */ sky2_write8(hw, B0_CTST, CS_RST_SET); @@ -2638,6 +2663,49 @@ static void sky2_reset(struct sky2_hw *hw) sky2_write8(hw, STAT_ISR_TIMER_CTRL, TIM_START); } +static void sky2_restart(struct work_struct *work) +{ + struct sky2_hw *hw = container_of(work, struct sky2_hw, restart_work); + struct net_device *dev; + int i, err; + + dev_dbg(&hw->pdev->dev, "restarting\n"); + + del_timer_sync(&hw->idle_timer); + + rtnl_lock(); + sky2_write32(hw, B0_IMSK, 0); + sky2_read32(hw, B0_IMSK); + + netif_poll_disable(hw->dev[0]); + + for (i = 0; i < hw->ports; i++) { + dev = hw->dev[i]; + if (netif_running(dev)) + sky2_down(dev); + } + + sky2_reset(hw); + sky2_write32(hw, B0_IMSK, Y2_IS_BASE); + netif_poll_enable(hw->dev[0]); + + for (i = 0; i < hw->ports; i++) { + dev = hw->dev[i]; + if (netif_running(dev)) { + err = sky2_up(dev); + if (err) { + printk(KERN_INFO PFX "%s: could not restart %d\n", + dev->name, err); + dev_close(dev); + } + } + } + + sky2_idle_start(hw); + + rtnl_unlock(); +} + static inline u8 sky2_wol_supported(const struct sky2_hw *hw) { return sky2_is_copper(hw) ? (WAKE_PHY | WAKE_MAGIC) : 0; @@ -3275,6 +3343,36 @@ static void sky2_get_regs(struct net_device *dev, struct ethtool_regs *regs, regs->len - B3_RI_WTO_R1); } +/* In order to do Jumbo packets on these chips, need to turn off the + * transmit store/forward. Therefore checksum offload won't work. + */ +static int no_tx_offload(struct net_device *dev) +{ + const struct sky2_port *sky2 = netdev_priv(dev); + const struct sky2_hw *hw = sky2->hw; + + return dev->mtu > ETH_DATA_LEN && + (hw->chip_id == CHIP_ID_YUKON_EX + || hw->chip_id == CHIP_ID_YUKON_EC_U); +} + +static int sky2_set_tx_csum(struct net_device *dev, u32 data) +{ + if (data && no_tx_offload(dev)) + return -EINVAL; + + return ethtool_op_set_tx_csum(dev, data); +} + + +static int sky2_set_tso(struct net_device *dev, u32 data) +{ + if (data && no_tx_offload(dev)) + return -EINVAL; + + return ethtool_op_set_tso(dev, data); +} + static const struct ethtool_ops sky2_ethtool_ops = { .get_settings = sky2_get_settings, .set_settings = sky2_set_settings, @@ -3290,9 +3388,9 @@ static const struct ethtool_ops sky2_ethtool_ops = { .get_sg = ethtool_op_get_sg, .set_sg = ethtool_op_set_sg, .get_tx_csum = ethtool_op_get_tx_csum, - .set_tx_csum = ethtool_op_set_tx_csum, + .set_tx_csum = sky2_set_tx_csum, .get_tso = ethtool_op_get_tso, - .set_tso = ethtool_op_set_tso, + .set_tso = sky2_set_tso, .get_rx_csum = sky2_get_rx_csum, .set_rx_csum = sky2_set_rx_csum, .get_strings = sky2_get_strings, @@ -3600,6 +3698,8 @@ static int __devinit sky2_probe(struct pci_dev *pdev, } setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw); + INIT_WORK(&hw->restart_work, sky2_restart); + sky2_idle_start(hw); pci_set_drvdata(pdev, hw); @@ -3636,6 +3736,8 @@ static void __devexit sky2_remove(struct pci_dev *pdev) del_timer_sync(&hw->idle_timer); + flush_scheduled_work(); + sky2_write32(hw, B0_IMSK, 0); synchronize_irq(hw->pdev->irq); @@ -3713,6 +3815,11 @@ static int sky2_resume(struct pci_dev *pdev) goto out; pci_enable_wake(pdev, PCI_D0, 0); + + /* Re-enable all clocks */ + if (hw->chip_id == CHIP_ID_YUKON_EX || hw->chip_id == CHIP_ID_YUKON_EC_U) + sky2_pci_write32(hw, PCI_DEV_REG3, 0); + sky2_reset(hw); sky2_write32(hw, B0_IMSK, Y2_IS_BASE); diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h index 3b01895..5efb5af 100644 --- a/drivers/net/sky2.h +++ b/drivers/net/sky2.h @@ -288,6 +288,9 @@ enum { | Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1, Y2_IS_PORT_2 = Y2_IS_IRQ_PHY2 | Y2_IS_IRQ_MAC2 | Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2, + Y2_IS_ERROR = Y2_IS_HW_ERR | + Y2_IS_IRQ_MAC1 | Y2_IS_CHK_TXA1 | Y2_IS_CHK_RX1 | + Y2_IS_IRQ_MAC2 | Y2_IS_CHK_TXA2 | Y2_IS_CHK_RX2, }; /* B2_IRQM_HWE_MSK 32 bit IRQ Moderation HW Error Mask */ @@ -738,6 +741,11 @@ enum { TX_GMF_RP = 0x0d70,/* 32 bit Tx GMAC FIFO Read Pointer */ TX_GMF_RSTP = 0x0d74,/* 32 bit Tx GMAC FIFO Restart Pointer */ TX_GMF_RLEV = 0x0d78,/* 32 bit Tx GMAC FIFO Read Level */ + + /* Threshold values for Yukon-EC Ultra and Extreme */ + ECU_AE_THR = 0x0070, /* Almost Empty Threshold */ + ECU_TXFF_LEV = 0x01a0, /* Tx BMU FIFO Level */ + ECU_JUMBO_WM = 0x0080, /* Jumbo Mode Watermark */ }; /* Descriptor Poll Timer Registers */ @@ -1589,7 +1597,7 @@ enum { GMR_FS_ANY_ERR = GMR_FS_RX_FF_OV | GMR_FS_CRC_ERR | GMR_FS_FRAGMENT | GMR_FS_LONG_ERR | - GMR_FS_MII_ERR | GMR_FS_GOOD_FC | GMR_FS_BAD_FC | + GMR_FS_MII_ERR | GMR_FS_BAD_FC | GMR_FS_UN_SIZE | GMR_FS_JABBER, }; @@ -1631,6 +1639,9 @@ enum { TX_VLAN_TAG_ON = 1<<25,/* enable VLAN tagging */ TX_VLAN_TAG_OFF = 1<<24,/* disable VLAN tagging */ + TX_JUMBO_ENA = 1<<23,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */ + TX_JUMBO_DIS = 1<<22,/* PCI Jumbo Mode enable (Yukon-EC Ultra) */ + GMF_WSP_TST_ON = 1<<18,/* Write Shadow Pointer Test On */ GMF_WSP_TST_OFF = 1<<17,/* Write Shadow Pointer Test Off */ GMF_WSP_STEP = 1<<16,/* Write Shadow Pointer Step/Increment */ @@ -1933,6 +1944,7 @@ struct sky2_hw { dma_addr_t st_dma; struct timer_list idle_timer; + struct work_struct restart_work; int msi; wait_queue_head_t msi_wait; }; diff --git a/drivers/net/slip.c b/drivers/net/slip.c index 2f4b1de..65bd20f 100644 --- a/drivers/net/slip.c +++ b/drivers/net/slip.c @@ -363,7 +363,7 @@ sl_bump(struct slip *sl) } skb->dev = sl->dev; memcpy(skb_put(skb,count), sl->rbuff, count); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->protocol=htons(ETH_P_IP); netif_rx(skb); sl->dev->last_rx = jiffies; diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index c956141..81f2484 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -499,10 +499,9 @@ static inline void smc911x_rcv(struct net_device *dev) SMC_SET_RX_CFG(RX_CFG_RX_END_ALGN4_ | ((2<<8) & RX_CFG_RXDOFF_)); SMC_PULL_DATA(data, pkt_len+2+3); - DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name,); + DBG(SMC_DEBUG_PKTS, "%s: Received packet\n", dev->name); PRINT_PKT(data, ((pkt_len - 4) <= 64) ? pkt_len - 4 : 64); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; @@ -1307,7 +1306,6 @@ smc911x_rx_dma_irq(int dma, void *data) lp->current_rx_skb = NULL; PRINT_PKT(skb->data, skb->len); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; diff --git a/drivers/net/smc9194.c b/drivers/net/smc9194.c index bd6e845..36c1eba 100644 --- a/drivers/net/smc9194.c +++ b/drivers/net/smc9194.c @@ -1262,7 +1262,6 @@ static void smc_rcv(struct net_device *dev) skb_reserve( skb, 2 ); /* 16 bit alignment */ - skb->dev = dev; data = skb_put( skb, packet_length); #ifdef USE_32_BIT diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 49f4b77..01cc3c7 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -568,7 +568,6 @@ static inline void smc_rcv(struct net_device *dev) PRINT_PKT(data, packet_len - 4); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); lp->stats.rx_packets++; diff --git a/drivers/net/sonic.c b/drivers/net/sonic.c index ed7aa0a..c6320c7 100644 --- a/drivers/net/sonic.c +++ b/drivers/net/sonic.c @@ -85,7 +85,6 @@ static int sonic_open(struct net_device *dev) dev->name); return -ENOMEM; } - skb->dev = dev; /* align IP header unless DMA requires otherwise */ if (SONIC_BUS_SCALE(lp->dma_bitmode) == 2) skb_reserve(skb, 2); @@ -451,7 +450,6 @@ static void sonic_rx(struct net_device *dev) lp->stats.rx_dropped++; break; } - new_skb->dev = dev; /* provide 16 byte IP header alignment unless DMA requires otherwise */ if(SONIC_BUS_SCALE(lp->dma_bitmode) == 2) skb_reserve(new_skb, 2); diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c index 64ed8ff..230da14 100644 --- a/drivers/net/spider_net.c +++ b/drivers/net/spider_net.c @@ -1,7 +1,8 @@ /* - * Network device driver for Cell Processor-Based Blade + * Network device driver for Cell Processor-Based Blade and Celleb platform * * (C) Copyright IBM Corp. 2005 + * (C) Copyright 2006 TOSHIBA CORPORATION * * Authors : Utz Bacher <utz.bacher@de.ibm.com> * Jens Osterkamp <Jens.Osterkamp@de.ibm.com> @@ -166,6 +167,41 @@ spider_net_read_phy(struct net_device *netdev, int mii_id, int reg) } /** + * spider_net_setup_aneg - initial auto-negotiation setup + * @card: device structure + **/ +static void +spider_net_setup_aneg(struct spider_net_card *card) +{ + struct mii_phy *phy = &card->phy; + u32 advertise = 0; + u16 bmcr, bmsr, stat1000, estat; + + bmcr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMCR); + bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); + stat1000 = spider_net_read_phy(card->netdev, phy->mii_id, MII_STAT1000); + estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS); + + if (bmsr & BMSR_10HALF) + advertise |= ADVERTISED_10baseT_Half; + if (bmsr & BMSR_10FULL) + advertise |= ADVERTISED_10baseT_Full; + if (bmsr & BMSR_100HALF) + advertise |= ADVERTISED_100baseT_Half; + if (bmsr & BMSR_100FULL) + advertise |= ADVERTISED_100baseT_Full; + + if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL)) + advertise |= SUPPORTED_1000baseT_Full; + if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF)) + advertise |= SUPPORTED_1000baseT_Half; + + mii_phy_probe(phy, phy->mii_id); + phy->def->ops->setup_aneg(phy, advertise); + +} + +/** * spider_net_rx_irq_off - switch off rx irq on this spider card * @card: device structure * @@ -263,9 +299,9 @@ spider_net_get_mac_address(struct net_device *netdev) * returns the status as in the dmac_cmd_status field of the descriptor */ static inline int -spider_net_get_descr_status(struct spider_net_descr *descr) +spider_net_get_descr_status(struct spider_net_hw_descr *hwdescr) { - return descr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK; + return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK; } /** @@ -283,12 +319,12 @@ spider_net_free_chain(struct spider_net_card *card, descr = chain->ring; do { descr->bus_addr = 0; - descr->next_descr_addr = 0; + descr->hwdescr->next_descr_addr = 0; descr = descr->next; } while (descr != chain->ring); dma_free_coherent(&card->pdev->dev, chain->num_desc, - chain->ring, chain->dma_addr); + chain->hwring, chain->dma_addr); } /** @@ -307,31 +343,34 @@ spider_net_init_chain(struct spider_net_card *card, { int i; struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; dma_addr_t buf; size_t alloc_size; - alloc_size = chain->num_desc * sizeof (struct spider_net_descr); + alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr); - chain->ring = dma_alloc_coherent(&card->pdev->dev, alloc_size, + chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size, &chain->dma_addr, GFP_KERNEL); - if (!chain->ring) + if (!chain->hwring) return -ENOMEM; - descr = chain->ring; - memset(descr, 0, alloc_size); + memset(chain->ring, 0, chain->num_desc * sizeof(struct spider_net_descr)); /* Set up the hardware pointers in each descriptor */ + descr = chain->ring; + hwdescr = chain->hwring; buf = chain->dma_addr; - for (i=0; i < chain->num_desc; i++, descr++) { - descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) { + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + hwdescr->next_descr_addr = 0; + descr->hwdescr = hwdescr; descr->bus_addr = buf; - descr->next_descr_addr = 0; descr->next = descr + 1; descr->prev = descr - 1; - buf += sizeof(struct spider_net_descr); + buf += sizeof(struct spider_net_hw_descr); } /* do actual circular list */ (descr-1)->next = chain->ring; @@ -357,10 +396,11 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card) descr = card->rx_chain.head; do { if (descr->skb) { - dev_kfree_skb(descr->skb); - pci_unmap_single(card->pdev, descr->buf_addr, + pci_unmap_single(card->pdev, descr->hwdescr->buf_addr, SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL); + dev_kfree_skb(descr->skb); + descr->skb = NULL; } descr = descr->next; } while (descr != card->rx_chain.head); @@ -380,6 +420,7 @@ static int spider_net_prepare_rx_descr(struct spider_net_card *card, struct spider_net_descr *descr) { + struct spider_net_hw_descr *hwdescr = descr->hwdescr; dma_addr_t buf; int offset; int bufsize; @@ -398,11 +439,11 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, card->spider_stats.alloc_rx_skb_error++; return -ENOMEM; } - descr->buf_size = bufsize; - descr->result_size = 0; - descr->valid_size = 0; - descr->data_status = 0; - descr->data_error = 0; + hwdescr->buf_size = bufsize; + hwdescr->result_size = 0; + hwdescr->valid_size = 0; + hwdescr->data_status = 0; + hwdescr->data_error = 0; offset = ((unsigned long)descr->skb->data) & (SPIDER_NET_RXBUF_ALIGN - 1); @@ -411,21 +452,22 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, /* iommu-map the skb */ buf = pci_map_single(card->pdev, descr->skb->data, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); - descr->buf_addr = buf; if (pci_dma_mapping_error(buf)) { dev_kfree_skb_any(descr->skb); + descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) pr_err("Could not iommu-map rx buffer\n"); card->spider_stats.rx_iommu_map_error++; - descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; } else { - descr->next_descr_addr = 0; + hwdescr->buf_addr = buf; + hwdescr->next_descr_addr = 0; wmb(); - descr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOINTR_COMPLETE; wmb(); - descr->prev->next_descr_addr = descr->bus_addr; + descr->prev->hwdescr->next_descr_addr = descr->bus_addr; } return 0; @@ -481,7 +523,7 @@ spider_net_refill_rx_chain(struct spider_net_card *card) if (!spin_trylock_irqsave(&chain->lock, flags)) return; - while (spider_net_get_descr_status(chain->head) == + while (spider_net_get_descr_status(chain->head->hwdescr) == SPIDER_NET_DESCR_NOT_IN_USE) { if (spider_net_prepare_rx_descr(card, chain->head)) break; @@ -642,7 +684,9 @@ static int spider_net_prepare_tx_descr(struct spider_net_card *card, struct sk_buff *skb) { + struct spider_net_descr_chain *chain = &card->tx_chain; struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; dma_addr_t buf; unsigned long flags; @@ -655,32 +699,39 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, return -ENOMEM; } - spin_lock_irqsave(&card->tx_chain.lock, flags); + spin_lock_irqsave(&chain->lock, flags); descr = card->tx_chain.head; - card->tx_chain.head = descr->next; + if (descr->next == chain->tail->prev) { + spin_unlock_irqrestore(&chain->lock, flags); + pci_unmap_single(card->pdev, buf, skb->len, PCI_DMA_TODEVICE); + return -ENOMEM; + } + hwdescr = descr->hwdescr; + chain->head = descr->next; - descr->buf_addr = buf; - descr->buf_size = skb->len; - descr->next_descr_addr = 0; descr->skb = skb; - descr->data_status = 0; + hwdescr->buf_addr = buf; + hwdescr->buf_size = skb->len; + hwdescr->next_descr_addr = 0; + hwdescr->data_status = 0; - descr->dmac_cmd_status = + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOCS; - spin_unlock_irqrestore(&card->tx_chain.lock, flags); + spin_unlock_irqrestore(&chain->lock, flags); - if (skb->protocol == htons(ETH_P_IP)) - switch (skb->nh.iph->protocol) { + if (skb->protocol == htons(ETH_P_IP) && skb->ip_summed == CHECKSUM_PARTIAL) + switch (ip_hdr(skb)->protocol) { case IPPROTO_TCP: - descr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; + hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; break; case IPPROTO_UDP: - descr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP; + hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP; break; } /* Chain the bus address, so that the DMA engine finds this descr. */ - descr->prev->next_descr_addr = descr->bus_addr; + wmb(); + descr->prev->hwdescr->next_descr_addr = descr->bus_addr; card->netdev->trans_start = jiffies; /* set netdev watchdog timer */ return 0; @@ -689,16 +740,17 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, static int spider_net_set_low_watermark(struct spider_net_card *card) { + struct spider_net_descr *descr = card->tx_chain.tail; + struct spider_net_hw_descr *hwdescr; unsigned long flags; int status; int cnt=0; int i; - struct spider_net_descr *descr = card->tx_chain.tail; /* Measure the length of the queue. Measurement does not * need to be precise -- does not need a lock. */ while (descr != card->tx_chain.head) { - status = descr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; + status = descr->hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; if (status == SPIDER_NET_DESCR_NOT_IN_USE) break; descr = descr->next; @@ -717,10 +769,12 @@ spider_net_set_low_watermark(struct spider_net_card *card) /* Set the new watermark, clear the old watermark */ spin_lock_irqsave(&card->tx_chain.lock, flags); - descr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; - if (card->low_watermark && card->low_watermark != descr) - card->low_watermark->dmac_cmd_status = - card->low_watermark->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; + descr->hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; + if (card->low_watermark && card->low_watermark != descr) { + hwdescr = card->low_watermark->hwdescr; + hwdescr->dmac_cmd_status = + hwdescr->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; + } card->low_watermark = descr; spin_unlock_irqrestore(&card->tx_chain.lock, flags); return cnt; @@ -743,16 +797,22 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) { struct spider_net_descr_chain *chain = &card->tx_chain; struct spider_net_descr *descr; + struct spider_net_hw_descr *hwdescr; struct sk_buff *skb; u32 buf_addr; unsigned long flags; int status; - while (chain->tail != chain->head) { + while (1) { spin_lock_irqsave(&chain->lock, flags); + if (chain->tail == chain->head) { + spin_unlock_irqrestore(&chain->lock, flags); + return 0; + } descr = chain->tail; + hwdescr = descr->hwdescr; - status = spider_net_get_descr_status(descr); + status = spider_net_get_descr_status(hwdescr); switch (status) { case SPIDER_NET_DESCR_COMPLETE: card->netdev_stats.tx_packets++; @@ -788,9 +848,10 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) } chain->tail = descr->next; - descr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; + hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; skb = descr->skb; - buf_addr = descr->buf_addr; + descr->skb = NULL; + buf_addr = hwdescr->buf_addr; spin_unlock_irqrestore(&chain->lock, flags); /* unmap the skb */ @@ -826,7 +887,7 @@ spider_net_kick_tx_dma(struct spider_net_card *card) descr = card->tx_chain.tail; for (;;) { - if (spider_net_get_descr_status(descr) == + if (spider_net_get_descr_status(descr->hwdescr) == SPIDER_NET_DESCR_CARDOWNED) { spider_net_write_reg(card, SPIDER_NET_GDTDCHA, descr->bus_addr); @@ -855,13 +916,10 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) { int cnt; struct spider_net_card *card = netdev_priv(netdev); - struct spider_net_descr_chain *chain = &card->tx_chain; spider_net_release_tx_chain(card, 0); - if ((chain->head->next == chain->tail->prev) || - (spider_net_prepare_tx_descr(card, skb) != 0)) { - + if (spider_net_prepare_tx_descr(card, skb) != 0) { card->netdev_stats.tx_dropped++; netif_stop_queue(netdev); return NETDEV_TX_BUSY; @@ -922,17 +980,17 @@ static void spider_net_pass_skb_up(struct spider_net_descr *descr, struct spider_net_card *card) { + struct spider_net_hw_descr *hwdescr= descr->hwdescr; struct sk_buff *skb; struct net_device *netdev; u32 data_status, data_error; - data_status = descr->data_status; - data_error = descr->data_error; + data_status = hwdescr->data_status; + data_error = hwdescr->data_error; netdev = card->netdev; skb = descr->skb; - skb->dev = netdev; - skb_put(skb, descr->valid_size); + skb_put(skb, hwdescr->valid_size); /* the card seems to add 2 bytes of junk in front * of the ethernet frame */ @@ -994,23 +1052,25 @@ static void show_rx_chain(struct spider_net_card *card) #endif /** - * spider_net_decode_one_descr - processes an rx descriptor + * spider_net_decode_one_descr - processes an RX descriptor * @card: card structure * - * Returns 1 if a packet has been sent to the stack, otherwise 0 + * Returns 1 if a packet has been sent to the stack, otherwise 0. * - * Processes an rx descriptor by iommu-unmapping the data buffer and passing - * the packet up to the stack. This function is called in softirq - * context, e.g. either bottom half from interrupt or NAPI polling context + * Processes an RX descriptor by iommu-unmapping the data buffer + * and passing the packet up to the stack. This function is called + * in softirq context, e.g. either bottom half from interrupt or + * NAPI polling context. */ static int spider_net_decode_one_descr(struct spider_net_card *card) { struct spider_net_descr_chain *chain = &card->rx_chain; struct spider_net_descr *descr = chain->tail; + struct spider_net_hw_descr *hwdescr = descr->hwdescr; int status; - status = spider_net_get_descr_status(descr); + status = spider_net_get_descr_status(hwdescr); /* Nothing in the descriptor, or ring must be empty */ if ((status == SPIDER_NET_DESCR_CARDOWNED) || @@ -1021,7 +1081,7 @@ spider_net_decode_one_descr(struct spider_net_card *card) chain->tail = descr->next; /* unmap descriptor */ - pci_unmap_single(card->pdev, descr->buf_addr, + pci_unmap_single(card->pdev, hwdescr->buf_addr, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) || @@ -1037,34 +1097,33 @@ spider_net_decode_one_descr(struct spider_net_card *card) if ( (status != SPIDER_NET_DESCR_COMPLETE) && (status != SPIDER_NET_DESCR_FRAME_END) ) { if (netif_msg_rx_err(card)) - pr_err("%s: RX descriptor with unkown state %d\n", + pr_err("%s: RX descriptor with unknown state %d\n", card->netdev->name, status); card->spider_stats.rx_desc_unk_state++; goto bad_desc; } /* The cases we'll throw away the packet immediately */ - if (descr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { + if (hwdescr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { if (netif_msg_rx_err(card)) pr_err("%s: error in received descriptor found, " "data_status=x%08x, data_error=x%08x\n", card->netdev->name, - descr->data_status, descr->data_error); + hwdescr->data_status, hwdescr->data_error); goto bad_desc; } - if (descr->dmac_cmd_status & 0xfefe) { + if (hwdescr->dmac_cmd_status & 0xfefe) { pr_err("%s: bad status, cmd_status=x%08x\n", card->netdev->name, - descr->dmac_cmd_status); - pr_err("buf_addr=x%08x\n", descr->buf_addr); - pr_err("buf_size=x%08x\n", descr->buf_size); - pr_err("next_descr_addr=x%08x\n", descr->next_descr_addr); - pr_err("result_size=x%08x\n", descr->result_size); - pr_err("valid_size=x%08x\n", descr->valid_size); - pr_err("data_status=x%08x\n", descr->data_status); - pr_err("data_error=x%08x\n", descr->data_error); - pr_err("bus_addr=x%08x\n", descr->bus_addr); + hwdescr->dmac_cmd_status); + pr_err("buf_addr=x%08x\n", hwdescr->buf_addr); + pr_err("buf_size=x%08x\n", hwdescr->buf_size); + pr_err("next_descr_addr=x%08x\n", hwdescr->next_descr_addr); + pr_err("result_size=x%08x\n", hwdescr->result_size); + pr_err("valid_size=x%08x\n", hwdescr->valid_size); + pr_err("data_status=x%08x\n", hwdescr->data_status); + pr_err("data_error=x%08x\n", hwdescr->data_error); pr_err("which=%ld\n", descr - card->rx_chain.ring); card->spider_stats.rx_desc_error++; @@ -1073,12 +1132,13 @@ spider_net_decode_one_descr(struct spider_net_card *card) /* Ok, we've got a packet in descr */ spider_net_pass_skb_up(descr, card); - descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; return 1; bad_desc: dev_kfree_skb_irq(descr->skb); - descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; + descr->skb = NULL; + hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; return 0; } @@ -1248,6 +1308,33 @@ spider_net_set_mac(struct net_device *netdev, void *p) } /** + * spider_net_link_reset + * @netdev: net device structure + * + * This is called when the PHY_LINK signal is asserted. For the blade this is + * not connected so we should never get here. + * + */ +static void +spider_net_link_reset(struct net_device *netdev) +{ + + struct spider_net_card *card = netdev_priv(netdev); + + del_timer_sync(&card->aneg_timer); + + /* clear interrupt, block further interrupts */ + spider_net_write_reg(card, SPIDER_NET_GMACST, + spider_net_read_reg(card, SPIDER_NET_GMACST)); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); + + /* reset phy and setup aneg */ + spider_net_setup_aneg(card); + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + +} + +/** * spider_net_handle_error_irq - handles errors raised by an interrupt * @card: card structure * @status_reg: interrupt status register 0 (GHIINT0STS) @@ -1359,8 +1446,8 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) switch (i) { case SPIDER_NET_GTMFLLINT: - if (netif_msg_intr(card) && net_ratelimit()) - pr_err("Spider TX RAM full\n"); + /* TX RAM full may happen on a usual case. + * Logging is not needed. */ show_error = 0; break; case SPIDER_NET_GRFDFLLINT: /* fallthrough */ @@ -1500,6 +1587,9 @@ spider_net_interrupt(int irq, void *ptr) if (status_reg & SPIDER_NET_TXINT) netif_rx_schedule(netdev); + if (status_reg & SPIDER_NET_LINKINT) + spider_net_link_reset(netdev); + if (status_reg & SPIDER_NET_ERRINT ) spider_net_handle_error_irq(card, status_reg); @@ -1540,6 +1630,11 @@ spider_net_init_card(struct spider_net_card *card) spider_net_write_reg(card, SPIDER_NET_CKRCTRL, SPIDER_NET_CKRCTRL_RUN_VALUE); + + /* trigger ETOMOD signal */ + spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, + spider_net_read_reg(card, SPIDER_NET_GMACOPEMD) | 0x4); + } /** @@ -1624,8 +1719,6 @@ spider_net_enable_card(struct spider_net_card *card) spider_net_write_reg(card, SPIDER_NET_GMACLENLMT, SPIDER_NET_LENLMT_VALUE); - spider_net_write_reg(card, SPIDER_NET_GMACMODE, - SPIDER_NET_MACMODE_VALUE); spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, SPIDER_NET_OPMODE_VALUE); @@ -1642,98 +1735,6 @@ spider_net_enable_card(struct spider_net_card *card) } /** - * spider_net_open - called upon ifonfig up - * @netdev: interface device structure - * - * returns 0 on success, <0 on failure - * - * spider_net_open allocates all the descriptors and memory needed for - * operation, sets up multicast list and enables interrupts - */ -int -spider_net_open(struct net_device *netdev) -{ - struct spider_net_card *card = netdev_priv(netdev); - int result; - - result = spider_net_init_chain(card, &card->tx_chain); - if (result) - goto alloc_tx_failed; - card->low_watermark = NULL; - - result = spider_net_init_chain(card, &card->rx_chain); - if (result) - goto alloc_rx_failed; - - /* Allocate rx skbs */ - if (spider_net_alloc_rx_skbs(card)) - goto alloc_skbs_failed; - - spider_net_set_multi(netdev); - - /* further enhancement: setup hw vlan, if needed */ - - result = -EBUSY; - if (request_irq(netdev->irq, spider_net_interrupt, - IRQF_SHARED, netdev->name, netdev)) - goto register_int_failed; - - spider_net_enable_card(card); - - netif_start_queue(netdev); - netif_carrier_on(netdev); - netif_poll_enable(netdev); - - return 0; - -register_int_failed: - spider_net_free_rx_chain_contents(card); -alloc_skbs_failed: - spider_net_free_chain(card, &card->rx_chain); -alloc_rx_failed: - spider_net_free_chain(card, &card->tx_chain); -alloc_tx_failed: - return result; -} - -/** - * spider_net_setup_phy - setup PHY - * @card: card structure - * - * returns 0 on success, <0 on failure - * - * spider_net_setup_phy is used as part of spider_net_probe. Sets - * the PHY to 1000 Mbps - **/ -static int -spider_net_setup_phy(struct spider_net_card *card) -{ - struct mii_phy *phy = &card->phy; - - spider_net_write_reg(card, SPIDER_NET_GDTDMASEL, - SPIDER_NET_DMASEL_VALUE); - spider_net_write_reg(card, SPIDER_NET_GPCCTRL, - SPIDER_NET_PHY_CTRL_VALUE); - phy->mii_id = 1; - phy->dev = card->netdev; - phy->mdio_read = spider_net_read_phy; - phy->mdio_write = spider_net_write_phy; - - mii_phy_probe(phy, phy->mii_id); - - if (phy->def->ops->setup_forced) - phy->def->ops->setup_forced(phy, SPEED_1000, DUPLEX_FULL); - - phy->def->ops->enable_fiber(phy); - - phy->def->ops->read_link(phy); - pr_info("Found %s with %i Mbps, %s-duplex.\n", phy->def->name, - phy->speed, phy->duplex==1 ? "Full" : "Half"); - - return 0; -} - -/** * spider_net_download_firmware - loads firmware into the adapter * @card: card structure * @firmware_ptr: pointer to firmware data @@ -1852,6 +1853,179 @@ out_err: } /** + * spider_net_open - called upon ifonfig up + * @netdev: interface device structure + * + * returns 0 on success, <0 on failure + * + * spider_net_open allocates all the descriptors and memory needed for + * operation, sets up multicast list and enables interrupts + */ +int +spider_net_open(struct net_device *netdev) +{ + struct spider_net_card *card = netdev_priv(netdev); + int result; + + result = spider_net_init_firmware(card); + if (result) + goto init_firmware_failed; + + /* start probing with copper */ + spider_net_setup_aneg(card); + if (card->phy.def->phy_id) + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + + result = spider_net_init_chain(card, &card->tx_chain); + if (result) + goto alloc_tx_failed; + card->low_watermark = NULL; + + result = spider_net_init_chain(card, &card->rx_chain); + if (result) + goto alloc_rx_failed; + + /* Allocate rx skbs */ + if (spider_net_alloc_rx_skbs(card)) + goto alloc_skbs_failed; + + spider_net_set_multi(netdev); + + /* further enhancement: setup hw vlan, if needed */ + + result = -EBUSY; + if (request_irq(netdev->irq, spider_net_interrupt, + IRQF_SHARED, netdev->name, netdev)) + goto register_int_failed; + + spider_net_enable_card(card); + + netif_start_queue(netdev); + netif_carrier_on(netdev); + netif_poll_enable(netdev); + + return 0; + +register_int_failed: + spider_net_free_rx_chain_contents(card); +alloc_skbs_failed: + spider_net_free_chain(card, &card->rx_chain); +alloc_rx_failed: + spider_net_free_chain(card, &card->tx_chain); +alloc_tx_failed: + del_timer_sync(&card->aneg_timer); +init_firmware_failed: + return result; +} + +/** + * spider_net_link_phy + * @data: used for pointer to card structure + * + */ +static void spider_net_link_phy(unsigned long data) +{ + struct spider_net_card *card = (struct spider_net_card *)data; + struct mii_phy *phy = &card->phy; + + /* if link didn't come up after SPIDER_NET_ANEG_TIMEOUT tries, setup phy again */ + if (card->aneg_count > SPIDER_NET_ANEG_TIMEOUT) { + + pr_info("%s: link is down trying to bring it up\n", card->netdev->name); + + switch (card->medium) { + case BCM54XX_COPPER: + /* enable fiber with autonegotiation first */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 1); + card->medium = BCM54XX_FIBER; + break; + + case BCM54XX_FIBER: + /* fiber didn't come up, try to disable fiber autoneg */ + if (phy->def->ops->enable_fiber) + phy->def->ops->enable_fiber(phy, 0); + card->medium = BCM54XX_UNKNOWN; + break; + + case BCM54XX_UNKNOWN: + /* copper, fiber with and without failed, + * retry from beginning */ + spider_net_setup_aneg(card); + card->medium = BCM54XX_COPPER; + break; + } + + card->aneg_count = 0; + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + return; + } + + /* link still not up, try again later */ + if (!(phy->def->ops->poll_link(phy))) { + card->aneg_count++; + mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); + return; + } + + /* link came up, get abilities */ + phy->def->ops->read_link(phy); + + spider_net_write_reg(card, SPIDER_NET_GMACST, + spider_net_read_reg(card, SPIDER_NET_GMACST)); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0x4); + + if (phy->speed == 1000) + spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0x00000001); + else + spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0); + + card->aneg_count = 0; + + pr_debug("Found %s with %i Mbps, %s-duplex %sautoneg.\n", + phy->def->name, phy->speed, phy->duplex==1 ? "Full" : "Half", + phy->autoneg==1 ? "" : "no "); + + return; +} + +/** + * spider_net_setup_phy - setup PHY + * @card: card structure + * + * returns 0 on success, <0 on failure + * + * spider_net_setup_phy is used as part of spider_net_probe. + **/ +static int +spider_net_setup_phy(struct spider_net_card *card) +{ + struct mii_phy *phy = &card->phy; + + spider_net_write_reg(card, SPIDER_NET_GDTDMASEL, + SPIDER_NET_DMASEL_VALUE); + spider_net_write_reg(card, SPIDER_NET_GPCCTRL, + SPIDER_NET_PHY_CTRL_VALUE); + + phy->dev = card->netdev; + phy->mdio_read = spider_net_read_phy; + phy->mdio_write = spider_net_write_phy; + + for (phy->mii_id = 1; phy->mii_id <= 31; phy->mii_id++) { + unsigned short id; + id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); + if (id != 0x0000 && id != 0xffff) { + if (!mii_phy_probe(phy, phy->mii_id)) { + pr_info("Found %s.\n", phy->def->name); + break; + } + } + } + + return 0; +} + +/** * spider_net_workaround_rxramfull - work around firmware bug * @card: card structure * @@ -1900,14 +2074,15 @@ spider_net_stop(struct net_device *netdev) netif_carrier_off(netdev); netif_stop_queue(netdev); del_timer_sync(&card->tx_timer); + del_timer_sync(&card->aneg_timer); /* disable/mask all interrupts */ spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0); + spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); - /* free_irq(netdev->irq, netdev);*/ - free_irq(to_pci_dev(netdev->dev.parent)->irq, netdev); + free_irq(netdev->irq, netdev); spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, SPIDER_NET_DMA_TX_FEND_VALUE); @@ -1919,8 +2094,6 @@ spider_net_stop(struct net_device *netdev) spider_net_release_tx_chain(card, 1); spider_net_free_rx_chain_contents(card); - spider_net_free_rx_chain_contents(card); - spider_net_free_chain(card, &card->tx_chain); spider_net_free_chain(card, &card->rx_chain); @@ -1952,8 +2125,6 @@ spider_net_tx_timeout_task(struct work_struct *work) if (spider_net_setup_phy(card)) goto out; - if (spider_net_init_firmware(card)) - goto out; spider_net_open(netdev); spider_net_kick_tx_dma(card); @@ -2046,10 +2217,12 @@ spider_net_setup_netdev(struct spider_net_card *card) card->tx_timer.data = (unsigned long) card; netdev->irq = card->pdev->irq; - card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; + card->aneg_count = 0; + init_timer(&card->aneg_timer); + card->aneg_timer.function = spider_net_link_phy; + card->aneg_timer.data = (unsigned long) card; - card->tx_chain.num_desc = tx_descriptors; - card->rx_chain.num_desc = rx_descriptors; + card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; spider_net_setup_netdev_ops(netdev); @@ -2098,8 +2271,11 @@ spider_net_alloc_card(void) { struct net_device *netdev; struct spider_net_card *card; + size_t alloc_size; - netdev = alloc_etherdev(sizeof(struct spider_net_card)); + alloc_size = sizeof(struct spider_net_card) + + (tx_descriptors + rx_descriptors) * sizeof(struct spider_net_descr); + netdev = alloc_etherdev(alloc_size); if (!netdev) return NULL; @@ -2110,6 +2286,11 @@ spider_net_alloc_card(void) init_waitqueue_head(&card->waitq); atomic_set(&card->tx_timeout_task_counter, 0); + card->rx_chain.num_desc = rx_descriptors; + card->rx_chain.ring = card->darray; + card->tx_chain.num_desc = tx_descriptors; + card->tx_chain.ring = card->darray + rx_descriptors; + return card; } @@ -2220,10 +2401,6 @@ spider_net_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) goto out_undo_pci; - err = spider_net_init_firmware(card); - if (err) - goto out_undo_pci; - err = spider_net_setup_netdev(card); if (err) goto out_undo_pci; diff --git a/drivers/net/spider_net.h b/drivers/net/spider_net.h index 2fec5cf..4a1e0d2 100644 --- a/drivers/net/spider_net.h +++ b/drivers/net/spider_net.h @@ -1,7 +1,8 @@ /* - * Network device driver for Cell Processor-Based Blade + * Network device driver for Cell Processor-Based Blade and Celleb platform * * (C) Copyright IBM Corp. 2005 + * (C) Copyright 2006 TOSHIBA CORPORATION * * Authors : Utz Bacher <utz.bacher@de.ibm.com> * Jens Osterkamp <Jens.Osterkamp@de.ibm.com> @@ -24,7 +25,7 @@ #ifndef _SPIDER_NET_H #define _SPIDER_NET_H -#define VERSION "1.6 B" +#define VERSION "2.0 A" #include "sungem_phy.h" @@ -50,6 +51,8 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_TX_DESCRIPTORS_MAX 512 #define SPIDER_NET_TX_TIMER (HZ/5) +#define SPIDER_NET_ANEG_TIMER (HZ) +#define SPIDER_NET_ANEG_TIMEOUT 2 #define SPIDER_NET_RX_CSUM_DEFAULT 1 @@ -104,6 +107,7 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_GMACOPEMD 0x00000100 #define SPIDER_NET_GMACLENLMT 0x00000108 +#define SPIDER_NET_GMACST 0x00000110 #define SPIDER_NET_GMACINTEN 0x00000118 #define SPIDER_NET_GMACPHYCTRL 0x00000120 @@ -181,7 +185,8 @@ extern char spider_net_driver_name[]; /* pause frames: automatic, no upper retransmission count */ /* outside loopback mode: ETOMOD signal dont matter, not connected */ -#define SPIDER_NET_OPMODE_VALUE 0x00000063 +/* ETOMOD signal is brought to PHY reset. bit 2 must be 1 in Celleb */ +#define SPIDER_NET_OPMODE_VALUE 0x00000067 /*#define SPIDER_NET_OPMODE_VALUE 0x001b0062*/ #define SPIDER_NET_LENLMT_VALUE 0x00000908 @@ -333,9 +338,12 @@ enum spider_net_int2_status { /* We rely on flagged descriptor interrupts */ #define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) ) +#define SPIDER_NET_LINKINT ( 1 << SPIDER_NET_GMAC2INT ) + #define SPIDER_NET_ERRINT ( 0xffffffff & \ (~SPIDER_NET_TXINT) & \ - (~SPIDER_NET_RXINT) ) + (~SPIDER_NET_RXINT) & \ + (~SPIDER_NET_LINKINT) ) #define SPIDER_NET_GPREXEC 0x80000000 #define SPIDER_NET_GPRDAT_MASK 0x0000ffff @@ -356,8 +364,8 @@ enum spider_net_int2_status { #define SPIDER_NET_DESCR_NOT_IN_USE 0xF0000000 #define SPIDER_NET_DESCR_TXDESFLG 0x00800000 -struct spider_net_descr { - /* as defined by the hardware */ +/* Descriptor, as defined by the hardware */ +struct spider_net_hw_descr { u32 buf_addr; u32 buf_size; u32 next_descr_addr; @@ -366,13 +374,15 @@ struct spider_net_descr { u32 valid_size; /* all zeroes for tx */ u32 data_status; u32 data_error; /* all zeroes for tx */ +} __attribute__((aligned(32))); - /* used in the driver */ +struct spider_net_descr { + struct spider_net_hw_descr *hwdescr; struct sk_buff *skb; u32 bus_addr; struct spider_net_descr *next; struct spider_net_descr *prev; -} __attribute__((aligned(32))); +}; struct spider_net_descr_chain { spinlock_t lock; @@ -380,6 +390,7 @@ struct spider_net_descr_chain { struct spider_net_descr *tail; struct spider_net_descr *ring; int num_desc; + struct spider_net_hw_descr *hwring; dma_addr_t dma_addr; }; @@ -436,12 +447,16 @@ struct spider_net_card { struct pci_dev *pdev; struct mii_phy phy; + int medium; + void __iomem *regs; struct spider_net_descr_chain tx_chain; struct spider_net_descr_chain rx_chain; struct spider_net_descr *low_watermark; + int aneg_count; + struct timer_list aneg_timer; struct timer_list tx_timer; struct work_struct tx_timeout_task; atomic_t tx_timeout_task_counter; @@ -452,6 +467,9 @@ struct spider_net_card { struct net_device_stats netdev_stats; struct spider_net_extra_stats spider_stats; struct spider_net_options options; + + /* Must be last item in struct */ + struct spider_net_descr darray[0]; }; #define pr_err(fmt,arg...) \ diff --git a/drivers/net/starfire.c b/drivers/net/starfire.c index bf873ea..9d6e454 100644 --- a/drivers/net/starfire.c +++ b/drivers/net/starfire.c @@ -677,8 +677,7 @@ static void netdev_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) spin_lock(&np->lock); if (debug > 1) printk("%s: removing vlanid %d from vlan filter\n", dev->name, vid); - if (np->vlgrp) - np->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(np->vlgrp, vid, NULL); set_rx_mode(dev); spin_unlock(&np->lock); } @@ -1453,7 +1452,6 @@ static int __netdev_rx(struct net_device *dev, int *quota) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, np->rx_info[entry].mapping, @@ -1738,7 +1736,7 @@ static void set_rx_mode(struct net_device *dev) int vlan_count = 0; void __iomem *filter_addr = ioaddr + HashTable + 8; for (i = 0; i < VLAN_VID_MASK; i++) { - if (np->vlgrp->vlan_devices[i]) { + if (vlan_group_get_device(np->vlgrp, i)) { if (vlan_count >= 32) break; writew(cpu_to_be16(i), filter_addr); diff --git a/drivers/net/sun3_82586.c b/drivers/net/sun3_82586.c index a3220a9..396c3d9 100644 --- a/drivers/net/sun3_82586.c +++ b/drivers/net/sun3_82586.c @@ -28,8 +28,6 @@ static int automatic_resume = 0; /* experimental .. better should be zero */ static int rfdadd = 0; /* rfdadd=1 may be better for 8K MEM cards */ static int fifo=0x8; /* don't change */ -/* #define REALLY_SLOW_IO */ - #include <linux/module.h> #include <linux/kernel.h> #include <linux/string.h> @@ -777,7 +775,6 @@ static void sun3_82586_rcv_int(struct net_device *dev) skb = (struct sk_buff *) dev_alloc_skb(totlen+2); if(skb != NULL) { - skb->dev = dev; skb_reserve(skb,2); skb_put(skb,totlen); eth_copy_and_sum(skb,(char *) p->base+swab32((unsigned long) rbd->buffer),totlen,0); @@ -1029,7 +1026,7 @@ static int sun3_82586_send_packet(struct sk_buff *skb, struct net_device *dev) memset((char *)p->xmit_cbuffs[p->xmit_count], 0, ETH_ZLEN); len = ETH_ZLEN; } - memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len); + skb_copy_from_linear_data(skb, p->xmit_cbuffs[p->xmit_count], skb->len); #if (NUM_XMIT_BUFFS == 1) # ifdef NO_NOPCOMMANDS diff --git a/drivers/net/sun3lance.c b/drivers/net/sun3lance.c index c62e85d..791e081 100644 --- a/drivers/net/sun3lance.c +++ b/drivers/net/sun3lance.c @@ -336,13 +336,27 @@ static int __init lance_probe( struct net_device *dev) /* XXX - leak? */ MEM = dvma_malloc_align(sizeof(struct lance_memory), 0x10000); + if (MEM == NULL) { +#ifdef CONFIG_SUN3 + iounmap((void __iomem *)ioaddr); +#endif + printk(KERN_WARNING "SUN3 Lance couldn't allocate DVMA memory\n"); + return 0; + } lp->iobase = (volatile unsigned short *)ioaddr; dev->base_addr = (unsigned long)ioaddr; /* informational only */ REGA(CSR0) = CSR0_STOP; - request_irq(LANCE_IRQ, lance_interrupt, IRQF_DISABLED, "SUN3 Lance", dev); + if (request_irq(LANCE_IRQ, lance_interrupt, IRQF_DISABLED, "SUN3 Lance", dev) < 0) { +#ifdef CONFIG_SUN3 + iounmap((void __iomem *)ioaddr); +#endif + dvma_free((void *)MEM); + printk(KERN_WARNING "SUN3 Lance unable to allocate IRQ\n"); + return 0; + } dev->irq = (unsigned short)LANCE_IRQ; @@ -615,7 +629,7 @@ static int lance_start_xmit( struct sk_buff *skb, struct net_device *dev ) head->length = (-len) | 0xf000; head->misc = 0; - memcpy( PKTBUF_ADDR(head), (void *)skb->data, skb->len ); + skb_copy_from_linear_data(skb, PKTBUF_ADDR(head), skb->len); if (len != skb->len) memset(PKTBUF_ADDR(head) + skb->len, 0, len-skb->len); @@ -837,10 +851,9 @@ static int lance_rx( struct net_device *dev ) } - skb->dev = dev; skb_reserve( skb, 2 ); /* 16 byte align */ skb_put( skb, pkt_len ); /* Make room */ -// memcpy( skb->data, PKTBUF_ADDR(head), pkt_len ); +// skb_copy_to_linear_data(skb, PKTBUF_ADDR(head), pkt_len); eth_copy_and_sum(skb, PKTBUF_ADDR(head), pkt_len, 0); diff --git a/drivers/net/sunbmac.c b/drivers/net/sunbmac.c index 18f8885..2ad8d58 100644 --- a/drivers/net/sunbmac.c +++ b/drivers/net/sunbmac.c @@ -855,7 +855,6 @@ static void bigmac_rx(struct bigmac *bp) drops++; goto drop_it; } - copy_skb->dev = bp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); sbus_dma_sync_single_for_cpu(bp->bigmac_sdev, diff --git a/drivers/net/sundance.c b/drivers/net/sundance.c index c06ecc8..f51ba31 100644 --- a/drivers/net/sundance.c +++ b/drivers/net/sundance.c @@ -1308,7 +1308,6 @@ static void rx_poll(unsigned long data) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev, desc->frag[0].addr, diff --git a/drivers/net/sungem.c b/drivers/net/sungem.c index 616be8d..5da7321 100644 --- a/drivers/net/sungem.c +++ b/drivers/net/sungem.c @@ -64,11 +64,9 @@ #include <asm/uaccess.h> #include <asm/irq.h> -#ifdef __sparc__ +#ifdef CONFIG_SPARC #include <asm/idprom.h> -#include <asm/openprom.h> -#include <asm/oplib.h> -#include <asm/pbm.h> +#include <asm/prom.h> #endif #ifdef CONFIG_PPC_PMAC @@ -845,11 +843,10 @@ static int gem_rx(struct gem *gp, int work_to_do) goto drop_it; } - copy_skb->dev = gp->dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); pci_dma_sync_single_for_cpu(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); pci_dma_sync_single_for_device(gp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ @@ -1029,10 +1026,8 @@ static int gem_start_xmit(struct sk_buff *skb, struct net_device *dev) ctrl = 0; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u64 csum_start_off, csum_stuff_off; - - csum_start_off = (u64) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u64 csum_start_off = skb_transport_offset(skb); + const u64 csum_stuff_off = csum_start_off + skb->csum_offset; ctrl = (TXDCTRL_CENAB | (csum_start_off << 15) | @@ -2530,6 +2525,35 @@ static struct net_device_stats *gem_get_stats(struct net_device *dev) return &gp->net_stats; } +static int gem_set_mac_address(struct net_device *dev, void *addr) +{ + struct sockaddr *macaddr = (struct sockaddr *) addr; + struct gem *gp = dev->priv; + unsigned char *e = &dev->dev_addr[0]; + + if (!is_valid_ether_addr(macaddr->sa_data)) + return -EADDRNOTAVAIL; + + if (!netif_running(dev) || !netif_device_present(dev)) { + /* We'll just catch it later when the + * device is up'd or resumed. + */ + memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len); + return 0; + } + + mutex_lock(&gp->pm_mutex); + memcpy(dev->dev_addr, macaddr->sa_data, dev->addr_len); + if (gp->running) { + writel((e[4] << 8) | e[5], gp->regs + MAC_ADDR0); + writel((e[2] << 8) | e[3], gp->regs + MAC_ADDR1); + writel((e[0] << 8) | e[1], gp->regs + MAC_ADDR2); + } + mutex_unlock(&gp->pm_mutex); + + return 0; +} + static void gem_set_multicast(struct net_device *dev) { struct gem *gp = dev->priv; @@ -2820,7 +2844,7 @@ static int gem_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return rc; } -#if (!defined(__sparc__) && !defined(CONFIG_PPC_PMAC)) +#if (!defined(CONFIG_SPARC) && !defined(CONFIG_PPC_PMAC)) /* Fetch MAC address from vital product data of PCI ROM. */ static int find_eth_addr_in_vpd(void __iomem *rom_base, int len, unsigned char *dev_addr) { @@ -2875,36 +2899,19 @@ static void get_gem_mac_nonobp(struct pci_dev *pdev, unsigned char *dev_addr) static int __devinit gem_get_device_address(struct gem *gp) { -#if defined(__sparc__) || defined(CONFIG_PPC_PMAC) +#if defined(CONFIG_SPARC) || defined(CONFIG_PPC_PMAC) struct net_device *dev = gp->dev; -#endif - -#if defined(__sparc__) - struct pci_dev *pdev = gp->pdev; - struct pcidev_cookie *pcp = pdev->sysdata; - int use_idprom = 1; - - if (pcp != NULL) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, "local-mac-address", - &len); - if (addr && len == 6) { - use_idprom = 0; - memcpy(dev->dev_addr, addr, 6); - } - } - if (use_idprom) - memcpy(dev->dev_addr, idprom->id_ethaddr, 6); -#elif defined(CONFIG_PPC_PMAC) const unsigned char *addr; addr = get_property(gp->of_node, "local-mac-address", NULL); if (addr == NULL) { +#ifdef CONFIG_SPARC + addr = idprom->id_ethaddr; +#else printk("\n"); printk(KERN_ERR "%s: can't get mac-address\n", dev->name); return -1; +#endif } memcpy(dev->dev_addr, addr, 6); #else @@ -3062,7 +3069,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev, /* On Apple, we want a reference to the Open Firmware device-tree * node. We use it for clock control. */ -#ifdef CONFIG_PPC_PMAC +#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC) gp->of_node = pci_device_to_OF_node(pdev); #endif @@ -3122,6 +3129,7 @@ static int __devinit gem_init_one(struct pci_dev *pdev, dev->change_mtu = gem_change_mtu; dev->irq = pdev->irq; dev->dma = 0; + dev->set_mac_address = gem_set_mac_address; #ifdef CONFIG_NET_POLL_CONTROLLER dev->poll_controller = gem_poll_controller; #endif diff --git a/drivers/net/sungem.h b/drivers/net/sungem.h index a70067c..58cf87c 100644 --- a/drivers/net/sungem.h +++ b/drivers/net/sungem.h @@ -1025,7 +1025,7 @@ struct gem { struct pci_dev *pdev; struct net_device *dev; -#ifdef CONFIG_PPC_PMAC +#if defined(CONFIG_PPC_PMAC) || defined(CONFIG_SPARC) struct device_node *of_node; #endif }; diff --git a/drivers/net/sungem_phy.c b/drivers/net/sungem_phy.c index 701ba4f..56a110c 100644 --- a/drivers/net/sungem_phy.c +++ b/drivers/net/sungem_phy.c @@ -310,6 +310,107 @@ static int bcm5411_init(struct mii_phy* phy) return 0; } +static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) +{ + u16 ctl, adv; + + phy->autoneg = 1; + phy->speed = SPEED_10; + phy->duplex = DUPLEX_HALF; + phy->pause = 0; + phy->advertising = advertise; + + /* Setup standard advertise */ + adv = phy_read(phy, MII_ADVERTISE); + adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); + if (advertise & ADVERTISED_10baseT_Half) + adv |= ADVERTISE_10HALF; + if (advertise & ADVERTISED_10baseT_Full) + adv |= ADVERTISE_10FULL; + if (advertise & ADVERTISED_100baseT_Half) + adv |= ADVERTISE_100HALF; + if (advertise & ADVERTISED_100baseT_Full) + adv |= ADVERTISE_100FULL; + phy_write(phy, MII_ADVERTISE, adv); + + /* Start/Restart aneg */ + ctl = phy_read(phy, MII_BMCR); + ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); + phy_write(phy, MII_BMCR, ctl); + + return 0; +} + +static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) +{ + u16 ctl; + + phy->autoneg = 0; + phy->speed = speed; + phy->duplex = fd; + phy->pause = 0; + + ctl = phy_read(phy, MII_BMCR); + ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); + + /* First reset the PHY */ + phy_write(phy, MII_BMCR, ctl | BMCR_RESET); + + /* Select speed & duplex */ + switch(speed) { + case SPEED_10: + break; + case SPEED_100: + ctl |= BMCR_SPEED100; + break; + case SPEED_1000: + default: + return -EINVAL; + } + if (fd == DUPLEX_FULL) + ctl |= BMCR_FULLDPLX; + phy_write(phy, MII_BMCR, ctl); + + return 0; +} + +static int genmii_poll_link(struct mii_phy *phy) +{ + u16 status; + + (void)phy_read(phy, MII_BMSR); + status = phy_read(phy, MII_BMSR); + if ((status & BMSR_LSTATUS) == 0) + return 0; + if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) + return 0; + return 1; +} + +static int genmii_read_link(struct mii_phy *phy) +{ + u16 lpa; + + if (phy->autoneg) { + lpa = phy_read(phy, MII_LPA); + + if (lpa & (LPA_10FULL | LPA_100FULL)) + phy->duplex = DUPLEX_FULL; + else + phy->duplex = DUPLEX_HALF; + if (lpa & (LPA_100FULL | LPA_100HALF)) + phy->speed = SPEED_100; + else + phy->speed = SPEED_10; + phy->pause = 0; + } + /* On non-aneg, we assume what we put in BMCR is the speed, + * though magic-aneg shouldn't prevent this case from occurring + */ + + return 0; +} + static int generic_suspend(struct mii_phy* phy) { phy_write(phy, MII_BMCR, BMCR_PDOWN); @@ -364,30 +465,6 @@ static int bcm5421_init(struct mii_phy* phy) return 0; } -static int bcm5421_enable_fiber(struct mii_phy* phy) -{ - /* enable fiber mode */ - phy_write(phy, MII_NCONFIG, 0x9020); - /* LEDs active in both modes, autosense prio = fiber */ - phy_write(phy, MII_NCONFIG, 0x945f); - - /* switch off fibre autoneg */ - phy_write(phy, MII_NCONFIG, 0xfc01); - phy_write(phy, 0x0b, 0x0004); - - return 0; -} - -static int bcm5461_enable_fiber(struct mii_phy* phy) -{ - phy_write(phy, MII_NCONFIG, 0xfc0c); - phy_write(phy, MII_BMCR, 0x4140); - phy_write(phy, MII_NCONFIG, 0xfc0b); - phy_write(phy, MII_BMCR, 0x0140); - - return 0; -} - static int bcm54xx_setup_aneg(struct mii_phy *phy, u32 advertise) { u16 ctl, adv; @@ -515,6 +592,155 @@ static int marvell88e1111_init(struct mii_phy* phy) return 0; } +#define BCM5421_MODE_MASK (1 << 5) + +static int bcm5421_poll_link(struct mii_phy* phy) +{ + u32 phy_reg; + int mode; + + /* find out in what mode we are */ + phy_write(phy, MII_NCONFIG, 0x1000); + phy_reg = phy_read(phy, MII_NCONFIG); + + mode = (phy_reg & BCM5421_MODE_MASK) >> 5; + + if ( mode == BCM54XX_COPPER) + return genmii_poll_link(phy); + + /* try to find out wether we have a link */ + phy_write(phy, MII_NCONFIG, 0x2000); + phy_reg = phy_read(phy, MII_NCONFIG); + + if (phy_reg & 0x0020) + return 0; + else + return 1; +} + +static int bcm5421_read_link(struct mii_phy* phy) +{ + u32 phy_reg; + int mode; + + /* find out in what mode we are */ + phy_write(phy, MII_NCONFIG, 0x1000); + phy_reg = phy_read(phy, MII_NCONFIG); + + mode = (phy_reg & BCM5421_MODE_MASK ) >> 5; + + if ( mode == BCM54XX_COPPER) + return bcm54xx_read_link(phy); + + phy->speed = SPEED_1000; + + /* find out wether we are running half- or full duplex */ + phy_write(phy, MII_NCONFIG, 0x2000); + phy_reg = phy_read(phy, MII_NCONFIG); + + if ( (phy_reg & 0x0080) >> 7) + phy->duplex |= DUPLEX_HALF; + else + phy->duplex |= DUPLEX_FULL; + + return 0; +} + +static int bcm5421_enable_fiber(struct mii_phy* phy, int autoneg) +{ + /* enable fiber mode */ + phy_write(phy, MII_NCONFIG, 0x9020); + /* LEDs active in both modes, autosense prio = fiber */ + phy_write(phy, MII_NCONFIG, 0x945f); + + if (!autoneg) { + /* switch off fibre autoneg */ + phy_write(phy, MII_NCONFIG, 0xfc01); + phy_write(phy, 0x0b, 0x0004); + } + + phy->autoneg = autoneg; + + return 0; +} + +#define BCM5461_FIBER_LINK (1 << 2) +#define BCM5461_MODE_MASK (3 << 1) + +static int bcm5461_poll_link(struct mii_phy* phy) +{ + u32 phy_reg; + int mode; + + /* find out in what mode we are */ + phy_write(phy, MII_NCONFIG, 0x7c00); + phy_reg = phy_read(phy, MII_NCONFIG); + + mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; + + if ( mode == BCM54XX_COPPER) + return genmii_poll_link(phy); + + /* find out wether we have a link */ + phy_write(phy, MII_NCONFIG, 0x7000); + phy_reg = phy_read(phy, MII_NCONFIG); + + if (phy_reg & BCM5461_FIBER_LINK) + return 1; + else + return 0; +} + +#define BCM5461_FIBER_DUPLEX (1 << 3) + +static int bcm5461_read_link(struct mii_phy* phy) +{ + u32 phy_reg; + int mode; + + /* find out in what mode we are */ + phy_write(phy, MII_NCONFIG, 0x7c00); + phy_reg = phy_read(phy, MII_NCONFIG); + + mode = (phy_reg & BCM5461_MODE_MASK ) >> 1; + + if ( mode == BCM54XX_COPPER) { + return bcm54xx_read_link(phy); + } + + phy->speed = SPEED_1000; + + /* find out wether we are running half- or full duplex */ + phy_write(phy, MII_NCONFIG, 0x7000); + phy_reg = phy_read(phy, MII_NCONFIG); + + if (phy_reg & BCM5461_FIBER_DUPLEX) + phy->duplex |= DUPLEX_FULL; + else + phy->duplex |= DUPLEX_HALF; + + return 0; +} + +static int bcm5461_enable_fiber(struct mii_phy* phy, int autoneg) +{ + /* select fiber mode, enable 1000 base-X registers */ + phy_write(phy, MII_NCONFIG, 0xfc0b); + + if (autoneg) { + /* enable fiber with no autonegotiation */ + phy_write(phy, MII_ADVERTISE, 0x01e0); + phy_write(phy, MII_BMCR, 0x1140); + } else { + /* enable fiber with autonegotiation */ + phy_write(phy, MII_BMCR, 0x0140); + } + + phy->autoneg = autoneg; + + return 0; +} + static int marvell_setup_aneg(struct mii_phy *phy, u32 advertise) { u16 ctl, adv; @@ -645,113 +871,6 @@ static int marvell_read_link(struct mii_phy *phy) return 0; } -static int genmii_setup_aneg(struct mii_phy *phy, u32 advertise) -{ - u16 ctl, adv; - - phy->autoneg = 1; - phy->speed = SPEED_10; - phy->duplex = DUPLEX_HALF; - phy->pause = 0; - phy->advertising = advertise; - - /* Setup standard advertise */ - adv = phy_read(phy, MII_ADVERTISE); - adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); - if (advertise & ADVERTISED_10baseT_Half) - adv |= ADVERTISE_10HALF; - if (advertise & ADVERTISED_10baseT_Full) - adv |= ADVERTISE_10FULL; - if (advertise & ADVERTISED_100baseT_Half) - adv |= ADVERTISE_100HALF; - if (advertise & ADVERTISED_100baseT_Full) - adv |= ADVERTISE_100FULL; - if (advertise & ADVERTISED_Pause) - adv |= ADVERTISE_PAUSE_CAP; - if (advertise & ADVERTISED_Asym_Pause) - adv |= ADVERTISE_PAUSE_ASYM; - phy_write(phy, MII_ADVERTISE, adv); - - /* Start/Restart aneg */ - ctl = phy_read(phy, MII_BMCR); - ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); - phy_write(phy, MII_BMCR, ctl); - - return 0; -} - -static int genmii_setup_forced(struct mii_phy *phy, int speed, int fd) -{ - u16 ctl; - - phy->autoneg = 0; - phy->speed = speed; - phy->duplex = fd; - phy->pause = 0; - - ctl = phy_read(phy, MII_BMCR); - ctl &= ~(BMCR_FULLDPLX|BMCR_SPEED100|BMCR_ANENABLE); - - /* First reset the PHY */ - phy_write(phy, MII_BMCR, ctl | BMCR_RESET); - - /* Select speed & duplex */ - switch(speed) { - case SPEED_10: - break; - case SPEED_100: - ctl |= BMCR_SPEED100; - break; - case SPEED_1000: - default: - return -EINVAL; - } - if (fd == DUPLEX_FULL) - ctl |= BMCR_FULLDPLX; - phy_write(phy, MII_BMCR, ctl); - - return 0; -} - -static int genmii_poll_link(struct mii_phy *phy) -{ - u16 status; - - (void)phy_read(phy, MII_BMSR); - status = phy_read(phy, MII_BMSR); - if ((status & BMSR_LSTATUS) == 0) - return 0; - if (phy->autoneg && !(status & BMSR_ANEGCOMPLETE)) - return 0; - return 1; -} - -static int genmii_read_link(struct mii_phy *phy) -{ - u16 lpa; - - if (phy->autoneg) { - lpa = phy_read(phy, MII_LPA); - - if (lpa & (LPA_10FULL | LPA_100FULL)) - phy->duplex = DUPLEX_FULL; - else - phy->duplex = DUPLEX_HALF; - if (lpa & (LPA_100FULL | LPA_100HALF)) - phy->speed = SPEED_100; - else - phy->speed = SPEED_10; - phy->pause = (phy->duplex == DUPLEX_FULL) && - ((lpa & LPA_PAUSE) != 0); - } - /* On non-aneg, we assume what we put in BMCR is the speed, - * though magic-aneg shouldn't prevent this case from occurring - */ - - return 0; -} - - #define MII_BASIC_FEATURES \ (SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | \ SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | \ @@ -885,8 +1004,8 @@ static struct mii_phy_ops bcm5421_phy_ops = { .suspend = generic_suspend, .setup_aneg = bcm54xx_setup_aneg, .setup_forced = bcm54xx_setup_forced, - .poll_link = genmii_poll_link, - .read_link = bcm54xx_read_link, + .poll_link = bcm5421_poll_link, + .read_link = bcm5421_read_link, .enable_fiber = bcm5421_enable_fiber, }; @@ -923,8 +1042,8 @@ static struct mii_phy_ops bcm5461_phy_ops = { .suspend = generic_suspend, .setup_aneg = bcm54xx_setup_aneg, .setup_forced = bcm54xx_setup_forced, - .poll_link = genmii_poll_link, - .read_link = bcm54xx_read_link, + .poll_link = bcm5461_poll_link, + .read_link = bcm5461_read_link, .enable_fiber = bcm5461_enable_fiber, }; diff --git a/drivers/net/sungem_phy.h b/drivers/net/sungem_phy.h index 1d70ba6..af02f94 100644 --- a/drivers/net/sungem_phy.h +++ b/drivers/net/sungem_phy.h @@ -12,7 +12,7 @@ struct mii_phy_ops int (*setup_forced)(struct mii_phy *phy, int speed, int fd); int (*poll_link)(struct mii_phy *phy); int (*read_link)(struct mii_phy *phy); - int (*enable_fiber)(struct mii_phy *phy); + int (*enable_fiber)(struct mii_phy *phy, int autoneg); }; /* Structure used to statically define an mii/gii based PHY */ @@ -26,6 +26,14 @@ struct mii_phy_def const struct mii_phy_ops* ops; }; +enum { + BCM54XX_COPPER, + BCM54XX_FIBER, + BCM54XX_GBIC, + BCM54XX_SGMII, + BCM54XX_UNKNOWN, +}; + /* An instance of a PHY, partially borrowed from mii_if_info */ struct mii_phy { diff --git a/drivers/net/sunhme.c b/drivers/net/sunhme.c index ef67173..51c3fe2 100644 --- a/drivers/net/sunhme.c +++ b/drivers/net/sunhme.c @@ -55,9 +55,6 @@ #ifdef CONFIG_PCI #include <linux/pci.h> -#ifdef CONFIG_SPARC -#include <asm/pbm.h> -#endif #endif #include "sunhme.h" @@ -2058,11 +2055,10 @@ static void happy_meal_rx(struct happy_meal *hp, struct net_device *dev) goto drop_it; } - copy_skb->dev = dev; skb_reserve(copy_skb, 2); skb_put(copy_skb, len); hme_dma_sync_for_cpu(hp, dma_addr, len, DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); hme_dma_sync_for_device(hp, dma_addr, len, DMA_FROMDEVICE); /* Reuse original ring buffer. */ @@ -2270,10 +2266,8 @@ static int happy_meal_start_xmit(struct sk_buff *skb, struct net_device *dev) tx_flags = TXFLAG_OWN; if (skb->ip_summed == CHECKSUM_PARTIAL) { - u32 csum_start_off, csum_stuff_off; - - csum_start_off = (u32) (skb->h.raw - skb->data); - csum_stuff_off = csum_start_off + skb->csum_offset; + const u32 csum_start_off = skb_transport_offset(skb); + const u32 csum_stuff_off = csum_start_off + skb->csum_offset; tx_flags = (TXFLAG_OWN | TXFLAG_CSENABLE | ((csum_start_off << 14) & TXFLAG_CSBUFBEGIN) | @@ -2704,7 +2698,7 @@ static int __devinit happy_meal_sbus_probe_one(struct sbus_dev *sdev, int is_qfe dev->dev_addr[i] = macaddr[i]; macaddr[5]++; } else { - unsigned char *addr; + const unsigned char *addr; int len; addr = of_get_property(dp, "local-mac-address", &len); @@ -2986,7 +2980,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, { struct quattro *qp = NULL; #ifdef CONFIG_SPARC - struct pcidev_cookie *pcp; + struct device_node *dp; #endif struct happy_meal *hp; struct net_device *dev; @@ -2998,13 +2992,8 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, /* Now make sure pci_dev cookie is there. */ #ifdef CONFIG_SPARC - pcp = pdev->sysdata; - if (pcp == NULL) { - printk(KERN_ERR "happymeal(PCI): Some PCI device info missing\n"); - return -ENODEV; - } - - strcpy(prom_name, pcp->prom_node->name); + dp = pci_device_to_OF_node(pdev); + strcpy(prom_name, dp->name); #else if (is_quattro_p(pdev)) strcpy(prom_name, "SUNW,qfe"); @@ -3081,11 +3070,11 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, macaddr[5]++; } else { #ifdef CONFIG_SPARC - unsigned char *addr; + const unsigned char *addr; int len; if (qfe_slot != -1 && - (addr = of_get_property(pcp->prom_node, + (addr = of_get_property(dp, "local-mac-address", &len)) != NULL && len == 6) { memcpy(dev->dev_addr, addr, 6); @@ -3105,7 +3094,7 @@ static int __devinit happy_meal_pci_probe(struct pci_dev *pdev, hp->tcvregs = (hpreg_base + 0x7000UL); #ifdef CONFIG_SPARC - hp->hm_revision = of_getintprop_default(pcp->prom_node, "hm-rev", 0xff); + hp->hm_revision = of_getintprop_default(dp, "hm-rev", 0xff); if (hp->hm_revision == 0xff) { unsigned char prev; @@ -3300,7 +3289,7 @@ static int __devinit hme_sbus_probe(struct of_device *dev, const struct of_devic { struct sbus_dev *sdev = to_sbus_device(&dev->dev); struct device_node *dp = dev->node; - char *model = of_get_property(dp, "model", NULL); + const char *model = of_get_property(dp, "model", NULL); int is_qfe = (match->data != NULL); if (!is_qfe && model && !strcmp(model, "SUNW,sbus-qfe")) @@ -3314,7 +3303,7 @@ static int __devexit hme_sbus_remove(struct of_device *dev) struct happy_meal *hp = dev_get_drvdata(&dev->dev); struct net_device *net_dev = hp->dev; - unregister_netdevice(net_dev); + unregister_netdev(net_dev); /* XXX qfe parent interrupt... */ diff --git a/drivers/net/sunlance.c b/drivers/net/sunlance.c index 5b00d79..4272253 100644 --- a/drivers/net/sunlance.c +++ b/drivers/net/sunlance.c @@ -547,7 +547,6 @@ static void lance_rx_dvma(struct net_device *dev) lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ eth_copy_and_sum(skb, @@ -721,7 +720,6 @@ static void lance_rx_pio(struct net_device *dev) lp->stats.rx_bytes += len; - skb->dev = dev; skb_reserve (skb, 2); /* 16 byte align */ skb_put(skb, len); /* make room */ lance_piocopy_to_skb(skb, &(ib->rx_buf[entry][0]), len); @@ -1145,7 +1143,7 @@ static int lance_start_xmit(struct sk_buff *skb, struct net_device *dev) struct lance_init_block *ib = lp->init_block_mem; ib->btx_ring [entry].length = (-len) | 0xf000; ib->btx_ring [entry].misc = 0; - memcpy((char *)&ib->tx_buf [entry][0], skb->data, skblen); + skb_copy_from_linear_data(skb, &ib->tx_buf [entry][0], skblen); if (len != skblen) memset((char *) &ib->tx_buf [entry][skblen], 0, len - skblen); ib->btx_ring [entry].tmd1_bits = (LE_T1_POK | LE_T1_OWN); @@ -1550,7 +1548,7 @@ static int __exit sunlance_sun4_remove(void) struct lance_private *lp = dev_get_drvdata(&sun4_sdev.ofdev.dev); struct net_device *net_dev = lp->dev; - unregister_netdevice(net_dev); + unregister_netdev(net_dev); lance_free_hwresources(lp); @@ -1590,7 +1588,7 @@ static int __devexit sunlance_sbus_remove(struct of_device *dev) struct lance_private *lp = dev_get_drvdata(&dev->dev); struct net_device *net_dev = lp->dev; - unregister_netdevice(net_dev); + unregister_netdev(net_dev); lance_free_hwresources(lp); diff --git a/drivers/net/sunqe.c b/drivers/net/sunqe.c index 7874eb1..fa70e0b 100644 --- a/drivers/net/sunqe.c +++ b/drivers/net/sunqe.c @@ -437,7 +437,6 @@ static void qe_rx(struct sunqe *qep) drops++; qep->net_stats.rx_dropped++; } else { - skb->dev = qep->dev; skb_reserve(skb, 2); skb_put(skb, len); eth_copy_and_sum(skb, (unsigned char *) this_qbuf, @@ -593,7 +592,7 @@ static int qe_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Avoid a race... */ qep->qe_block->qe_txd[entry].tx_flags = TXD_UPDATE; - memcpy(txbuf, skb->data, len); + skb_copy_from_linear_data(skb, txbuf, len); qep->qe_block->qe_txd[entry].tx_addr = txbuf_dvma; qep->qe_block->qe_txd[entry].tx_flags = @@ -845,6 +844,8 @@ static int __init qec_ether_init(struct sbus_dev *sdev) if (!dev) return -ENOMEM; + memcpy(dev->dev_addr, idprom->id_ethaddr, 6); + qe = netdev_priv(dev); i = of_getintprop_default(sdev->ofdev.node, "channel#", -1); @@ -960,7 +961,7 @@ static int __devexit qec_sbus_remove(struct of_device *dev) struct sunqe *qp = dev_get_drvdata(&dev->dev); struct net_device *net_dev = qp->dev; - unregister_netdevice(net_dev); + unregister_netdev(net_dev); sbus_iounmap(qp->qcregs, CREG_REG_SIZE); sbus_iounmap(qp->mregs, MREGS_REG_SIZE); diff --git a/drivers/net/tc35815.c b/drivers/net/tc35815.c index 81ed82f0..f1e2dfc 100644 --- a/drivers/net/tc35815.c +++ b/drivers/net/tc35815.c @@ -1,35 +1,34 @@ -/* tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. - * - * Copyright 2001 MontaVista Software Inc. - * Author: MontaVista Software, Inc. - * ahennessy@mvista.com +/* + * tc35815.c: A TOSHIBA TC35815CF PCI 10/100Mbps ethernet driver for linux. * * Based on skelton.c by Donald Becker. - * Copyright (C) 2000-2001 Toshiba Corporation * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This driver is a replacement of older and less maintained version. + * This is a header of the older version: + * -----<snip>----- + * Copyright 2001 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * ahennessy@mvista.com + * Copyright (C) 2000-2001 Toshiba Corporation + * static const char *version = + * "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; + * -----<snip>----- * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. + * (C) Copyright TOSHIBA CORPORATION 2004-2005 + * All Rights Reserved. */ -static const char *version = - "tc35815.c:v0.00 26/07/2000 by Toshiba Corporation\n"; +#ifdef TC35815_NAPI +#define DRV_VERSION "1.35-NAPI" +#else +#define DRV_VERSION "1.35" +#endif +static const char *version = "tc35815.c:v" DRV_VERSION "\n"; +#define MODNAME "tc35815" #include <linux/module.h> #include <linux/kernel.h> @@ -40,6 +39,7 @@ static const char *version = #include <linux/in.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/spinlock.h> #include <linux/errno.h> #include <linux/init.h> #include <linux/netdevice.h> @@ -47,36 +47,47 @@ static const char *version = #include <linux/skbuff.h> #include <linux/delay.h> #include <linux/pci.h> -#include <linux/proc_fs.h> -#include <linux/spinlock.h> -#include <linux/bitops.h> - -#include <asm/system.h> +#include <linux/mii.h> +#include <linux/ethtool.h> #include <asm/io.h> -#include <asm/dma.h> #include <asm/byteorder.h> -/* - * The name of the card. Is used for messages and in the requests for - * io regions, irqs and dma channels - */ -static const char* cardname = "TC35815CF"; -#define TC35815_PROC_ENTRY "net/tc35815" - -#define TC35815_MODULE_NAME "TC35815CF" -#define TX_TIMEOUT (4*HZ) - /* First, a few definitions that the brave might change. */ -/* use 0 for production, 1 for verification, >2 for debug */ -#ifndef TC35815_DEBUG -#define TC35815_DEBUG 1 -#endif -static unsigned int tc35815_debug = TC35815_DEBUG; - #define GATHER_TXINT /* On-Demand Tx Interrupt */ +#define WORKAROUND_LOSTCAR +#define WORKAROUND_100HALF_PROMISC +/* #define TC35815_USE_PACKEDBUFFER */ + +typedef enum { + TC35815CF = 0, + TC35815_NWU, + TC35815_TX4939, +} board_t; + +/* indexed by board_t, above */ +static const struct { + const char *name; +} board_info[] __devinitdata = { + { "TOSHIBA TC35815CF 10/100BaseTX" }, + { "TOSHIBA TC35815 with Wake on LAN" }, + { "TOSHIBA TC35815/TX4939" }, +}; + +static const struct pci_device_id tc35815_pci_tbl[] = { + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF), .driver_data = TC35815CF }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_NWU), .driver_data = TC35815_NWU }, + {PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815_TX4939), .driver_data = TC35815_TX4939 }, + {0,} +}; +MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl); -#define vtonocache(p) KSEG1ADDR(virt_to_phys(p)) +/* see MODULE_PARM_DESC */ +static struct tc35815_options { + int speed; + int duplex; + int doforce; +} options; /* * Registers @@ -119,6 +130,11 @@ struct tc35815_regs { * Bit assignments */ /* DMA_Ctl bit asign ------------------------------------------------------- */ +#define DMA_RxAlign 0x00c00000 /* 1:Reception Alignment */ +#define DMA_RxAlign_1 0x00400000 +#define DMA_RxAlign_2 0x00800000 +#define DMA_RxAlign_3 0x00c00000 +#define DMA_M66EnStat 0x00080000 /* 1:66MHz Enable State */ #define DMA_IntMask 0x00040000 /* 1:Interupt mask */ #define DMA_SWIntReq 0x00020000 /* 1:Software Interrupt request */ #define DMA_TxWakeUp 0x00010000 /* 1:Transmit Wake Up */ @@ -269,42 +285,6 @@ struct tc35815_regs { #define MD_CA_Wr 0x00000400 /* 1:Write 0:Read */ -/* MII register offsets */ -#define MII_CONTROL 0x0000 -#define MII_STATUS 0x0001 -#define MII_PHY_ID0 0x0002 -#define MII_PHY_ID1 0x0003 -#define MII_ANAR 0x0004 -#define MII_ANLPAR 0x0005 -#define MII_ANER 0x0006 -/* MII Control register bit definitions. */ -#define MIICNTL_FDX 0x0100 -#define MIICNTL_RST_AUTO 0x0200 -#define MIICNTL_ISOLATE 0x0400 -#define MIICNTL_PWRDWN 0x0800 -#define MIICNTL_AUTO 0x1000 -#define MIICNTL_SPEED 0x2000 -#define MIICNTL_LPBK 0x4000 -#define MIICNTL_RESET 0x8000 -/* MII Status register bit significance. */ -#define MIISTAT_EXT 0x0001 -#define MIISTAT_JAB 0x0002 -#define MIISTAT_LINK 0x0004 -#define MIISTAT_CAN_AUTO 0x0008 -#define MIISTAT_FAULT 0x0010 -#define MIISTAT_AUTO_DONE 0x0020 -#define MIISTAT_CAN_T 0x0800 -#define MIISTAT_CAN_T_FDX 0x1000 -#define MIISTAT_CAN_TX 0x2000 -#define MIISTAT_CAN_TX_FDX 0x4000 -#define MIISTAT_CAN_T4 0x8000 -/* MII Auto-Negotiation Expansion/RemoteEnd Register Bits */ -#define MII_AN_TX_FDX 0x0100 -#define MII_AN_TX_HDX 0x0080 -#define MII_AN_10_FDX 0x0040 -#define MII_AN_10_HDX 0x0020 - - /* * Descriptors */ @@ -352,32 +332,51 @@ struct BDesc { #ifdef NO_CHECK_CARRIER #define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ - Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ - Tx_En) /* maybe 0x7d01 */ + Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \ + Tx_En) /* maybe 0x7b01 */ #else #define TX_CTL_CMD (Tx_EnComp | Tx_EnTxPar | Tx_EnLateColl | \ - Tx_EnExColl | Tx_EnExDefer | Tx_EnUnder | \ - Tx_En) /* maybe 0x7f01 */ + Tx_EnExColl | Tx_EnLCarr | Tx_EnExDefer | Tx_EnUnder | \ + Tx_En) /* maybe 0x7b01 */ #endif #define RX_CTL_CMD (Rx_EnGood | Rx_EnRxPar | Rx_EnLongErr | Rx_EnOver \ | Rx_EnCRCErr | Rx_EnAlign | Rx_RxEn) /* maybe 0x6f01 */ - #define INT_EN_CMD (Int_NRAbtEn | \ - Int_DParDEn | Int_DParErrEn | \ + Int_DmParErrEn | Int_DParDEn | Int_DParErrEn | \ Int_SSysErrEn | Int_RMasAbtEn | Int_RTargAbtEn | \ Int_STargAbtEn | \ Int_BLExEn | Int_FDAExEn) /* maybe 0xb7f*/ +#define DMA_CTL_CMD DMA_BURST_SIZE +#define HAVE_DMA_RXALIGN(lp) likely((lp)->boardtype != TC35815CF) /* Tuning parameters */ #define DMA_BURST_SIZE 32 #define TX_THRESHOLD 1024 +#define TX_THRESHOLD_MAX 1536 /* used threshold with packet max byte for low pci transfer ability.*/ +#define TX_THRESHOLD_KEEP_LIMIT 10 /* setting threshold max value when overrun error occured this count. */ +/* 16 + RX_BUF_NUM * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*FD_PAGE_NUM */ +#ifdef TC35815_USE_PACKEDBUFFER #define FD_PAGE_NUM 2 -#define FD_PAGE_ORDER 1 -/* 16 + RX_BUF_PAGES * 8 + RX_FD_NUM * 16 + TX_FD_NUM * 32 <= PAGE_SIZE*2 */ -#define RX_BUF_PAGES 8 /* >= 2 */ +#define RX_BUF_NUM 8 /* >= 2 */ #define RX_FD_NUM 250 /* >= 32 */ #define TX_FD_NUM 128 +#define RX_BUF_SIZE PAGE_SIZE +#else /* TC35815_USE_PACKEDBUFFER */ +#define FD_PAGE_NUM 4 +#define RX_BUF_NUM 128 /* < 256 */ +#define RX_FD_NUM 256 /* >= 32 */ +#define TX_FD_NUM 128 +#if RX_CTL_CMD & Rx_LongEn +#define RX_BUF_SIZE PAGE_SIZE +#elif RX_CTL_CMD & Rx_StripCRC +#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 4 + 2, 32) /* +2: reserve */ +#else +#define RX_BUF_SIZE ALIGN(ETH_FRAME_LEN + 2, 32) /* +2: reserve */ +#endif +#endif /* TC35815_USE_PACKEDBUFFER */ +#define RX_FD_RESERVE (2 / 2) /* max 2 BD per RxFD */ +#define NAPI_WEIGHT 16 struct TxFD { struct FDesc fd; @@ -392,18 +391,27 @@ struct RxFD { struct FrFD { struct FDesc fd; - struct BDesc bd[RX_BUF_PAGES]; + struct BDesc bd[RX_BUF_NUM]; }; -extern unsigned long tc_readl(volatile __u32 *addr); -extern void tc_writel(unsigned long data, volatile __u32 *addr); +#define tc_readl(addr) readl(addr) +#define tc_writel(d, addr) writel(d, addr) + +#define TC35815_TX_TIMEOUT msecs_to_jiffies(400) -dma_addr_t priv_dma_handle; +/* Timer state engine. */ +enum tc35815_timer_state { + arbwait = 0, /* Waiting for auto negotiation to complete. */ + lupwait = 1, /* Auto-neg complete, awaiting link-up status. */ + ltrywait = 2, /* Forcing try of all modes, from fastest to slowest. */ + asleep = 3, /* Time inactive. */ + lcheck = 4, /* Check link status. */ +}; /* Information that need to be kept for each board. */ struct tc35815_local { - struct net_device *next_module; + struct pci_dev *pci_dev; /* statistics */ struct net_device_stats stats; @@ -411,216 +419,372 @@ struct tc35815_local { int max_tx_qlen; int tx_ints; int rx_ints; + int tx_underrun; } lstats; - int tbusy; - int option; -#define TC35815_OPT_AUTO 0x00 -#define TC35815_OPT_10M 0x01 -#define TC35815_OPT_100M 0x02 -#define TC35815_OPT_FULLDUP 0x04 - int linkspeed; /* 10 or 100 */ + /* Tx control lock. This protects the transmit buffer ring + * state along with the "tx full" state of the driver. This + * means all netif_queue flow control actions are protected + * by this lock as well. + */ + spinlock_t lock; + + int phy_addr; int fullduplex; + unsigned short saved_lpa; + struct timer_list timer; + enum tc35815_timer_state timer_state; /* State of auto-neg timer. */ + unsigned int timer_ticks; /* Number of clicks at each state */ /* * Transmitting: Batch Mode. * 1 BD in 1 TxFD. - * Receiving: Packing Mode. + * Receiving: Packing Mode. (TC35815_USE_PACKEDBUFFER) * 1 circular FD for Free Buffer List. - * RX_BUG_PAGES BD in Free Buffer FD. + * RX_BUF_NUM BD in Free Buffer FD. * One Free Buffer BD has PAGE_SIZE data buffer. + * Or Non-Packing Mode. + * 1 circular FD for Free Buffer List. + * RX_BUF_NUM BD in Free Buffer FD. + * One Free Buffer BD has ETH_FRAME_LEN data buffer. */ - struct pci_dev *pdev; - dma_addr_t fd_buf_dma_handle; - void * fd_buf; /* for TxFD, TxFD, FrFD */ + void * fd_buf; /* for TxFD, RxFD, FrFD */ + dma_addr_t fd_buf_dma; struct TxFD *tfd_base; - int tfd_start; - int tfd_end; + unsigned int tfd_start; + unsigned int tfd_end; struct RxFD *rfd_base; struct RxFD *rfd_limit; struct RxFD *rfd_cur; struct FrFD *fbl_ptr; +#ifdef TC35815_USE_PACKEDBUFFER unsigned char fbl_curid; - dma_addr_t data_buf_dma_handle[RX_BUF_PAGES]; - void * data_buf[RX_BUF_PAGES]; /* packing */ - spinlock_t lock; + void * data_buf[RX_BUF_NUM]; /* packing */ + dma_addr_t data_buf_dma[RX_BUF_NUM]; + struct { + struct sk_buff *skb; + dma_addr_t skb_dma; + } tx_skbs[TX_FD_NUM]; +#else + unsigned int fbl_count; + struct { + struct sk_buff *skb; + dma_addr_t skb_dma; + } tx_skbs[TX_FD_NUM], rx_skbs[RX_BUF_NUM]; +#endif + struct mii_if_info mii; + unsigned short mii_id[2]; + u32 msg_enable; + board_t boardtype; }; -/* Index to functions, as function prototypes. */ +static inline dma_addr_t fd_virt_to_bus(struct tc35815_local *lp, void *virt) +{ + return lp->fd_buf_dma + ((u8 *)virt - (u8 *)lp->fd_buf); +} +#ifdef DEBUG +static inline void *fd_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) +{ + return (void *)((u8 *)lp->fd_buf + (bus - lp->fd_buf_dma)); +} +#endif +#ifdef TC35815_USE_PACKEDBUFFER +static inline void *rxbuf_bus_to_virt(struct tc35815_local *lp, dma_addr_t bus) +{ + int i; + for (i = 0; i < RX_BUF_NUM; i++) { + if (bus >= lp->data_buf_dma[i] && + bus < lp->data_buf_dma[i] + PAGE_SIZE) + return (void *)((u8 *)lp->data_buf[i] + + (bus - lp->data_buf_dma[i])); + } + return NULL; +} -static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq); +#define TC35815_DMA_SYNC_ONDEMAND +static void* alloc_rxbuf_page(struct pci_dev *hwdev, dma_addr_t *dma_handle) +{ +#ifdef TC35815_DMA_SYNC_ONDEMAND + void *buf; + /* pci_map + pci_dma_sync will be more effective than + * pci_alloc_consistent on some archs. */ + if ((buf = (void *)__get_free_page(GFP_ATOMIC)) == NULL) + return NULL; + *dma_handle = pci_map_single(hwdev, buf, PAGE_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(*dma_handle)) { + free_page((unsigned long)buf); + return NULL; + } + return buf; +#else + return pci_alloc_consistent(hwdev, PAGE_SIZE, dma_handle); +#endif +} + +static void free_rxbuf_page(struct pci_dev *hwdev, void *buf, dma_addr_t dma_handle) +{ +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_unmap_single(hwdev, dma_handle, PAGE_SIZE, PCI_DMA_FROMDEVICE); + free_page((unsigned long)buf); +#else + pci_free_consistent(hwdev, PAGE_SIZE, buf, dma_handle); +#endif +} +#else /* TC35815_USE_PACKEDBUFFER */ +static struct sk_buff *alloc_rxbuf_skb(struct net_device *dev, + struct pci_dev *hwdev, + dma_addr_t *dma_handle) +{ + struct sk_buff *skb; + skb = dev_alloc_skb(RX_BUF_SIZE); + if (!skb) + return NULL; + skb->dev = dev; + *dma_handle = pci_map_single(hwdev, skb->data, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(*dma_handle)) { + dev_kfree_skb_any(skb); + return NULL; + } + skb_reserve(skb, 2); /* make IP header 4byte aligned */ + return skb; +} + +static void free_rxbuf_skb(struct pci_dev *hwdev, struct sk_buff *skb, dma_addr_t dma_handle) +{ + pci_unmap_single(hwdev, dma_handle, RX_BUF_SIZE, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); +} +#endif /* TC35815_USE_PACKEDBUFFER */ + +/* Index to functions, as function prototypes. */ static int tc35815_open(struct net_device *dev); static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev); -static void tc35815_tx_timeout(struct net_device *dev); -static irqreturn_t tc35815_interrupt(int irq, void *dev_id); +static irqreturn_t tc35815_interrupt(int irq, void *dev_id); +#ifdef TC35815_NAPI +static int tc35815_rx(struct net_device *dev, int limit); +static int tc35815_poll(struct net_device *dev, int *budget); +#else static void tc35815_rx(struct net_device *dev); +#endif static void tc35815_txdone(struct net_device *dev); static int tc35815_close(struct net_device *dev); static struct net_device_stats *tc35815_get_stats(struct net_device *dev); static void tc35815_set_multicast_list(struct net_device *dev); +static void tc35815_tx_timeout(struct net_device *dev); +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev); +#endif +static const struct ethtool_ops tc35815_ethtool_ops; +/* Example routines you must write ;->. */ static void tc35815_chip_reset(struct net_device *dev); static void tc35815_chip_init(struct net_device *dev); +static void tc35815_find_phy(struct net_device *dev); static void tc35815_phy_chip_init(struct net_device *dev); -/* A list of all installed tc35815 devices. */ -static struct net_device *root_tc35815_dev = NULL; +#ifdef DEBUG +static void panic_queues(struct net_device *dev); +#endif -/* - * PCI device identifiers for "new style" Linux PCI Device Drivers - */ -static struct pci_device_id tc35815_pci_tbl[] = { - { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_TC35815CF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, - { 0, } -}; +static void tc35815_timer(unsigned long data); +static void tc35815_start_auto_negotiation(struct net_device *dev, + struct ethtool_cmd *ep); +static int tc_mdio_read(struct net_device *dev, int phy_id, int location); +static void tc_mdio_write(struct net_device *dev, int phy_id, int location, + int val); -MODULE_DEVICE_TABLE (pci, tc35815_pci_tbl); +static void __devinit tc35815_init_dev_addr (struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; -int -tc35815_probe(struct pci_dev *pdev, - const struct pci_device_id *ent) + /* dev_addr will be overwritten on NETDEV_REGISTER event */ + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + for (i = 0; i < 6; i += 2) { + unsigned short data; + tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); + while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) + ; + data = tc_readl(&tr->PROM_Data); + dev->dev_addr[i] = data & 0xff; + dev->dev_addr[i+1] = data >> 8; + } +} + +static int __devinit tc35815_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) { - int err = 0; - int ret; - unsigned long pci_memaddr; - unsigned int pci_irq_line; + void __iomem *ioaddr = NULL; + struct net_device *dev; + struct tc35815_local *lp; + int rc; + unsigned long mmio_start, mmio_end, mmio_flags, mmio_len; + + static int printed_version; + if (!printed_version++) { + printk(version); + dev_printk(KERN_DEBUG, &pdev->dev, + "speed:%d duplex:%d doforce:%d\n", + options.speed, options.duplex, options.doforce); + } - printk(KERN_INFO "tc35815_probe: found device %#08x.%#08x\n", ent->vendor, ent->device); + if (!pdev->irq) { + dev_warn(&pdev->dev, "no IRQ assigned.\n"); + return -ENODEV; + } - err = pci_enable_device(pdev); - if (err) - return err; + /* dev zeroed in alloc_etherdev */ + dev = alloc_etherdev (sizeof (*lp)); + if (dev == NULL) { + dev_err(&pdev->dev, "unable to alloc new ethernet\n"); + return -ENOMEM; + } + SET_MODULE_OWNER(dev); + SET_NETDEV_DEV(dev, &pdev->dev); + lp = dev->priv; + + /* enable device (incl. PCI PM wakeup), and bus-mastering */ + rc = pci_enable_device (pdev); + if (rc) + goto err_out; - pci_memaddr = pci_resource_start (pdev, 1); + mmio_start = pci_resource_start (pdev, 1); + mmio_end = pci_resource_end (pdev, 1); + mmio_flags = pci_resource_flags (pdev, 1); + mmio_len = pci_resource_len (pdev, 1); - printk(KERN_INFO " pci_memaddr=%#08lx resource_flags=%#08lx\n", pci_memaddr, pci_resource_flags (pdev, 0)); + /* set this immediately, we need to know before + * we talk to the chip directly */ - if (!pci_memaddr) { - printk(KERN_WARNING "no PCI MEM resources, aborting\n"); - ret = -ENODEV; + /* make sure PCI base addr 1 is MMIO */ + if (!(mmio_flags & IORESOURCE_MEM)) { + dev_err(&pdev->dev, "region #1 not an MMIO resource, aborting\n"); + rc = -ENODEV; goto err_out; } - pci_irq_line = pdev->irq; - /* irq disabled. */ - if (pci_irq_line == 0) { - printk(KERN_WARNING "no PCI irq, aborting\n"); - ret = -ENODEV; + + /* check for weird/broken PCI region reporting */ + if ((mmio_len < sizeof(struct tc35815_regs))) { + dev_err(&pdev->dev, "Invalid PCI region size(s), aborting\n"); + rc = -ENODEV; goto err_out; } - ret = tc35815_probe1(pdev, pci_memaddr, pci_irq_line); - if (ret) + rc = pci_request_regions (pdev, MODNAME); + if (rc) goto err_out; - pci_set_master(pdev); - - return 0; + pci_set_master (pdev); -err_out: - pci_disable_device(pdev); - return ret; -} + /* ioremap MMIO region */ + ioaddr = ioremap (mmio_start, mmio_len); + if (ioaddr == NULL) { + dev_err(&pdev->dev, "cannot remap MMIO, aborting\n"); + rc = -EIO; + goto err_out_free_res; + } -static int __devinit tc35815_probe1(struct pci_dev *pdev, unsigned int base_addr, unsigned int irq) -{ - static unsigned version_printed = 0; - int i, ret; - struct tc35815_local *lp; - struct tc35815_regs *tr; - struct net_device *dev; + /* Initialize the device structure. */ + dev->open = tc35815_open; + dev->hard_start_xmit = tc35815_send_packet; + dev->stop = tc35815_close; + dev->get_stats = tc35815_get_stats; + dev->set_multicast_list = tc35815_set_multicast_list; + dev->do_ioctl = tc35815_ioctl; + dev->ethtool_ops = &tc35815_ethtool_ops; + dev->tx_timeout = tc35815_tx_timeout; + dev->watchdog_timeo = TC35815_TX_TIMEOUT; +#ifdef TC35815_NAPI + dev->poll = tc35815_poll; + dev->weight = NAPI_WEIGHT; +#endif +#ifdef CONFIG_NET_POLL_CONTROLLER + dev->poll_controller = tc35815_poll_controller; +#endif - /* Allocate a new 'dev' if needed. */ - dev = alloc_etherdev(sizeof(struct tc35815_local)); - if (dev == NULL) - return -ENOMEM; + dev->irq = pdev->irq; + dev->base_addr = (unsigned long) ioaddr; - /* - * alloc_etherdev allocs and zeros dev->priv - */ + /* dev->priv/lp zeroed and aligned in alloc_etherdev */ lp = dev->priv; + spin_lock_init(&lp->lock); + lp->pci_dev = pdev; + lp->boardtype = ent->driver_data; - if (tc35815_debug && version_printed++ == 0) - printk(KERN_DEBUG "%s", version); - - /* Fill in the 'dev' fields. */ - dev->irq = irq; - dev->base_addr = (unsigned long)ioremap(base_addr, - sizeof(struct tc35815_regs)); - if (!dev->base_addr) { - ret = -ENOMEM; - goto err_out; - } - tr = (struct tc35815_regs*)dev->base_addr; + lp->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV | NETIF_MSG_LINK; + pci_set_drvdata(pdev, dev); + /* Soft reset the chip. */ tc35815_chip_reset(dev); - /* Retrieve and print the ethernet address. */ - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - for (i = 0; i < 6; i += 2) { - unsigned short data; - tc_writel(PROM_Busy | PROM_Read | (i / 2 + 2), &tr->PROM_Ctl); - while (tc_readl(&tr->PROM_Ctl) & PROM_Busy) - ; - data = tc_readl(&tr->PROM_Data); - dev->dev_addr[i] = data & 0xff; - dev->dev_addr[i+1] = data >> 8; - } + /* Retrieve the ethernet address. */ + tc35815_init_dev_addr(dev); + + rc = register_netdev (dev); + if (rc) + goto err_out_unmap; + + memcpy(dev->perm_addr, dev->dev_addr, dev->addr_len); + printk(KERN_INFO "%s: %s at 0x%lx, " + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " + "IRQ %d\n", + dev->name, + board_info[ent->driver_data].name, + dev->base_addr, + dev->dev_addr[0], dev->dev_addr[1], + dev->dev_addr[2], dev->dev_addr[3], + dev->dev_addr[4], dev->dev_addr[5], + dev->irq); + + setup_timer(&lp->timer, tc35815_timer, (unsigned long) dev); + lp->mii.dev = dev; + lp->mii.mdio_read = tc_mdio_read; + lp->mii.mdio_write = tc_mdio_write; + lp->mii.phy_id_mask = 0x1f; + lp->mii.reg_num_mask = 0x1f; + tc35815_find_phy(dev); + lp->mii.phy_id = lp->phy_addr; + lp->mii.full_duplex = 0; + lp->mii.force_media = 0; - /* Initialize the device structure. */ - lp->pdev = pdev; - lp->next_module = root_tc35815_dev; - root_tc35815_dev = dev; + return 0; - spin_lock_init(&lp->lock); +err_out_unmap: + iounmap(ioaddr); +err_out_free_res: + pci_release_regions (pdev); +err_out: + free_netdev (dev); + return rc; +} - if (dev->mem_start > 0) { - lp->option = dev->mem_start; - if ((lp->option & TC35815_OPT_10M) && - (lp->option & TC35815_OPT_100M)) { - /* if both speed speficied, auto select. */ - lp->option &= ~(TC35815_OPT_10M | TC35815_OPT_100M); - } - } - //XXX fixme - lp->option |= TC35815_OPT_10M; - /* do auto negotiation */ - tc35815_phy_chip_init(dev); +static void __devexit tc35815_remove_one (struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata (pdev); + unsigned long mmio_addr; - dev->open = tc35815_open; - dev->stop = tc35815_close; - dev->tx_timeout = tc35815_tx_timeout; - dev->watchdog_timeo = TX_TIMEOUT; - dev->hard_start_xmit = tc35815_send_packet; - dev->get_stats = tc35815_get_stats; - dev->set_multicast_list = tc35815_set_multicast_list; - SET_MODULE_OWNER(dev); - SET_NETDEV_DEV(dev, &pdev->dev); + mmio_addr = dev->base_addr; - ret = register_netdev(dev); - if (ret) - goto err_out_iounmap; + unregister_netdev (dev); - printk(KERN_INFO "%s: %s found at %#x, irq %d, MAC", - dev->name, cardname, base_addr, irq); - for (i = 0; i < 6; i++) - printk(" %2.2x", dev->dev_addr[i]); - printk("\n"); - printk(KERN_INFO "%s: linkspeed %dMbps, %s Duplex\n", - dev->name, lp->linkspeed, lp->fullduplex ? "Full" : "Half"); + if (mmio_addr) { + iounmap ((void __iomem *)mmio_addr); + pci_release_regions (pdev); + } - return 0; + free_netdev (dev); -err_out_iounmap: - iounmap((void *) dev->base_addr); -err_out: - free_netdev(dev); - return ret; + pci_set_drvdata (pdev, NULL); } - static int tc35815_init_queues(struct net_device *dev) { @@ -629,44 +793,64 @@ tc35815_init_queues(struct net_device *dev) unsigned long fd_addr; if (!lp->fd_buf) { - if (sizeof(struct FDesc) + - sizeof(struct BDesc) * RX_BUF_PAGES + - sizeof(struct FDesc) * RX_FD_NUM + - sizeof(struct TxFD) * TX_FD_NUM > PAGE_SIZE * FD_PAGE_NUM) { - printk(KERN_WARNING "%s: Invalid Queue Size.\n", dev->name); - return -ENOMEM; - } + BUG_ON(sizeof(struct FDesc) + + sizeof(struct BDesc) * RX_BUF_NUM + + sizeof(struct FDesc) * RX_FD_NUM + + sizeof(struct TxFD) * TX_FD_NUM > + PAGE_SIZE * FD_PAGE_NUM); - if ((lp->fd_buf = (void *)__get_free_pages(GFP_KERNEL, FD_PAGE_ORDER)) == 0) + if ((lp->fd_buf = pci_alloc_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, &lp->fd_buf_dma)) == 0) return -ENOMEM; - for (i = 0; i < RX_BUF_PAGES; i++) { - if ((lp->data_buf[i] = (void *)get_zeroed_page(GFP_KERNEL)) == 0) { + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + if ((lp->data_buf[i] = alloc_rxbuf_page(lp->pci_dev, &lp->data_buf_dma[i])) == NULL) { while (--i >= 0) { - free_page((unsigned long)lp->data_buf[i]); - lp->data_buf[i] = 0; + free_rxbuf_page(lp->pci_dev, + lp->data_buf[i], + lp->data_buf_dma[i]); + lp->data_buf[i] = NULL; } - free_page((unsigned long)lp->fd_buf); - lp->fd_buf = 0; + pci_free_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, + lp->fd_buf_dma); + lp->fd_buf = NULL; + return -ENOMEM; + } +#else + lp->rx_skbs[i].skb = + alloc_rxbuf_skb(dev, lp->pci_dev, + &lp->rx_skbs[i].skb_dma); + if (!lp->rx_skbs[i].skb) { + while (--i >= 0) { + free_rxbuf_skb(lp->pci_dev, + lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } + pci_free_consistent(lp->pci_dev, + PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, + lp->fd_buf_dma); + lp->fd_buf = NULL; return -ENOMEM; } -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->data_buf[i], PAGE_SIZE * FD_PAGE_NUM); #endif } -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM); + printk(KERN_DEBUG "%s: FD buf %p DataBuf", + dev->name, lp->fd_buf); +#ifdef TC35815_USE_PACKEDBUFFER + printk(" DataBuf"); + for (i = 0; i < RX_BUF_NUM; i++) + printk(" %p", lp->data_buf[i]); #endif + printk("\n"); } else { - clear_page(lp->fd_buf); -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)lp->fd_buf, PAGE_SIZE * FD_PAGE_NUM); -#endif + for (i = 0; i < FD_PAGE_NUM; i++) { + clear_page((void *)((unsigned long)lp->fd_buf + i * PAGE_SIZE)); + } } -#ifdef __mips__ - fd_addr = (unsigned long)vtonocache(lp->fd_buf); -#else fd_addr = (unsigned long)lp->fd_buf; -#endif /* Free Descriptors (for Receive) */ lp->rfd_base = (struct RxFD *)fd_addr; @@ -675,34 +859,66 @@ tc35815_init_queues(struct net_device *dev) lp->rfd_base[i].fd.FDCtl = cpu_to_le32(FD_CownsFD); } lp->rfd_cur = lp->rfd_base; - lp->rfd_limit = (struct RxFD *)(fd_addr - - sizeof(struct FDesc) - - sizeof(struct BDesc) * 30); + lp->rfd_limit = (struct RxFD *)fd_addr - (RX_FD_RESERVE + 1); /* Transmit Descriptors */ lp->tfd_base = (struct TxFD *)fd_addr; fd_addr += sizeof(struct TxFD) * TX_FD_NUM; for (i = 0; i < TX_FD_NUM; i++) { - lp->tfd_base[i].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[i+1])); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + lp->tfd_base[i].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[i+1])); + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); lp->tfd_base[i].fd.FDCtl = cpu_to_le32(0); } - lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(virt_to_bus(&lp->tfd_base[0])); + lp->tfd_base[TX_FD_NUM-1].fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, &lp->tfd_base[0])); lp->tfd_start = 0; lp->tfd_end = 0; /* Buffer List (for Receive) */ lp->fbl_ptr = (struct FrFD *)fd_addr; - lp->fbl_ptr->fd.FDNext = cpu_to_le32(virt_to_bus(lp->fbl_ptr)); - lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_PAGES | FD_CownsFD); - for (i = 0; i < RX_BUF_PAGES; i++) { - lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(virt_to_bus(lp->data_buf[i])); + lp->fbl_ptr->fd.FDNext = cpu_to_le32(fd_virt_to_bus(lp, lp->fbl_ptr)); + lp->fbl_ptr->fd.FDCtl = cpu_to_le32(RX_BUF_NUM | FD_CownsFD); +#ifndef TC35815_USE_PACKEDBUFFER + /* + * move all allocated skbs to head of rx_skbs[] array. + * fbl_count mighe not be RX_BUF_NUM if alloc_rxbuf_skb() in + * tc35815_rx() had failed. + */ + lp->fbl_count = 0; + for (i = 0; i < RX_BUF_NUM; i++) { + if (lp->rx_skbs[i].skb) { + if (i != lp->fbl_count) { + lp->rx_skbs[lp->fbl_count].skb = + lp->rx_skbs[i].skb; + lp->rx_skbs[lp->fbl_count].skb_dma = + lp->rx_skbs[i].skb_dma; + } + lp->fbl_count++; + } + } +#endif + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + lp->fbl_ptr->bd[i].BuffData = cpu_to_le32(lp->data_buf_dma[i]); +#else + if (i >= lp->fbl_count) { + lp->fbl_ptr->bd[i].BuffData = 0; + lp->fbl_ptr->bd[i].BDCtl = 0; + continue; + } + lp->fbl_ptr->bd[i].BuffData = + cpu_to_le32(lp->rx_skbs[i].skb_dma); +#endif /* BDID is index of FrFD.bd[] */ lp->fbl_ptr->bd[i].BDCtl = - cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | PAGE_SIZE); + cpu_to_le32(BD_CownsBD | (i << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); } +#ifdef TC35815_USE_PACKEDBUFFER lp->fbl_curid = 0; +#endif + printk(KERN_DEBUG "%s: TxFD %p RxFD %p FrFD %p\n", + dev->name, lp->tfd_base, lp->rfd_base, lp->fbl_ptr); return 0; } @@ -713,11 +929,25 @@ tc35815_clear_queues(struct net_device *dev) int i; for (i = 0; i < TX_FD_NUM; i++) { - struct sk_buff *skb = (struct sk_buff *) - le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - if (skb) + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; dev_kfree_skb_any(skb); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); } tc35815_init_queues(dev); @@ -731,28 +961,53 @@ tc35815_free_queues(struct net_device *dev) if (lp->tfd_base) { for (i = 0; i < TX_FD_NUM; i++) { - struct sk_buff *skb = (struct sk_buff *) - le32_to_cpu(lp->tfd_base[i].fd.FDSystem); - if (skb) - dev_kfree_skb_any(skb); - lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0); + u32 fdsystem = le32_to_cpu(lp->tfd_base[i].fd.FDSystem); + struct sk_buff *skb = + fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[i].skb != skb) { + printk("%s: tx_skbs mismatch(%d).\n", dev->name, i); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[i].skb != skb); +#endif + if (skb) { + dev_kfree_skb(skb); + pci_unmap_single(lp->pci_dev, lp->tx_skbs[i].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[i].skb = NULL; + lp->tx_skbs[i].skb_dma = 0; + } + lp->tfd_base[i].fd.FDSystem = cpu_to_le32(0xffffffff); } } lp->rfd_base = NULL; - lp->rfd_base = NULL; lp->rfd_limit = NULL; lp->rfd_cur = NULL; lp->fbl_ptr = NULL; - for (i = 0; i < RX_BUF_PAGES; i++) { - if (lp->data_buf[i]) - free_page((unsigned long)lp->data_buf[i]); - lp->data_buf[i] = 0; + for (i = 0; i < RX_BUF_NUM; i++) { +#ifdef TC35815_USE_PACKEDBUFFER + if (lp->data_buf[i]) { + free_rxbuf_page(lp->pci_dev, + lp->data_buf[i], lp->data_buf_dma[i]); + lp->data_buf[i] = NULL; + } +#else + if (lp->rx_skbs[i].skb) { + free_rxbuf_skb(lp->pci_dev, lp->rx_skbs[i].skb, + lp->rx_skbs[i].skb_dma); + lp->rx_skbs[i].skb = NULL; + } +#endif + } + if (lp->fd_buf) { + pci_free_consistent(lp->pci_dev, PAGE_SIZE * FD_PAGE_NUM, + lp->fd_buf, lp->fd_buf_dma); + lp->fd_buf = NULL; } - if (lp->fd_buf) - __free_pages(lp->fd_buf, FD_PAGE_ORDER); - lp->fd_buf = NULL; } static void @@ -792,6 +1047,7 @@ dump_rxfd(struct RxFD *fd) return bd_count; } +#if defined(DEBUG) || defined(TC35815_USE_PACKEDBUFFER) static void dump_frfd(struct FrFD *fd) { @@ -802,20 +1058,22 @@ dump_frfd(struct FrFD *fd) le32_to_cpu(fd->fd.FDStat), le32_to_cpu(fd->fd.FDCtl)); printk("BD: "); - for (i = 0; i < RX_BUF_PAGES; i++) + for (i = 0; i < RX_BUF_NUM; i++) printk(" %08x %08x", le32_to_cpu(fd->bd[i].BuffData), le32_to_cpu(fd->bd[i].BDCtl)); printk("\n"); } +#endif +#ifdef DEBUG static void panic_queues(struct net_device *dev) { struct tc35815_local *lp = dev->priv; int i; - printk("TxFD base %p, start %d, end %d\n", + printk("TxFD base %p, start %u, end %u\n", lp->tfd_base, lp->tfd_start, lp->tfd_end); printk("RxFD base %p limit %p cur %p\n", lp->rfd_base, lp->rfd_limit, lp->rfd_cur); @@ -829,31 +1087,13 @@ panic_queues(struct net_device *dev) dump_frfd(lp->fbl_ptr); panic("%s: Illegal queue state.", dev->name); } - -#if 0 -static void print_buf(char *add, int length) -{ - int i; - int len = length; - - printk("print_buf(%08x)(%x)\n", (unsigned int) add,length); - - if (len > 100) - len = 100; - for (i = 0; i < len; i++) { - printk(" %2.2X", (unsigned char) add[i]); - if (!(i % 16)) - printk("\n"); - } - printk("\n"); -} #endif static void print_eth(char *add) { int i; - printk("print_eth(%08x)\n", (unsigned int) add); + printk("print_eth(%p)\n", add); for (i = 0; i < 6; i++) printk(" %2.2X", (unsigned char) add[i + 6]); printk(" =>"); @@ -862,6 +1102,73 @@ static void print_eth(char *add) printk(" : %2.2X%2.2X\n", (unsigned char) add[12], (unsigned char) add[13]); } +static int tc35815_tx_full(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return ((lp->tfd_start + 1) % TX_FD_NUM == lp->tfd_end); +} + +static void tc35815_restart(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + int do_phy_reset = 1; + del_timer(&lp->timer); /* Kill if running */ + + if (lp->mii_id[0] == 0x0016 && (lp->mii_id[1] & 0xfc00) == 0xf800) { + /* Resetting PHY cause problem on some chip... (SEEQ 80221) */ + do_phy_reset = 0; + } + if (do_phy_reset) { + int timeout; + tc_mdio_write(dev, pid, MII_BMCR, BMCR_RESET); + timeout = 100; + while (--timeout) { + if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_RESET)) + break; + udelay(1); + } + if (!timeout) + printk(KERN_ERR "%s: BMCR reset failed.\n", dev->name); + } + + tc35815_chip_reset(dev); + tc35815_clear_queues(dev); + tc35815_chip_init(dev); + /* Reconfigure CAM again since tc35815_chip_init() initialize it. */ + tc35815_set_multicast_list(dev); +} + +static void tc35815_tx_timeout(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + + printk(KERN_WARNING "%s: transmit timed out, status %#x\n", + dev->name, tc_readl(&tr->Tx_Stat)); + + /* Try to restart the adaptor. */ + spin_lock_irq(&lp->lock); + tc35815_restart(dev); + spin_unlock_irq(&lp->lock); + + lp->stats.tx_errors++; + + /* If we have space available to accept new transmit + * requests, wake up the queueing layer. This would + * be the case if the chipset_init() call above just + * flushes out the tx queue and empties it. + * + * If instead, the tx queue is retained then the + * netif_wake_queue() call should be placed in the + * TX completion interrupt handler of the driver instead + * of here. + */ + if (!tc35815_tx_full(dev)) + netif_wake_queue(dev); +} + /* * Open/initialize the board. This is called (in the current kernel) * sometime after booting when the 'ifconfig' program is run. @@ -874,16 +1181,16 @@ static int tc35815_open(struct net_device *dev) { struct tc35815_local *lp = dev->priv; + /* * This is used if the interrupt line can turned off (shared). * See 3c503.c for an example of selecting the IRQ at config-time. */ - - if (dev->irq == 0 || - request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, cardname, dev)) { + if (request_irq(dev->irq, &tc35815_interrupt, IRQF_SHARED, dev->name, dev)) { return -EAGAIN; } + del_timer(&lp->timer); /* Kill if running */ tc35815_chip_reset(dev); if (tc35815_init_queues(dev) != 0) { @@ -892,138 +1199,119 @@ tc35815_open(struct net_device *dev) } /* Reset the hardware here. Don't forget to set the station address. */ + spin_lock_irq(&lp->lock); tc35815_chip_init(dev); + spin_unlock_irq(&lp->lock); - lp->tbusy = 0; + /* We are now ready to accept transmit requeusts from + * the queueing layer of the networking. + */ netif_start_queue(dev); return 0; } -static void tc35815_tx_timeout(struct net_device *dev) -{ - struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr; - unsigned long flags; - - spin_lock_irqsave(&lp->lock, flags); - printk(KERN_WARNING "%s: transmit timed out, status %#lx\n", - dev->name, tc_readl(&tr->Tx_Stat)); - /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); - lp->tbusy=0; - spin_unlock_irqrestore(&lp->lock, flags); - dev->trans_start = jiffies; - netif_wake_queue(dev); -} - +/* This will only be invoked if your driver is _not_ in XOFF state. + * What this means is that you need not check it, and that this + * invariant will hold if you make sure that the netif_*_queue() + * calls are done at the proper times. + */ static int tc35815_send_packet(struct sk_buff *skb, struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs *)dev->base_addr; - - if (netif_queue_stopped(dev)) { - /* - * If we get here, some higher level has decided we are broken. - * There should really be a "kick me" function call instead. - */ - int tickssofar = jiffies - dev->trans_start; - if (tickssofar < 5) - return 1; - printk(KERN_WARNING "%s: transmit timed out, status %#lx\n", - dev->name, tc_readl(&tr->Tx_Stat)); - /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); - lp->tbusy=0; - dev->trans_start = jiffies; - netif_wake_queue(dev); - } + struct TxFD *txfd; + unsigned long flags; - /* - * Block a timer-based transmit from overlapping. This could better be - * done with atomic_swap(1, lp->tbusy), but set_bit() works as well. + /* If some error occurs while trying to transmit this + * packet, you should return '1' from this function. + * In such a case you _may not_ do anything to the + * SKB, it is still owned by the network queueing + * layer when an error is returned. This means you + * may not modify any SKB fields, you may not free + * the SKB, etc. */ - if (test_and_set_bit(0, (void*)&lp->tbusy) != 0) { - printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name); - dev_kfree_skb_any(skb); - } else { - short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN; - unsigned char *buf = skb->data; - struct TxFD *txfd = &lp->tfd_base[lp->tfd_start]; - unsigned long flags; - lp->stats.tx_bytes += skb->len; + /* This is the most common case for modern hardware. + * The spinlock protects this code from the TX complete + * hardware interrupt handler. Queue flow control is + * thus managed under this lock as well. + */ + spin_lock_irqsave(&lp->lock, flags); -#ifdef __mips__ - dma_cache_wback_inv((unsigned long)buf, length); + /* failsafe... (handle txdone now if half of FDs are used) */ + if ((lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM > + TX_FD_NUM / 2) + tc35815_txdone(dev); + + if (netif_msg_pktdata(lp)) + print_eth(skb->data); +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_start].skb) { + printk("%s: tx_skbs conflict.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_start].skb); #endif - - spin_lock_irqsave(&lp->lock, flags); - - /* failsafe... */ - if (lp->tfd_start != lp->tfd_end) - tc35815_txdone(dev); - - - txfd->bd.BuffData = cpu_to_le32(virt_to_bus(buf)); - - txfd->bd.BDCtl = cpu_to_le32(length); - txfd->fd.FDSystem = cpu_to_le32((__u32)skb); - txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); - - if (lp->tfd_start == lp->tfd_end) { - /* Start DMA Transmitter. */ - txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); + lp->tx_skbs[lp->tfd_start].skb = skb; + lp->tx_skbs[lp->tfd_start].skb_dma = pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE); + + /*add to ring */ + txfd = &lp->tfd_base[lp->tfd_start]; + txfd->bd.BuffData = cpu_to_le32(lp->tx_skbs[lp->tfd_start].skb_dma); + txfd->bd.BDCtl = cpu_to_le32(skb->len); + txfd->fd.FDSystem = cpu_to_le32(lp->tfd_start); + txfd->fd.FDCtl = cpu_to_le32(FD_CownsFD | (1 << FD_BDCnt_SHIFT)); + + if (lp->tfd_start == lp->tfd_end) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + /* Start DMA Transmitter. */ + txfd->fd.FDNext |= cpu_to_le32(FD_Next_EOL); #ifdef GATHER_TXINT - txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); + txfd->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); #endif - if (tc35815_debug > 2) { - printk("%s: starting TxFD.\n", dev->name); - dump_txfd(txfd); - if (tc35815_debug > 3) - print_eth(buf); - } - tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr); - } else { - txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); - if (tc35815_debug > 2) { - printk("%s: queueing TxFD.\n", dev->name); - dump_txfd(txfd); - if (tc35815_debug > 3) - print_eth(buf); - } + if (netif_msg_tx_queued(lp)) { + printk("%s: starting TxFD.\n", dev->name); + dump_txfd(txfd); } - lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); + } else { + txfd->fd.FDNext &= cpu_to_le32(~FD_Next_EOL); + if (netif_msg_tx_queued(lp)) { + printk("%s: queueing TxFD.\n", dev->name); + dump_txfd(txfd); + } + } + lp->tfd_start = (lp->tfd_start + 1) % TX_FD_NUM; - dev->trans_start = jiffies; + dev->trans_start = jiffies; - if ((lp->tfd_start + 1) % TX_FD_NUM != lp->tfd_end) { - /* we can send another packet */ - lp->tbusy = 0; - netif_start_queue(dev); - } else { - netif_stop_queue(dev); - if (tc35815_debug > 1) - printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); - } - spin_unlock_irqrestore(&lp->lock, flags); + /* If we just used up the very last entry in the + * TX ring on this device, tell the queueing + * layer to send no more. + */ + if (tc35815_tx_full(dev)) { + if (netif_msg_tx_queued(lp)) + printk(KERN_WARNING "%s: TxFD Exhausted.\n", dev->name); + netif_stop_queue(dev); } + /* When the TX completion hw interrupt arrives, this + * is when the transmit statistics are updated. + */ + + spin_unlock_irqrestore(&lp->lock, flags); return 0; } #define FATAL_ERROR_INT \ (Int_IntPCI | Int_DmParErr | Int_IntNRAbt) -static void tc35815_fatal_error_interrupt(struct net_device *dev, int status) +static void tc35815_fatal_error_interrupt(struct net_device *dev, u32 status) { static int count; printk(KERN_WARNING "%s: Fatal Error Intterrupt (%#x):", dev->name, status); - if (status & Int_IntPCI) printk(" IntPCI"); if (status & Int_DmParErr) @@ -1033,110 +1321,170 @@ static void tc35815_fatal_error_interrupt(struct net_device *dev, int status) printk("\n"); if (count++ > 100) panic("%s: Too many fatal errors.", dev->name); - printk(KERN_WARNING "%s: Resetting %s...\n", dev->name, cardname); + printk(KERN_WARNING "%s: Resetting ...\n", dev->name); /* Try to restart the adaptor. */ - tc35815_chip_reset(dev); - tc35815_clear_queues(dev); - tc35815_chip_init(dev); + tc35815_restart(dev); +} + +#ifdef TC35815_NAPI +static int tc35815_do_interrupt(struct net_device *dev, u32 status, int limit) +#else +static int tc35815_do_interrupt(struct net_device *dev, u32 status) +#endif +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int ret = -1; + + /* Fatal errors... */ + if (status & FATAL_ERROR_INT) { + tc35815_fatal_error_interrupt(dev, status); + return 0; + } + /* recoverable errors */ + if (status & Int_IntFDAEx) { + /* disable FDAEx int. (until we make rooms...) */ + tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En); + printk(KERN_WARNING + "%s: Free Descriptor Area Exhausted (%#x).\n", + dev->name, status); + lp->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntBLEx) { + /* disable BLEx int. (until we make rooms...) */ + tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En); + printk(KERN_WARNING + "%s: Buffer List Exhausted (%#x).\n", + dev->name, status); + lp->stats.rx_dropped++; + ret = 0; + } + if (status & Int_IntExBD) { + printk(KERN_WARNING + "%s: Excessive Buffer Descriptiors (%#x).\n", + dev->name, status); + lp->stats.rx_length_errors++; + ret = 0; + } + + /* normal notification */ + if (status & Int_IntMacRx) { + /* Got a packet(s). */ +#ifdef TC35815_NAPI + ret = tc35815_rx(dev, limit); +#else + tc35815_rx(dev); + ret = 0; +#endif + lp->lstats.rx_ints++; + } + if (status & Int_IntMacTx) { + /* Transmit complete. */ + lp->lstats.tx_ints++; + tc35815_txdone(dev); + netif_wake_queue(dev); + ret = 0; + } + return ret; } /* * The typical workload of the driver: - * Handle the network interface interrupts. + * Handle the network interface interrupts. */ static irqreturn_t tc35815_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; - struct tc35815_regs *tr; - struct tc35815_local *lp; - int status, boguscount = 0; - int handled = 0; - - if (dev == NULL) { - printk(KERN_WARNING "%s: irq %d for unknown device.\n", cardname, irq); - return IRQ_NONE; - } - - tr = (struct tc35815_regs*)dev->base_addr; - lp = dev->priv; - - do { - status = tc_readl(&tr->Int_Src); - if (status == 0) - break; - handled = 1; - tc_writel(status, &tr->Int_Src); /* write to clear */ - - /* Fatal errors... */ - if (status & FATAL_ERROR_INT) { - tc35815_fatal_error_interrupt(dev, status); - break; - } - /* recoverable errors */ - if (status & Int_IntFDAEx) { - /* disable FDAEx int. (until we make rooms...) */ - tc_writel(tc_readl(&tr->Int_En) & ~Int_FDAExEn, &tr->Int_En); - printk(KERN_WARNING - "%s: Free Descriptor Area Exhausted (%#x).\n", - dev->name, status); - lp->stats.rx_dropped++; - } - if (status & Int_IntBLEx) { - /* disable BLEx int. (until we make rooms...) */ - tc_writel(tc_readl(&tr->Int_En) & ~Int_BLExEn, &tr->Int_En); - printk(KERN_WARNING - "%s: Buffer List Exhausted (%#x).\n", - dev->name, status); - lp->stats.rx_dropped++; - } - if (status & Int_IntExBD) { - printk(KERN_WARNING - "%s: Excessive Buffer Descriptiors (%#x).\n", - dev->name, status); - lp->stats.rx_length_errors++; - } - /* normal notification */ - if (status & Int_IntMacRx) { - /* Got a packet(s). */ - lp->lstats.rx_ints++; - tc35815_rx(dev); - } - if (status & Int_IntMacTx) { - lp->lstats.tx_ints++; - tc35815_txdone(dev); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; +#ifdef TC35815_NAPI + u32 dmactl = tc_readl(&tr->DMA_Ctl); + + if (!(dmactl & DMA_IntMask)) { + /* disable interrupts */ + tc_writel(dmactl | DMA_IntMask, &tr->DMA_Ctl); + if (netif_rx_schedule_prep(dev)) + __netif_rx_schedule(dev); + else { + printk(KERN_ERR "%s: interrupt taken in poll\n", + dev->name); + BUG(); } - } while (++boguscount < 20) ; + (void)tc_readl(&tr->Int_Src); /* flush */ + return IRQ_HANDLED; + } + return IRQ_NONE; +#else + struct tc35815_local *lp = dev->priv; + int handled; + u32 status; + + spin_lock(&lp->lock); + status = tc_readl(&tr->Int_Src); + tc_writel(status, &tr->Int_Src); /* write to clear */ + handled = tc35815_do_interrupt(dev, status); + (void)tc_readl(&tr->Int_Src); /* flush */ + spin_unlock(&lp->lock); + return IRQ_RETVAL(handled >= 0); +#endif /* TC35815_NAPI */ +} - return IRQ_RETVAL(handled); +#ifdef CONFIG_NET_POLL_CONTROLLER +static void tc35815_poll_controller(struct net_device *dev) +{ + disable_irq(dev->irq); + tc35815_interrupt(dev->irq, dev); + enable_irq(dev->irq); } +#endif /* We have a good packet(s), get it/them out of the buffers. */ +#ifdef TC35815_NAPI +static int +tc35815_rx(struct net_device *dev, int limit) +#else static void tc35815_rx(struct net_device *dev) +#endif { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; unsigned int fdctl; int i; int buf_free_count = 0; int fd_free_count = 0; +#ifdef TC35815_NAPI + int received = 0; +#endif while (!((fdctl = le32_to_cpu(lp->rfd_cur->fd.FDCtl)) & FD_CownsFD)) { int status = le32_to_cpu(lp->rfd_cur->fd.FDStat); int pkt_len = fdctl & FD_FDLength_MASK; - struct RxFD *next_rfd; int bd_count = (fdctl & FD_BDCnt_MASK) >> FD_BDCnt_SHIFT; +#ifdef DEBUG + struct RxFD *next_rfd; +#endif +#if (RX_CTL_CMD & Rx_StripCRC) == 0 + pkt_len -= 4; +#endif - if (tc35815_debug > 2) + if (netif_msg_rx_status(lp)) dump_rxfd(lp->rfd_cur); if (status & Rx_Good) { - /* Malloc up new buffer. */ struct sk_buff *skb; unsigned char *data; - int cur_bd, offset; - - lp->stats.rx_bytes += pkt_len; + int cur_bd; +#ifdef TC35815_USE_PACKEDBUFFER + int offset; +#endif +#ifdef TC35815_NAPI + if (--limit < 0) + break; +#endif +#ifdef TC35815_USE_PACKEDBUFFER + BUG_ON(bd_count > 2); skb = dev_alloc_skb(pkt_len + 2); /* +2: for reserve */ if (skb == NULL) { printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n", @@ -1145,7 +1493,6 @@ tc35815_rx(struct net_device *dev) break; } skb_reserve(skb, 2); /* 16 bit alignment */ - skb->dev = dev; data = skb_put(skb, pkt_len); @@ -1155,25 +1502,69 @@ tc35815_rx(struct net_device *dev) while (offset < pkt_len && cur_bd < bd_count) { int len = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BDCtl) & BD_BuffLength_MASK; - void *rxbuf = - bus_to_virt(le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData)); -#ifdef __mips__ - dma_cache_inv((unsigned long)rxbuf, len); + dma_addr_t dma = le32_to_cpu(lp->rfd_cur->bd[cur_bd].BuffData); + void *rxbuf = rxbuf_bus_to_virt(lp, dma); + if (offset + len > pkt_len) + len = pkt_len - offset; +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_dma_sync_single_for_cpu(lp->pci_dev, + dma, len, + PCI_DMA_FROMDEVICE); #endif memcpy(data + offset, rxbuf, len); +#ifdef TC35815_DMA_SYNC_ONDEMAND + pci_dma_sync_single_for_device(lp->pci_dev, + dma, len, + PCI_DMA_FROMDEVICE); +#endif offset += len; cur_bd++; } -#if 0 - print_buf(data,pkt_len); +#else /* TC35815_USE_PACKEDBUFFER */ + BUG_ON(bd_count > 1); + cur_bd = (le32_to_cpu(lp->rfd_cur->bd[0].BDCtl) + & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; +#ifdef DEBUG + if (cur_bd >= RX_BUF_NUM) { + printk("%s: invalid BDID.\n", dev->name); + panic_queues(dev); + } + BUG_ON(lp->rx_skbs[cur_bd].skb_dma != + (le32_to_cpu(lp->rfd_cur->bd[0].BuffData) & ~3)); + if (!lp->rx_skbs[cur_bd].skb) { + printk("%s: NULL skb.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(cur_bd >= RX_BUF_NUM); #endif - if (tc35815_debug > 3) + skb = lp->rx_skbs[cur_bd].skb; + prefetch(skb->data); + lp->rx_skbs[cur_bd].skb = NULL; + lp->fbl_count--; + pci_unmap_single(lp->pci_dev, + lp->rx_skbs[cur_bd].skb_dma, + RX_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (!HAVE_DMA_RXALIGN(lp)) + memmove(skb->data, skb->data - 2, pkt_len); + data = skb_put(skb, pkt_len); +#endif /* TC35815_USE_PACKEDBUFFER */ + if (netif_msg_pktdata(lp)) print_eth(data); skb->protocol = eth_type_trans(skb, dev); +#ifdef TC35815_NAPI + netif_receive_skb(skb); + received++; +#else netif_rx(skb); +#endif + dev->last_rx = jiffies; lp->stats.rx_packets++; + lp->stats.rx_bytes += pkt_len; } else { lp->stats.rx_errors++; + printk(KERN_DEBUG "%s: Rx error (status %x)\n", + dev->name, status & Rx_Stat_Mask); /* WORKAROUND: LongErr and CRCErr means Overflow. */ if ((status & Rx_LongErr) && (status & Rx_CRCErr)) { status &= ~(Rx_LongErr|Rx_CRCErr); @@ -1190,62 +1581,149 @@ tc35815_rx(struct net_device *dev) int bdctl = le32_to_cpu(lp->rfd_cur->bd[bd_count - 1].BDCtl); unsigned char id = (bdctl & BD_RxBDID_MASK) >> BD_RxBDID_SHIFT; - if (id >= RX_BUF_PAGES) { +#ifdef DEBUG + if (id >= RX_BUF_NUM) { printk("%s: invalid BDID.\n", dev->name); panic_queues(dev); } +#else + BUG_ON(id >= RX_BUF_NUM); +#endif /* free old buffers */ - while (lp->fbl_curid != id) { - bdctl = le32_to_cpu(lp->fbl_ptr->bd[lp->fbl_curid].BDCtl); +#ifdef TC35815_USE_PACKEDBUFFER + while (lp->fbl_curid != id) +#else + while (lp->fbl_count < RX_BUF_NUM) +#endif + { +#ifdef TC35815_USE_PACKEDBUFFER + unsigned char curid = lp->fbl_curid; +#else + unsigned char curid = + (id + 1 + lp->fbl_count) % RX_BUF_NUM; +#endif + struct BDesc *bd = &lp->fbl_ptr->bd[curid]; +#ifdef DEBUG + bdctl = le32_to_cpu(bd->BDCtl); if (bdctl & BD_CownsBD) { printk("%s: Freeing invalid BD.\n", dev->name); panic_queues(dev); } +#endif /* pass BD to controler */ +#ifndef TC35815_USE_PACKEDBUFFER + if (!lp->rx_skbs[curid].skb) { + lp->rx_skbs[curid].skb = + alloc_rxbuf_skb(dev, + lp->pci_dev, + &lp->rx_skbs[curid].skb_dma); + if (!lp->rx_skbs[curid].skb) + break; /* try on next reception */ + bd->BuffData = cpu_to_le32(lp->rx_skbs[curid].skb_dma); + } +#endif /* TC35815_USE_PACKEDBUFFER */ /* Note: BDLength was modified by chip. */ - lp->fbl_ptr->bd[lp->fbl_curid].BDCtl = - cpu_to_le32(BD_CownsBD | - (lp->fbl_curid << BD_RxBDID_SHIFT) | - PAGE_SIZE); - lp->fbl_curid = - (lp->fbl_curid + 1) % RX_BUF_PAGES; - if (tc35815_debug > 2) { + bd->BDCtl = cpu_to_le32(BD_CownsBD | + (curid << BD_RxBDID_SHIFT) | + RX_BUF_SIZE); +#ifdef TC35815_USE_PACKEDBUFFER + lp->fbl_curid = (curid + 1) % RX_BUF_NUM; + if (netif_msg_rx_status(lp)) { printk("%s: Entering new FBD %d\n", dev->name, lp->fbl_curid); dump_frfd(lp->fbl_ptr); } +#else + lp->fbl_count++; +#endif buf_free_count++; } } /* put RxFD back to controller */ - next_rfd = bus_to_virt(le32_to_cpu(lp->rfd_cur->fd.FDNext)); -#ifdef __mips__ - next_rfd = (struct RxFD *)vtonocache(next_rfd); -#endif +#ifdef DEBUG + next_rfd = fd_bus_to_virt(lp, + le32_to_cpu(lp->rfd_cur->fd.FDNext)); if (next_rfd < lp->rfd_base || next_rfd > lp->rfd_limit) { printk("%s: RxFD FDNext invalid.\n", dev->name); panic_queues(dev); } +#endif for (i = 0; i < (bd_count + 1) / 2 + 1; i++) { /* pass FD to controler */ - lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); /* for debug */ +#ifdef DEBUG + lp->rfd_cur->fd.FDNext = cpu_to_le32(0xdeaddead); +#else + lp->rfd_cur->fd.FDNext = cpu_to_le32(FD_Next_EOL); +#endif lp->rfd_cur->fd.FDCtl = cpu_to_le32(FD_CownsFD); lp->rfd_cur++; fd_free_count++; } - - lp->rfd_cur = next_rfd; + if (lp->rfd_cur > lp->rfd_limit) + lp->rfd_cur = lp->rfd_base; +#ifdef DEBUG + if (lp->rfd_cur != next_rfd) + printk("rfd_cur = %p, next_rfd %p\n", + lp->rfd_cur, next_rfd); +#endif } /* re-enable BL/FDA Exhaust interrupts. */ if (fd_free_count) { - tc_writel(tc_readl(&tr->Int_En) | Int_FDAExEn, &tr->Int_En); + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 en, en_old = tc_readl(&tr->Int_En); + en = en_old | Int_FDAExEn; if (buf_free_count) - tc_writel(tc_readl(&tr->Int_En) | Int_BLExEn, &tr->Int_En); + en |= Int_BLExEn; + if (en != en_old) + tc_writel(en, &tr->Int_En); } +#ifdef TC35815_NAPI + return received; +#endif +} + +#ifdef TC35815_NAPI +static int +tc35815_poll(struct net_device *dev, int *budget) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int limit = min(*budget, dev->quota); + int received = 0, handled; + u32 status; + + spin_lock(&lp->lock); + status = tc_readl(&tr->Int_Src); + do { + tc_writel(status, &tr->Int_Src); /* write to clear */ + + handled = tc35815_do_interrupt(dev, status, limit); + if (handled >= 0) { + received += handled; + limit -= handled; + if (limit <= 0) + break; + } + status = tc_readl(&tr->Int_Src); + } while (status); + spin_unlock(&lp->lock); + + dev->quota -= received; + *budget -= received; + if (limit <= 0) + return 1; + + netif_rx_complete(dev); + /* enable interrupts */ + tc_writel(tc_readl(&tr->DMA_Ctl) & ~DMA_IntMask, &tr->DMA_Ctl); + return 0; } +#endif #ifdef NO_CHECK_CARRIER #define TX_STA_ERR (Tx_ExColl|Tx_Under|Tx_Defer|Tx_LateColl|Tx_TxPar|Tx_SQErr) @@ -1265,9 +1743,17 @@ tc35815_check_tx_stat(struct net_device *dev, int status) if (status & Tx_TxColl_MASK) lp->stats.collisions += status & Tx_TxColl_MASK; +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have NCarr */ + if (lp->boardtype == TC35815_TX4939) + status &= ~Tx_NCarr; +#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (lp->fullduplex) + if ((lp->timer_state != asleep && lp->timer_state != lcheck) + || lp->fullduplex) status &= ~Tx_NCarr; +#endif +#endif if (!(status & TX_STA_ERR)) { /* no error. */ @@ -1283,6 +1769,15 @@ tc35815_check_tx_stat(struct net_device *dev, int status) if (status & Tx_Under) { lp->stats.tx_fifo_errors++; msg = "Tx FIFO Underrun."; + if (lp->lstats.tx_underrun < TX_THRESHOLD_KEEP_LIMIT) { + lp->lstats.tx_underrun++; + if (lp->lstats.tx_underrun >= TX_THRESHOLD_KEEP_LIMIT) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + tc_writel(TX_THRESHOLD_MAX, &tr->TxThrsh); + msg = "Tx FIFO Underrun.Change Tx threshold to max."; + } + } } if (status & Tx_Defer) { lp->stats.tx_fifo_errors++; @@ -1306,18 +1801,19 @@ tc35815_check_tx_stat(struct net_device *dev, int status) lp->stats.tx_heartbeat_errors++; msg = "Signal Quality Error."; } - if (msg) + if (msg && netif_msg_tx_err(lp)) printk(KERN_WARNING "%s: %s (%#x)\n", dev->name, msg, status); } +/* This handles TX complete events posted by the device + * via interrupts. + */ static void tc35815_txdone(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; struct TxFD *txfd; unsigned int fdctl; - int num_done = 0; txfd = &lp->tfd_base[lp->tfd_end]; while (lp->tfd_start != lp->tfd_end && @@ -1325,38 +1821,61 @@ tc35815_txdone(struct net_device *dev) int status = le32_to_cpu(txfd->fd.FDStat); struct sk_buff *skb; unsigned long fdnext = le32_to_cpu(txfd->fd.FDNext); + u32 fdsystem = le32_to_cpu(txfd->fd.FDSystem); - if (tc35815_debug > 2) { + if (netif_msg_tx_done(lp)) { printk("%s: complete TxFD.\n", dev->name); dump_txfd(txfd); } tc35815_check_tx_stat(dev, status); - skb = (struct sk_buff *)le32_to_cpu(txfd->fd.FDSystem); + skb = fdsystem != 0xffffffff ? + lp->tx_skbs[fdsystem].skb : NULL; +#ifdef DEBUG + if (lp->tx_skbs[lp->tfd_end].skb != skb) { + printk("%s: tx_skbs mismatch.\n", dev->name); + panic_queues(dev); + } +#else + BUG_ON(lp->tx_skbs[lp->tfd_end].skb != skb); +#endif if (skb) { + lp->stats.tx_bytes += skb->len; + pci_unmap_single(lp->pci_dev, lp->tx_skbs[lp->tfd_end].skb_dma, skb->len, PCI_DMA_TODEVICE); + lp->tx_skbs[lp->tfd_end].skb = NULL; + lp->tx_skbs[lp->tfd_end].skb_dma = 0; +#ifdef TC35815_NAPI dev_kfree_skb_any(skb); +#else + dev_kfree_skb_irq(skb); +#endif } - txfd->fd.FDSystem = cpu_to_le32(0); + txfd->fd.FDSystem = cpu_to_le32(0xffffffff); - num_done++; lp->tfd_end = (lp->tfd_end + 1) % TX_FD_NUM; txfd = &lp->tfd_base[lp->tfd_end]; - if ((fdnext & ~FD_Next_EOL) != virt_to_bus(txfd)) { +#ifdef DEBUG + if ((fdnext & ~FD_Next_EOL) != fd_virt_to_bus(lp, txfd)) { printk("%s: TxFD FDNext invalid.\n", dev->name); panic_queues(dev); } +#endif if (fdnext & FD_Next_EOL) { /* DMA Transmitter has been stopping... */ if (lp->tfd_end != lp->tfd_start) { + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; int head = (lp->tfd_start + TX_FD_NUM - 1) % TX_FD_NUM; struct TxFD* txhead = &lp->tfd_base[head]; int qlen = (lp->tfd_start + TX_FD_NUM - lp->tfd_end) % TX_FD_NUM; +#ifdef DEBUG if (!(le32_to_cpu(txfd->fd.FDCtl) & FD_CownsFD)) { printk("%s: TxFD FDCtl invalid.\n", dev->name); panic_queues(dev); } +#endif /* log max queue length */ if (lp->lstats.max_tx_qlen < qlen) lp->lstats.max_tx_qlen = qlen; @@ -1367,21 +1886,23 @@ tc35815_txdone(struct net_device *dev) #ifdef GATHER_TXINT txhead->fd.FDCtl |= cpu_to_le32(FD_FrmOpt_IntTx); #endif - if (tc35815_debug > 2) { + if (netif_msg_tx_queued(lp)) { printk("%s: start TxFD on queue.\n", dev->name); dump_txfd(txfd); } - tc_writel(virt_to_bus(txfd), &tr->TxFrmPtr); + tc_writel(fd_virt_to_bus(lp, txfd), &tr->TxFrmPtr); } break; } } - if (num_done > 0 && lp->tbusy) { - lp->tbusy = 0; - netif_start_queue(dev); - } + /* If we had stopped the queue due to a "tx full" + * condition, and space has now been made available, + * wake up the queue. + */ + if (netif_queue_stopped(dev) && ! tc35815_tx_full(dev)) + netif_wake_queue(dev); } /* The inverse routine to tc35815_open(). */ @@ -1389,18 +1910,18 @@ static int tc35815_close(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - - lp->tbusy = 1; netif_stop_queue(dev); /* Flush the Tx and disable Rx here. */ + del_timer(&lp->timer); /* Kill if running */ tc35815_chip_reset(dev); free_irq(dev->irq, dev); tc35815_free_queues(dev); return 0; + } /* @@ -1410,29 +1931,29 @@ tc35815_close(struct net_device *dev) static struct net_device_stats *tc35815_get_stats(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - unsigned long flags; - + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; if (netif_running(dev)) { - spin_lock_irqsave(&lp->lock, flags); /* Update the statistics from the device registers. */ lp->stats.rx_missed_errors = tc_readl(&tr->Miss_Cnt); - spin_unlock_irqrestore(&lp->lock, flags); } return &lp->stats; } -static void tc35815_set_cam_entry(struct tc35815_regs *tr, int index, unsigned char *addr) +static void tc35815_set_cam_entry(struct net_device *dev, int index, unsigned char *addr) { + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; int cam_index = index * 6; - unsigned long cam_data; - unsigned long saved_addr; + u32 cam_data; + u32 saved_addr; saved_addr = tc_readl(&tr->CAM_Adr); - if (tc35815_debug > 1) { + if (netif_msg_hw(lp)) { int i; - printk(KERN_DEBUG "%s: CAM %d:", cardname, index); + printk(KERN_DEBUG "%s: CAM %d:", dev->name, index); for (i = 0; i < 6; i++) printk(" %02x", addr[i]); printk("\n"); @@ -1459,14 +1980,6 @@ static void tc35815_set_cam_entry(struct tc35815_regs *tr, int index, unsigned c tc_writel(cam_data, &tr->CAM_Data); } - if (tc35815_debug > 2) { - int i; - for (i = cam_index / 4; i < cam_index / 4 + 2; i++) { - tc_writel(i * 4, &tr->CAM_Adr); - printk("CAM 0x%x: %08lx", - i * 4, tc_readl(&tr->CAM_Data)); - } - } tc_writel(saved_addr, &tr->CAM_Adr); } @@ -1481,10 +1994,19 @@ static void tc35815_set_cam_entry(struct tc35815_regs *tr, int index, unsigned c static void tc35815_set_multicast_list(struct net_device *dev) { - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; if (dev->flags&IFF_PROMISC) { +#ifdef WORKAROUND_100HALF_PROMISC + /* With some (all?) 100MHalf HUB, controller will hang + * if we enabled promiscuous mode before linkup... */ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + if (!(tc_mdio_read(dev, pid, MII_BMSR) & BMSR_LSTATUS)) + return; +#endif /* Enable promiscuous mode */ tc_writel(CAM_CompEn | CAM_BroadAcc | CAM_GroupAcc | CAM_StationAcc, &tr->CAM_Ctl); } @@ -1506,7 +2028,7 @@ tc35815_set_multicast_list(struct net_device *dev) if (!cur_addr) break; /* entry 0,1 is reserved. */ - tc35815_set_cam_entry(tr, i + 2, cur_addr->dmi_addr); + tc35815_set_cam_entry(dev, i + 2, cur_addr->dmi_addr); ena_bits |= CAM_Ena_Bit(i + 2); } tc_writel(ena_bits, &tr->CAM_Ena); @@ -1518,122 +2040,753 @@ tc35815_set_multicast_list(struct net_device *dev) } } -static unsigned long tc_phy_read(struct net_device *dev, struct tc35815_regs *tr, int phy, int phy_reg) +static void tc35815_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct tc35815_local *lp = dev->priv; - unsigned long data; - unsigned long flags; + strcpy(info->driver, MODNAME); + strcpy(info->version, DRV_VERSION); + strcpy(info->bus_info, pci_name(lp->pci_dev)); +} - spin_lock_irqsave(&lp->lock, flags); +static int tc35815_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = dev->priv; + spin_lock_irq(&lp->lock); + mii_ethtool_gset(&lp->mii, cmd); + spin_unlock_irq(&lp->lock); + return 0; +} - tc_writel(MD_CA_Busy | (phy << 5) | phy_reg, &tr->MD_CA); +static int tc35815_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) +{ + struct tc35815_local *lp = dev->priv; + int rc; +#if 1 /* use our negotiation method... */ + /* Verify the settings we care about. */ + if (cmd->autoneg != AUTONEG_ENABLE && + cmd->autoneg != AUTONEG_DISABLE) + return -EINVAL; + if (cmd->autoneg == AUTONEG_DISABLE && + ((cmd->speed != SPEED_100 && + cmd->speed != SPEED_10) || + (cmd->duplex != DUPLEX_HALF && + cmd->duplex != DUPLEX_FULL))) + return -EINVAL; + + /* Ok, do it to it. */ + spin_lock_irq(&lp->lock); + del_timer(&lp->timer); + tc35815_start_auto_negotiation(dev, cmd); + spin_unlock_irq(&lp->lock); + rc = 0; +#else + spin_lock_irq(&lp->lock); + rc = mii_ethtool_sset(&lp->mii, cmd); + spin_unlock_irq(&lp->lock); +#endif + return rc; +} + +static int tc35815_nway_reset(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int rc; + spin_lock_irq(&lp->lock); + rc = mii_nway_restart(&lp->mii); + spin_unlock_irq(&lp->lock); + return rc; +} + +static u32 tc35815_get_link(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int rc; + spin_lock_irq(&lp->lock); + rc = mii_link_ok(&lp->mii); + spin_unlock_irq(&lp->lock); + return rc; +} + +static u32 tc35815_get_msglevel(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return lp->msg_enable; +} + +static void tc35815_set_msglevel(struct net_device *dev, u32 datum) +{ + struct tc35815_local *lp = dev->priv; + lp->msg_enable = datum; +} + +static int tc35815_get_stats_count(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + return sizeof(lp->lstats) / sizeof(int); +} + +static void tc35815_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) +{ + struct tc35815_local *lp = dev->priv; + data[0] = lp->lstats.max_tx_qlen; + data[1] = lp->lstats.tx_ints; + data[2] = lp->lstats.rx_ints; + data[3] = lp->lstats.tx_underrun; +} + +static struct { + const char str[ETH_GSTRING_LEN]; +} ethtool_stats_keys[] = { + { "max_tx_qlen" }, + { "tx_ints" }, + { "rx_ints" }, + { "tx_underrun" }, +}; + +static void tc35815_get_strings(struct net_device *dev, u32 stringset, u8 *data) +{ + memcpy(data, ethtool_stats_keys, sizeof(ethtool_stats_keys)); +} + +static const struct ethtool_ops tc35815_ethtool_ops = { + .get_drvinfo = tc35815_get_drvinfo, + .get_settings = tc35815_get_settings, + .set_settings = tc35815_set_settings, + .nway_reset = tc35815_nway_reset, + .get_link = tc35815_get_link, + .get_msglevel = tc35815_get_msglevel, + .set_msglevel = tc35815_set_msglevel, + .get_strings = tc35815_get_strings, + .get_stats_count = tc35815_get_stats_count, + .get_ethtool_stats = tc35815_get_ethtool_stats, + .get_perm_addr = ethtool_op_get_perm_addr, +}; + +static int tc35815_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) +{ + struct tc35815_local *lp = dev->priv; + int rc; + + if (!netif_running(dev)) + return -EINVAL; + + spin_lock_irq(&lp->lock); + rc = generic_mii_ioctl(&lp->mii, if_mii(rq), cmd, NULL); + spin_unlock_irq(&lp->lock); + + return rc; +} + +static int tc_mdio_read(struct net_device *dev, int phy_id, int location) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + u32 data; + tc_writel(MD_CA_Busy | (phy_id << 5) | location, &tr->MD_CA); while (tc_readl(&tr->MD_CA) & MD_CA_Busy) ; data = tc_readl(&tr->MD_Data); - spin_unlock_irqrestore(&lp->lock, flags); - return data; + return data & 0xffff; +} + +static void tc_mdio_write(struct net_device *dev, int phy_id, int location, + int val) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + tc_writel(val, &tr->MD_Data); + tc_writel(MD_CA_Busy | MD_CA_Wr | (phy_id << 5) | location, &tr->MD_CA); + while (tc_readl(&tr->MD_CA) & MD_CA_Busy) + ; } -static void tc_phy_write(struct net_device *dev, unsigned long d, struct tc35815_regs *tr, int phy, int phy_reg) +/* Auto negotiation. The scheme is very simple. We have a timer routine + * that keeps watching the auto negotiation process as it progresses. + * The DP83840 is first told to start doing it's thing, we set up the time + * and place the timer state machine in it's initial state. + * + * Here the timer peeks at the DP83840 status registers at each click to see + * if the auto negotiation has completed, we assume here that the DP83840 PHY + * will time out at some point and just tell us what (didn't) happen. For + * complete coverage we only allow so many of the ticks at this level to run, + * when this has expired we print a warning message and try another strategy. + * This "other" strategy is to force the interface into various speed/duplex + * configurations and we stop when we see a link-up condition before the + * maximum number of "peek" ticks have occurred. + * + * Once a valid link status has been detected we configure the BigMAC and + * the rest of the Happy Meal to speak the most efficient protocol we could + * get a clean link for. The priority for link configurations, highest first + * is: + * 100 Base-T Full Duplex + * 100 Base-T Half Duplex + * 10 Base-T Full Duplex + * 10 Base-T Half Duplex + * + * We start a new timer now, after a successful auto negotiation status has + * been detected. This timer just waits for the link-up bit to get set in + * the BMCR of the DP83840. When this occurs we print a kernel log message + * describing the link type in use and the fact that it is up. + * + * If a fatal error of some sort is signalled and detected in the interrupt + * service routine, and the chip is reset, or the link is ifconfig'd down + * and then back up, this entire process repeats itself all over again. + */ +/* Note: Above comments are come from sunhme driver. */ + +static int tc35815_try_next_permutation(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - unsigned long flags; + int pid = lp->phy_addr; + unsigned short bmcr; - spin_lock_irqsave(&lp->lock, flags); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); - tc_writel(d, &tr->MD_Data); - tc_writel(MD_CA_Busy | MD_CA_Wr | (phy << 5) | phy_reg, &tr->MD_CA); - while (tc_readl(&tr->MD_CA) & MD_CA_Busy) - ; - spin_unlock_irqrestore(&lp->lock, flags); + /* Downgrade from full to half duplex. Only possible via ethtool. */ + if (bmcr & BMCR_FULLDPLX) { + bmcr &= ~BMCR_FULLDPLX; + printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr); + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + return 0; + } + + /* Downgrade from 100 to 10. */ + if (bmcr & BMCR_SPEED100) { + bmcr &= ~BMCR_SPEED100; + printk(KERN_DEBUG "%s: try next permutation (BMCR %x)\n", dev->name, bmcr); + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + return 0; + } + + /* We've tried everything. */ + return -1; } -static void tc35815_phy_chip_init(struct net_device *dev) +static void +tc35815_display_link_mode(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - static int first = 1; - unsigned short ctl; - - if (first) { - unsigned short id0, id1; - int count; - first = 0; - - /* first data written to the PHY will be an ID number */ - tc_phy_write(dev, 0, tr, 0, MII_CONTROL); /* ID:0 */ -#if 0 - tc_phy_write(dev, MIICNTL_RESET, tr, 0, MII_CONTROL); - printk(KERN_INFO "%s: Resetting PHY...", dev->name); - while (tc_phy_read(dev, tr, 0, MII_CONTROL) & MIICNTL_RESET) - ; - printk("\n"); - tc_phy_write(dev, MIICNTL_AUTO|MIICNTL_SPEED|MIICNTL_FDX, tr, 0, - MII_CONTROL); -#endif - id0 = tc_phy_read(dev, tr, 0, MII_PHY_ID0); - id1 = tc_phy_read(dev, tr, 0, MII_PHY_ID1); - printk(KERN_DEBUG "%s: PHY ID %04x %04x\n", dev->name, - id0, id1); - if (lp->option & TC35815_OPT_10M) { - lp->linkspeed = 10; - lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0; - } else if (lp->option & TC35815_OPT_100M) { - lp->linkspeed = 100; - lp->fullduplex = (lp->option & TC35815_OPT_FULLDUP) != 0; + int pid = lp->phy_addr; + unsigned short lpa, bmcr; + char *speed = "", *duplex = ""; + + lpa = tc_mdio_read(dev, pid, MII_LPA); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL))) + speed = "100Mb/s"; + else + speed = "10Mb/s"; + if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL))) + duplex = "Full Duplex"; + else + duplex = "Half Duplex"; + + if (netif_msg_link(lp)) + printk(KERN_INFO "%s: Link is up at %s, %s.\n", + dev->name, speed, duplex); + printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n", + dev->name, + bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa); +} + +static void tc35815_display_forced_link_mode(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmcr; + char *speed = "", *duplex = ""; + + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_SPEED100) + speed = "100Mb/s"; + else + speed = "10Mb/s"; + if (bmcr & BMCR_FULLDPLX) + duplex = "Full Duplex.\n"; + else + duplex = "Half Duplex.\n"; + + if (netif_msg_link(lp)) + printk(KERN_INFO "%s: Link has been forced up at %s, %s", + dev->name, speed, duplex); +} + +static void tc35815_set_link_modes(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int pid = lp->phy_addr; + unsigned short bmcr, lpa; + int speed; + + if (lp->timer_state == arbwait) { + lpa = tc_mdio_read(dev, pid, MII_LPA); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + printk(KERN_DEBUG "%s: MII BMCR %04x BMSR %04x LPA %04x\n", + dev->name, + bmcr, tc_mdio_read(dev, pid, MII_BMSR), lpa); + if (!(lpa & (LPA_10HALF | LPA_10FULL | + LPA_100HALF | LPA_100FULL))) { + /* fall back to 10HALF */ + printk(KERN_INFO "%s: bad ability %04x - falling back to 10HD.\n", + dev->name, lpa); + lpa = LPA_10HALF; + } + if (options.duplex ? (bmcr & BMCR_FULLDPLX) : (lpa & (LPA_100FULL | LPA_10FULL))) + lp->fullduplex = 1; + else + lp->fullduplex = 0; + if (options.speed ? (bmcr & BMCR_SPEED100) : (lpa & (LPA_100HALF | LPA_100FULL))) + speed = 100; + else + speed = 10; + } else { + /* Forcing a link mode. */ + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_FULLDPLX) + lp->fullduplex = 1; + else + lp->fullduplex = 0; + if (bmcr & BMCR_SPEED100) + speed = 100; + else + speed = 10; + } + + tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_HaltReq, &tr->MAC_Ctl); + if (lp->fullduplex) { + tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl); + } else { + tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_FullDup, &tr->MAC_Ctl); + } + tc_writel(tc_readl(&tr->MAC_Ctl) & ~MAC_HaltReq, &tr->MAC_Ctl); + + /* TX4939 PCFG.SPEEDn bit will be changed on NETDEV_CHANGE event. */ + +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have EnLCarr */ + if (lp->boardtype != TC35815_TX4939) { +#ifdef WORKAROUND_LOSTCAR + /* WORKAROUND: enable LostCrS only if half duplex operation */ + if (!lp->fullduplex && lp->boardtype != TC35815_TX4939) + tc_writel(tc_readl(&tr->Tx_Ctl) | Tx_EnLCarr, &tr->Tx_Ctl); +#endif + } +#endif + lp->mii.full_duplex = lp->fullduplex; +} + +static void tc35815_timer(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmsr, bmcr, lpa; + int restart_timer = 0; + + spin_lock_irq(&lp->lock); + + lp->timer_ticks++; + switch (lp->timer_state) { + case arbwait: + /* + * Only allow for 5 ticks, thats 10 seconds and much too + * long to wait for arbitration to complete. + */ + /* TC35815 need more times... */ + if (lp->timer_ticks >= 10) { + /* Enter force mode. */ + if (!options.doforce) { + printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful," + " cable probblem?\n", dev->name); + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; + } + printk(KERN_NOTICE "%s: Auto-Negotiation unsuccessful," + " trying force link mode\n", dev->name); + printk(KERN_DEBUG "%s: BMCR %x BMSR %x\n", dev->name, + tc_mdio_read(dev, pid, MII_BMCR), + tc_mdio_read(dev, pid, MII_BMSR)); + bmcr = BMCR_SPEED100; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* + * OK, seems we need do disable the transceiver + * for the first tick to make sure we get an + * accurate link state at the second tick. + */ + + lp->timer_state = ltrywait; + lp->timer_ticks = 0; + restart_timer = 1; } else { - /* auto negotiation */ - unsigned long neg_result; - tc_phy_write(dev, MIICNTL_AUTO | MIICNTL_RST_AUTO, tr, 0, MII_CONTROL); - printk(KERN_INFO "%s: Auto Negotiation...", dev->name); - count = 0; - while (!(tc_phy_read(dev, tr, 0, MII_STATUS) & MIISTAT_AUTO_DONE)) { - if (count++ > 5000) { - printk(" failed. Assume 10Mbps\n"); - lp->linkspeed = 10; - lp->fullduplex = 0; - goto done; + /* Anything interesting happen? */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + if (bmsr & BMSR_ANEGCOMPLETE) { + /* Just what we've been waiting for... */ + tc35815_set_link_modes(dev); + + /* + * Success, at least so far, advance our state + * engine. + */ + lp->timer_state = lupwait; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case lupwait: + /* + * Auto negotiation was successful and we are awaiting a + * link up status. I have decided to let this timer run + * forever until some sort of error is signalled, reporting + * a message to the user at 10 second intervals. + */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + if (bmsr & BMSR_LSTATUS) { + /* + * Wheee, it's up, display the link mode in use and put + * the timer to sleep. + */ + tc35815_display_link_mode(dev); + netif_carrier_on(dev); +#ifdef WORKAROUND_100HALF_PROMISC + /* delayed promiscuous enabling */ + if (dev->flags & IFF_PROMISC) + tc35815_set_multicast_list(dev); +#endif +#if 1 + lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA); + lp->timer_state = lcheck; + restart_timer = 1; +#else + lp->timer_state = asleep; + restart_timer = 0; +#endif + } else { + if (lp->timer_ticks >= 10) { + printk(KERN_NOTICE "%s: Auto negotiation successful, link still " + "not completely up.\n", dev->name); + lp->timer_ticks = 0; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case ltrywait: + /* + * Making the timeout here too long can make it take + * annoyingly long to attempt all of the link mode + * permutations, but then again this is essentially + * error recovery code for the most part. + */ + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (lp->timer_ticks == 1) { + /* + * Re-enable transceiver, we'll re-enable the + * transceiver next tick, then check link state + * on the following tick. + */ + restart_timer = 1; + break; + } + if (lp->timer_ticks == 2) { + restart_timer = 1; + break; + } + if (bmsr & BMSR_LSTATUS) { + /* Force mode selection success. */ + tc35815_display_forced_link_mode(dev); + netif_carrier_on(dev); + tc35815_set_link_modes(dev); +#ifdef WORKAROUND_100HALF_PROMISC + /* delayed promiscuous enabling */ + if (dev->flags & IFF_PROMISC) + tc35815_set_multicast_list(dev); +#endif +#if 1 + lp->saved_lpa = tc_mdio_read(dev, pid, MII_LPA); + lp->timer_state = lcheck; + restart_timer = 1; +#else + lp->timer_state = asleep; + restart_timer = 0; +#endif + } else { + if (lp->timer_ticks >= 4) { /* 6 seconds or so... */ + int ret; + + ret = tc35815_try_next_permutation(dev); + if (ret == -1) { + /* + * Aieee, tried them all, reset the + * chip and try all over again. + */ + printk(KERN_NOTICE "%s: Link down, " + "cable problem?\n", + dev->name); + + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; } - if (count % 512 == 0) - printk("."); - mdelay(1); + lp->timer_ticks = 0; + restart_timer = 1; + } else { + restart_timer = 1; + } + } + break; + + case lcheck: + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + lpa = tc_mdio_read(dev, pid, MII_LPA); + if (bmcr & (BMCR_PDOWN | BMCR_ISOLATE | BMCR_RESET)) { + printk(KERN_ERR "%s: PHY down? (BMCR %x)\n", dev->name, + bmcr); + } else if ((lp->saved_lpa ^ lpa) & + (LPA_100FULL|LPA_100HALF|LPA_10FULL|LPA_10HALF)) { + printk(KERN_NOTICE "%s: link status changed" + " (BMCR %x LPA %x->%x)\n", dev->name, + bmcr, lp->saved_lpa, lpa); + } else { + /* go on */ + restart_timer = 1; + break; + } + /* Try to restart the adaptor. */ + tc35815_restart(dev); + goto out; + + case asleep: + default: + /* Can't happens.... */ + printk(KERN_ERR "%s: Aieee, link timer is asleep but we got " + "one anyways!\n", dev->name); + restart_timer = 0; + lp->timer_ticks = 0; + lp->timer_state = asleep; /* foo on you */ + break; + } + + if (restart_timer) { + lp->timer.expires = jiffies + msecs_to_jiffies(1200); + add_timer(&lp->timer); + } +out: + spin_unlock_irq(&lp->lock); +} + +static void tc35815_start_auto_negotiation(struct net_device *dev, + struct ethtool_cmd *ep) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmsr, bmcr, advertize; + int timeout; + + netif_carrier_off(dev); + bmsr = tc_mdio_read(dev, pid, MII_BMSR); + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + advertize = tc_mdio_read(dev, pid, MII_ADVERTISE); + + if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + if (options.speed || options.duplex) { + /* Advertise only specified configuration. */ + advertize &= ~(ADVERTISE_10HALF | + ADVERTISE_10FULL | + ADVERTISE_100HALF | + ADVERTISE_100FULL); + if (options.speed != 10) { + if (options.duplex != 1) + advertize |= ADVERTISE_100FULL; + if (options.duplex != 2) + advertize |= ADVERTISE_100HALF; + } + if (options.speed != 100) { + if (options.duplex != 1) + advertize |= ADVERTISE_10FULL; + if (options.duplex != 2) + advertize |= ADVERTISE_10HALF; } - printk(" done.\n"); - neg_result = tc_phy_read(dev, tr, 0, MII_ANLPAR); - if (neg_result & (MII_AN_TX_FDX | MII_AN_TX_HDX)) - lp->linkspeed = 100; + if (options.speed == 100) + bmcr |= BMCR_SPEED100; + else if (options.speed == 10) + bmcr &= ~BMCR_SPEED100; + if (options.duplex == 2) + bmcr |= BMCR_FULLDPLX; + else if (options.duplex == 1) + bmcr &= ~BMCR_FULLDPLX; + } else { + /* Advertise everything we can support. */ + if (bmsr & BMSR_10HALF) + advertize |= ADVERTISE_10HALF; else - lp->linkspeed = 10; - if (neg_result & (MII_AN_TX_FDX | MII_AN_10_FDX)) - lp->fullduplex = 1; + advertize &= ~ADVERTISE_10HALF; + if (bmsr & BMSR_10FULL) + advertize |= ADVERTISE_10FULL; else - lp->fullduplex = 0; - done: - ; + advertize &= ~ADVERTISE_10FULL; + if (bmsr & BMSR_100HALF) + advertize |= ADVERTISE_100HALF; + else + advertize &= ~ADVERTISE_100HALF; + if (bmsr & BMSR_100FULL) + advertize |= ADVERTISE_100FULL; + else + advertize &= ~ADVERTISE_100FULL; } + + tc_mdio_write(dev, pid, MII_ADVERTISE, advertize); + + /* Enable Auto-Negotiation, this is usually on already... */ + bmcr |= BMCR_ANENABLE; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* Restart it to make sure it is going. */ + bmcr |= BMCR_ANRESTART; + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + printk(KERN_DEBUG "%s: ADVERTISE %x BMCR %x\n", dev->name, advertize, bmcr); + + /* BMCR_ANRESTART self clears when the process has begun. */ + timeout = 64; /* More than enough. */ + while (--timeout) { + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (!(bmcr & BMCR_ANRESTART)) + break; /* got it. */ + udelay(10); + } + if (!timeout) { + printk(KERN_ERR "%s: TC35815 would not start auto " + "negotiation BMCR=0x%04x\n", + dev->name, bmcr); + printk(KERN_NOTICE "%s: Performing force link " + "detection.\n", dev->name); + goto force_link; + } else { + printk(KERN_DEBUG "%s: auto negotiation started.\n", dev->name); + lp->timer_state = arbwait; + } + } else { +force_link: + /* Force the link up, trying first a particular mode. + * Either we are here at the request of ethtool or + * because the Happy Meal would not start to autoneg. + */ + + /* Disable auto-negotiation in BMCR, enable the duplex and + * speed setting, init the timer state machine, and fire it off. + */ + if (ep == NULL || ep->autoneg == AUTONEG_ENABLE) { + bmcr = BMCR_SPEED100; + } else { + if (ep->speed == SPEED_100) + bmcr = BMCR_SPEED100; + else + bmcr = 0; + if (ep->duplex == DUPLEX_FULL) + bmcr |= BMCR_FULLDPLX; + } + tc_mdio_write(dev, pid, MII_BMCR, bmcr); + + /* OK, seems we need do disable the transceiver for the first + * tick to make sure we get an accurate link state at the + * second tick. + */ + lp->timer_state = ltrywait; } - ctl = 0; - if (lp->linkspeed == 100) - ctl |= MIICNTL_SPEED; - if (lp->fullduplex) - ctl |= MIICNTL_FDX; - tc_phy_write(dev, ctl, tr, 0, MII_CONTROL); + del_timer(&lp->timer); + lp->timer_ticks = 0; + lp->timer.expires = jiffies + msecs_to_jiffies(1200); + add_timer(&lp->timer); +} - if (lp->fullduplex) { - tc_writel(tc_readl(&tr->MAC_Ctl) | MAC_FullDup, &tr->MAC_Ctl); +static void tc35815_find_phy(struct net_device *dev) +{ + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short id0; + + /* find MII phy */ + for (pid = 31; pid >= 0; pid--) { + id0 = tc_mdio_read(dev, pid, MII_BMSR); + if (id0 != 0xffff && id0 != 0x0000 && + (id0 & BMSR_RESV) != (0xffff & BMSR_RESV) /* paranoia? */ + ) { + lp->phy_addr = pid; + break; + } + } + if (pid < 0) { + printk(KERN_ERR "%s: No MII Phy found.\n", + dev->name); + lp->phy_addr = pid = 0; } + + lp->mii_id[0] = tc_mdio_read(dev, pid, MII_PHYSID1); + lp->mii_id[1] = tc_mdio_read(dev, pid, MII_PHYSID2); + if (netif_msg_hw(lp)) + printk(KERN_INFO "%s: PHY(%02x) ID %04x %04x\n", dev->name, + pid, lp->mii_id[0], lp->mii_id[1]); } -static void tc35815_chip_reset(struct net_device *dev) +static void tc35815_phy_chip_init(struct net_device *dev) { - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; + struct tc35815_local *lp = dev->priv; + int pid = lp->phy_addr; + unsigned short bmcr; + struct ethtool_cmd ecmd, *ep; + + /* dis-isolate if needed. */ + bmcr = tc_mdio_read(dev, pid, MII_BMCR); + if (bmcr & BMCR_ISOLATE) { + int count = 32; + printk(KERN_DEBUG "%s: unisolating...", dev->name); + tc_mdio_write(dev, pid, MII_BMCR, bmcr & ~BMCR_ISOLATE); + while (--count) { + if (!(tc_mdio_read(dev, pid, MII_BMCR) & BMCR_ISOLATE)) + break; + udelay(20); + } + printk(" %s.\n", count ? "done" : "failed"); + } + + if (options.speed && options.duplex) { + ecmd.autoneg = AUTONEG_DISABLE; + ecmd.speed = options.speed == 10 ? SPEED_10 : SPEED_100; + ecmd.duplex = options.duplex == 1 ? DUPLEX_HALF : DUPLEX_FULL; + ep = &ecmd; + } else { + ep = NULL; + } + tc35815_start_auto_negotiation(dev, ep); +} +static void tc35815_chip_reset(struct net_device *dev) +{ + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; + int i; /* reset the controller */ tc_writel(MAC_Reset, &tr->MAC_Ctl); - while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) - ; - + udelay(4); /* 3200ns */ + i = 0; + while (tc_readl(&tr->MAC_Ctl) & MAC_Reset) { + if (i++ > 100) { + printk(KERN_ERR "%s: MAC reset failed.\n", dev->name); + break; + } + mdelay(1); + } tc_writel(0, &tr->MAC_Ctl); /* initialize registers to default value */ @@ -1651,95 +2804,142 @@ static void tc35815_chip_reset(struct net_device *dev) tc_writel(0, &tr->CAM_Ena); (void)tc_readl(&tr->Miss_Cnt); /* Read to clear */ + /* initialize internal SRAM */ + tc_writel(DMA_TestMode, &tr->DMA_Ctl); + for (i = 0; i < 0x1000; i += 4) { + tc_writel(i, &tr->CAM_Adr); + tc_writel(0, &tr->CAM_Data); + } + tc_writel(0, &tr->DMA_Ctl); } static void tc35815_chip_init(struct net_device *dev) { struct tc35815_local *lp = dev->priv; - struct tc35815_regs *tr = (struct tc35815_regs*)dev->base_addr; - unsigned long flags; + struct tc35815_regs __iomem *tr = + (struct tc35815_regs __iomem *)dev->base_addr; unsigned long txctl = TX_CTL_CMD; tc35815_phy_chip_init(dev); /* load station address to CAM */ - tc35815_set_cam_entry(tr, CAM_ENTRY_SOURCE, dev->dev_addr); + tc35815_set_cam_entry(dev, CAM_ENTRY_SOURCE, dev->dev_addr); /* Enable CAM (broadcast and unicast) */ tc_writel(CAM_Ena_Bit(CAM_ENTRY_SOURCE), &tr->CAM_Ena); tc_writel(CAM_CompEn | CAM_BroadAcc, &tr->CAM_Ctl); - spin_lock_irqsave(&lp->lock, flags); - - tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); - + /* Use DMA_RxAlign_2 to make IP header 4-byte aligned. */ + if (HAVE_DMA_RXALIGN(lp)) + tc_writel(DMA_BURST_SIZE | DMA_RxAlign_2, &tr->DMA_Ctl); + else + tc_writel(DMA_BURST_SIZE, &tr->DMA_Ctl); +#ifdef TC35815_USE_PACKEDBUFFER tc_writel(RxFrag_EnPack | ETH_ZLEN, &tr->RxFragSize); /* Packing */ +#else + tc_writel(ETH_ZLEN, &tr->RxFragSize); +#endif tc_writel(0, &tr->TxPollCtr); /* Batch mode */ tc_writel(TX_THRESHOLD, &tr->TxThrsh); tc_writel(INT_EN_CMD, &tr->Int_En); /* set queues */ - tc_writel(virt_to_bus(lp->rfd_base), &tr->FDA_Bas); + tc_writel(fd_virt_to_bus(lp, lp->rfd_base), &tr->FDA_Bas); tc_writel((unsigned long)lp->rfd_limit - (unsigned long)lp->rfd_base, &tr->FDA_Lim); /* * Activation method: - * First, enable eht MAC Transmitter and the DMA Receive circuits. + * First, enable the MAC Transmitter and the DMA Receive circuits. * Then enable the DMA Transmitter and the MAC Receive circuits. */ - tc_writel(virt_to_bus(lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ + tc_writel(fd_virt_to_bus(lp, lp->fbl_ptr), &tr->BLFrmPtr); /* start DMA receiver */ tc_writel(RX_CTL_CMD, &tr->Rx_Ctl); /* start MAC receiver */ + /* start MAC transmitter */ +#ifndef NO_CHECK_CARRIER + /* TX4939 does not have EnLCarr */ + if (lp->boardtype == TC35815_TX4939) + txctl &= ~Tx_EnLCarr; +#ifdef WORKAROUND_LOSTCAR /* WORKAROUND: ignore LostCrS in full duplex operation */ - if (lp->fullduplex) - txctl = TX_CTL_CMD & ~Tx_EnLCarr; + if ((lp->timer_state != asleep && lp->timer_state != lcheck) || + lp->fullduplex) + txctl &= ~Tx_EnLCarr; +#endif +#endif /* !NO_CHECK_CARRIER */ #ifdef GATHER_TXINT txctl &= ~Tx_EnComp; /* disable global tx completion int. */ #endif tc_writel(txctl, &tr->Tx_Ctl); -#if 0 /* No need to polling */ - tc_writel(virt_to_bus(lp->tfd_base), &tr->TxFrmPtr); /* start DMA transmitter */ -#endif - spin_unlock_irqrestore(&lp->lock, flags); } -/* XXX */ -void -tc35815_killall(void) +#ifdef CONFIG_PM +static int tc35815_suspend(struct pci_dev *pdev, pm_message_t state) { - struct net_device *dev; + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = dev->priv; + unsigned long flags; - for (dev = root_tc35815_dev; dev; dev = ((struct tc35815_local *)dev->priv)->next_module) { - if (dev->flags&IFF_UP){ - dev->stop(dev); - } - } + pci_save_state(pdev); + if (!netif_running(dev)) + return 0; + netif_device_detach(dev); + spin_lock_irqsave(&lp->lock, flags); + del_timer(&lp->timer); /* Kill if running */ + tc35815_chip_reset(dev); + spin_unlock_irqrestore(&lp->lock, flags); + pci_set_power_state(pdev, PCI_D3hot); + return 0; } -static struct pci_driver tc35815_driver = { - .name = TC35815_MODULE_NAME, - .probe = tc35815_probe, - .remove = NULL, - .id_table = tc35815_pci_tbl, +static int tc35815_resume(struct pci_dev *pdev) +{ + struct net_device *dev = pci_get_drvdata(pdev); + struct tc35815_local *lp = dev->priv; + unsigned long flags; + + pci_restore_state(pdev); + if (!netif_running(dev)) + return 0; + pci_set_power_state(pdev, PCI_D0); + spin_lock_irqsave(&lp->lock, flags); + tc35815_restart(dev); + spin_unlock_irqrestore(&lp->lock, flags); + netif_device_attach(dev); + return 0; +} +#endif /* CONFIG_PM */ + +static struct pci_driver tc35815_pci_driver = { + .name = MODNAME, + .id_table = tc35815_pci_tbl, + .probe = tc35815_init_one, + .remove = __devexit_p(tc35815_remove_one), +#ifdef CONFIG_PM + .suspend = tc35815_suspend, + .resume = tc35815_resume, +#endif }; +module_param_named(speed, options.speed, int, 0); +MODULE_PARM_DESC(speed, "0:auto, 10:10Mbps, 100:100Mbps"); +module_param_named(duplex, options.duplex, int, 0); +MODULE_PARM_DESC(duplex, "0:auto, 1:half, 2:full"); +module_param_named(doforce, options.doforce, int, 0); +MODULE_PARM_DESC(doforce, "try force link mode if auto-negotiation failed"); + static int __init tc35815_init_module(void) { - return pci_register_driver(&tc35815_driver); + return pci_register_driver(&tc35815_pci_driver); } static void __exit tc35815_cleanup_module(void) { - struct net_device *next_dev; - - while (root_tc35815_dev) { - struct net_device *dev = root_tc35815_dev; - next_dev = ((struct tc35815_local *)dev->priv)->next_module; - iounmap((void *)(dev->base_addr)); - unregister_netdev(dev); - free_netdev(dev); - root_tc35815_dev = next_dev; - } + pci_unregister_driver(&tc35815_pci_driver); } + module_init(tc35815_init_module); module_exit(tc35815_cleanup_module); + +MODULE_DESCRIPTION("TOSHIBA TC35815 PCI 10M/100M Ethernet driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 81a1c2e..9488f49 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -40,16 +40,16 @@ #include <linux/dma-mapping.h> #include <net/checksum.h> +#include <net/ip.h> #include <asm/system.h> #include <asm/io.h> #include <asm/byteorder.h> #include <asm/uaccess.h> -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC #include <asm/idprom.h> -#include <asm/oplib.h> -#include <asm/pbm.h> +#include <asm/prom.h> #endif #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) @@ -64,8 +64,8 @@ #define DRV_MODULE_NAME "tg3" #define PFX DRV_MODULE_NAME ": " -#define DRV_MODULE_VERSION "3.73" -#define DRV_MODULE_RELDATE "February 12, 2007" +#define DRV_MODULE_VERSION "3.75" +#define DRV_MODULE_RELDATE "March 23, 2007" #define TG3_DEF_MAC_MODE 0 #define TG3_DEF_RX_MODE 0 @@ -3349,7 +3349,7 @@ static int tg3_rx(struct tg3 *tp, int budget) skb_reserve(copy_skb, 2); skb_put(copy_skb, len); pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); - memcpy(copy_skb->data, skb->data, len); + skb_copy_from_linear_data(skb, copy_skb->data, len); pci_dma_sync_single_for_device(tp->pdev, dma_addr, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ @@ -3568,32 +3568,34 @@ static irqreturn_t tg3_interrupt(int irq, void *dev_id) * Reading the PCI State register will confirm whether the * interrupt is ours and will flush the status block. */ - if ((sblk->status & SD_STATUS_UPDATED) || - !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { - /* - * Writing any value to intr-mbox-0 clears PCI INTA# and - * chip-internal interrupt pending events. - * Writing non-zero to intr-mbox-0 additional tells the - * NIC to stop sending us irqs, engaging "in-intr-handler" - * event coalescing. - */ - tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, - 0x00000001); - if (tg3_irq_sync(tp)) + if (unlikely(!(sblk->status & SD_STATUS_UPDATED))) { + if ((tp->tg3_flags & TG3_FLAG_CHIP_RESETTING) || + (tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { + handled = 0; goto out; - sblk->status &= ~SD_STATUS_UPDATED; - if (likely(tg3_has_work(tp))) { - prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); - netif_rx_schedule(dev); /* schedule NAPI poll */ - } else { - /* No work, shared interrupt perhaps? re-enable - * interrupts, and flush that PCI write - */ - tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, - 0x00000000); } - } else { /* shared interrupt */ - handled = 0; + } + + /* + * Writing any value to intr-mbox-0 clears PCI INTA# and + * chip-internal interrupt pending events. + * Writing non-zero to intr-mbox-0 additional tells the + * NIC to stop sending us irqs, engaging "in-intr-handler" + * event coalescing. + */ + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); + if (tg3_irq_sync(tp)) + goto out; + sblk->status &= ~SD_STATUS_UPDATED; + if (likely(tg3_has_work(tp))) { + prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); + netif_rx_schedule(dev); /* schedule NAPI poll */ + } else { + /* No work, shared interrupt perhaps? re-enable + * interrupts, and flush that PCI write + */ + tw32_mailbox_f(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, + 0x00000000); } out: return IRQ_RETVAL(handled); @@ -3611,31 +3613,33 @@ static irqreturn_t tg3_interrupt_tagged(int irq, void *dev_id) * Reading the PCI State register will confirm whether the * interrupt is ours and will flush the status block. */ - if ((sblk->status_tag != tp->last_tag) || - !(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { - /* - * writing any value to intr-mbox-0 clears PCI INTA# and - * chip-internal interrupt pending events. - * writing non-zero to intr-mbox-0 additional tells the - * NIC to stop sending us irqs, engaging "in-intr-handler" - * event coalescing. - */ - tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, - 0x00000001); - if (tg3_irq_sync(tp)) + if (unlikely(sblk->status_tag == tp->last_tag)) { + if ((tp->tg3_flags & TG3_FLAG_CHIP_RESETTING) || + (tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) { + handled = 0; goto out; - if (netif_rx_schedule_prep(dev)) { - prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); - /* Update last_tag to mark that this status has been - * seen. Because interrupt may be shared, we may be - * racing with tg3_poll(), so only update last_tag - * if tg3_poll() is not scheduled. - */ - tp->last_tag = sblk->status_tag; - __netif_rx_schedule(dev); } - } else { /* shared interrupt */ - handled = 0; + } + + /* + * writing any value to intr-mbox-0 clears PCI INTA# and + * chip-internal interrupt pending events. + * writing non-zero to intr-mbox-0 additional tells the + * NIC to stop sending us irqs, engaging "in-intr-handler" + * event coalescing. + */ + tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW, 0x00000001); + if (tg3_irq_sync(tp)) + goto out; + if (netif_rx_schedule_prep(dev)) { + prefetch(&tp->rx_rcb[tp->rx_rcb_ptr]); + /* Update last_tag to mark that this status has been + * seen. Because interrupt may be shared, we may be + * racing with tg3_poll(), so only update last_tag + * if tg3_poll() is not scheduled. + */ + tp->last_tag = sblk->status_tag; + __netif_rx_schedule(dev); } out: return IRQ_RETVAL(handled); @@ -3904,20 +3908,20 @@ static int tg3_start_xmit(struct sk_buff *skb, struct net_device *dev) if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) mss |= (skb_headlen(skb) - ETH_HLEN) << 9; else { - tcp_opt_len = ((skb->h.th->doff - 5) * 4); - ip_tcp_len = (skb->nh.iph->ihl * 4) + - sizeof(struct tcphdr); + struct iphdr *iph = ip_hdr(skb); - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + ip_tcp_len + - tcp_opt_len); + tcp_opt_len = tcp_optlen(skb); + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); + + iph->check = 0; + iph->tot_len = htons(mss + ip_tcp_len + tcp_opt_len); mss |= (ip_tcp_len + tcp_opt_len) << 9; } base_flags |= (TXD_FLAG_CPU_PRE_DMA | TXD_FLAG_CPU_POST_DMA); - skb->h.th->check = 0; + tcp_hdr(skb)->check = 0; } else if (skb->ip_summed == CHECKSUM_PARTIAL) @@ -3993,7 +3997,10 @@ static int tg3_tso_bug(struct tg3 *tp, struct sk_buff *skb) /* Estimate the number of fragments in the worst case */ if (unlikely(tg3_tx_avail(tp) <= (skb_shinfo(skb)->gso_segs * 3))) { netif_stop_queue(tp->dev); - return NETDEV_TX_BUSY; + if (tg3_tx_avail(tp) <= (skb_shinfo(skb)->gso_segs * 3)) + return NETDEV_TX_BUSY; + + netif_wake_queue(tp->dev); } segs = skb_gso_segment(skb, tp->dev->features & ~NETIF_F_TSO); @@ -4048,6 +4055,7 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev) mss = 0; if (skb->len > (tp->dev->mtu + ETH_HLEN) && (mss = skb_shinfo(skb)->gso_size) != 0) { + struct iphdr *iph; int tcp_opt_len, ip_tcp_len, hdr_len; if (skb_header_cloned(skb) && @@ -4056,45 +4064,42 @@ static int tg3_start_xmit_dma_bug(struct sk_buff *skb, struct net_device *dev) goto out_unlock; } - tcp_opt_len = ((skb->h.th->doff - 5) * 4); - ip_tcp_len = (skb->nh.iph->ihl * 4) + sizeof(struct tcphdr); + tcp_opt_len = tcp_optlen(skb); + ip_tcp_len = ip_hdrlen(skb) + sizeof(struct tcphdr); hdr_len = ip_tcp_len + tcp_opt_len; if (unlikely((ETH_HLEN + hdr_len) > 80) && - (tp->tg3_flags2 & TG3_FLG2_HW_TSO_1_BUG)) + (tp->tg3_flags2 & TG3_FLG2_TSO_BUG)) return (tg3_tso_bug(tp, skb)); base_flags |= (TXD_FLAG_CPU_PRE_DMA | TXD_FLAG_CPU_POST_DMA); - skb->nh.iph->check = 0; - skb->nh.iph->tot_len = htons(mss + hdr_len); + iph = ip_hdr(skb); + iph->check = 0; + iph->tot_len = htons(mss + hdr_len); if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) { - skb->h.th->check = 0; + tcp_hdr(skb)->check = 0; base_flags &= ~TXD_FLAG_TCPUDP_CSUM; - } - else { - skb->h.th->check = - ~csum_tcpudp_magic(skb->nh.iph->saddr, - skb->nh.iph->daddr, - 0, IPPROTO_TCP, 0); - } + } else + tcp_hdr(skb)->check = ~csum_tcpudp_magic(iph->saddr, + iph->daddr, 0, + IPPROTO_TCP, + 0); if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO) || (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705)) { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { + if (tcp_opt_len || iph->ihl > 5) { int tsflags; - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); + tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2); mss |= (tsflags << 11); } } else { - if (tcp_opt_len || skb->nh.iph->ihl > 5) { + if (tcp_opt_len || iph->ihl > 5) { int tsflags; - tsflags = ((skb->nh.iph->ihl - 5) + - (tcp_opt_len >> 2)); + tsflags = (iph->ihl - 5) + (tcp_opt_len >> 2); base_flags |= tsflags << 12; } } @@ -4820,6 +4825,21 @@ static int tg3_chip_reset(struct tg3 *tp) if (write_op == tg3_write_flush_reg32) tp->write32 = tg3_write32; + /* Prevent the irq handler from reading or writing PCI registers + * during chip reset when the memory enable bit in the PCI command + * register may be cleared. The chip does not generate interrupt + * at this time, but the irq handler may still be called due to irq + * sharing or irqpoll. + */ + tp->tg3_flags |= TG3_FLAG_CHIP_RESETTING; + if (tp->hw_status) { + tp->hw_status->status = 0; + tp->hw_status->status_tag = 0; + } + tp->last_tag = 0; + smp_mb(); + synchronize_irq(tp->pdev->irq); + /* do the reset */ val = GRC_MISC_CFG_CORECLK_RESET; @@ -4901,6 +4921,8 @@ static int tg3_chip_reset(struct tg3 *tp) pci_restore_state(tp->pdev); + tp->tg3_flags &= ~TG3_FLAG_CHIP_RESETTING; + /* Make sure PCI-X relaxed ordering bit is clear. */ pci_read_config_dword(tp->pdev, TG3PCI_X_CAPS, &val); val &= ~PCIX_CAPS_RELAXED_ORDERING; @@ -6318,8 +6340,6 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) RDMAC_MODE_ADDROFLOW_ENAB | RDMAC_MODE_FIFOOFLOW_ENAB | RDMAC_MODE_FIFOURUN_ENAB | RDMAC_MODE_FIFOOREAD_ENAB | RDMAC_MODE_LNGREAD_ENAB); - if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) - rdmac_mode |= RDMAC_MODE_SPLIT_ENABLE; /* If statement applies to 5705 and 5750 PCI devices only */ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && @@ -6492,9 +6512,6 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy) } else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704) { val &= ~(PCIX_CAPS_SPLIT_MASK | PCIX_CAPS_BURST_MASK); val |= (PCIX_CAPS_MAX_BURST_CPIOB << PCIX_CAPS_BURST_SHIFT); - if (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) - val |= (tp->split_mode_max_reqs << - PCIX_CAPS_SPLIT_SHIFT); } tw32(TG3PCI_X_CAPS, val); } @@ -8137,7 +8154,7 @@ static int tg3_set_ringparam(struct net_device *dev, struct ethtool_ringparam *e (ering->rx_jumbo_pending > TG3_RX_JUMBO_RING_SIZE - 1) || (ering->tx_pending > TG3_TX_RING_SIZE - 1) || (ering->tx_pending <= MAX_SKB_FRAGS) || - ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_1_BUG) && + ((tp->tg3_flags2 & TG3_FLG2_TSO_BUG) && (ering->tx_pending <= (MAX_SKB_FRAGS * 3)))) return -EINVAL; @@ -9111,8 +9128,7 @@ static void tg3_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) tg3_netif_stop(tp); tg3_full_lock(tp, 0); - if (tp->vlgrp) - tp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(tp->vlgrp, vid, NULL); tg3_full_unlock(tp); if (netif_running(dev)) @@ -10557,12 +10573,11 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2; tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI; } else { - tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | - TG3_FLG2_HW_TSO_1_BUG; + tp->tg3_flags2 |= TG3_FLG2_HW_TSO_1 | TG3_FLG2_TSO_BUG; if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5750 && tp->pci_chip_rev_id >= CHIPREV_ID_5750_C2) - tp->tg3_flags2 &= ~TG3_FLG2_HW_TSO_1_BUG; + tp->tg3_flags2 &= ~TG3_FLG2_TSO_BUG; } } @@ -10862,14 +10877,6 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) grc_misc_cfg = tr32(GRC_MISC_CFG); grc_misc_cfg &= GRC_MISC_CFG_BOARD_ID_MASK; - /* Broadcom's driver says that CIOBE multisplit has a bug */ -#if 0 - if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5704 && - grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5704CIOBE) { - tp->tg3_flags |= TG3_FLAG_SPLIT_MODE; - tp->split_mode_max_reqs = SPLIT_MODE_5704_MAX_REQ; - } -#endif if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705 && (grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788 || grc_misc_cfg == GRC_MISC_CFG_BOARD_ID_5788M)) @@ -10979,24 +10986,20 @@ static int __devinit tg3_get_invariants(struct tg3 *tp) return err; } -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC static int __devinit tg3_get_macaddr_sparc(struct tg3 *tp) { struct net_device *dev = tp->dev; struct pci_dev *pdev = tp->pdev; - struct pcidev_cookie *pcp = pdev->sysdata; - - if (pcp != NULL) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, "local-mac-address", - &len); - if (addr && len == 6) { - memcpy(dev->dev_addr, addr, 6); - memcpy(dev->perm_addr, dev->dev_addr, 6); - return 0; - } + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + int len; + + addr = of_get_property(dp, "local-mac-address", &len); + if (addr && len == 6) { + memcpy(dev->dev_addr, addr, 6); + memcpy(dev->perm_addr, dev->dev_addr, 6); + return 0; } return -ENODEV; } @@ -11017,7 +11020,7 @@ static int __devinit tg3_get_device_address(struct tg3 *tp) u32 hi, lo, mac_offset; int addr_ok = 0; -#ifdef CONFIG_SPARC64 +#ifdef CONFIG_SPARC if (!tg3_get_macaddr_sparc(tp)) return 0; #endif @@ -11867,7 +11870,7 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0) { tp->tg3_flags2 &= ~TG3_FLG2_TSO_CAPABLE; } else { - tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE; + tp->tg3_flags2 |= TG3_FLG2_TSO_CAPABLE | TG3_FLG2_TSO_BUG; } /* TSO is on by default on chips that support hardware TSO. @@ -11967,14 +11970,12 @@ static int __devinit tg3_init_one(struct pci_dev *pdev, i == 5 ? '\n' : ':'); printk(KERN_INFO "%s: RXcsums[%d] LinkChgREG[%d] " - "MIirq[%d] ASF[%d] Split[%d] WireSpeed[%d] " - "TSOcap[%d] \n", + "MIirq[%d] ASF[%d] WireSpeed[%d] TSOcap[%d]\n", dev->name, (tp->tg3_flags & TG3_FLAG_RX_CHECKSUMS) != 0, (tp->tg3_flags & TG3_FLAG_USE_LINKCHG_REG) != 0, (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT) != 0, (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) != 0, - (tp->tg3_flags & TG3_FLAG_SPLIT_MODE) != 0, (tp->tg3_flags2 & TG3_FLG2_NO_ETH_WIRE_SPEED) == 0, (tp->tg3_flags2 & TG3_FLG2_TSO_CAPABLE) != 0); printk(KERN_INFO "%s: dma_rwctrl[%08x] dma_mask[%d-bit]\n", diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h index 45d477e..d515ed2 100644 --- a/drivers/net/tg3.h +++ b/drivers/net/tg3.h @@ -2223,11 +2223,11 @@ struct tg3 { #define TG3_FLAG_40BIT_DMA_BUG 0x08000000 #define TG3_FLAG_BROKEN_CHECKSUMS 0x10000000 #define TG3_FLAG_GOT_SERDES_FLOWCTL 0x20000000 -#define TG3_FLAG_SPLIT_MODE 0x40000000 +#define TG3_FLAG_CHIP_RESETTING 0x40000000 #define TG3_FLAG_INIT_COMPLETE 0x80000000 u32 tg3_flags2; #define TG3_FLG2_RESTART_TIMER 0x00000001 -#define TG3_FLG2_HW_TSO_1_BUG 0x00000002 +#define TG3_FLG2_TSO_BUG 0x00000002 #define TG3_FLG2_NO_ETH_WIRE_SPEED 0x00000004 #define TG3_FLG2_IS_5788 0x00000008 #define TG3_FLG2_MAX_RXPEND_64 0x00000010 @@ -2262,9 +2262,6 @@ struct tg3 { #define TG3_FLG2_NO_FWARE_REPORTED 0x40000000 #define TG3_FLG2_PHY_ADJUST_TRIM 0x80000000 - u32 split_mode_max_reqs; -#define SPLIT_MODE_5704_MAX_REQ 3 - struct timer_list timer; u16 timer_counter; u16 timer_multiplier; diff --git a/drivers/net/tlan.c b/drivers/net/tlan.c index f85f002..106dc1e 100644 --- a/drivers/net/tlan.c +++ b/drivers/net/tlan.c @@ -1112,7 +1112,7 @@ static int TLan_StartTx( struct sk_buff *skb, struct net_device *dev ) if ( bbuf ) { tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE ); - memcpy( tail_buffer, skb->data, skb->len ); + skb_copy_from_linear_data(skb, tail_buffer, skb->len); } else { tail_list->buffer[0].address = pci_map_single(priv->pciDev, skb->data, skb->len, PCI_DMA_TODEVICE); TLan_StoreSKB(tail_list, skb); @@ -1577,7 +1577,6 @@ u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int ) printk(KERN_INFO "TLAN: Couldn't allocate memory for received data.\n"); else { head_buffer = priv->rxBuffer + (priv->rxHead * TLAN_MAX_FRAME_SIZE); - skb->dev = dev; skb_reserve(skb, 2); t = (void *) skb_put(skb, frameSize); @@ -1608,7 +1607,6 @@ u32 TLan_HandleRxEOF( struct net_device *dev, u16 host_int ) skb->protocol = eth_type_trans( skb, dev ); netif_rx( skb ); - new_skb->dev = dev; skb_reserve( new_skb, 2 ); t = (void *) skb_put( new_skb, TLAN_MAX_FRAME_SIZE ); head_list->buffer[0].address = pci_map_single(priv->pciDev, new_skb->data, TLAN_MAX_FRAME_SIZE, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/tokenring/3c359.c b/drivers/net/tokenring/3c359.c index 7580bde..e22a3f5 100644 --- a/drivers/net/tokenring/3c359.c +++ b/drivers/net/tokenring/3c359.c @@ -933,20 +933,21 @@ static void xl_rx(struct net_device *dev) return ; } - skb->dev = dev ; - while (xl_priv->rx_ring_tail != temp_ring_loc) { copy_len = xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfraglen & 0x7FFF ; frame_length -= copy_len ; pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, copy_len) ; + skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail], + skb_put(skb, copy_len), + copy_len); pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; } /* Now we have found the last fragment */ pci_dma_sync_single_for_cpu(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,copy_len), xl_priv->rx_ring_skb[xl_priv->rx_ring_tail]->data, frame_length) ; + skb_copy_from_linear_data(xl_priv->rx_ring_skb[xl_priv->rx_ring_tail], + skb_put(skb,copy_len), frame_length); /* memcpy(skb_put(skb,frame_length), bus_to_virt(xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr), frame_length) ; */ pci_dma_sync_single_for_device(xl_priv->pdev,xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr,xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; adv_rx_ring(dev) ; @@ -967,8 +968,6 @@ static void xl_rx(struct net_device *dev) return ; } - skb->dev = dev ; - skb2 = xl_priv->rx_ring_skb[xl_priv->rx_ring_tail] ; pci_unmap_single(xl_priv->pdev, xl_priv->xl_rx_ring[xl_priv->rx_ring_tail].upfragaddr, xl_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; skb_put(skb2, frame_length) ; diff --git a/drivers/net/tokenring/ibmtr.c b/drivers/net/tokenring/ibmtr.c index 0d97e10..1e8958e 100644 --- a/drivers/net/tokenring/ibmtr.c +++ b/drivers/net/tokenring/ibmtr.c @@ -186,7 +186,6 @@ static char __devinit *adapter_def(char type) #define TRC_INITV 0x02 /* verbose init trace points */ static unsigned char ibmtr_debug_trace = 0; -static int ibmtr_probe(struct net_device *dev); static int ibmtr_probe1(struct net_device *dev, int ioaddr); static unsigned char get_sram_size(struct tok_info *adapt_info); static int trdev_init(struct net_device *dev); @@ -335,17 +334,6 @@ static void ibmtr_cleanup_card(struct net_device *dev) #endif } -int ibmtr_probe_card(struct net_device *dev) -{ - int err = ibmtr_probe(dev); - if (!err) { - err = register_netdev(dev); - if (err) - ibmtr_cleanup_card(dev); - } - return err; -} - /**************************************************************************** * ibmtr_probe(): Routine specified in the network device structure * to probe for an IBM Token Ring Adapter. Routine outline: @@ -358,7 +346,7 @@ int ibmtr_probe_card(struct net_device *dev) * which references it. ****************************************************************************/ -static int ibmtr_probe(struct net_device *dev) +static int __devinit ibmtr_probe(struct net_device *dev) { int i; int base_addr = dev->base_addr; @@ -378,6 +366,17 @@ static int ibmtr_probe(struct net_device *dev) return -ENODEV; } +int __devinit ibmtr_probe_card(struct net_device *dev) +{ + int err = ibmtr_probe(dev); + if (!err) { + err = register_netdev(dev); + if (err) + ibmtr_cleanup_card(dev); + } + return err; +} + /*****************************************************************************/ static int __devinit ibmtr_probe1(struct net_device *dev, int PIOaddr) @@ -1772,7 +1771,6 @@ static void tr_rx(struct net_device *dev) /*BMS again, if she comes in with few but leaves with many */ skb_reserve(skb, sizeof(struct trh_hdr) - lan_hdr_len); skb_put(skb, length); - skb->dev = dev; data = skb->data; rbuffer_len = ntohs(readw(rbuf + offsetof(struct rec_buf, buf_len))); rbufdata = rbuf + offsetof(struct rec_buf, data); diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c index e999feb..5d849c0 100644 --- a/drivers/net/tokenring/lanstreamer.c +++ b/drivers/net/tokenring/lanstreamer.c @@ -944,8 +944,6 @@ static void streamer_rx(struct net_device *dev) printk(KERN_WARNING "%s: Not enough memory to copy packet to upper layers. \n", dev->name); streamer_priv->streamer_stats.rx_dropped++; } else { /* we allocated an skb OK */ - skb->dev = dev; - if (buffer_cnt == 1) { /* release the DMA mapping */ pci_unmap_single(streamer_priv->pci_dev, @@ -1607,10 +1605,11 @@ static void streamer_arb_cmd(struct net_device *dev) frame_data, buffer_len); } while (next_ptr && (buff_off = next_ptr)); + mac_frame->protocol = tr_type_trans(mac_frame, dev); #if STREAMER_NETWORK_MONITOR printk(KERN_WARNING "%s: Received MAC Frame, details: \n", dev->name); - mac_hdr = (struct trh_hdr *) mac_frame->data; + mac_hdr = tr_hdr(mac_frame); printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name, mac_hdr->daddr[0], mac_hdr->daddr[1], @@ -1622,8 +1621,6 @@ static void streamer_arb_cmd(struct net_device *dev) mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]); #endif - mac_frame->dev = dev; - mac_frame->protocol = tr_type_trans(mac_frame, dev); netif_rx(mac_frame); /* Now tell the card we have dealt with the received frame */ diff --git a/drivers/net/tokenring/olympic.c b/drivers/net/tokenring/olympic.c index 8f4ecc1..09b3cfb 100644 --- a/drivers/net/tokenring/olympic.c +++ b/drivers/net/tokenring/olympic.c @@ -814,8 +814,6 @@ static void olympic_rx(struct net_device *dev) olympic_priv->rx_ring_last_received += i ; olympic_priv->rx_ring_last_received &= (OLYMPIC_RX_RING_SIZE -1) ; } else { - skb->dev = dev ; - /* Optimise based upon number of buffers used. If only one buffer is used we can simply swap the buffers around. If more than one then we must use the new buffer and copy the information @@ -847,7 +845,9 @@ static void olympic_rx(struct net_device *dev) pci_dma_sync_single_for_cpu(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; - memcpy(skb_put(skb,length-4),olympic_priv->rx_ring_skb[rx_ring_last_received]->data,length-4) ; + skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received], + skb_put(skb,length - 4), + length - 4); pci_dma_sync_single_for_device(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; @@ -864,7 +864,9 @@ static void olympic_rx(struct net_device *dev) olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; rx_desc = &(olympic_priv->olympic_rx_ring[rx_ring_last_received]); cpy_length = (i == 1 ? frag_len : le32_to_cpu(rx_desc->res_length)); - memcpy(skb_put(skb, cpy_length), olympic_priv->rx_ring_skb[rx_ring_last_received]->data, cpy_length) ; + skb_copy_from_linear_data(olympic_priv->rx_ring_skb[rx_ring_last_received], + skb_put(skb, cpy_length), + cpy_length); pci_dma_sync_single_for_device(olympic_priv->pdev, le32_to_cpu(olympic_priv->olympic_rx_ring[rx_ring_last_received].buffer), olympic_priv->pkt_buf_sz,PCI_DMA_FROMDEVICE) ; @@ -1440,16 +1442,16 @@ static void olympic_arb_cmd(struct net_device *dev) next_ptr=readw(buf_ptr+offsetof(struct mac_receive_buffer,next)); } while (next_ptr && (buf_ptr=olympic_priv->olympic_lap + ntohs(next_ptr))); + mac_frame->protocol = tr_type_trans(mac_frame, dev); + if (olympic_priv->olympic_network_monitor) { struct trh_hdr *mac_hdr ; printk(KERN_WARNING "%s: Received MAC Frame, details: \n",dev->name) ; - mac_hdr = (struct trh_hdr *)mac_frame->data ; + mac_hdr = tr_hdr(mac_frame); printk(KERN_WARNING "%s: MAC Frame Dest. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->daddr[0], mac_hdr->daddr[1], mac_hdr->daddr[2], mac_hdr->daddr[3], mac_hdr->daddr[4], mac_hdr->daddr[5]) ; printk(KERN_WARNING "%s: MAC Frame Srce. Addr: %02x:%02x:%02x:%02x:%02x:%02x \n", dev->name , mac_hdr->saddr[0], mac_hdr->saddr[1], mac_hdr->saddr[2], mac_hdr->saddr[3], mac_hdr->saddr[4], mac_hdr->saddr[5]) ; } - mac_frame->dev = dev ; - mac_frame->protocol = tr_type_trans(mac_frame,dev); - netif_rx(mac_frame) ; + netif_rx(mac_frame); dev->last_rx = jiffies; drop_frame: diff --git a/drivers/net/tokenring/smctr.c b/drivers/net/tokenring/smctr.c index cec282a..9bbea5c 100644 --- a/drivers/net/tokenring/smctr.c +++ b/drivers/net/tokenring/smctr.c @@ -3889,14 +3889,13 @@ static int smctr_process_rx_packet(MAC_HEADER *rmf, __u16 size, /* Slide data into a sleek skb. */ skb_put(skb, skb->len); - memcpy(skb->data, rmf, skb->len); + skb_copy_to_linear_data(skb, rmf, skb->len); /* Update Counters */ tp->MacStat.rx_packets++; tp->MacStat.rx_bytes += skb->len; /* Kick the packet on up. */ - skb->dev = dev; skb->protocol = tr_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -4476,14 +4475,13 @@ static int smctr_rx_frame(struct net_device *dev) if (skb) { skb_put(skb, rx_size); - memcpy(skb->data, pbuff, rx_size); + skb_copy_to_linear_data(skb, pbuff, rx_size); /* Update Counters */ tp->MacStat.rx_packets++; tp->MacStat.rx_bytes += skb->len; /* Kick the packet on up. */ - skb->dev = dev; skb->protocol = tr_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; diff --git a/drivers/net/tokenring/tms380tr.c b/drivers/net/tokenring/tms380tr.c index ea797ca..12bd2940 100644 --- a/drivers/net/tokenring/tms380tr.c +++ b/drivers/net/tokenring/tms380tr.c @@ -644,7 +644,7 @@ static int tms380tr_hardware_send_packet(struct sk_buff *skb, struct net_device dmabuf = 0; i = tp->TplFree->TPLIndex; buf = tp->LocalTxBuffers[i]; - memcpy(buf, skb->data, length); + skb_copy_from_linear_data(skb, buf, length); newbuf = ((char *)buf - (char *)tp) + tp->dmabuffer; } else { @@ -2168,7 +2168,6 @@ static void tms380tr_rcv_status_irq(struct net_device *dev) } else { - skb->dev = dev; skb_put(skb, tp->MaxPacketSize); rpl->SkbStat = SKB_DATA_COPY; ReceiveDataPtr = rpl->MData; @@ -2179,7 +2178,8 @@ static void tms380tr_rcv_status_irq(struct net_device *dev) || rpl->SkbStat == SKB_DMA_DIRECT)) { if(rpl->SkbStat == SKB_DATA_COPY) - memcpy(skb->data, ReceiveDataPtr, Length); + skb_copy_to_linear_data(skb, ReceiveDataPtr, + Length); /* Deliver frame to system */ rpl->Skb = NULL; diff --git a/drivers/net/tsi108_eth.c b/drivers/net/tsi108_eth.c index d92c5c5..0bfc2c9 100644 --- a/drivers/net/tsi108_eth.c +++ b/drivers/net/tsi108_eth.c @@ -788,7 +788,6 @@ static int tsi108_complete_rx(struct net_device *dev, int budget) printk(".\n"); } - skb->dev = dev; skb_put(skb, data->rxring[rx].len); skb->protocol = eth_type_trans(skb, dev); netif_receive_skb(skb); diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c index 9d67f11..8617298 100644 --- a/drivers/net/tulip/de2104x.c +++ b/drivers/net/tulip/de2104x.c @@ -63,7 +63,7 @@ MODULE_PARM_DESC (debug, "de2104x bitmapped message enable number"); /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc_) || defined(__ia64__) \ + || defined(CONFIG_SPARC) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else @@ -435,7 +435,6 @@ static void de_rx (struct de_private *de) rx_work = 100; goto rx_next; } - copy_skb->dev = de->dev; if (!copying_skb) { pci_unmap_single(de->pdev, mapping, @@ -450,8 +449,8 @@ static void de_rx (struct de_private *de) } else { pci_dma_sync_single_for_cpu(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); skb_reserve(copy_skb, RX_OFFSET); - memcpy(skb_put(copy_skb, len), skb->data, len); - + skb_copy_from_linear_data(skb, skb_put(copy_skb, len), + len); pci_dma_sync_single_for_device(de->pdev, mapping, len, PCI_DMA_FROMDEVICE); /* We'll reuse the original ring buffer. */ @@ -1685,7 +1684,7 @@ static const struct ethtool_ops de_ethtool_ops = { .get_regs = de_get_regs, }; -static void __init de21040_get_mac_address (struct de_private *de) +static void __devinit de21040_get_mac_address (struct de_private *de) { unsigned i; @@ -1703,7 +1702,7 @@ static void __init de21040_get_mac_address (struct de_private *de) } } -static void __init de21040_get_media_info(struct de_private *de) +static void __devinit de21040_get_media_info(struct de_private *de) { unsigned int i; @@ -1765,7 +1764,7 @@ static unsigned __devinit tulip_read_eeprom(void __iomem *regs, int location, in return retval; } -static void __init de21041_get_srom_info (struct de_private *de) +static void __devinit de21041_get_srom_info (struct de_private *de) { unsigned i, sa_offset = 0, ofs; u8 ee_data[DE_EEPROM_SIZE + 6] = {}; diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c index 4b3cd3d..62143f9 100644 --- a/drivers/net/tulip/de4x5.c +++ b/drivers/net/tulip/de4x5.c @@ -1160,7 +1160,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) sprintf(lp->adapter_name,"%s (%s)", name, gendev->bus_id); lp->dma_size = (NUM_RX_DESC + NUM_TX_DESC) * sizeof(struct de4x5_desc); -#if defined(__alpha__) || defined(__powerpc__) || defined(__sparc_v9__) || defined(DE4X5_DO_MEMCPY) +#if defined(__alpha__) || defined(__powerpc__) || defined(CONFIG_SPARC) || defined(DE4X5_DO_MEMCPY) lp->dma_size += RX_BUFF_SZ * NUM_RX_DESC + DE4X5_ALIGN; #endif lp->rx_ring = dma_alloc_coherent(gendev, lp->dma_size, @@ -1175,7 +1175,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) ** Set up the RX descriptor ring (Intels) ** Allocate contiguous receive buffers, long word aligned (Alphas) */ -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) for (i=0; i<NUM_RX_DESC; i++) { lp->rx_ring[i].status = 0; lp->rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ); @@ -1252,11 +1252,7 @@ de4x5_hw_init(struct net_device *dev, u_long iobase, struct device *gendev) mii_get_phy(dev); } -#ifndef __sparc_v9__ printk(" and requires IRQ%d (provided by %s).\n", dev->irq, -#else - printk(" and requires IRQ%x (provided by %s).\n", dev->irq, -#endif ((lp->bus == PCI) ? "PCI BIOS" : "EISA CNFG")); } @@ -3627,14 +3623,13 @@ de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) struct de4x5_private *lp = netdev_priv(dev); struct sk_buff *p; -#if !defined(__alpha__) && !defined(__powerpc__) && !defined(__sparc_v9__) && !defined(DE4X5_DO_MEMCPY) +#if !defined(__alpha__) && !defined(__powerpc__) && !defined(CONFIG_SPARC) && !defined(DE4X5_DO_MEMCPY) struct sk_buff *ret; u_long i=0, tmp; p = dev_alloc_skb(IEEE802_3_SZ + DE4X5_ALIGN + 2); if (!p) return NULL; - p->dev = dev; tmp = virt_to_bus(p->data); i = ((tmp + DE4X5_ALIGN) & ~DE4X5_ALIGN) - tmp; skb_reserve(p, i); @@ -3655,7 +3650,6 @@ de4x5_alloc_rx_buff(struct net_device *dev, int index, int len) p = dev_alloc_skb(len + 2); if (!p) return NULL; - p->dev = dev; skb_reserve(p, 2); /* Align */ if (index < lp->rx_old) { /* Wrapped buffer */ short tlen = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ; diff --git a/drivers/net/tulip/dmfe.c b/drivers/net/tulip/dmfe.c index 7f59a3d..4ed67ff 100644 --- a/drivers/net/tulip/dmfe.c +++ b/drivers/net/tulip/dmfe.c @@ -55,9 +55,6 @@ TODO - Implement pci_driver::suspend() and pci_driver::resume() - power management methods. - Check on 64 bit boxes. Check and fix on big endian boxes. @@ -125,6 +122,11 @@ #define DM9801_NOISE_FLOOR 8 #define DM9802_NOISE_FLOOR 5 +#define DMFE_WOL_LINKCHANGE 0x20000000 +#define DMFE_WOL_SAMPLEPACKET 0x10000000 +#define DMFE_WOL_MAGICPACKET 0x08000000 + + #define DMFE_10MHF 0 #define DMFE_100MHF 1 #define DMFE_10MFD 4 @@ -143,9 +145,16 @@ #define DMFE_TX_TIMEOUT ((3*HZ)/2) /* tx packet time-out time 1.5 s" */ #define DMFE_TX_KICK (HZ/2) /* tx packet Kick-out time 0.5 s" */ -#define DMFE_DBUG(dbug_now, msg, value) if (dmfe_debug || (dbug_now)) printk(KERN_ERR DRV_NAME ": %s %lx\n", (msg), (long) (value)) +#define DMFE_DBUG(dbug_now, msg, value) \ + do { \ + if (dmfe_debug || (dbug_now)) \ + printk(KERN_ERR DRV_NAME ": %s %lx\n",\ + (msg), (long) (value)); \ + } while (0) -#define SHOW_MEDIA_TYPE(mode) printk(KERN_ERR DRV_NAME ": Change Speed to %sMhz %s duplex\n",mode & 1 ?"100":"10", mode & 4 ? "full":"half"); +#define SHOW_MEDIA_TYPE(mode) \ + printk (KERN_INFO DRV_NAME ": Change Speed to %sMhz %s duplex\n" , \ + (mode & 1) ? "100":"10", (mode & 4) ? "full":"half"); /* CR9 definition: SROM/MII */ @@ -163,23 +172,33 @@ #define SROM_V41_CODE 0x14 -#define SROM_CLK_WRITE(data, ioaddr) outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr);udelay(5);outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr);udelay(5); +#define SROM_CLK_WRITE(data, ioaddr) \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS|CR9_SRCLK,ioaddr); \ + udelay(5); \ + outl(data|CR9_SROM_READ|CR9_SRCS,ioaddr); \ + udelay(5); + +#define __CHK_IO_SIZE(pci_id, dev_rev) \ + (( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? \ + DM9102A_IO_SIZE: DM9102_IO_SIZE) -#define __CHK_IO_SIZE(pci_id, dev_rev) ( ((pci_id)==PCI_DM9132_ID) || ((dev_rev) >= 0x02000030) ) ? DM9102A_IO_SIZE: DM9102_IO_SIZE -#define CHK_IO_SIZE(pci_dev, dev_rev) __CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev) +#define CHK_IO_SIZE(pci_dev, dev_rev) \ + (__CHK_IO_SIZE(((pci_dev)->device << 16) | (pci_dev)->vendor, dev_rev)) /* Sten Check */ #define DEVICE net_device /* Structure/enum declaration ------------------------------- */ struct tx_desc { - u32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ + __le32 tdes0, tdes1, tdes2, tdes3; /* Data for the card */ char *tx_buf_ptr; /* Data for us */ struct tx_desc *next_tx_desc; } __attribute__(( aligned(32) )); struct rx_desc { - u32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ + __le32 rdes0, rdes1, rdes2, rdes3; /* Data for the card */ struct sk_buff *rx_skb_ptr; /* Data for us */ struct rx_desc *next_rx_desc; } __attribute__(( aligned(32) )); @@ -187,7 +206,7 @@ struct rx_desc { struct dmfe_board_info { u32 chip_id; /* Chip vendor/Device ID */ u32 chip_revision; /* Chip revision */ - struct DEVICE *dev; /* net device */ + struct DEVICE *next_dev; /* next device */ struct pci_dev *pdev; /* PCI device */ spinlock_t lock; @@ -231,10 +250,10 @@ struct dmfe_board_info { u8 media_mode; /* user specify media mode */ u8 op_mode; /* real work media mode */ u8 phy_addr; - u8 link_failed; /* Ever link failed */ u8 wait_reset; /* Hardware failed, need to reset */ u8 dm910x_chk_mode; /* Operating mode check */ u8 first_in_callback; /* Flag to record state */ + u8 wol_mode; /* user WOL settings */ struct timer_list timer; /* System defined statistic counter */ @@ -329,7 +348,7 @@ static void dmfe_program_DM9802(struct dmfe_board_info *); static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * ); static void dmfe_set_phyxcer(struct dmfe_board_info *); -/* DM910X network baord routine ---------------------------- */ +/* DM910X network board routine ---------------------------- */ /* * Search DM910X board ,allocate space and register it @@ -356,7 +375,8 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, SET_NETDEV_DEV(dev, &pdev->dev); if (pci_set_dma_mask(pdev, DMA_32BIT_MASK)) { - printk(KERN_WARNING DRV_NAME ": 32-bit PCI DMA not available.\n"); + printk(KERN_WARNING DRV_NAME + ": 32-bit PCI DMA not available.\n"); err = -ENODEV; goto err_out_free; } @@ -399,11 +419,12 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, /* Init system & device */ db = netdev_priv(dev); - db->dev = dev; - /* Allocate Tx/Rx descriptor memory */ - db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); - db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); + db->desc_pool_ptr = pci_alloc_consistent(pdev, sizeof(struct tx_desc) * + DESC_ALL_CNT + 0x20, &db->desc_pool_dma_ptr); + + db->buf_pool_ptr = pci_alloc_consistent(pdev, TX_BUF_ALLOC * + TX_DESC_CNT + 4, &db->buf_pool_dma_ptr); db->first_tx_desc = (struct tx_desc *) db->desc_pool_ptr; db->first_tx_desc_dma = db->desc_pool_dma_ptr; @@ -413,6 +434,7 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, db->chip_id = ent->driver_data; db->ioaddr = pci_resource_start(pdev, 0); db->chip_revision = dev_rev; + db->wol_mode = 0; db->pdev = pdev; @@ -428,7 +450,7 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, dev->poll_controller = &poll_dmfe; #endif dev->ethtool_ops = &netdev_ethtool_ops; - netif_carrier_off(db->dev); + netif_carrier_off(dev); spin_lock_init(&db->lock); pci_read_config_dword(pdev, 0x50, &pci_pmr); @@ -440,7 +462,8 @@ static int __devinit dmfe_init_one (struct pci_dev *pdev, /* read 64 word srom data */ for (i = 0; i < 64; i++) - ((u16 *) db->srom)[i] = cpu_to_le16(read_srom_word(db->ioaddr, i)); + ((__le16 *) db->srom)[i] = + cpu_to_le16(read_srom_word(db->ioaddr, i)); /* Set Node address */ for (i = 0; i < 6; i++) @@ -482,14 +505,17 @@ static void __devexit dmfe_remove_one (struct pci_dev *pdev) DMFE_DBUG(0, "dmfe_remove_one()", 0); if (dev) { + + unregister_netdev(dev); + pci_free_consistent(db->pdev, sizeof(struct tx_desc) * DESC_ALL_CNT + 0x20, db->desc_pool_ptr, db->desc_pool_dma_ptr); pci_free_consistent(db->pdev, TX_BUF_ALLOC * TX_DESC_CNT + 4, db->buf_pool_ptr, db->buf_pool_dma_ptr); - unregister_netdev(dev); pci_release_regions(pdev); free_netdev(dev); /* free board information */ + pci_set_drvdata(pdev, NULL); } @@ -509,7 +535,8 @@ static int dmfe_open(struct DEVICE *dev) DMFE_DBUG(0, "dmfe_open", 0); - ret = request_irq(dev->irq, &dmfe_interrupt, IRQF_SHARED, dev->name, dev); + ret = request_irq(dev->irq, &dmfe_interrupt, + IRQF_SHARED, dev->name, dev); if (ret) return ret; @@ -518,7 +545,6 @@ static int dmfe_open(struct DEVICE *dev) db->tx_packet_cnt = 0; db->tx_queue_cnt = 0; db->rx_avail_cnt = 0; - db->link_failed = 1; db->wait_reset = 0; db->first_in_callback = 0; @@ -650,7 +676,8 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) /* No Tx resource check, it never happen nromally */ if (db->tx_queue_cnt >= TX_FREE_DESC_CNT) { spin_unlock_irqrestore(&db->lock, flags); - printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", db->tx_queue_cnt); + printk(KERN_ERR DRV_NAME ": No Tx resource %ld\n", + db->tx_queue_cnt); return 1; } @@ -659,7 +686,7 @@ static int dmfe_start_xmit(struct sk_buff *skb, struct DEVICE *dev) /* transmit this packet */ txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); /* Point to next transmit free descriptor */ @@ -722,7 +749,8 @@ static int dmfe_stop(struct DEVICE *dev) #if 0 /* show statistic counter */ - printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", + printk(DRV_NAME ": FU:%lx EC:%lx LC:%lx NC:%lx" + " LOC:%lx TXJT:%lx RESET:%lx RCR8:%lx FAL:%lx TT:%lx\n", db->tx_fifo_underrun, db->tx_excessive_collision, db->tx_late_collision, db->tx_no_carrier, db->tx_loss_carrier, db->tx_jabber_timeout, db->reset_count, db->reset_cr8, @@ -905,7 +933,7 @@ static inline u32 cal_CRC(unsigned char * Data, unsigned int Len, u8 flag) static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) { struct rx_desc *rxptr; - struct sk_buff *skb; + struct sk_buff *skb, *newskb; int rxlen; u32 rdes0; @@ -919,7 +947,9 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) db->rx_avail_cnt--; db->interval_rx_cnt++; - pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + pci_unmap_single(db->pdev, le32_to_cpu(rxptr->rdes2), + RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE); + if ( (rdes0 & 0x300) != 0x300) { /* A packet without First/Last flag */ /* reuse this SKB */ @@ -956,18 +986,20 @@ static void dmfe_rx_packet(struct DEVICE *dev, struct dmfe_board_info * db) } else { /* Good packet, send to upper layer */ /* Shorst packet used new SKB */ - if ( (rxlen < RX_COPY_SIZE) && - ( (skb = dev_alloc_skb(rxlen + 2) ) - != NULL) ) { + if ((rxlen < RX_COPY_SIZE) && + ((newskb = dev_alloc_skb(rxlen + 2)) + != NULL)) { + + skb = newskb; /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->data, rxlen); + skb_copy_from_linear_data(rxptr->rx_skb_ptr, + skb_put(skb, rxlen), + rxlen); dmfe_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; + } else skb_put(skb, rxlen); - } + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -1037,7 +1069,11 @@ static void dmfe_set_filter_mode(struct DEVICE * dev) spin_unlock_irqrestore(&db->lock, flags); } -static void netdev_get_drvinfo(struct net_device *dev, +/* + * Ethtool interace + */ + +static void dmfe_ethtool_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { struct dmfe_board_info *np = netdev_priv(dev); @@ -1051,9 +1087,35 @@ static void netdev_get_drvinfo(struct net_device *dev, dev->base_addr, dev->irq); } +static int dmfe_ethtool_set_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + if (wolinfo->wolopts & (WAKE_UCAST | WAKE_MCAST | WAKE_BCAST | + WAKE_ARP | WAKE_MAGICSECURE)) + return -EOPNOTSUPP; + + db->wol_mode = wolinfo->wolopts; + return 0; +} + +static void dmfe_ethtool_get_wol(struct net_device *dev, + struct ethtool_wolinfo *wolinfo) +{ + struct dmfe_board_info *db = netdev_priv(dev); + + wolinfo->supported = WAKE_PHY | WAKE_MAGIC; + wolinfo->wolopts = db->wol_mode; + return; +} + + static const struct ethtool_ops netdev_ethtool_ops = { - .get_drvinfo = netdev_get_drvinfo, + .get_drvinfo = dmfe_ethtool_get_drvinfo, .get_link = ethtool_op_get_link, + .set_wol = dmfe_ethtool_set_wol, + .get_wol = dmfe_ethtool_get_wol, }; /* @@ -1069,6 +1131,8 @@ static void dmfe_timer(unsigned long data) struct dmfe_board_info *db = netdev_priv(dev); unsigned long flags; + int link_ok, link_ok_phy; + DMFE_DBUG(0, "dmfe_timer()", 0); spin_lock_irqsave(&db->lock, flags); @@ -1078,7 +1142,8 @@ static void dmfe_timer(unsigned long data) if (db->chip_type && (db->chip_id==PCI_DM9102_ID)) { db->cr6_data &= ~0x40000; update_cr6(db->cr6_data, db->ioaddr); - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + phy_write(db->ioaddr, + db->phy_addr, 0, 0x1000, db->chip_id); db->cr6_data |= 0x40000; update_cr6(db->cr6_data, db->ioaddr); db->timer.expires = DMFE_TIMER_WUT + HZ * 2; @@ -1139,21 +1204,41 @@ static void dmfe_timer(unsigned long data) (db->chip_revision == 0x02000010)) ) { /* DM9102A Chip */ if (tmp_cr12 & 2) - tmp_cr12 = 0x0; /* Link failed */ + link_ok = 0; else - tmp_cr12 = 0x3; /* Link OK */ + link_ok = 1; } + else + /*0x43 is used instead of 0x3 because bit 6 should represent + link status of external PHY */ + link_ok = (tmp_cr12 & 0x43) ? 1 : 0; + - if ( !(tmp_cr12 & 0x3) && !db->link_failed ) { + /* If chip reports that link is failed it could be because external + PHY link status pin is not conected correctly to chip + To be sure ask PHY too. + */ + + /* need a dummy read because of PHY's register latch*/ + phy_read (db->ioaddr, db->phy_addr, 1, db->chip_id); + link_ok_phy = (phy_read (db->ioaddr, + db->phy_addr, 1, db->chip_id) & 0x4) ? 1 : 0; + + if (link_ok_phy != link_ok) { + DMFE_DBUG (0, "PHY and chip report different link status", 0); + link_ok = link_ok | link_ok_phy; + } + + if ( !link_ok && netif_carrier_ok(dev)) { /* Link Failed */ DMFE_DBUG(0, "Link Failed", tmp_cr12); - db->link_failed = 1; - netif_carrier_off(db->dev); + netif_carrier_off(dev); /* For Force 10/100M Half/Full mode: Enable Auto-Nego mode */ /* AUTO or force 1M Homerun/Longrun don't need */ if ( !(db->media_mode & 0x38) ) - phy_write(db->ioaddr, db->phy_addr, 0, 0x1000, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, + 0, 0x1000, db->chip_id); /* AUTO mode, if INT phyxcer link failed, select EXT device */ if (db->media_mode & DMFE_AUTO) { @@ -1162,21 +1247,19 @@ static void dmfe_timer(unsigned long data) db->cr6_data&=~0x00000200; /* bit9=0, HD mode */ update_cr6(db->cr6_data, db->ioaddr); } - } else - if ((tmp_cr12 & 0x3) && db->link_failed) { - DMFE_DBUG(0, "Link link OK", tmp_cr12); - db->link_failed = 0; - - /* Auto Sense Speed */ - if ( (db->media_mode & DMFE_AUTO) && - dmfe_sense_speed(db) ) - db->link_failed = 1; - else - netif_carrier_on(db->dev); - dmfe_process_mode(db); - /* SHOW_MEDIA_TYPE(db->op_mode); */ + } else if (!netif_carrier_ok(dev)) { + + DMFE_DBUG(0, "Link link OK", tmp_cr12); + + /* Auto Sense Speed */ + if ( !(db->media_mode & DMFE_AUTO) || !dmfe_sense_speed(db)) { + netif_carrier_on(dev); + SHOW_MEDIA_TYPE(db->op_mode); } + dmfe_process_mode(db); + } + /* HPNA remote command check */ if (db->HPNA_command & 0xf00) { db->HPNA_timer--; @@ -1221,7 +1304,7 @@ static void dmfe_dynamic_reset(struct DEVICE *dev) db->tx_packet_cnt = 0; db->tx_queue_cnt = 0; db->rx_avail_cnt = 0; - db->link_failed = 1; + netif_carrier_off(dev); db->wait_reset = 0; /* Re-initilize DM910X board */ @@ -1259,7 +1342,8 @@ static void dmfe_reuse_skb(struct dmfe_board_info *db, struct sk_buff * skb) if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { rxptr->rx_skb_ptr = skb; - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->data, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, + skb->data, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); db->rx_avail_cnt++; @@ -1291,8 +1375,11 @@ static void dmfe_descriptor_init(struct dmfe_board_info *db, unsigned long ioadd outl(db->first_tx_desc_dma, ioaddr + DCR4); /* TX DESC address */ /* rx descriptor start pointer */ - db->first_rx_desc = (void *)db->first_tx_desc + sizeof(struct tx_desc) * TX_DESC_CNT; - db->first_rx_desc_dma = db->first_tx_desc_dma + sizeof(struct tx_desc) * TX_DESC_CNT; + db->first_rx_desc = (void *)db->first_tx_desc + + sizeof(struct tx_desc) * TX_DESC_CNT; + + db->first_rx_desc_dma = db->first_tx_desc_dma + + sizeof(struct tx_desc) * TX_DESC_CNT; db->rx_insert_ptr = db->first_rx_desc; db->rx_ready_ptr = db->first_rx_desc; outl(db->first_rx_desc_dma, ioaddr + DCR3); /* RX DESC address */ @@ -1470,7 +1557,8 @@ static void allocate_rx_buffer(struct dmfe_board_info *db) if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) break; rxptr->rx_skb_ptr = skb; /* FIXME (?) */ - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->data, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->data, + RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); rxptr = rxptr->next_rx_desc; @@ -1510,7 +1598,8 @@ static u16 read_srom_word(long ioaddr, int offset) for (i = 16; i > 0; i--) { outl(CR9_SROM_READ | CR9_SRCS | CR9_SRCLK, cr9_ioaddr); udelay(5); - srom_data = (srom_data << 1) | ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); + srom_data = (srom_data << 1) | + ((inl(cr9_ioaddr) & CR9_CRDOUT) ? 1 : 0); outl(CR9_SROM_READ | CR9_SRCS, cr9_ioaddr); udelay(5); } @@ -1537,9 +1626,11 @@ static u8 dmfe_sense_speed(struct dmfe_board_info * db) if ( (phy_mode & 0x24) == 0x24 ) { if (db->chip_id == PCI_DM9132_ID) /* DM9132 */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 7, db->chip_id) & 0xf000; + phy_mode = phy_read(db->ioaddr, + db->phy_addr, 7, db->chip_id) & 0xf000; else /* DM9102/DM9102A */ - phy_mode = phy_read(db->ioaddr, db->phy_addr, 17, db->chip_id) & 0xf000; + phy_mode = phy_read(db->ioaddr, + db->phy_addr, 17, db->chip_id) & 0xf000; /* printk(DRV_NAME ": Phy_mode %x ",phy_mode); */ switch (phy_mode) { case 0x1000: db->op_mode = DMFE_10MHF; break; @@ -1576,8 +1667,11 @@ static void dmfe_set_phyxcer(struct dmfe_board_info *db) /* DM9009 Chip: Phyxcer reg18 bit12=0 */ if (db->chip_id == PCI_DM9009_ID) { - phy_reg = phy_read(db->ioaddr, db->phy_addr, 18, db->chip_id) & ~0x1000; - phy_write(db->ioaddr, db->phy_addr, 18, phy_reg, db->chip_id); + phy_reg = phy_read(db->ioaddr, + db->phy_addr, 18, db->chip_id) & ~0x1000; + + phy_write(db->ioaddr, + db->phy_addr, 18, phy_reg, db->chip_id); } /* Phyxcer capability setting */ @@ -1650,10 +1744,12 @@ static void dmfe_process_mode(struct dmfe_board_info *db) case DMFE_100MHF: phy_reg = 0x2000; break; case DMFE_100MFD: phy_reg = 0x2100; break; } - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + phy_write(db->ioaddr, + db->phy_addr, 0, phy_reg, db->chip_id); if ( db->chip_type && (db->chip_id == PCI_DM9102_ID) ) mdelay(20); - phy_write(db->ioaddr, db->phy_addr, 0, phy_reg, db->chip_id); + phy_write(db->ioaddr, + db->phy_addr, 0, phy_reg, db->chip_id); } } } @@ -1663,7 +1759,8 @@ static void dmfe_process_mode(struct dmfe_board_info *db) * Write a word to Phy register */ -static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data, u32 chip_id) +static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, + u16 phy_data, u32 chip_id) { u16 i; unsigned long ioaddr; @@ -1689,11 +1786,13 @@ static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data /* Send Phy address */ for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); /* Send register address */ for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : PHY_DATA_0); /* written trasnition */ phy_write_1bit(ioaddr, PHY_DATA_1); @@ -1701,7 +1800,8 @@ static void phy_write(unsigned long iobase, u8 phy_addr, u8 offset, u16 phy_data /* Write a word data to PHY controller */ for ( i = 0x8000; i > 0; i >>= 1) - phy_write_1bit(ioaddr, phy_data & i ? PHY_DATA_1 : PHY_DATA_0); + phy_write_1bit(ioaddr, + phy_data & i ? PHY_DATA_1 : PHY_DATA_0); } } @@ -1738,11 +1838,13 @@ static u16 phy_read(unsigned long iobase, u8 phy_addr, u8 offset, u32 chip_id) /* Send Phy address */ for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); + phy_write_1bit(ioaddr, + phy_addr & i ? PHY_DATA_1 : PHY_DATA_0); /* Send register address */ for (i = 0x10; i > 0; i = i >> 1) - phy_write_1bit(ioaddr, offset & i ? PHY_DATA_1 : PHY_DATA_0); + phy_write_1bit(ioaddr, + offset & i ? PHY_DATA_1 : PHY_DATA_0); /* Skip transition state */ phy_read_1bit(ioaddr); @@ -1963,7 +2065,8 @@ static void dmfe_HPNA_remote_cmd_chk(struct dmfe_board_info * db) /* Check remote device status match our setting ot not */ if ( phy_reg != (db->HPNA_command & 0x0f00) ) { - phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, db->chip_id); + phy_write(db->ioaddr, db->phy_addr, 16, db->HPNA_command, + db->chip_id); db->HPNA_timer=8; } else db->HPNA_timer=600; /* Match, every 10 minutes, check */ @@ -1981,11 +2084,85 @@ static struct pci_device_id dmfe_pci_tbl[] = { MODULE_DEVICE_TABLE(pci, dmfe_pci_tbl); +#ifdef CONFIG_PM +static int dmfe_suspend(struct pci_dev *pci_dev, pm_message_t state) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + struct dmfe_board_info *db = netdev_priv(dev); + u32 tmp; + + /* Disable upper layer interface */ + netif_device_detach(dev); + + /* Disable Tx/Rx */ + db->cr6_data &= ~(CR6_RXSC | CR6_TXSC); + update_cr6(db->cr6_data, dev->base_addr); + + /* Disable Interrupt */ + outl(0, dev->base_addr + DCR7); + outl(inl (dev->base_addr + DCR5), dev->base_addr + DCR5); + + /* Fre RX buffers */ + dmfe_free_rxbuffer(db); + + /* Enable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + tmp &= ~(DMFE_WOL_LINKCHANGE|DMFE_WOL_MAGICPACKET); + + if (db->wol_mode & WAKE_PHY) + tmp |= DMFE_WOL_LINKCHANGE; + if (db->wol_mode & WAKE_MAGIC) + tmp |= DMFE_WOL_MAGICPACKET; + + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 1); + pci_enable_wake(pci_dev, PCI_D3cold, 1); + + /* Power down device*/ + pci_set_power_state(pci_dev, pci_choose_state (pci_dev,state)); + pci_save_state(pci_dev); + + return 0; +} + +static int dmfe_resume(struct pci_dev *pci_dev) +{ + struct net_device *dev = pci_get_drvdata(pci_dev); + u32 tmp; + + pci_restore_state(pci_dev); + pci_set_power_state(pci_dev, PCI_D0); + + /* Re-initilize DM910X board */ + dmfe_init_dm910x(dev); + + /* Disable WOL */ + pci_read_config_dword(pci_dev, 0x40, &tmp); + + tmp &= ~(DMFE_WOL_LINKCHANGE | DMFE_WOL_MAGICPACKET); + pci_write_config_dword(pci_dev, 0x40, tmp); + + pci_enable_wake(pci_dev, PCI_D3hot, 0); + pci_enable_wake(pci_dev, PCI_D3cold, 0); + + /* Restart upper layer interface */ + netif_device_attach(dev); + + return 0; +} +#else +#define dmfe_suspend NULL +#define dmfe_resume NULL +#endif + static struct pci_driver dmfe_driver = { .name = "dmfe", .id_table = dmfe_pci_tbl, .probe = dmfe_init_one, .remove = __devexit_p(dmfe_remove_one), + .suspend = dmfe_suspend, + .resume = dmfe_resume }; MODULE_AUTHOR("Sten Wang, sten_wang@davicom.com.tw"); @@ -2003,8 +2180,11 @@ module_param(HPNA_tx_cmd, byte, 0); module_param(HPNA_NoiseFloor, byte, 0); module_param(SF_mode, byte, 0); MODULE_PARM_DESC(debug, "Davicom DM9xxx enable debugging (0-1)"); -MODULE_PARM_DESC(mode, "Davicom DM9xxx: Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); -MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function (bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); +MODULE_PARM_DESC(mode, "Davicom DM9xxx: " + "Bit 0: 10/100Mbps, bit 2: duplex, bit 8: HomePNA"); + +MODULE_PARM_DESC(SF_mode, "Davicom DM9xxx special function " + "(bit 0: VLAN, bit 1 Flow Control, bit 2: TX pause packet)"); /* Description: * when user used insmod to add module, system invoked init_module() diff --git a/drivers/net/tulip/interrupt.c b/drivers/net/tulip/interrupt.c index e3488d7..9b08afb 100644 --- a/drivers/net/tulip/interrupt.c +++ b/drivers/net/tulip/interrupt.c @@ -192,7 +192,6 @@ int tulip_poll(struct net_device *dev, int *budget) to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, @@ -416,7 +415,6 @@ static int tulip_rx(struct net_device *dev) to a minimally-sized skbuff. */ if (pkt_len < tulip_rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(tp->pdev, tp->rx_buffers[entry].mapping, @@ -675,7 +673,7 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance) if (tp->link_change) (tp->link_change)(dev, csr5); } - if (csr5 & SytemError) { + if (csr5 & SystemError) { int error = (csr5 >> 23) & 7; /* oops, we hit a PCI error. The code produced corresponds * to the reason: @@ -745,7 +743,7 @@ irqreturn_t tulip_interrupt(int irq, void *dev_instance) TxFIFOUnderflow | TxJabber | TPLnkFail | - SytemError )) != 0); + SystemError )) != 0); #else } while ((csr5 & (NormalIntr|AbnormalIntr)) != 0); diff --git a/drivers/net/tulip/media.c b/drivers/net/tulip/media.c index 20bd52b..b562566 100644 --- a/drivers/net/tulip/media.c +++ b/drivers/net/tulip/media.c @@ -44,8 +44,10 @@ static const unsigned char comet_miireg2offset[32] = { /* MII transceiver control section. Read and write the MII registers using software-generated serial - MDIO protocol. See the MII specifications or DP83840A data sheet - for details. */ + MDIO protocol. + See IEEE 802.3-2002.pdf (Section 2, Chapter "22.2.4 Management functions") + or DP83840A data sheet for more details. + */ int tulip_mdio_read(struct net_device *dev, int phy_id, int location) { @@ -261,24 +263,56 @@ void tulip_select_media(struct net_device *dev, int startup) u16 *reset_sequence = &((u16*)(p+3))[init_length]; int reset_length = p[2 + init_length*2]; misc_info = reset_sequence + reset_length; - if (startup) + if (startup) { + int timeout = 10; /* max 1 ms */ for (i = 0; i < reset_length; i++) iowrite32(get_u16(&reset_sequence[i]) << 16, ioaddr + CSR15); + + /* flush posted writes */ + ioread32(ioaddr + CSR15); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); + } for (i = 0; i < init_length; i++) iowrite32(get_u16(&init_sequence[i]) << 16, ioaddr + CSR15); + + ioread32(ioaddr + CSR15); /* flush posted writes */ } else { u8 *init_sequence = p + 2; u8 *reset_sequence = p + 3 + init_length; int reset_length = p[2 + init_length]; misc_info = (u16*)(reset_sequence + reset_length); if (startup) { + int timeout = 10; /* max 1 ms */ iowrite32(mtable->csr12dir | 0x100, ioaddr + CSR12); for (i = 0; i < reset_length; i++) iowrite32(reset_sequence[i], ioaddr + CSR12); + + /* flush posted writes */ + ioread32(ioaddr + CSR12); + + /* Sect 3.10.3 in DP83840A.pdf (p39) */ + udelay(500); + + /* Section 4.2 in DP83840A.pdf (p43) */ + /* and IEEE 802.3 "22.2.4.1.1 Reset" */ + while (timeout-- && + (tulip_mdio_read (dev, phy_num, MII_BMCR) & BMCR_RESET)) + udelay(100); } for (i = 0; i < init_length; i++) iowrite32(init_sequence[i], ioaddr + CSR12); + + ioread32(ioaddr + CSR12); /* flush posted writes */ } + tmp_info = get_u16(&misc_info[1]); if (tmp_info) tp->advertising[phy_num] = tmp_info | 1; diff --git a/drivers/net/tulip/tulip.h b/drivers/net/tulip/tulip.h index 25f25da..c840d2e 100644 --- a/drivers/net/tulip/tulip.h +++ b/drivers/net/tulip/tulip.h @@ -132,7 +132,7 @@ enum pci_cfg_driver_reg { /* The bits in the CSR5 status registers, mostly interrupt sources. */ enum status_bits { TimerInt = 0x800, - SytemError = 0x2000, + SystemError = 0x2000, TPLnkFail = 0x1000, TPLnkPass = 0x10, NormalIntr = 0x10000, @@ -482,8 +482,11 @@ static inline void tulip_stop_rxtx(struct tulip_private *tp) udelay(10); if (!i) - printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed\n", - pci_name(tp->pdev)); + printk(KERN_DEBUG "%s: tulip_stop_rxtx() failed" + " (CSR5 0x%x CSR6 0x%x)\n", + pci_name(tp->pdev), + ioread32(ioaddr + CSR5), + ioread32(ioaddr + CSR6)); } } diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 5a35354..041af63 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -17,11 +17,11 @@ #define DRV_NAME "tulip" #ifdef CONFIG_TULIP_NAPI -#define DRV_VERSION "1.1.14-NAPI" /* Keep at least for test */ +#define DRV_VERSION "1.1.15-NAPI" /* Keep at least for test */ #else -#define DRV_VERSION "1.1.14" +#define DRV_VERSION "1.1.15" #endif -#define DRV_RELDATE "May 11, 2002" +#define DRV_RELDATE "Feb 27, 2007" #include <linux/module.h> @@ -36,8 +36,8 @@ #include <asm/unaligned.h> #include <asm/uaccess.h> -#ifdef __sparc__ -#include <asm/pbm.h> +#ifdef CONFIG_SPARC +#include <asm/prom.h> #endif static char version[] __devinitdata = @@ -67,7 +67,7 @@ const char * const medianame[32] = { /* Set the copy breakpoint for the copy-only-tiny-buffer Rx structure. */ #if defined(__alpha__) || defined(__arm__) || defined(__hppa__) \ - || defined(__sparc_) || defined(__ia64__) \ + || defined(CONFIG_SPARC) || defined(__ia64__) \ || defined(__sh__) || defined(__mips__) static int rx_copybreak = 1518; #else @@ -91,7 +91,7 @@ static int rx_copybreak = 100; static int csr0 = 0x01A00000 | 0xE000; #elif defined(__i386__) || defined(__powerpc__) || defined(__x86_64__) static int csr0 = 0x01A00000 | 0x8000; -#elif defined(__sparc__) || defined(__hppa__) +#elif defined(CONFIG_SPARC) || defined(__hppa__) /* The UltraSparc PCI controllers will disconnect at every 64-byte * crossing anyways so it makes no sense to tell Tulip to burst * any more than that. @@ -1315,7 +1315,7 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, /* DM9102A has troubles with MRM & clear reserved bits 24:22, 20, 16, 7:1 */ if (tulip_uli_dm_quirk(pdev)) { csr0 &= ~0x01f100ff; -#if defined(__sparc__) +#if defined(CONFIG_SPARC) csr0 = (csr0 & ~0xff00) | 0xe000; #endif } @@ -1535,23 +1535,19 @@ static int __devinit tulip_init_one (struct pci_dev *pdev, Many PCI BIOSes also incorrectly report the IRQ line, so we correct that here as well. */ if (sum == 0 || sum == 6*0xff) { -#if defined(__sparc__) - struct pcidev_cookie *pcp = pdev->sysdata; +#if defined(CONFIG_SPARC) + struct device_node *dp = pci_device_to_OF_node(pdev); + const unsigned char *addr; + int len; #endif eeprom_missing = 1; for (i = 0; i < 5; i++) dev->dev_addr[i] = last_phys_addr[i]; dev->dev_addr[i] = last_phys_addr[i] + 1; -#if defined(__sparc__) - if (pcp) { - unsigned char *addr; - int len; - - addr = of_get_property(pcp->prom_node, - "local-mac-address", &len); - if (addr && len == 6) - memcpy(dev->dev_addr, addr, 6); - } +#if defined(CONFIG_SPARC) + addr = of_get_property(dp, "local-mac-address", &len); + if (addr && len == 6) + memcpy(dev->dev_addr, addr, 6); #endif #if defined(__i386__) || defined(__x86_64__) /* Patch up x86 BIOS bug. */ if (last_irq) diff --git a/drivers/net/tulip/uli526x.c b/drivers/net/tulip/uli526x.c index 229158e..ca2548e 100644 --- a/drivers/net/tulip/uli526x.c +++ b/drivers/net/tulip/uli526x.c @@ -583,7 +583,7 @@ static int uli526x_start_xmit(struct sk_buff *skb, struct net_device *dev) /* transmit this packet */ txptr = db->tx_insert_ptr; - memcpy(txptr->tx_buf_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, txptr->tx_buf_ptr, skb->len); txptr->tdes1 = cpu_to_le32(0xe1000000 | skb->len); /* Point to next transmit free descriptor */ @@ -828,14 +828,14 @@ static void uli526x_rx_packet(struct net_device *dev, struct uli526x_board_info ( (skb = dev_alloc_skb(rxlen + 2) ) != NULL) ) { /* size less than COPY_SIZE, allocate a rxlen SKB */ - skb->dev = dev; skb_reserve(skb, 2); /* 16byte align */ - memcpy(skb_put(skb, rxlen), rxptr->rx_skb_ptr->tail, rxlen); + memcpy(skb_put(skb, rxlen), + skb_tail_pointer(rxptr->rx_skb_ptr), + rxlen); uli526x_reuse_skb(db, rxptr->rx_skb_ptr); - } else { - skb->dev = dev; + } else skb_put(skb, rxlen); - } + skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->last_rx = jiffies; @@ -1177,7 +1177,10 @@ static void uli526x_reuse_skb(struct uli526x_board_info *db, struct sk_buff * sk if (!(rxptr->rdes0 & cpu_to_le32(0x80000000))) { rxptr->rx_skb_ptr = skb; - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); db->rx_avail_cnt++; @@ -1341,7 +1344,10 @@ static void allocate_rx_buffer(struct uli526x_board_info *db) if ( ( skb = dev_alloc_skb(RX_ALLOC_SIZE) ) == NULL ) break; rxptr->rx_skb_ptr = skb; /* FIXME (?) */ - rxptr->rdes2 = cpu_to_le32( pci_map_single(db->pdev, skb->tail, RX_ALLOC_SIZE, PCI_DMA_FROMDEVICE) ); + rxptr->rdes2 = cpu_to_le32(pci_map_single(db->pdev, + skb_tail_pointer(skb), + RX_ALLOC_SIZE, + PCI_DMA_FROMDEVICE)); wmb(); rxptr->rdes0 = cpu_to_le32(0x80000000); rxptr = rxptr->next_rx_desc; diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c index 002a05e..fa44070 100644 --- a/drivers/net/tulip/winbond-840.c +++ b/drivers/net/tulip/winbond-840.c @@ -813,7 +813,6 @@ static void init_rxtx_rings(struct net_device *dev) np->rx_skbuff[i] = skb; if (skb == NULL) break; - skb->dev = dev; /* Mark as being used by this device. */ np->rx_addr[i] = pci_map_single(np->pci_dev,skb->data, np->rx_buf_sz,PCI_DMA_FROMDEVICE); @@ -903,7 +902,7 @@ static void init_registers(struct net_device *dev) } #elif defined(__powerpc__) || defined(__i386__) || defined(__alpha__) || defined(__ia64__) || defined(__x86_64__) i |= 0xE000; -#elif defined(__sparc__) || defined (CONFIG_PARISC) +#elif defined(CONFIG_SPARC) || defined (CONFIG_PARISC) i |= 0x4800; #else #warning Processor architecture undefined @@ -1148,7 +1147,7 @@ static irqreturn_t intr_handler(int irq, void *dev_instance) } /* Abnormal error summary/uncommon events handlers. */ - if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SytemError | + if (intr_status & (AbnormalIntr | TxFIFOUnderflow | SystemError | TimerInt | TxDied)) netdev_error(dev, intr_status); @@ -1229,7 +1228,6 @@ static int netdev_rx(struct net_device *dev) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(np->pci_dev,np->rx_addr[entry], np->rx_skbuff[entry]->len, @@ -1278,7 +1276,6 @@ static int netdev_rx(struct net_device *dev) np->rx_skbuff[entry] = skb; if (skb == NULL) break; /* Better luck next round. */ - skb->dev = dev; /* Mark as being used by this device. */ np->rx_addr[entry] = pci_map_single(np->pci_dev, skb->data, np->rx_buf_sz, PCI_DMA_FROMDEVICE); diff --git a/drivers/net/tulip/xircom_cb.c b/drivers/net/tulip/xircom_cb.c index 61d3130..985a181 100644 --- a/drivers/net/tulip/xircom_cb.c +++ b/drivers/net/tulip/xircom_cb.c @@ -411,9 +411,9 @@ static int xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) sometimes sends more than you ask it to. */ memset(&card->tx_buffer[bufferoffsets[desc]/4],0,1536); - memcpy(&(card->tx_buffer[bufferoffsets[desc]/4]),skb->data,skb->len); - - + skb_copy_from_linear_data(skb, + &(card->tx_buffer[bufferoffsets[desc] / 4]), + skb->len); /* FIXME: The specification tells us that the length we send HAS to be a multiple of 4 bytes. */ @@ -1207,7 +1207,6 @@ static void investigate_read_descriptor(struct net_device *dev,struct xircom_pri card->stats.rx_dropped++; goto out; } - skb->dev = dev; skb_reserve(skb, 2); eth_copy_and_sum(skb, (unsigned char*)&card->rx_buffer[bufferoffset / 4], pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/net/tulip/xircom_tulip_cb.c b/drivers/net/tulip/xircom_tulip_cb.c index a998c5d..f641729 100644 --- a/drivers/net/tulip/xircom_tulip_cb.c +++ b/drivers/net/tulip/xircom_tulip_cb.c @@ -65,7 +65,7 @@ static int rx_copybreak = 100; static int csr0 = 0x01A00000 | 0xE000; #elif defined(__powerpc__) static int csr0 = 0x01B00000 | 0x8000; -#elif defined(__sparc__) +#elif defined(CONFIG_SPARC) static int csr0 = 0x01B00080 | 0x8000; #elif defined(__i386__) static int csr0 = 0x01A00000 | 0x8000; @@ -915,7 +915,9 @@ xircom_start_xmit(struct sk_buff *skb, struct net_device *dev) tp->tx_skbuff[entry] = skb; if (tp->chip_id == X3201_3) { - memcpy(tp->tx_aligned_skbuff[entry]->data,skb->data,skb->len); + skb_copy_from_linear_data(skb, + tp->tx_aligned_skbuff[entry]->data, + skb->len); tp->tx_ring[entry].buffer1 = virt_to_bus(tp->tx_aligned_skbuff[entry]->data); } else tp->tx_ring[entry].buffer1 = virt_to_bus(skb->data); @@ -1238,7 +1240,6 @@ xircom_rx(struct net_device *dev) to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ #if ! defined(__alpha__) eth_copy_and_sum(skb, bus_to_virt(tp->rx_ring[entry].buffer1), diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 5643d1e..a2c6caa 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -18,6 +18,10 @@ /* * Changes: * + * Brian Braunstein <linuxkernel@bristyle.com> 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 <dragorn@kismetwireless.net> 2005/08/14 * Add TUNSETLINK ioctl to set the link encapsulation * @@ -196,7 +200,10 @@ static void tun_net_init(struct net_device *dev) dev->set_multicast_list = tun_net_mclist; ether_setup(dev); - random_ether_addr(dev->dev_addr); + + /* 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)) ); + dev->tx_queue_len = TUN_READQ_SIZE; /* We prefer our own queue length */ break; } @@ -254,11 +261,11 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, struct iovec *iv, return -EFAULT; } - skb->dev = tun->dev; switch (tun->flags & TUN_TYPE_MASK) { case TUN_TUN_DEV: - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->protocol = pi.proto; + skb->dev = tun->dev; break; case TUN_TAP_DEV: skb->protocol = eth_type_trans(skb, tun->dev); @@ -386,8 +393,8 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, * - we are multicast promiscous. * - we belong to the multicast group. */ - memcpy(addr, skb->data, - min_t(size_t, sizeof addr, skb->len)); + 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 || @@ -636,6 +643,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, return 0; 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)) @@ -643,16 +651,24 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, return 0; case SIOCSIFHWADDR: - /** 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 0; + { + /* try to set the actual net device's hw address */ + int ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); + + 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 diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c index 9781b16..f2dd776 100644 --- a/drivers/net/typhoon.c +++ b/drivers/net/typhoon.c @@ -746,8 +746,7 @@ typhoon_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) { struct typhoon *tp = netdev_priv(dev); spin_lock_bh(&tp->state_lock); - if(tp->vlgrp) - tp->vlgrp->vlan_devices[vid] = NULL; + vlan_group_set_device(tp->vlgrp, vid, NULL); spin_unlock_bh(&tp->state_lock); } @@ -1709,7 +1708,6 @@ typhoon_rx(struct typhoon *tp, struct basic_ring *rxRing, volatile u32 * ready, if(pkt_len < rx_copybreak && (new_skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - new_skb->dev = tp->dev; skb_reserve(new_skb, 2); pci_dma_sync_single_for_cpu(tp->pdev, dma_addr, PKT_BUF_SZ, diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index a2fc2bb..16b9acd 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -29,6 +29,7 @@ #include <linux/fsl_devices.h> #include <linux/ethtool.h> #include <linux/mii.h> +#include <linux/phy.h> #include <linux/workqueue.h> #include <asm/of_platform.h> @@ -41,12 +42,13 @@ #include <asm/ucc_fast.h> #include "ucc_geth.h" -#include "ucc_geth_phy.h" +#include "ucc_geth_mii.h" #undef DEBUG -#define DRV_DESC "QE UCC Gigabit Ethernet Controller version:Sept 11, 2006" +#define DRV_DESC "QE UCC Gigabit Ethernet Controller" #define DRV_NAME "ucc_geth" +#define DRV_VERSION "1.1" #define ugeth_printk(level, format, arg...) \ printk(level format "\n", ## arg) @@ -73,22 +75,13 @@ static struct ucc_geth_info ugeth_primary_info = { .bd_mem_part = MEM_PART_SYSTEM, .rtsm = UCC_FAST_SEND_IDLES_BETWEEN_FRAMES, .max_rx_buf_length = 1536, -/* FIXME: should be changed in run time for 1G and 100M */ -#ifdef CONFIG_UGETH_HAS_GIGA - .urfs = UCC_GETH_URFS_GIGA_INIT, - .urfet = UCC_GETH_URFET_GIGA_INIT, - .urfset = UCC_GETH_URFSET_GIGA_INIT, - .utfs = UCC_GETH_UTFS_GIGA_INIT, - .utfet = UCC_GETH_UTFET_GIGA_INIT, - .utftt = UCC_GETH_UTFTT_GIGA_INIT, -#else + /* adjusted at startup if max-speed 1000 */ .urfs = UCC_GETH_URFS_INIT, .urfet = UCC_GETH_URFET_INIT, .urfset = UCC_GETH_URFSET_INIT, .utfs = UCC_GETH_UTFS_INIT, .utfet = UCC_GETH_UTFET_INIT, .utftt = UCC_GETH_UTFTT_INIT, -#endif .ufpt = 256, .mode = UCC_FAST_PROTOCOL_MODE_ETHERNET, .ttx_trx = UCC_FAST_GUMR_TRANSPARENT_TTX_TRX_NORMAL, @@ -217,70 +210,6 @@ static struct list_head *dequeue(struct list_head *lh) } } -static int get_interface_details(enum enet_interface enet_interface, - enum enet_speed *speed, - int *r10m, - int *rmm, - int *rpm, - int *tbi, int *limited_to_full_duplex) -{ - /* Analyze enet_interface according to Interface Mode - Configuration table */ - switch (enet_interface) { - case ENET_10_MII: - *speed = ENET_SPEED_10BT; - break; - case ENET_10_RMII: - *speed = ENET_SPEED_10BT; - *r10m = 1; - *rmm = 1; - break; - case ENET_10_RGMII: - *speed = ENET_SPEED_10BT; - *rpm = 1; - *r10m = 1; - *limited_to_full_duplex = 1; - break; - case ENET_100_MII: - *speed = ENET_SPEED_100BT; - break; - case ENET_100_RMII: - *speed = ENET_SPEED_100BT; - *rmm = 1; - break; - case ENET_100_RGMII: - *speed = ENET_SPEED_100BT; - *rpm = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_GMII: - *speed = ENET_SPEED_1000BT; - *limited_to_full_duplex = 1; - break; - case ENET_1000_RGMII: - *speed = ENET_SPEED_1000BT; - *rpm = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_TBI: - *speed = ENET_SPEED_1000BT; - *tbi = 1; - *limited_to_full_duplex = 1; - break; - case ENET_1000_RTBI: - *speed = ENET_SPEED_1000BT; - *rpm = 1; - *tbi = 1; - *limited_to_full_duplex = 1; - break; - default: - return -EINVAL; - break; - } - - return 0; -} - static struct sk_buff *get_new_skb(struct ucc_geth_private *ugeth, u8 *bd) { struct sk_buff *skb = NULL; @@ -758,24 +687,6 @@ static void dump_regs(struct ucc_geth_private *ugeth) ugeth_info("hafdup : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->hafdup, in_be32(&ugeth->ug_regs->hafdup)); - ugeth_info("miimcfg : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcfg, - in_be32(&ugeth->ug_regs->miimng.miimcfg)); - ugeth_info("miimcom : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcom, - in_be32(&ugeth->ug_regs->miimng.miimcom)); - ugeth_info("miimadd : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimadd, - in_be32(&ugeth->ug_regs->miimng.miimadd)); - ugeth_info("miimcon : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimcon, - in_be32(&ugeth->ug_regs->miimng.miimcon)); - ugeth_info("miimstat : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimstat, - in_be32(&ugeth->ug_regs->miimng.miimstat)); - ugeth_info("miimmind : addr - 0x%08x, val - 0x%08x", - (u32) & ugeth->ug_regs->miimng.miimind, - in_be32(&ugeth->ug_regs->miimng.miimind)); ugeth_info("ifctl : addr - 0x%08x, val - 0x%08x", (u32) & ugeth->ug_regs->ifctl, in_be32(&ugeth->ug_regs->ifctl)); @@ -1425,27 +1336,6 @@ static int init_mac_station_addr_regs(u8 address_byte_0, return 0; } -static int init_mac_duplex_mode(int full_duplex, - int limited_to_full_duplex, - volatile u32 *maccfg2_register) -{ - u32 value = 0; - - /* some interfaces must work in full duplex mode */ - if ((full_duplex == 0) && (limited_to_full_duplex == 1)) - return -EINVAL; - - value = in_be32(maccfg2_register); - - if (full_duplex) - value |= MACCFG2_FDX; - else - value &= ~MACCFG2_FDX; - - out_be32(maccfg2_register, value); - return 0; -} - static int init_check_frame_length_mode(int length_check, volatile u32 *maccfg2_register) { @@ -1477,40 +1367,6 @@ static int init_preamble_length(u8 preamble_length, return 0; } -static int init_mii_management_configuration(int reset_mgmt, - int preamble_supress, - volatile u32 *miimcfg_register, - volatile u32 *miimind_register) -{ - unsigned int timeout = PHY_INIT_TIMEOUT; - u32 value = 0; - - value = in_be32(miimcfg_register); - if (reset_mgmt) { - value |= MIIMCFG_RESET_MANAGEMENT; - out_be32(miimcfg_register, value); - } - - value = 0; - - if (preamble_supress) - value |= MIIMCFG_NO_PREAMBLE; - - value |= UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT; - out_be32(miimcfg_register, value); - - /* Wait until the bus is free */ - while ((in_be32(miimind_register) & MIIMIND_BUSY) && timeout--) - cpu_relax(); - - if (timeout <= 0) { - ugeth_err("%s: The MII Bus is stuck!", __FUNCTION__); - return -ETIMEDOUT; - } - - return 0; -} - static int init_rx_parameters(int reject_broadcast, int receive_short_frames, int promiscuous, volatile u32 *upsmr_register) @@ -1570,10 +1426,8 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) struct ucc_geth_info *ug_info; struct ucc_geth *ug_regs; struct ucc_fast *uf_regs; - enum enet_speed speed; - int ret_val, rpm = 0, tbi = 0, r10m = 0, rmm = - 0, limited_to_full_duplex = 0; - u32 upsmr, maccfg2, utbipar, tbiBaseAddress; + int ret_val; + u32 upsmr, maccfg2, tbiBaseAddress; u16 value; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -1582,24 +1436,13 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) ug_regs = ugeth->ug_regs; uf_regs = ugeth->uccf->uf_regs; - /* Analyze enet_interface according to Interface Mode Configuration - table */ - ret_val = - get_interface_details(ug_info->enet_interface, &speed, &r10m, &rmm, - &rpm, &tbi, &limited_to_full_duplex); - if (ret_val != 0) { - ugeth_err - ("%s: half duplex not supported in requested configuration.", - __FUNCTION__); - return ret_val; - } - /* Set MACCFG2 */ maccfg2 = in_be32(&ug_regs->maccfg2); maccfg2 &= ~MACCFG2_INTERFACE_MODE_MASK; - if ((speed == ENET_SPEED_10BT) || (speed == ENET_SPEED_100BT)) + if ((ugeth->max_speed == SPEED_10) || + (ugeth->max_speed == SPEED_100)) maccfg2 |= MACCFG2_INTERFACE_MODE_NIBBLE; - else if (speed == ENET_SPEED_1000BT) + else if (ugeth->max_speed == SPEED_1000) maccfg2 |= MACCFG2_INTERFACE_MODE_BYTE; maccfg2 |= ug_info->padAndCrc; out_be32(&ug_regs->maccfg2, maccfg2); @@ -1607,54 +1450,39 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) /* Set UPSMR */ upsmr = in_be32(&uf_regs->upsmr); upsmr &= ~(UPSMR_RPM | UPSMR_R10M | UPSMR_TBIM | UPSMR_RMM); - if (rpm) + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UPSMR_RPM; - if (r10m) - upsmr |= UPSMR_R10M; - if (tbi) + switch (ugeth->max_speed) { + case SPEED_10: + upsmr |= UPSMR_R10M; + /* FALLTHROUGH */ + case SPEED_100: + if (ugeth->phy_interface != PHY_INTERFACE_MODE_RTBI) + upsmr |= UPSMR_RMM; + } + } + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { upsmr |= UPSMR_TBIM; - if (rmm) - upsmr |= UPSMR_RMM; + } out_be32(&uf_regs->upsmr, upsmr); - /* Set UTBIPAR */ - utbipar = in_be32(&ug_regs->utbipar); - utbipar &= ~UTBIPAR_PHY_ADDRESS_MASK; - if (tbi) - utbipar |= - (ug_info->phy_address + - ugeth->ug_info->uf_info. - ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT; - else - utbipar |= - (0x10 + - ugeth->ug_info->uf_info. - ucc_num) << UTBIPAR_PHY_ADDRESS_SHIFT; - out_be32(&ug_regs->utbipar, utbipar); - /* Disable autonegotiation in tbi mode, because by default it comes up in autonegotiation mode. */ /* Note that this depends on proper setting in utbipar register. */ - if (tbi) { + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_TBI) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { tbiBaseAddress = in_be32(&ug_regs->utbipar); tbiBaseAddress &= UTBIPAR_PHY_ADDRESS_MASK; tbiBaseAddress >>= UTBIPAR_PHY_ADDRESS_SHIFT; - value = - ugeth->mii_info->mdio_read(ugeth->dev, (u8) tbiBaseAddress, - ENET_TBI_MII_CR); + value = ugeth->phydev->bus->read(ugeth->phydev->bus, + (u8) tbiBaseAddress, ENET_TBI_MII_CR); value &= ~0x1000; /* Turn off autonegotiation */ - ugeth->mii_info->mdio_write(ugeth->dev, (u8) tbiBaseAddress, - ENET_TBI_MII_CR, value); - } - - ret_val = init_mac_duplex_mode(1, - limited_to_full_duplex, - &ug_regs->maccfg2); - if (ret_val != 0) { - ugeth_err - ("%s: half duplex not supported in requested configuration.", - __FUNCTION__); - return ret_val; + ugeth->phydev->bus->write(ugeth->phydev->bus, + (u8) tbiBaseAddress, ENET_TBI_MII_CR, value); } init_check_frame_length_mode(ug_info->lengthCheckRx, &ug_regs->maccfg2); @@ -1676,76 +1504,88 @@ static int adjust_enet_interface(struct ucc_geth_private *ugeth) * function converts those variables into the appropriate * register values, and can bring down the device if needed. */ + static void adjust_link(struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_geth *ug_regs; - u32 tempval; - struct ugeth_mii_info *mii_info = ugeth->mii_info; + struct ucc_fast *uf_regs; + struct phy_device *phydev = ugeth->phydev; + unsigned long flags; + int new_state = 0; ug_regs = ugeth->ug_regs; + uf_regs = ugeth->uccf->uf_regs; + + spin_lock_irqsave(&ugeth->lock, flags); - if (mii_info->link) { + if (phydev->link) { + u32 tempval = in_be32(&ug_regs->maccfg2); + u32 upsmr = in_be32(&uf_regs->upsmr); /* Now we make sure that we can be in full duplex mode. * If not, we operate in half-duplex mode. */ - if (mii_info->duplex != ugeth->oldduplex) { - if (!(mii_info->duplex)) { - tempval = in_be32(&ug_regs->maccfg2); + if (phydev->duplex != ugeth->oldduplex) { + new_state = 1; + if (!(phydev->duplex)) tempval &= ~(MACCFG2_FDX); - out_be32(&ug_regs->maccfg2, tempval); - - ugeth_info("%s: Half Duplex", dev->name); - } else { - tempval = in_be32(&ug_regs->maccfg2); + else tempval |= MACCFG2_FDX; - out_be32(&ug_regs->maccfg2, tempval); - - ugeth_info("%s: Full Duplex", dev->name); - } - - ugeth->oldduplex = mii_info->duplex; + ugeth->oldduplex = phydev->duplex; } - if (mii_info->speed != ugeth->oldspeed) { - switch (mii_info->speed) { - case 1000: - ugeth->ug_info->enet_interface = ENET_1000_RGMII; - break; - case 100: - ugeth->ug_info->enet_interface = ENET_100_RGMII; + if (phydev->speed != ugeth->oldspeed) { + new_state = 1; + switch (phydev->speed) { + case SPEED_1000: + tempval = ((tempval & + ~(MACCFG2_INTERFACE_MODE_MASK)) | + MACCFG2_INTERFACE_MODE_BYTE); break; - case 10: - ugeth->ug_info->enet_interface = ENET_10_RGMII; + case SPEED_100: + case SPEED_10: + tempval = ((tempval & + ~(MACCFG2_INTERFACE_MODE_MASK)) | + MACCFG2_INTERFACE_MODE_NIBBLE); + /* if reduced mode, re-set UPSMR.R10M */ + if ((ugeth->phy_interface == PHY_INTERFACE_MODE_RMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RGMII_ID) || + (ugeth->phy_interface == PHY_INTERFACE_MODE_RTBI)) { + if (phydev->speed == SPEED_10) + upsmr |= UPSMR_R10M; + else + upsmr &= ~(UPSMR_R10M); + } break; default: - ugeth_warn - ("%s: Ack! Speed (%d) is not 10/100/1000!", - dev->name, mii_info->speed); + if (netif_msg_link(ugeth)) + ugeth_warn( + "%s: Ack! Speed (%d) is not 10/100/1000!", + dev->name, phydev->speed); break; } - adjust_enet_interface(ugeth); - - ugeth_info("%s: Speed %dBT", dev->name, - mii_info->speed); - - ugeth->oldspeed = mii_info->speed; + ugeth->oldspeed = phydev->speed; } + out_be32(&ug_regs->maccfg2, tempval); + out_be32(&uf_regs->upsmr, upsmr); + if (!ugeth->oldlink) { - ugeth_info("%s: Link is up", dev->name); + new_state = 1; ugeth->oldlink = 1; - netif_carrier_on(dev); netif_schedule(dev); } - } else { - if (ugeth->oldlink) { - ugeth_info("%s: Link is down", dev->name); + } else if (ugeth->oldlink) { + new_state = 1; ugeth->oldlink = 0; ugeth->oldspeed = 0; ugeth->oldduplex = -1; - netif_carrier_off(dev); - } } + + if (new_state && netif_msg_link(ugeth)) + phy_print_status(phydev); + + spin_unlock_irqrestore(&ugeth->lock, flags); } /* Configure the PHY for dev. @@ -1753,102 +1593,40 @@ static void adjust_link(struct net_device *dev) */ static int init_phy(struct net_device *dev) { - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct phy_info *curphy; - struct ucc_mii_mng *mii_regs; - struct ugeth_mii_info *mii_info; - int err; + struct ucc_geth_private *priv = netdev_priv(dev); + struct phy_device *phydev; + char phy_id[BUS_ID_SIZE]; - mii_regs = &ugeth->ug_regs->miimng; + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; - ugeth->oldlink = 0; - ugeth->oldspeed = 0; - ugeth->oldduplex = -1; + snprintf(phy_id, BUS_ID_SIZE, PHY_ID_FMT, priv->ug_info->mdio_bus, + priv->ug_info->phy_address); - mii_info = kmalloc(sizeof(struct ugeth_mii_info), GFP_KERNEL); + phydev = phy_connect(dev, phy_id, &adjust_link, 0, priv->phy_interface); - if (NULL == mii_info) { - ugeth_err("%s: Could not allocate mii_info", dev->name); - return -ENOMEM; + if (IS_ERR(phydev)) { + printk("%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(phydev); } - mii_info->mii_regs = mii_regs; - mii_info->speed = SPEED_1000; - mii_info->duplex = DUPLEX_FULL; - mii_info->pause = 0; - mii_info->link = 0; - - mii_info->advertising = (ADVERTISED_10baseT_Half | + phydev->supported &= (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | - ADVERTISED_100baseT_Full | - ADVERTISED_1000baseT_Full); - mii_info->autoneg = 1; - - mii_info->mii_id = ugeth->ug_info->phy_address; + ADVERTISED_100baseT_Full); - mii_info->dev = dev; + if (priv->max_speed == SPEED_1000) + phydev->supported |= ADVERTISED_1000baseT_Full; - mii_info->mdio_read = &read_phy_reg; - mii_info->mdio_write = &write_phy_reg; + phydev->advertising = phydev->supported; - spin_lock_init(&mii_info->mdio_lock); - - ugeth->mii_info = mii_info; - - spin_lock_irq(&ugeth->lock); - - /* Set this UCC to be the master of the MII managment */ - ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num); - - if (init_mii_management_configuration(1, - ugeth->ug_info-> - miiPreambleSupress, - &mii_regs->miimcfg, - &mii_regs->miimind)) { - ugeth_err("%s: The MII Bus is stuck!", dev->name); - err = -1; - goto bus_fail; - } - - spin_unlock_irq(&ugeth->lock); - - /* get info for this PHY */ - curphy = get_phy_info(ugeth->mii_info); - - if (curphy == NULL) { - ugeth_err("%s: No PHY found", dev->name); - err = -1; - goto no_phy; - } - - mii_info->phyinfo = curphy; - - /* Run the commands which initialize the PHY */ - if (curphy->init) { - err = curphy->init(ugeth->mii_info); - if (err) - goto phy_init_fail; - } + priv->phydev = phydev; return 0; - - phy_init_fail: - no_phy: - bus_fail: - kfree(mii_info); - - return err; } -#ifdef CONFIG_UGETH_TX_ON_DEMOND -static int ugeth_transmit_on_demand(struct ucc_geth_private *ugeth) -{ - struct ucc_fastransmit_on_demand(ugeth->uccf); - return 0; -} -#endif static int ugeth_graceful_stop_tx(struct ucc_geth_private *ugeth) { @@ -2356,6 +2134,8 @@ static void ucc_geth_memclean(struct ucc_geth_private *ugeth) } for (i = 0; i < ugeth->ug_info->numQueuesTx; i++) { bd = ugeth->p_tx_bd_ring[i]; + if (!bd) + continue; for (j = 0; j < ugeth->ug_info->bdRingLenTx[i]; j++) { if (ugeth->tx_skbuff[i][j]) { dma_unmap_single(NULL, @@ -2487,6 +2267,7 @@ static void ucc_geth_set_multi(struct net_device *dev) static void ucc_geth_stop(struct ucc_geth_private *ugeth) { struct ucc_geth *ug_regs = ugeth->ug_regs; + struct phy_device *phydev = ugeth->phydev; u32 tempval; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -2495,8 +2276,7 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth) ugeth_disable(ugeth, COMM_DIR_RX_AND_TX); /* Tell the kernel the link is down */ - ugeth->mii_info->link = 0; - adjust_link(ugeth->dev); + phy_stop(phydev); /* Mask all interrupts */ out_be32(ugeth->uccf->p_ucce, 0x00000000); @@ -2509,50 +2289,24 @@ static void ucc_geth_stop(struct ucc_geth_private *ugeth) tempval &= ~(MACCFG1_ENABLE_RX | MACCFG1_ENABLE_TX); out_be32(&ug_regs->maccfg1, tempval); - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - /* Clear any pending interrupts */ - mii_clear_phy_interrupt(ugeth->mii_info); - - /* Disable PHY Interrupts */ - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_DISABLED); - } - free_irq(ugeth->ug_info->uf_info.irq, ugeth->dev); - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - free_irq(ugeth->ug_info->phy_interrupt, ugeth->dev); - } else { - del_timer_sync(&ugeth->phy_info_timer); - } - ucc_geth_memclean(ugeth); } -static int ucc_geth_startup(struct ucc_geth_private *ugeth) +static int ucc_struct_init(struct ucc_geth_private *ugeth) { - struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt; - struct ucc_geth_init_pram *p_init_enet_pram; - struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; struct ucc_fast_info *uf_info; - struct ucc_fast *uf_regs; - struct ucc_geth *ug_regs; - int ret_val = -EINVAL; - u32 remoder = UCC_GETH_REMODER_INIT; - u32 init_enet_pram_offset, cecr_subblock, command, maccfg1; - u32 ifstat, i, j, size, l2qt, l3qt, length; - u16 temoder = UCC_GETH_TEMODER_INIT; - u16 test; - u8 function_code = 0; - u8 *bd, *endOfRing; - u8 numThreadsRxNumerical, numThreadsTxNumerical; - - ugeth_vdbg("%s: IN", __FUNCTION__); + int i; ug_info = ugeth->ug_info; uf_info = &ug_info->uf_info; + /* Create CQs for hash tables */ + INIT_LIST_HEAD(&ugeth->group_hash_q); + INIT_LIST_HEAD(&ugeth->ind_hash_q); + if (!((uf_info->bd_mem_part == MEM_PART_SYSTEM) || (uf_info->bd_mem_part == MEM_PART_MURAM))) { ugeth_err("%s: Bad memory partition value.", __FUNCTION__); @@ -2647,12 +2401,42 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) for (i = 0; i < ug_info->numQueuesTx; i++) uf_info->uccm_mask |= (UCCE_TXBF_SINGLE_MASK << i); /* Initialize the general fast UCC block. */ - if (ucc_fast_init(uf_info, &uccf)) { + if (ucc_fast_init(uf_info, &ugeth->uccf)) { ugeth_err("%s: Failed to init uccf.", __FUNCTION__); ucc_geth_memclean(ugeth); return -ENOMEM; } - ugeth->uccf = uccf; + + ugeth->ug_regs = (struct ucc_geth *) ioremap(uf_info->regs, sizeof(struct ucc_geth)); + + return 0; +} + +static int ucc_geth_startup(struct ucc_geth_private *ugeth) +{ + struct ucc_geth_82xx_address_filtering_pram *p_82xx_addr_filt; + struct ucc_geth_init_pram *p_init_enet_pram; + struct ucc_fast_private *uccf; + struct ucc_geth_info *ug_info; + struct ucc_fast_info *uf_info; + struct ucc_fast *uf_regs; + struct ucc_geth *ug_regs; + int ret_val = -EINVAL; + u32 remoder = UCC_GETH_REMODER_INIT; + u32 init_enet_pram_offset, cecr_subblock, command, maccfg1; + u32 ifstat, i, j, size, l2qt, l3qt, length; + u16 temoder = UCC_GETH_TEMODER_INIT; + u16 test; + u8 function_code = 0; + u8 *bd, *endOfRing; + u8 numThreadsRxNumerical, numThreadsTxNumerical; + + ugeth_vdbg("%s: IN", __FUNCTION__); + uccf = ugeth->uccf; + ug_info = ugeth->ug_info; + uf_info = &ug_info->uf_info; + uf_regs = uccf->uf_regs; + ug_regs = ugeth->ug_regs; switch (ug_info->numThreadsRx) { case UCC_GETH_NUM_OF_THREADS_1: @@ -2711,10 +2495,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) || (ug_info->vlanOperationNonTagged != UCC_GETH_VLAN_OPERATION_NON_TAGGED_NOP); - uf_regs = uccf->uf_regs; - ug_regs = (struct ucc_geth *) (uccf->uf_regs); - ugeth->ug_regs = ug_regs; - init_default_reg_vals(&uf_regs->upsmr, &ug_regs->maccfg1, &ug_regs->maccfg2); @@ -3177,8 +2957,8 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) /* Size varies with number of Rx queues */ ugeth->rx_irq_coalescing_tbl_offset = qe_muram_alloc(ug_info->numQueuesRx * - sizeof(struct ucc_geth_rx_interrupt_coalescing_entry), - UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); + sizeof(struct ucc_geth_rx_interrupt_coalescing_entry) + + 4, UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT); if (IS_MURAM_ERR(ugeth->rx_irq_coalescing_tbl_offset)) { ugeth_err ("%s: Can not allocate DPRAM memory for" @@ -3359,13 +3139,6 @@ static int ucc_geth_startup(struct ucc_geth_private *ugeth) for (j = 0; j < NUM_OF_PADDRS; j++) ugeth_82xx_filtering_clear_addr_in_paddr(ugeth, (u8) j); - /* Create CQs for hash tables */ - if (ug_info->maxGroupAddrInHash > 0) { - INIT_LIST_HEAD(&ugeth->group_hash_q); - } - if (ug_info->maxIndAddrInHash > 0) { - INIT_LIST_HEAD(&ugeth->ind_hash_q); - } p_82xx_addr_filt = (struct ucc_geth_82xx_address_filtering_pram *) ugeth-> p_rx_glbl_pram->addressfiltering; @@ -3562,6 +3335,9 @@ static void ucc_geth_timeout(struct net_device *dev) static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct ucc_geth_private *ugeth = netdev_priv(dev); +#ifdef CONFIG_UGETH_TX_ON_DEMAND + struct ucc_fast_private *uccf; +#endif u8 *bd; /* BD pointer */ u32 bd_status; u8 txQ = 0; @@ -3598,9 +3374,9 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Move to next BD in the ring */ if (!(bd_status & T_W)) - ugeth->txBd[txQ] = bd + sizeof(struct qe_bd); + bd += sizeof(struct qe_bd); else - ugeth->txBd[txQ] = ugeth->p_tx_bd_ring[txQ]; + bd = ugeth->p_tx_bd_ring[txQ]; /* If the next BD still needs to be cleaned up, then the bds are full. We need to tell the kernel to stop sending us stuff. */ @@ -3609,6 +3385,8 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) netif_stop_queue(dev); } + ugeth->txBd[txQ] = bd; + if (ugeth->p_scheduler) { ugeth->cpucount[txQ]++; /* Indicate to QE that there are more Tx bds ready for @@ -3618,6 +3396,10 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) out_be16(ugeth->p_cpucount[txQ], ugeth->cpucount[txQ]); } +#ifdef CONFIG_UGETH_TX_ON_DEMAND + uccf = ugeth->uccf; + out_be16(uccf->p_utodr, UCC_FAST_TOD); +#endif spin_unlock_irq(&ugeth->lock); return 0; @@ -3633,7 +3415,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit ugeth_vdbg("%s: IN", __FUNCTION__); - spin_lock(&ugeth->lock); /* collect received buffers */ bd = ugeth->rxBd[rxQ]; @@ -3681,7 +3462,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit skb = get_new_skb(ugeth, bd); if (!skb) { ugeth_warn("%s: No Rx Data Buffer", __FUNCTION__); - spin_unlock(&ugeth->lock); ugeth->stats.rx_dropped++; break; } @@ -3702,7 +3482,6 @@ static int ucc_geth_rx(struct ucc_geth_private *ugeth, u8 rxQ, int rx_work_limit } ugeth->rxBd[rxQ] = bd; - spin_unlock(&ugeth->lock); return howmany; } @@ -3722,7 +3501,7 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) /* Handle the transmitted buffer and release */ /* the BD to be used with the current frame */ - if ((bd = ugeth->txBd[txQ]) && (netif_queue_stopped(dev) == 0)) + if ((bd == ugeth->txBd[txQ]) && (netif_queue_stopped(dev) == 0)) break; ugeth->stats.tx_packets++; @@ -3741,10 +3520,12 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) /* Advance the confirmation BD pointer */ if (!(bd_status & T_W)) - ugeth->confBd[txQ] += sizeof(struct qe_bd); + bd += sizeof(struct qe_bd); else - ugeth->confBd[txQ] = ugeth->p_tx_bd_ring[txQ]; + bd = ugeth->p_tx_bd_ring[txQ]; + bd_status = in_be32((u32 *)bd); } + ugeth->confBd[txQ] = bd; return 0; } @@ -3752,23 +3533,38 @@ static int ucc_geth_tx(struct net_device *dev, u8 txQ) static int ucc_geth_poll(struct net_device *dev, int *budget) { struct ucc_geth_private *ugeth = netdev_priv(dev); + struct ucc_geth_info *ug_info; + struct ucc_fast_private *uccf; int howmany; - int rx_work_limit = *budget; - u8 rxQ = 0; + u8 i; + int rx_work_limit; + register u32 uccm; + ug_info = ugeth->ug_info; + + rx_work_limit = *budget; if (rx_work_limit > dev->quota) rx_work_limit = dev->quota; - howmany = ucc_geth_rx(ugeth, rxQ, rx_work_limit); + howmany = 0; + + for (i = 0; i < ug_info->numQueuesRx; i++) { + howmany += ucc_geth_rx(ugeth, i, rx_work_limit); + } dev->quota -= howmany; rx_work_limit -= howmany; *budget -= howmany; - if (rx_work_limit >= 0) + if (rx_work_limit > 0) { netif_rx_complete(dev); + uccf = ugeth->uccf; + uccm = in_be32(uccf->p_uccm); + uccm |= UCCE_RX_EVENTS; + out_be32(uccf->p_uccm, uccm); + } - return (rx_work_limit < 0) ? 1 : 0; + return (rx_work_limit > 0) ? 0 : 1; } #endif /* CONFIG_UGETH_NAPI */ @@ -3778,10 +3574,13 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info) struct ucc_geth_private *ugeth = netdev_priv(dev); struct ucc_fast_private *uccf; struct ucc_geth_info *ug_info; - register u32 ucce = 0; - register u32 bit_mask = UCCE_RXBF_SINGLE_MASK; - register u32 tx_mask = UCCE_TXBF_SINGLE_MASK; - register u8 i; + register u32 ucce; + register u32 uccm; +#ifndef CONFIG_UGETH_NAPI + register u32 rx_mask; +#endif + register u32 tx_mask; + u8 i; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -3791,174 +3590,57 @@ static irqreturn_t ucc_geth_irq_handler(int irq, void *info) uccf = ugeth->uccf; ug_info = ugeth->ug_info; - do { - ucce |= (u32) (in_be32(uccf->p_ucce) & in_be32(uccf->p_uccm)); - - /* clear event bits for next time */ - /* Side effect here is to mask ucce variable - for future processing below. */ - out_be32(uccf->p_ucce, ucce); /* Clear with ones, - but only bits in UCCM */ - - /* We ignore Tx interrupts because Tx confirmation is - done inside Tx routine */ + /* read and clear events */ + ucce = (u32) in_be32(uccf->p_ucce); + uccm = (u32) in_be32(uccf->p_uccm); + ucce &= uccm; + out_be32(uccf->p_ucce, ucce); + /* check for receive events that require processing */ + if (ucce & UCCE_RX_EVENTS) { +#ifdef CONFIG_UGETH_NAPI + if (netif_rx_schedule_prep(dev)) { + uccm &= ~UCCE_RX_EVENTS; + out_be32(uccf->p_uccm, uccm); + __netif_rx_schedule(dev); + } +#else + rx_mask = UCCE_RXBF_SINGLE_MASK; for (i = 0; i < ug_info->numQueuesRx; i++) { - if (ucce & bit_mask) - ucc_geth_rx(ugeth, i, - (int)ugeth->ug_info-> - bdRingLenRx[i]); - ucce &= ~bit_mask; - bit_mask <<= 1; + if (ucce & rx_mask) + ucc_geth_rx(ugeth, i, (int)ugeth->ug_info->bdRingLenRx[i]); + ucce &= ~rx_mask; + rx_mask <<= 1; } +#endif /* CONFIG_UGETH_NAPI */ + } + /* Tx event processing */ + if (ucce & UCCE_TX_EVENTS) { + spin_lock(&ugeth->lock); + tx_mask = UCCE_TXBF_SINGLE_MASK; for (i = 0; i < ug_info->numQueuesTx; i++) { if (ucce & tx_mask) ucc_geth_tx(dev, i); ucce &= ~tx_mask; tx_mask <<= 1; } + spin_unlock(&ugeth->lock); + } - /* Exceptions */ + /* Errors and other events */ + if (ucce & UCCE_OTHER) { if (ucce & UCCE_BSY) { - ugeth_vdbg("Got BUSY irq!!!!"); ugeth->stats.rx_errors++; - ucce &= ~UCCE_BSY; } - if (ucce & UCCE_OTHER) { - ugeth_vdbg("Got frame with error (ucce - 0x%08x)!!!!", - ucce); - ugeth->stats.rx_errors++; - ucce &= ~ucce; + if (ucce & UCCE_TXE) { + ugeth->stats.tx_errors++; } } - while (ucce); - - return IRQ_HANDLED; -} - -static irqreturn_t phy_interrupt(int irq, void *dev_id) -{ - struct net_device *dev = (struct net_device *)dev_id; - struct ucc_geth_private *ugeth = netdev_priv(dev); - - ugeth_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupt */ - mii_clear_phy_interrupt(ugeth->mii_info); - - /* Disable PHY interrupts */ - mii_configure_phy_interrupt(ugeth->mii_info, MII_INTERRUPT_DISABLED); - - /* Schedule the phy change */ - schedule_work(&ugeth->tq); return IRQ_HANDLED; } -/* Scheduled by the phy_interrupt/timer to handle PHY changes */ -static void ugeth_phy_change(struct work_struct *work) -{ - struct ucc_geth_private *ugeth = - container_of(work, struct ucc_geth_private, tq); - struct net_device *dev = ugeth->dev; - struct ucc_geth *ug_regs; - int result = 0; - - ugeth_vdbg("%s: IN", __FUNCTION__); - - ug_regs = ugeth->ug_regs; - - /* Delay to give the PHY a chance to change the - * register state */ - msleep(1); - - /* Update the link, speed, duplex */ - result = ugeth->mii_info->phyinfo->read_status(ugeth->mii_info); - - /* Adjust the known status as long as the link - * isn't still coming up */ - if ((0 == result) || (ugeth->mii_info->link == 0)) - adjust_link(dev); - - /* Reenable interrupts, if needed */ - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_ENABLED); -} - -/* Called every so often on systems that don't interrupt - * the core for PHY changes */ -static void ugeth_phy_timer(unsigned long data) -{ - struct net_device *dev = (struct net_device *)data; - struct ucc_geth_private *ugeth = netdev_priv(dev); - - schedule_work(&ugeth->tq); - - mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ); -} - -/* Keep trying aneg for some time - * If, after GFAR_AN_TIMEOUT seconds, it has not - * finished, we switch to forced. - * Either way, once the process has completed, we either - * request the interrupt, or switch the timer over to - * using ugeth_phy_timer to check status */ -static void ugeth_phy_startup_timer(unsigned long data) -{ - struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data; - struct ucc_geth_private *ugeth = netdev_priv(mii_info->dev); - static int secondary = UGETH_AN_TIMEOUT; - int result; - - /* Configure the Auto-negotiation */ - result = mii_info->phyinfo->config_aneg(mii_info); - - /* If autonegotiation failed to start, and - * we haven't timed out, reset the timer, and return */ - if (result && secondary--) { - mod_timer(&ugeth->phy_info_timer, jiffies + HZ); - return; - } else if (result) { - /* Couldn't start autonegotiation. - * Try switching to forced */ - mii_info->autoneg = 0; - result = mii_info->phyinfo->config_aneg(mii_info); - - /* Forcing failed! Give up */ - if (result) { - ugeth_err("%s: Forcing failed!", mii_info->dev->name); - return; - } - } - - /* Kill the timer so it can be restarted */ - del_timer_sync(&ugeth->phy_info_timer); - - /* Grab the PHY interrupt, if necessary/possible */ - if (ugeth->ug_info->board_flags & FSL_UGETH_BRD_HAS_PHY_INTR) { - if (request_irq(ugeth->ug_info->phy_interrupt, - phy_interrupt, IRQF_SHARED, - "phy_interrupt", mii_info->dev) < 0) { - ugeth_err("%s: Can't get IRQ %d (PHY)", - mii_info->dev->name, - ugeth->ug_info->phy_interrupt); - } else { - mii_configure_phy_interrupt(ugeth->mii_info, - MII_INTERRUPT_ENABLED); - return; - } - } - - /* Start the timer again, this time in order to - * handle a change in status */ - init_timer(&ugeth->phy_info_timer); - ugeth->phy_info_timer.function = &ugeth_phy_timer; - ugeth->phy_info_timer.data = (unsigned long)mii_info->dev; - mod_timer(&ugeth->phy_info_timer, jiffies + PHY_CHANGE_TIME * HZ); -} - /* Called when something needs to use the ethernet device */ /* Returns 0 for success. */ static int ucc_geth_open(struct net_device *dev) @@ -3975,6 +3657,12 @@ static int ucc_geth_open(struct net_device *dev) return -EINVAL; } + err = ucc_struct_init(ugeth); + if (err) { + ugeth_err("%s: Cannot configure internal struct, aborting.", dev->name); + return err; + } + err = ucc_geth_startup(ugeth); if (err) { ugeth_err("%s: Cannot configure net device, aborting.", @@ -4002,10 +3690,12 @@ static int ucc_geth_open(struct net_device *dev) err = init_phy(dev); if (err) { - ugeth_err("%s: Cannot initialzie PHY, aborting.", dev->name); + ugeth_err("%s: Cannot initialize PHY, aborting.", dev->name); return err; } -#ifndef CONFIG_UGETH_NAPI + + phy_start(ugeth->phydev); + err = request_irq(ugeth->ug_info->uf_info.irq, ucc_geth_irq_handler, 0, "UCC Geth", dev); @@ -4015,15 +3705,6 @@ static int ucc_geth_open(struct net_device *dev) ucc_geth_stop(ugeth); return err; } -#endif /* CONFIG_UGETH_NAPI */ - - /* Set up the PHY change work queue */ - INIT_WORK(&ugeth->tq, ugeth_phy_change); - - init_timer(&ugeth->phy_info_timer); - ugeth->phy_info_timer.function = &ugeth_phy_startup_timer; - ugeth->phy_info_timer.data = (unsigned long)ugeth->mii_info; - mod_timer(&ugeth->phy_info_timer, jiffies + HZ); err = ugeth_enable(ugeth, COMM_DIR_RX_AND_TX); if (err) { @@ -4046,11 +3727,8 @@ static int ucc_geth_close(struct net_device *dev) ucc_geth_stop(ugeth); - /* Shutdown the PHY */ - if (ugeth->mii_info->phyinfo->close) - ugeth->mii_info->phyinfo->close(ugeth->mii_info); - - kfree(ugeth->mii_info); + phy_disconnect(ugeth->phydev); + ugeth->phydev = NULL; netif_stop_queue(dev); @@ -4059,20 +3737,53 @@ static int ucc_geth_close(struct net_device *dev) const struct ethtool_ops ucc_geth_ethtool_ops = { }; +static phy_interface_t to_phy_interface(const char *interface_type) +{ + if (strcasecmp(interface_type, "mii") == 0) + return PHY_INTERFACE_MODE_MII; + if (strcasecmp(interface_type, "gmii") == 0) + return PHY_INTERFACE_MODE_GMII; + if (strcasecmp(interface_type, "tbi") == 0) + return PHY_INTERFACE_MODE_TBI; + if (strcasecmp(interface_type, "rmii") == 0) + return PHY_INTERFACE_MODE_RMII; + if (strcasecmp(interface_type, "rgmii") == 0) + return PHY_INTERFACE_MODE_RGMII; + if (strcasecmp(interface_type, "rgmii-id") == 0) + return PHY_INTERFACE_MODE_RGMII_ID; + if (strcasecmp(interface_type, "rtbi") == 0) + return PHY_INTERFACE_MODE_RTBI; + + return PHY_INTERFACE_MODE_MII; +} + static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *match) { struct device *device = &ofdev->dev; struct device_node *np = ofdev->node; + struct device_node *mdio; struct net_device *dev = NULL; struct ucc_geth_private *ugeth = NULL; struct ucc_geth_info *ug_info; struct resource res; struct device_node *phy; - int err, ucc_num, phy_interface; - static int mii_mng_configured = 0; + int err, ucc_num, max_speed = 0; const phandle *ph; const unsigned int *prop; const void *mac_addr; + phy_interface_t phy_interface; + static const int enet_to_speed[] = { + SPEED_10, SPEED_10, SPEED_10, + SPEED_100, SPEED_100, SPEED_100, + SPEED_1000, SPEED_1000, SPEED_1000, SPEED_1000, + }; + static const phy_interface_t enet_to_phy_interface[] = { + PHY_INTERFACE_MODE_MII, PHY_INTERFACE_MODE_RMII, + PHY_INTERFACE_MODE_RGMII, PHY_INTERFACE_MODE_MII, + PHY_INTERFACE_MODE_RMII, PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_GMII, PHY_INTERFACE_MODE_RGMII, + PHY_INTERFACE_MODE_TBI, PHY_INTERFACE_MODE_RTBI, + }; ugeth_vdbg("%s: IN", __FUNCTION__); @@ -4083,6 +3794,7 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma ug_info = &ugeth_info[ucc_num]; ug_info->uf_info.ucc_num = ucc_num; + prop = get_property(np, "rx-clock", NULL); ug_info->uf_info.rx_clock = *prop; prop = get_property(np, "tx-clock", NULL); @@ -4100,13 +3812,72 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma if (phy == NULL) return -ENODEV; + /* set the PHY address */ prop = get_property(phy, "reg", NULL); + if (prop == NULL) + return -1; ug_info->phy_address = *prop; - prop = get_property(phy, "interface", NULL); - ug_info->enet_interface = *prop; - ug_info->phy_interrupt = irq_of_parse_and_map(phy, 0); - ug_info->board_flags = (ug_info->phy_interrupt == NO_IRQ)? - 0:FSL_UGETH_BRD_HAS_PHY_INTR; + + /* get the phy interface type, or default to MII */ + prop = get_property(np, "interface-type", NULL); + if (!prop) { + /* handle interface property present in old trees */ + prop = get_property(phy, "interface", NULL); + if (prop != NULL) + phy_interface = enet_to_phy_interface[*prop]; + else + phy_interface = PHY_INTERFACE_MODE_MII; + } else { + phy_interface = to_phy_interface((const char *)prop); + } + + /* get speed, or derive from interface */ + prop = get_property(np, "max-speed", NULL); + if (!prop) { + /* handle interface property present in old trees */ + prop = get_property(phy, "interface", NULL); + if (prop != NULL) + max_speed = enet_to_speed[*prop]; + } else { + max_speed = *prop; + } + if (!max_speed) { + switch (phy_interface) { + case PHY_INTERFACE_MODE_GMII: + case PHY_INTERFACE_MODE_RGMII: + case PHY_INTERFACE_MODE_RGMII_ID: + case PHY_INTERFACE_MODE_TBI: + case PHY_INTERFACE_MODE_RTBI: + max_speed = SPEED_1000; + break; + default: + max_speed = SPEED_100; + break; + } + } + + if (max_speed == SPEED_1000) { + ug_info->uf_info.urfs = UCC_GETH_URFS_GIGA_INIT; + ug_info->uf_info.urfet = UCC_GETH_URFET_GIGA_INIT; + ug_info->uf_info.urfset = UCC_GETH_URFSET_GIGA_INIT; + ug_info->uf_info.utfs = UCC_GETH_UTFS_GIGA_INIT; + ug_info->uf_info.utfet = UCC_GETH_UTFET_GIGA_INIT; + ug_info->uf_info.utftt = UCC_GETH_UTFTT_GIGA_INIT; + } + + /* Set the bus id */ + mdio = of_get_parent(phy); + + if (mdio == NULL) + return -1; + + err = of_address_to_resource(mdio, 0, &res); + of_node_put(mdio); + + if (err) + return -1; + + ug_info->mdio_bus = res.start; printk(KERN_INFO "ucc_geth: UCC%1d at 0x%8x (irq = %d) \n", ug_info->uf_info.ucc_num + 1, ug_info->uf_info.regs, @@ -4118,43 +3889,6 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma return -ENODEV; } - /* FIXME: Work around for early chip rev. */ - /* There's a bug in initial chip rev(s) in the RGMII ac */ - /* timing. */ - /* The following compensates by writing to the reserved */ - /* QE Port Output Hold Registers (CPOH1?). */ - prop = get_property(phy, "interface", NULL); - phy_interface = *prop; - if ((phy_interface == ENET_1000_RGMII) || - (phy_interface == ENET_100_RGMII) || - (phy_interface == ENET_10_RGMII)) { - struct device_node *soc; - phys_addr_t immrbase = -1; - u32 *tmp_reg; - u32 tmp_val; - - soc = of_find_node_by_type(NULL, "soc"); - if (soc) { - unsigned int size; - const void *prop = get_property(soc, "reg", &size); - immrbase = of_translate_address(soc, prop); - of_node_put(soc); - }; - - tmp_reg = (u32 *) ioremap(immrbase + 0x14A8, 0x4); - tmp_val = in_be32(tmp_reg); - if (ucc_num == 1) - out_be32(tmp_reg, tmp_val | 0x00003000); - else if (ucc_num == 2) - out_be32(tmp_reg, tmp_val | 0x0c000000); - iounmap(tmp_reg); - } - - if (!mii_mng_configured) { - ucc_set_qe_mux_mii_mng(ucc_num); - mii_mng_configured = 1; - } - /* Create an ethernet device instance */ dev = alloc_etherdev(sizeof(*ugeth)); @@ -4188,6 +3922,10 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma dev->set_multicast_list = ucc_geth_set_multi; dev->ethtool_ops = &ucc_geth_ethtool_ops; + ugeth->msg_enable = (NETIF_MSG_IFUP << 1 ) - 1; + ugeth->phy_interface = phy_interface; + ugeth->max_speed = max_speed; + err = register_netdev(dev); if (err) { ugeth_err("%s: Cannot register net device, aborting.", @@ -4196,15 +3934,13 @@ static int ucc_geth_probe(struct of_device* ofdev, const struct of_device_id *ma return err; } - ugeth->ug_info = ug_info; - ugeth->dev = dev; - - mac_addr = get_property(np, "mac-address", NULL); - if (mac_addr == NULL) - mac_addr = get_property(np, "local-mac-address", NULL); + mac_addr = of_get_mac_address(np); if (mac_addr) memcpy(dev->dev_addr, mac_addr, 6); + ugeth->ug_info = ug_info; + ugeth->dev = dev; + return 0; } @@ -4240,19 +3976,30 @@ static struct of_platform_driver ucc_geth_driver = { static int __init ucc_geth_init(void) { - int i; + int i, ret; + + ret = uec_mdio_init(); + + if (ret) + return ret; printk(KERN_INFO "ucc_geth: " DRV_DESC "\n"); for (i = 0; i < 8; i++) memcpy(&(ugeth_info[i]), &ugeth_primary_info, sizeof(ugeth_primary_info)); - return of_register_platform_driver(&ucc_geth_driver); + ret = of_register_platform_driver(&ucc_geth_driver); + + if (ret) + uec_mdio_exit(); + + return ret; } static void __exit ucc_geth_exit(void) { of_unregister_platform_driver(&ucc_geth_driver); + uec_mdio_exit(); } module_init(ucc_geth_init); @@ -4260,4 +4007,5 @@ module_exit(ucc_geth_exit); MODULE_AUTHOR("Freescale Semiconductor, Inc"); MODULE_DESCRIPTION(DRV_DESC); +MODULE_VERSION(DRV_VERSION); MODULE_LICENSE("GPL"); diff --git a/drivers/net/ucc_geth.h b/drivers/net/ucc_geth.h index a665612..a29e1c3 100644 --- a/drivers/net/ucc_geth.h +++ b/drivers/net/ucc_geth.h @@ -28,6 +28,8 @@ #include <asm/ucc.h> #include <asm/ucc_fast.h> +#include "ucc_geth_mii.h" + #define NUM_TX_QUEUES 8 #define NUM_RX_QUEUES 8 #define NUM_BDS_IN_PREFETCHED_BDS 4 @@ -36,15 +38,6 @@ #define ENET_INIT_PARAM_MAX_ENTRIES_RX 9 #define ENET_INIT_PARAM_MAX_ENTRIES_TX 8 -struct ucc_mii_mng { - u32 miimcfg; /* MII management configuration reg */ - u32 miimcom; /* MII management command reg */ - u32 miimadd; /* MII management address reg */ - u32 miimcon; /* MII management control reg */ - u32 miimstat; /* MII management status reg */ - u32 miimind; /* MII management indication reg */ -} __attribute__ ((packed)); - struct ucc_geth { struct ucc_fast uccf; @@ -53,7 +46,7 @@ struct ucc_geth { u32 ipgifg; /* interframe gap reg. */ u32 hafdup; /* half-duplex reg. */ u8 res1[0x10]; - struct ucc_mii_mng miimng; /* MII management structure */ + u8 miimng[0x18]; /* MII management structure moved to _mii.h */ u32 ifctl; /* interface control reg */ u32 ifstat; /* interface statux reg */ u32 macstnaddr1; /* mac station address part 1 reg */ @@ -212,6 +205,9 @@ struct ucc_geth { #define UCCE_OTHER (UCCE_SCAR | UCCE_GRA | UCCE_CBPR | UCCE_BSY |\ UCCE_RXC | UCCE_TXC | UCCE_TXE) +#define UCCE_RX_EVENTS (UCCE_RXF | UCCE_BSY) +#define UCCE_TX_EVENTS (UCCE_TXB | UCCE_TXE) + /* UCC GETH UPSMR (Protocol Specific Mode Register) */ #define UPSMR_ECM 0x04000000 /* Enable CAM Miss or @@ -381,66 +377,6 @@ struct ucc_geth { #define UCCS_MPD 0x01 /* Magic Packet Detected */ -/* UCC GETH MIIMCFG (MII Management Configuration Register) */ -#define MIIMCFG_RESET_MANAGEMENT 0x80000000 /* Reset - management */ -#define MIIMCFG_NO_PREAMBLE 0x00000010 /* Preamble - suppress */ -#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) /* clock divide - << shift */ -#define MIIMCFG_CLOCK_DIVIDE_MAX 0xf /* clock divide max val - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2 0x00000000 /* divide by 2 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 /* divide by 4 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 /* divide by 6 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 /* divide by 8 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 /* divide by 10 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 /* divide by 14 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16 0x00000008 /* divide by 16 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 /* divide by 20 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 /* divide by 28 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32 0x00000009 /* divide by 32 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48 0x0000000a /* divide by 48 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64 0x0000000b /* divide by 64 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80 0x0000000c /* divide by 80 - */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112 0x0000000d /* divide by - 112 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160 0x0000000e /* divide by - 160 */ -#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224 0x0000000f /* divide by - 224 */ - -/* UCC GETH MIIMCOM (MII Management Command Register) */ -#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ -#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ - -/* UCC GETH MIIMADD (MII Management Address Register) */ -#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) /* PHY Address - << shift */ -#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) /* PHY Register - << shift */ - -/* UCC GETH MIIMCON (MII Management Control Register) */ -#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) /* PHY Control - << shift */ -#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) /* PHY Status - << shift */ - -/* UCC GETH MIIMIND (MII Management Indicator Register) */ -#define MIIMIND_NOT_VALID 0x00000004 /* Not valid */ -#define MIIMIND_SCAN 0x00000002 /* Scan in - progress */ -#define MIIMIND_BUSY 0x00000001 - /* UCC GETH IFSTAT (Interface Status Register) */ #define IFSTAT_EXCESS_DEFER 0x00000200 /* Excessive transmission @@ -931,8 +867,7 @@ struct ucc_geth_hardware_statistics { #define UCC_GETH_SCHEDULER_ALIGNMENT 4 /* This is a guess */ #define UCC_GETH_TX_STATISTICS_ALIGNMENT 4 /* This is a guess */ #define UCC_GETH_RX_STATISTICS_ALIGNMENT 4 /* This is a guess */ -#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT 4 /* This is a - guess */ +#define UCC_GETH_RX_INTERRUPT_COALESCING_ALIGNMENT 64 #define UCC_GETH_RX_BD_QUEUES_ALIGNMENT 8 /* This is a guess */ #define UCC_GETH_RX_PREFETCHED_BDS_ALIGNMENT 128 /* This is a guess */ #define UCC_GETH_RX_EXTENDED_FILTERING_GLOBAL_PARAMETERS_ALIGNMENT 4 /* This @@ -1009,15 +944,6 @@ struct ucc_geth_hardware_statistics { register */ #define UCC_GETH_MACCFG1_INIT 0 #define UCC_GETH_MACCFG2_INIT (MACCFG2_RESERVED_1) -#define UCC_GETH_MIIMCFG_MNGMNT_CLC_DIV_INIT \ - (MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112) - -/* Ethernet speed */ -enum enet_speed { - ENET_SPEED_10BT, /* 10 Base T */ - ENET_SPEED_100BT, /* 100 Base T */ - ENET_SPEED_1000BT /* 1000 Base T */ -}; /* Ethernet Address Type. */ enum enet_addr_type { @@ -1026,22 +952,6 @@ enum enet_addr_type { ENET_ADDR_TYPE_BROADCAST }; -/* TBI / MII Set Register */ -enum enet_tbi_mii_reg { - ENET_TBI_MII_CR = 0x00, /* Control (CR ) */ - ENET_TBI_MII_SR = 0x01, /* Status (SR ) */ - ENET_TBI_MII_ANA = 0x04, /* AN advertisement (ANA ) */ - ENET_TBI_MII_ANLPBPA = 0x05, /* AN link partner base page ability - (ANLPBPA) */ - ENET_TBI_MII_ANEX = 0x06, /* AN expansion (ANEX ) */ - ENET_TBI_MII_ANNPT = 0x07, /* AN next page transmit (ANNPT ) */ - ENET_TBI_MII_ANLPANP = 0x08, /* AN link partner ability next page - (ANLPANP) */ - ENET_TBI_MII_EXST = 0x0F, /* Extended status (EXST ) */ - ENET_TBI_MII_JD = 0x10, /* Jitter diagnostics (JD ) */ - ENET_TBI_MII_TBICON = 0x11 /* TBI control (TBICON ) */ -}; - /* UCC GETH 82xx Ethernet Address Recognition Location */ enum ucc_geth_enet_address_recognition_location { UCC_GETH_ENET_ADDRESS_RECOGNITION_LOCATION_STATION_ADDRESS,/* station @@ -1239,8 +1149,7 @@ struct ucc_geth_info { u16 pausePeriod; u16 extensionField; u8 phy_address; - u32 board_flags; - u32 phy_interrupt; + u32 mdio_bus; u8 weightfactor[NUM_TX_QUEUES]; u8 interruptcoalescingmaxvalue[NUM_RX_QUEUES]; u8 l2qt[UCC_GETH_VLAN_PRIORITY_MAX]; @@ -1249,7 +1158,6 @@ struct ucc_geth_info { u8 iphoffset[TX_IP_OFFSET_ENTRY_MAX]; u16 bdRingLenTx[NUM_TX_QUEUES]; u16 bdRingLenRx[NUM_RX_QUEUES]; - enum enet_interface enet_interface; enum ucc_geth_num_of_station_addresses numStationAddresses; enum qe_fltr_largest_external_tbl_lookup_key_size largestexternallookupkeysize; @@ -1326,9 +1234,11 @@ struct ucc_geth_private { /* index of the first skb which hasn't been transmitted yet. */ u16 skb_dirtytx[NUM_TX_QUEUES]; - struct work_struct tq; - struct timer_list phy_info_timer; struct ugeth_mii_info *mii_info; + struct phy_device *phydev; + phy_interface_t phy_interface; + int max_speed; + uint32_t msg_enable; int oldspeed; int oldduplex; int oldlink; diff --git a/drivers/net/ucc_geth_mii.c b/drivers/net/ucc_geth_mii.c new file mode 100644 index 0000000..73b5a53 --- /dev/null +++ b/drivers/net/ucc_geth_mii.c @@ -0,0 +1,279 @@ +/* + * drivers/net/ucc_geth_mii.c + * + * Gianfar Ethernet Driver -- MIIM bus implementation + * Provides Bus interface for MIIM regs + * + * Author: Li Yang + * + * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/unistd.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <asm/ocp.h> +#include <linux/crc32.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/fsl_devices.h> + +#include <asm/of_platform.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/uaccess.h> +#include <asm/ucc.h> + +#include "ucc_geth_mii.h" +#include "ucc_geth.h" + +#define DEBUG +#ifdef DEBUG +#define vdbg(format, arg...) printk(KERN_DEBUG , format "\n" , ## arg) +#else +#define vdbg(format, arg...) do {} while(0) +#endif + +#define DRV_DESC "QE UCC Ethernet Controller MII Bus" +#define DRV_NAME "fsl-uec_mdio" + +/* Write value to the PHY for this device to the register at regnum, */ +/* waiting until the write is done before it returns. All PHY */ +/* configuration has to be done through the master UEC MIIM regs */ +int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + + /* Setting up the MII Mangement Address Register */ + out_be32(®s->miimadd, + (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); + + /* Setting up the MII Mangement Control Register with the value */ + out_be32(®s->miimcon, value); + + /* Wait till MII management write is complete */ + while ((in_be32(®s->miimind)) & MIIMIND_BUSY) + cpu_relax(); + + return 0; +} + +/* Reads from register regnum in the PHY for device dev, */ +/* returning the value. Clears miimcom first. All PHY */ +/* configuration has to be done through the TSEC1 MIIM regs */ +int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + u16 value; + + /* Setting up the MII Mangement Address Register */ + out_be32(®s->miimadd, + (mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | regnum); + + /* Clear miimcom, perform an MII management read cycle */ + out_be32(®s->miimcom, 0); + out_be32(®s->miimcom, MIIMCOM_READ_CYCLE); + + /* Wait till MII management write is complete */ + while ((in_be32(®s->miimind)) & (MIIMIND_BUSY | MIIMIND_NOT_VALID)) + cpu_relax(); + + /* Read MII management status */ + value = in_be32(®s->miimstat); + + return value; +} + +/* Reset the MIIM registers, and wait for the bus to free */ +int uec_mdio_reset(struct mii_bus *bus) +{ + struct ucc_mii_mng __iomem *regs = (void __iomem *)bus->priv; + unsigned int timeout = PHY_INIT_TIMEOUT; + + spin_lock_bh(&bus->mdio_lock); + + /* Reset the management interface */ + out_be32(®s->miimcfg, MIIMCFG_RESET_MANAGEMENT); + + /* Setup the MII Mgmt clock speed */ + out_be32(®s->miimcfg, MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112); + + /* Wait until the bus is free */ + while ((in_be32(®s->miimind) & MIIMIND_BUSY) && timeout--) + cpu_relax(); + + spin_unlock_bh(&bus->mdio_lock); + + if (timeout <= 0) { + printk(KERN_ERR "%s: The MII Bus is stuck!\n", bus->name); + return -EBUSY; + } + + return 0; +} + +static int uec_mdio_probe(struct of_device *ofdev, const struct of_device_id *match) +{ + struct device *device = &ofdev->dev; + struct device_node *np = ofdev->node, *tempnp = NULL; + struct device_node *child = NULL; + struct ucc_mii_mng __iomem *regs; + struct mii_bus *new_bus; + struct resource res; + int k, err = 0; + + new_bus = kzalloc(sizeof(struct mii_bus), GFP_KERNEL); + + if (NULL == new_bus) + return -ENOMEM; + + new_bus->name = "UCC Ethernet Controller MII Bus"; + new_bus->read = &uec_mdio_read; + new_bus->write = &uec_mdio_write; + new_bus->reset = &uec_mdio_reset; + + memset(&res, 0, sizeof(res)); + + err = of_address_to_resource(np, 0, &res); + if (err) + goto reg_map_fail; + + new_bus->id = res.start; + + new_bus->irq = kmalloc(32 * sizeof(int), GFP_KERNEL); + + if (NULL == new_bus->irq) { + err = -ENOMEM; + goto reg_map_fail; + } + + for (k = 0; k < 32; k++) + new_bus->irq[k] = PHY_POLL; + + while ((child = of_get_next_child(np, child)) != NULL) { + int irq = irq_of_parse_and_map(child, 0); + if (irq != NO_IRQ) { + const u32 *id = get_property(child, "reg", NULL); + new_bus->irq[*id] = irq; + } + } + + /* Set the base address */ + regs = ioremap(res.start, sizeof(struct ucc_mii_mng)); + + if (NULL == regs) { + err = -ENOMEM; + goto ioremap_fail; + } + + new_bus->priv = (void __force *)regs; + + new_bus->dev = device; + dev_set_drvdata(device, new_bus); + + /* Read MII management master from device tree */ + while ((tempnp = of_find_compatible_node(tempnp, "network", "ucc_geth")) + != NULL) { + struct resource tempres; + + err = of_address_to_resource(tempnp, 0, &tempres); + if (err) + goto bus_register_fail; + + /* if our mdio regs fall within this UCC regs range */ + if ((res.start >= tempres.start) && + (res.end <= tempres.end)) { + /* set this UCC to be the MII master */ + const u32 *id = get_property(tempnp, "device-id", NULL); + if (id == NULL) + goto bus_register_fail; + + ucc_set_qe_mux_mii_mng(*id - 1); + + /* assign the TBI an address which won't + * conflict with the PHYs */ + out_be32(®s->utbipar, UTBIPAR_INIT_TBIPA); + break; + } + } + + err = mdiobus_register(new_bus); + if (0 != err) { + printk(KERN_ERR "%s: Cannot register as MDIO bus\n", + new_bus->name); + goto bus_register_fail; + } + + return 0; + +bus_register_fail: + iounmap(regs); +ioremap_fail: + kfree(new_bus->irq); +reg_map_fail: + kfree(new_bus); + + return err; +} + +int uec_mdio_remove(struct of_device *ofdev) +{ + struct device *device = &ofdev->dev; + struct mii_bus *bus = dev_get_drvdata(device); + + mdiobus_unregister(bus); + + dev_set_drvdata(device, NULL); + + iounmap((void __iomem *)bus->priv); + bus->priv = NULL; + kfree(bus); + + return 0; +} + +static struct of_device_id uec_mdio_match[] = { + { + .type = "mdio", + .compatible = "ucc_geth_phy", + }, + {}, +}; + +MODULE_DEVICE_TABLE(of, uec_mdio_match); + +static struct of_platform_driver uec_mdio_driver = { + .name = DRV_NAME, + .probe = uec_mdio_probe, + .remove = uec_mdio_remove, + .match_table = uec_mdio_match, +}; + +int __init uec_mdio_init(void) +{ + return of_register_platform_driver(&uec_mdio_driver); +} + +void __exit uec_mdio_exit(void) +{ + of_unregister_platform_driver(&uec_mdio_driver); +} diff --git a/drivers/net/ucc_geth_mii.h b/drivers/net/ucc_geth_mii.h new file mode 100644 index 0000000..98430fe --- /dev/null +++ b/drivers/net/ucc_geth_mii.h @@ -0,0 +1,100 @@ +/* + * drivers/net/ucc_geth_mii.h + * + * Gianfar Ethernet Driver -- MII Management Bus Implementation + * Driver for the MDIO bus controller in the Gianfar register space + * + * Author: Andy Fleming + * Maintainer: Kumar Gala + * + * Copyright (c) 2002-2004 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ +#ifndef __UEC_MII_H +#define __UEC_MII_H + +/* UCC GETH MIIMCFG (MII Management Configuration Register) */ +#define MIIMCFG_RESET_MANAGEMENT 0x80000000 /* Reset + management */ +#define MIIMCFG_NO_PREAMBLE 0x00000010 /* Preamble + suppress */ +#define MIIMCFG_CLOCK_DIVIDE_SHIFT (31 - 31) /* clock divide + << shift */ +#define MIIMCFG_CLOCK_DIVIDE_MAX 0xf /* max clock divide */ +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_2 0x00000000 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_4 0x00000001 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_6 0x00000002 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_8 0x00000003 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_10 0x00000004 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_14 0x00000005 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_16 0x00000008 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_20 0x00000006 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_28 0x00000007 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_32 0x00000009 +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_48 0x0000000a +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_64 0x0000000b +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_80 0x0000000c +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_112 0x0000000d +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_160 0x0000000e +#define MIIMCFG_MANAGEMENT_CLOCK_DIVIDE_BY_224 0x0000000f + +/* UCC GETH MIIMCOM (MII Management Command Register) */ +#define MIIMCOM_SCAN_CYCLE 0x00000002 /* Scan cycle */ +#define MIIMCOM_READ_CYCLE 0x00000001 /* Read cycle */ + +/* UCC GETH MIIMADD (MII Management Address Register) */ +#define MIIMADD_PHY_ADDRESS_SHIFT (31 - 23) /* PHY Address + << shift */ +#define MIIMADD_PHY_REGISTER_SHIFT (31 - 31) /* PHY Register + << shift */ + +/* UCC GETH MIIMCON (MII Management Control Register) */ +#define MIIMCON_PHY_CONTROL_SHIFT (31 - 31) /* PHY Control + << shift */ +#define MIIMCON_PHY_STATUS_SHIFT (31 - 31) /* PHY Status + << shift */ + +/* UCC GETH MIIMIND (MII Management Indicator Register) */ +#define MIIMIND_NOT_VALID 0x00000004 /* Not valid */ +#define MIIMIND_SCAN 0x00000002 /* Scan in + progress */ +#define MIIMIND_BUSY 0x00000001 + +/* Initial TBI Physical Address */ +#define UTBIPAR_INIT_TBIPA 0x1f + +struct ucc_mii_mng { + u32 miimcfg; /* MII management configuration reg */ + u32 miimcom; /* MII management command reg */ + u32 miimadd; /* MII management address reg */ + u32 miimcon; /* MII management control reg */ + u32 miimstat; /* MII management status reg */ + u32 miimind; /* MII management indication reg */ + u8 notcare[28]; /* Space holder */ + u32 utbipar; /* TBI phy address reg */ +} __attribute__ ((packed)); + +/* TBI / MII Set Register */ +enum enet_tbi_mii_reg { + ENET_TBI_MII_CR = 0x00, /* Control */ + ENET_TBI_MII_SR = 0x01, /* Status */ + ENET_TBI_MII_ANA = 0x04, /* AN advertisement */ + ENET_TBI_MII_ANLPBPA = 0x05, /* AN link partner base page ability */ + ENET_TBI_MII_ANEX = 0x06, /* AN expansion */ + ENET_TBI_MII_ANNPT = 0x07, /* AN next page transmit */ + ENET_TBI_MII_ANLPANP = 0x08, /* AN link partner ability next page */ + ENET_TBI_MII_EXST = 0x0F, /* Extended status */ + ENET_TBI_MII_JD = 0x10, /* Jitter diagnostics */ + ENET_TBI_MII_TBICON = 0x11 /* TBI control */ +}; + +int uec_mdio_read(struct mii_bus *bus, int mii_id, int regnum); +int uec_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value); +int __init uec_mdio_init(void); +void __exit uec_mdio_exit(void); +#endif /* __UEC_MII_H */ diff --git a/drivers/net/ucc_geth_phy.c b/drivers/net/ucc_geth_phy.c deleted file mode 100644 index 9373d895..0000000 --- a/drivers/net/ucc_geth_phy.c +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Shlomi Gridish <gridish@freescale.com> - * - * Description: - * UCC GETH Driver -- PHY handling - * - * Changelog: - * Jun 28, 2006 Li Yang <LeoLi@freescale.com> - * - Rearrange code and style fixes - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/errno.h> -#include <linux/slab.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/netdevice.h> -#include <linux/etherdevice.h> -#include <linux/skbuff.h> -#include <linux/spinlock.h> -#include <linux/mm.h> -#include <linux/module.h> -#include <linux/version.h> -#include <linux/crc32.h> -#include <linux/mii.h> -#include <linux/ethtool.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/uaccess.h> - -#include "ucc_geth.h" -#include "ucc_geth_phy.h" - -#define ugphy_printk(level, format, arg...) \ - printk(level format "\n", ## arg) - -#define ugphy_dbg(format, arg...) \ - ugphy_printk(KERN_DEBUG, format , ## arg) -#define ugphy_err(format, arg...) \ - ugphy_printk(KERN_ERR, format , ## arg) -#define ugphy_info(format, arg...) \ - ugphy_printk(KERN_INFO, format , ## arg) -#define ugphy_warn(format, arg...) \ - ugphy_printk(KERN_WARNING, format , ## arg) - -#ifdef UGETH_VERBOSE_DEBUG -#define ugphy_vdbg ugphy_dbg -#else -#define ugphy_vdbg(fmt, args...) do { } while (0) -#endif /* UGETH_VERBOSE_DEBUG */ - -static void config_genmii_advert(struct ugeth_mii_info *mii_info); -static void genmii_setup_forced(struct ugeth_mii_info *mii_info); -static void genmii_restart_aneg(struct ugeth_mii_info *mii_info); -static int gbit_config_aneg(struct ugeth_mii_info *mii_info); -static int genmii_config_aneg(struct ugeth_mii_info *mii_info); -static int genmii_update_link(struct ugeth_mii_info *mii_info); -static int genmii_read_status(struct ugeth_mii_info *mii_info); - -static u16 ucc_geth_phy_read(struct ugeth_mii_info *mii_info, u16 regnum) -{ - u16 retval; - unsigned long flags; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irqsave(&mii_info->mdio_lock, flags); - retval = mii_info->mdio_read(mii_info->dev, mii_info->mii_id, regnum); - spin_unlock_irqrestore(&mii_info->mdio_lock, flags); - - return retval; -} - -static void ucc_geth_phy_write(struct ugeth_mii_info *mii_info, u16 regnum, u16 val) -{ - unsigned long flags; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irqsave(&mii_info->mdio_lock, flags); - mii_info->mdio_write(mii_info->dev, mii_info->mii_id, regnum, val); - spin_unlock_irqrestore(&mii_info->mdio_lock, flags); -} - -/* Write value to the PHY for this device to the register at regnum, */ -/* waiting until the write is done before it returns. All PHY */ -/* configuration has to be done through the TSEC1 MIIM regs */ -void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value) -{ - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct ucc_mii_mng *mii_regs; - enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum; - u32 tmp_reg; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irq(&ugeth->lock); - - mii_regs = ugeth->mii_info->mii_regs; - - /* Set this UCC to be the master of the MII managment */ - ucc_set_qe_mux_mii_mng(ugeth->ug_info->uf_info.ucc_num); - - /* Stop the MII management read cycle */ - out_be32(&mii_regs->miimcom, 0); - /* Setting up the MII Mangement Address Register */ - tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; - out_be32(&mii_regs->miimadd, tmp_reg); - - /* Setting up the MII Mangement Control Register with the value */ - out_be32(&mii_regs->miimcon, (u32) value); - - /* Wait till MII management write is complete */ - while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY) - cpu_relax(); - - spin_unlock_irq(&ugeth->lock); - - udelay(10000); -} - -/* Reads from register regnum in the PHY for device dev, */ -/* returning the value. Clears miimcom first. All PHY */ -/* configuration has to be done through the TSEC1 MIIM regs */ -int read_phy_reg(struct net_device *dev, int mii_id, int regnum) -{ - struct ucc_geth_private *ugeth = netdev_priv(dev); - struct ucc_mii_mng *mii_regs; - enum enet_tbi_mii_reg mii_reg = (enum enet_tbi_mii_reg) regnum; - u32 tmp_reg; - u16 value; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - spin_lock_irq(&ugeth->lock); - - mii_regs = ugeth->mii_info->mii_regs; - - /* Setting up the MII Mangement Address Register */ - tmp_reg = ((u32) mii_id << MIIMADD_PHY_ADDRESS_SHIFT) | mii_reg; - out_be32(&mii_regs->miimadd, tmp_reg); - - /* Perform an MII management read cycle */ - out_be32(&mii_regs->miimcom, MIIMCOM_READ_CYCLE); - - /* Wait till MII management write is complete */ - while ((in_be32(&mii_regs->miimind)) & MIIMIND_BUSY) - cpu_relax(); - - udelay(10000); - - /* Read MII management status */ - value = (u16) in_be32(&mii_regs->miimstat); - out_be32(&mii_regs->miimcom, 0); - if (value == 0xffff) - ugphy_warn("read wrong value : mii_id %d,mii_reg %d, base %08x", - mii_id, mii_reg, (u32) & (mii_regs->miimcfg)); - - spin_unlock_irq(&ugeth->lock); - - return (value); -} - -void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->phyinfo->ack_interrupt) - mii_info->phyinfo->ack_interrupt(mii_info); -} - -void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info, - u32 interrupts) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - mii_info->interrupts = interrupts; - if (mii_info->phyinfo->config_intr) - mii_info->phyinfo->config_intr(mii_info); -} - -/* Writes MII_ADVERTISE with the appropriate values, after - * sanitizing advertise to make sure only supported features - * are advertised - */ -static void config_genmii_advert(struct ugeth_mii_info *mii_info) -{ - u32 advertise; - u16 adv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Only allow advertising what this PHY supports */ - mii_info->advertising &= mii_info->phyinfo->features; - advertise = mii_info->advertising; - - /* Setup standard advertisement */ - adv = ucc_geth_phy_read(mii_info, MII_ADVERTISE); - adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4); - if (advertise & ADVERTISED_10baseT_Half) - adv |= ADVERTISE_10HALF; - if (advertise & ADVERTISED_10baseT_Full) - adv |= ADVERTISE_10FULL; - if (advertise & ADVERTISED_100baseT_Half) - adv |= ADVERTISE_100HALF; - if (advertise & ADVERTISED_100baseT_Full) - adv |= ADVERTISE_100FULL; - ucc_geth_phy_write(mii_info, MII_ADVERTISE, adv); -} - -static void genmii_setup_forced(struct ugeth_mii_info *mii_info) -{ - u16 ctrl; - u32 features = mii_info->phyinfo->features; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - ctrl = ucc_geth_phy_read(mii_info, MII_BMCR); - - ctrl &= - ~(BMCR_FULLDPLX | BMCR_SPEED100 | BMCR_SPEED1000 | BMCR_ANENABLE); - ctrl |= BMCR_RESET; - - switch (mii_info->speed) { - case SPEED_1000: - if (features & (SUPPORTED_1000baseT_Half - | SUPPORTED_1000baseT_Full)) { - ctrl |= BMCR_SPEED1000; - break; - } - mii_info->speed = SPEED_100; - case SPEED_100: - if (features & (SUPPORTED_100baseT_Half - | SUPPORTED_100baseT_Full)) { - ctrl |= BMCR_SPEED100; - break; - } - mii_info->speed = SPEED_10; - case SPEED_10: - if (features & (SUPPORTED_10baseT_Half - | SUPPORTED_10baseT_Full)) - break; - default: /* Unsupported speed! */ - ugphy_err("%s: Bad speed!", mii_info->dev->name); - break; - } - - ucc_geth_phy_write(mii_info, MII_BMCR, ctrl); -} - -/* Enable and Restart Autonegotiation */ -static void genmii_restart_aneg(struct ugeth_mii_info *mii_info) -{ - u16 ctl; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - ctl = ucc_geth_phy_read(mii_info, MII_BMCR); - ctl |= (BMCR_ANENABLE | BMCR_ANRESTART); - ucc_geth_phy_write(mii_info, MII_BMCR, ctl); -} - -static int gbit_config_aneg(struct ugeth_mii_info *mii_info) -{ - u16 adv; - u32 advertise; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->autoneg) { - /* Configure the ADVERTISE register */ - config_genmii_advert(mii_info); - advertise = mii_info->advertising; - - adv = ucc_geth_phy_read(mii_info, MII_1000BASETCONTROL); - adv &= ~(MII_1000BASETCONTROL_FULLDUPLEXCAP | - MII_1000BASETCONTROL_HALFDUPLEXCAP); - if (advertise & SUPPORTED_1000baseT_Half) - adv |= MII_1000BASETCONTROL_HALFDUPLEXCAP; - if (advertise & SUPPORTED_1000baseT_Full) - adv |= MII_1000BASETCONTROL_FULLDUPLEXCAP; - ucc_geth_phy_write(mii_info, MII_1000BASETCONTROL, adv); - - /* Start/Restart aneg */ - genmii_restart_aneg(mii_info); - } else - genmii_setup_forced(mii_info); - - return 0; -} - -static int genmii_config_aneg(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->autoneg) { - config_genmii_advert(mii_info); - genmii_restart_aneg(mii_info); - } else - genmii_setup_forced(mii_info); - - return 0; -} - -static int genmii_update_link(struct ugeth_mii_info *mii_info) -{ - u16 status; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Do a fake read */ - ucc_geth_phy_read(mii_info, MII_BMSR); - - /* Read link and autonegotiation status */ - status = ucc_geth_phy_read(mii_info, MII_BMSR); - if ((status & BMSR_LSTATUS) == 0) - mii_info->link = 0; - else - mii_info->link = 1; - - /* If we are autonegotiating, and not done, - * return an error */ - if (mii_info->autoneg && !(status & BMSR_ANEGCOMPLETE)) - return -EAGAIN; - - return 0; -} - -static int genmii_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - if (mii_info->autoneg) { - status = ucc_geth_phy_read(mii_info, MII_LPA); - - if (status & (LPA_10FULL | LPA_100FULL)) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - if (status & (LPA_100FULL | LPA_100HALF)) - mii_info->speed = SPEED_100; - else - mii_info->speed = SPEED_10; - mii_info->pause = 0; - } - /* On non-aneg, we assume what we put in BMCR is the speed, - * though magic-aneg shouldn't prevent this case from occurring - */ - - return 0; -} - -static int marvell_init(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_write(mii_info, 0x14, 0x0cd2); - ucc_geth_phy_write(mii_info, 0x1b, - (ucc_geth_phy_read(mii_info, 0x1b) & ~0x000f) | 0x000b); - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET); - msleep(4000); - - return 0; -} - -static int marvell_config_aneg(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* The Marvell PHY has an errata which requires - * that certain registers get written in order - * to restart autonegotiation */ - ucc_geth_phy_write(mii_info, MII_BMCR, BMCR_RESET); - - ucc_geth_phy_write(mii_info, 0x1d, 0x1f); - ucc_geth_phy_write(mii_info, 0x1e, 0x200c); - ucc_geth_phy_write(mii_info, 0x1d, 0x5); - ucc_geth_phy_write(mii_info, 0x1e, 0); - ucc_geth_phy_write(mii_info, 0x1e, 0x100); - - gbit_config_aneg(mii_info); - - return 0; -} - -static int marvell_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - int speed; - status = ucc_geth_phy_read(mii_info, MII_M1011_PHY_SPEC_STATUS); - - /* Get the duplexity */ - if (status & MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - - /* Get the speed */ - speed = status & MII_M1011_PHY_SPEC_STATUS_SPD_MASK; - switch (speed) { - case MII_M1011_PHY_SPEC_STATUS_1000: - mii_info->speed = SPEED_1000; - break; - case MII_M1011_PHY_SPEC_STATUS_100: - mii_info->speed = SPEED_100; - break; - default: - mii_info->speed = SPEED_10; - break; - } - mii_info->pause = 0; - } - - return 0; -} - -static int marvell_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupts by reading the reg */ - ucc_geth_phy_read(mii_info, MII_M1011_IEVENT); - - return 0; -} - -static int marvell_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_INIT); - else - ucc_geth_phy_write(mii_info, MII_M1011_IMASK, MII_M1011_IMASK_CLEAR); - - return 0; -} - -static int cis820x_init(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_write(mii_info, MII_CIS8201_AUX_CONSTAT, - MII_CIS8201_AUXCONSTAT_INIT); - ucc_geth_phy_write(mii_info, MII_CIS8201_EXT_CON1, MII_CIS8201_EXTCON1_INIT); - - return 0; -} - -static int cis820x_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - int speed; - - status = ucc_geth_phy_read(mii_info, MII_CIS8201_AUX_CONSTAT); - if (status & MII_CIS8201_AUXCONSTAT_DUPLEX) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - - speed = status & MII_CIS8201_AUXCONSTAT_SPEED; - - switch (speed) { - case MII_CIS8201_AUXCONSTAT_GBIT: - mii_info->speed = SPEED_1000; - break; - case MII_CIS8201_AUXCONSTAT_100: - mii_info->speed = SPEED_100; - break; - default: - mii_info->speed = SPEED_10; - break; - } - } - - return 0; -} - -static int cis820x_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - ucc_geth_phy_read(mii_info, MII_CIS8201_ISTAT); - - return 0; -} - -static int cis820x_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, MII_CIS8201_IMASK_MASK); - else - ucc_geth_phy_write(mii_info, MII_CIS8201_IMASK, 0); - - return 0; -} - -#define DM9161_DELAY 10 - -static int dm9161_read_status(struct ugeth_mii_info *mii_info) -{ - u16 status; - int err; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Update the link, but return if there - * was an error */ - err = genmii_update_link(mii_info); - if (err) - return err; - - /* If the link is up, read the speed and duplex */ - /* If we aren't autonegotiating, assume speeds - * are as set */ - if (mii_info->autoneg && mii_info->link) { - status = ucc_geth_phy_read(mii_info, MII_DM9161_SCSR); - if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_100H)) - mii_info->speed = SPEED_100; - else - mii_info->speed = SPEED_10; - - if (status & (MII_DM9161_SCSR_100F | MII_DM9161_SCSR_10F)) - mii_info->duplex = DUPLEX_FULL; - else - mii_info->duplex = DUPLEX_HALF; - } - - return 0; -} - -static int dm9161_config_aneg(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv = mii_info->priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (0 == priv->resetdone) - return -EAGAIN; - - return 0; -} - -static void dm9161_timer(unsigned long data) -{ - struct ugeth_mii_info *mii_info = (struct ugeth_mii_info *)data; - struct dm9161_private *priv = mii_info->priv; - u16 status = ucc_geth_phy_read(mii_info, MII_BMSR); - - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (status & BMSR_ANEGCOMPLETE) { - priv->resetdone = 1; - } else - mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ); -} - -static int dm9161_init(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Allocate the private data structure */ - priv = kmalloc(sizeof(struct dm9161_private), GFP_KERNEL); - - if (NULL == priv) - return -ENOMEM; - - mii_info->priv = priv; - - /* Reset is not done yet */ - priv->resetdone = 0; - - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) | BMCR_RESET); - - ucc_geth_phy_write(mii_info, MII_BMCR, - ucc_geth_phy_read(mii_info, MII_BMCR) & ~BMCR_ISOLATE); - - config_genmii_advert(mii_info); - /* Start/Restart aneg */ - genmii_config_aneg(mii_info); - - /* Start a timer for DM9161_DELAY seconds to wait - * for the PHY to be ready */ - init_timer(&priv->timer); - priv->timer.function = &dm9161_timer; - priv->timer.data = (unsigned long)mii_info; - mod_timer(&priv->timer, jiffies + DM9161_DELAY * HZ); - - return 0; -} - -static void dm9161_close(struct ugeth_mii_info *mii_info) -{ - struct dm9161_private *priv = mii_info->priv; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - del_timer_sync(&priv->timer); - kfree(priv); -} - -static int dm9161_ack_interrupt(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Clear the interrupts by reading the reg */ - ucc_geth_phy_read(mii_info, MII_DM9161_INTR); - - - return 0; -} - -static int dm9161_config_intr(struct ugeth_mii_info *mii_info) -{ - ugphy_vdbg("%s: IN", __FUNCTION__); - - if (mii_info->interrupts == MII_INTERRUPT_ENABLED) - ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_INIT); - else - ucc_geth_phy_write(mii_info, MII_DM9161_INTR, MII_DM9161_INTR_STOP); - - return 0; -} - -/* Cicada 820x */ -static struct phy_info phy_info_cis820x = { - .phy_id = 0x000fc440, - .name = "Cicada Cis8204", - .phy_id_mask = 0x000fffc0, - .features = MII_GBIT_FEATURES, - .init = &cis820x_init, - .config_aneg = &gbit_config_aneg, - .read_status = &cis820x_read_status, - .ack_interrupt = &cis820x_ack_interrupt, - .config_intr = &cis820x_config_intr, -}; - -static struct phy_info phy_info_dm9161 = { - .phy_id = 0x0181b880, - .phy_id_mask = 0x0ffffff0, - .name = "Davicom DM9161E", - .init = dm9161_init, - .config_aneg = dm9161_config_aneg, - .read_status = dm9161_read_status, - .close = dm9161_close, -}; - -static struct phy_info phy_info_dm9161a = { - .phy_id = 0x0181b8a0, - .phy_id_mask = 0x0ffffff0, - .name = "Davicom DM9161A", - .features = MII_BASIC_FEATURES, - .init = dm9161_init, - .config_aneg = dm9161_config_aneg, - .read_status = dm9161_read_status, - .ack_interrupt = dm9161_ack_interrupt, - .config_intr = dm9161_config_intr, - .close = dm9161_close, -}; - -static struct phy_info phy_info_marvell = { - .phy_id = 0x01410c00, - .phy_id_mask = 0xffffff00, - .name = "Marvell 88E11x1", - .features = MII_GBIT_FEATURES, - .init = &marvell_init, - .config_aneg = &marvell_config_aneg, - .read_status = &marvell_read_status, - .ack_interrupt = &marvell_ack_interrupt, - .config_intr = &marvell_config_intr, -}; - -static struct phy_info phy_info_genmii = { - .phy_id = 0x00000000, - .phy_id_mask = 0x00000000, - .name = "Generic MII", - .features = MII_BASIC_FEATURES, - .config_aneg = genmii_config_aneg, - .read_status = genmii_read_status, -}; - -static struct phy_info *phy_info[] = { - &phy_info_cis820x, - &phy_info_marvell, - &phy_info_dm9161, - &phy_info_dm9161a, - &phy_info_genmii, - NULL -}; - -/* Use the PHY ID registers to determine what type of PHY is attached - * to device dev. return a struct phy_info structure describing that PHY - */ -struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info) -{ - u16 phy_reg; - u32 phy_ID; - int i; - struct phy_info *theInfo = NULL; - struct net_device *dev = mii_info->dev; - - ugphy_vdbg("%s: IN", __FUNCTION__); - - /* Grab the bits from PHYIR1, and put them in the upper half */ - phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID1); - phy_ID = (phy_reg & 0xffff) << 16; - - /* Grab the bits from PHYIR2, and put them in the lower half */ - phy_reg = ucc_geth_phy_read(mii_info, MII_PHYSID2); - phy_ID |= (phy_reg & 0xffff); - - /* loop through all the known PHY types, and find one that */ - /* matches the ID we read from the PHY. */ - for (i = 0; phy_info[i]; i++) - if (phy_info[i]->phy_id == (phy_ID & phy_info[i]->phy_id_mask)){ - theInfo = phy_info[i]; - break; - } - - /* This shouldn't happen, as we have generic PHY support */ - if (theInfo == NULL) { - ugphy_info("%s: PHY id %x is not supported!", dev->name, - phy_ID); - return NULL; - } else { - ugphy_info("%s: PHY is %s (%x)", dev->name, theInfo->name, - phy_ID); - } - - return theInfo; -} diff --git a/drivers/net/ucc_geth_phy.h b/drivers/net/ucc_geth_phy.h deleted file mode 100644 index f574078..0000000 --- a/drivers/net/ucc_geth_phy.h +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (C) Freescale Semicondutor, Inc. 2006. All rights reserved. - * - * Author: Shlomi Gridish <gridish@freescale.com> - * - * Description: - * UCC GETH Driver -- PHY handling - * - * Changelog: - * Jun 28, 2006 Li Yang <LeoLi@freescale.com> - * - Rearrange code and style fixes - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - */ -#ifndef __UCC_GETH_PHY_H__ -#define __UCC_GETH_PHY_H__ - -#define MII_end ((u32)-2) -#define MII_read ((u32)-1) - -#define MIIMIND_BUSY 0x00000001 -#define MIIMIND_NOTVALID 0x00000004 - -#define UGETH_AN_TIMEOUT 2000 - -/* 1000BT control (Marvell & BCM54xx at least) */ -#define MII_1000BASETCONTROL 0x09 -#define MII_1000BASETCONTROL_FULLDUPLEXCAP 0x0200 -#define MII_1000BASETCONTROL_HALFDUPLEXCAP 0x0100 - -/* Cicada Extended Control Register 1 */ -#define MII_CIS8201_EXT_CON1 0x17 -#define MII_CIS8201_EXTCON1_INIT 0x0000 - -/* Cicada Interrupt Mask Register */ -#define MII_CIS8201_IMASK 0x19 -#define MII_CIS8201_IMASK_IEN 0x8000 -#define MII_CIS8201_IMASK_SPEED 0x4000 -#define MII_CIS8201_IMASK_LINK 0x2000 -#define MII_CIS8201_IMASK_DUPLEX 0x1000 -#define MII_CIS8201_IMASK_MASK 0xf000 - -/* Cicada Interrupt Status Register */ -#define MII_CIS8201_ISTAT 0x1a -#define MII_CIS8201_ISTAT_STATUS 0x8000 -#define MII_CIS8201_ISTAT_SPEED 0x4000 -#define MII_CIS8201_ISTAT_LINK 0x2000 -#define MII_CIS8201_ISTAT_DUPLEX 0x1000 - -/* Cicada Auxiliary Control/Status Register */ -#define MII_CIS8201_AUX_CONSTAT 0x1c -#define MII_CIS8201_AUXCONSTAT_INIT 0x0004 -#define MII_CIS8201_AUXCONSTAT_DUPLEX 0x0020 -#define MII_CIS8201_AUXCONSTAT_SPEED 0x0018 -#define MII_CIS8201_AUXCONSTAT_GBIT 0x0010 -#define MII_CIS8201_AUXCONSTAT_100 0x0008 - -/* 88E1011 PHY Status Register */ -#define MII_M1011_PHY_SPEC_STATUS 0x11 -#define MII_M1011_PHY_SPEC_STATUS_1000 0x8000 -#define MII_M1011_PHY_SPEC_STATUS_100 0x4000 -#define MII_M1011_PHY_SPEC_STATUS_SPD_MASK 0xc000 -#define MII_M1011_PHY_SPEC_STATUS_FULLDUPLEX 0x2000 -#define MII_M1011_PHY_SPEC_STATUS_RESOLVED 0x0800 -#define MII_M1011_PHY_SPEC_STATUS_LINK 0x0400 - -#define MII_M1011_IEVENT 0x13 -#define MII_M1011_IEVENT_CLEAR 0x0000 - -#define MII_M1011_IMASK 0x12 -#define MII_M1011_IMASK_INIT 0x6400 -#define MII_M1011_IMASK_CLEAR 0x0000 - -#define MII_DM9161_SCR 0x10 -#define MII_DM9161_SCR_INIT 0x0610 - -/* DM9161 Specified Configuration and Status Register */ -#define MII_DM9161_SCSR 0x11 -#define MII_DM9161_SCSR_100F 0x8000 -#define MII_DM9161_SCSR_100H 0x4000 -#define MII_DM9161_SCSR_10F 0x2000 -#define MII_DM9161_SCSR_10H 0x1000 - -/* DM9161 Interrupt Register */ -#define MII_DM9161_INTR 0x15 -#define MII_DM9161_INTR_PEND 0x8000 -#define MII_DM9161_INTR_DPLX_MASK 0x0800 -#define MII_DM9161_INTR_SPD_MASK 0x0400 -#define MII_DM9161_INTR_LINK_MASK 0x0200 -#define MII_DM9161_INTR_MASK 0x0100 -#define MII_DM9161_INTR_DPLX_CHANGE 0x0010 -#define MII_DM9161_INTR_SPD_CHANGE 0x0008 -#define MII_DM9161_INTR_LINK_CHANGE 0x0004 -#define MII_DM9161_INTR_INIT 0x0000 -#define MII_DM9161_INTR_STOP \ -(MII_DM9161_INTR_DPLX_MASK | MII_DM9161_INTR_SPD_MASK \ - | MII_DM9161_INTR_LINK_MASK | MII_DM9161_INTR_MASK) - -/* DM9161 10BT Configuration/Status */ -#define MII_DM9161_10BTCSR 0x12 -#define MII_DM9161_10BTCSR_INIT 0x7800 - -#define MII_BASIC_FEATURES (SUPPORTED_10baseT_Half | \ - SUPPORTED_10baseT_Full | \ - SUPPORTED_100baseT_Half | \ - SUPPORTED_100baseT_Full | \ - SUPPORTED_Autoneg | \ - SUPPORTED_TP | \ - SUPPORTED_MII) - -#define MII_GBIT_FEATURES (MII_BASIC_FEATURES | \ - SUPPORTED_1000baseT_Half | \ - SUPPORTED_1000baseT_Full) - -#define MII_READ_COMMAND 0x00000001 - -#define MII_INTERRUPT_DISABLED 0x0 -#define MII_INTERRUPT_ENABLED 0x1 -/* Taken from mii_if_info and sungem_phy.h */ -struct ugeth_mii_info { - /* Information about the PHY type */ - /* And management functions */ - struct phy_info *phyinfo; - - struct ucc_mii_mng *mii_regs; - - /* forced speed & duplex (no autoneg) - * partner speed & duplex & pause (autoneg) - */ - int speed; - int duplex; - int pause; - - /* The most recently read link state */ - int link; - - /* Enabled Interrupts */ - u32 interrupts; - - u32 advertising; - int autoneg; - int mii_id; - - /* private data pointer */ - /* For use by PHYs to maintain extra state */ - void *priv; - - /* Provided by host chip */ - struct net_device *dev; - - /* A lock to ensure that only one thing can read/write - * the MDIO bus at a time */ - spinlock_t mdio_lock; - - /* Provided by ethernet driver */ - int (*mdio_read) (struct net_device * dev, int mii_id, int reg); - void (*mdio_write) (struct net_device * dev, int mii_id, int reg, - int val); -}; - -/* struct phy_info: a structure which defines attributes for a PHY - * - * id will contain a number which represents the PHY. During - * startup, the driver will poll the PHY to find out what its - * UID--as defined by registers 2 and 3--is. The 32-bit result - * gotten from the PHY will be ANDed with phy_id_mask to - * discard any bits which may change based on revision numbers - * unimportant to functionality - * - * There are 6 commands which take a ugeth_mii_info structure. - * Each PHY must declare config_aneg, and read_status. - */ -struct phy_info { - u32 phy_id; - char *name; - unsigned int phy_id_mask; - u32 features; - - /* Called to initialize the PHY */ - int (*init) (struct ugeth_mii_info * mii_info); - - /* Called to suspend the PHY for power */ - int (*suspend) (struct ugeth_mii_info * mii_info); - - /* Reconfigures autonegotiation (or disables it) */ - int (*config_aneg) (struct ugeth_mii_info * mii_info); - - /* Determines the negotiated speed and duplex */ - int (*read_status) (struct ugeth_mii_info * mii_info); - - /* Clears any pending interrupts */ - int (*ack_interrupt) (struct ugeth_mii_info * mii_info); - - /* Enables or disables interrupts */ - int (*config_intr) (struct ugeth_mii_info * mii_info); - - /* Clears up any memory if needed */ - void (*close) (struct ugeth_mii_info * mii_info); -}; - -struct phy_info *get_phy_info(struct ugeth_mii_info *mii_info); -void write_phy_reg(struct net_device *dev, int mii_id, int regnum, int value); -int read_phy_reg(struct net_device *dev, int mii_id, int regnum); -void mii_clear_phy_interrupt(struct ugeth_mii_info *mii_info); -void mii_configure_phy_interrupt(struct ugeth_mii_info *mii_info, - u32 interrupts); - -struct dm9161_private { - struct timer_list timer; - int resetdone; -}; - -#endif /* __UCC_GETH_PHY_H__ */ diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index ebbda1d..adea290 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -30,8 +30,8 @@ */ #define DRV_NAME "via-rhine" -#define DRV_VERSION "1.4.2" -#define DRV_RELDATE "Sept-11-2006" +#define DRV_VERSION "1.4.3" +#define DRV_RELDATE "2007-03-06" /* A few user-configurable values. @@ -105,6 +105,7 @@ static const int multicast_filter_limit = 32; #include <asm/io.h> #include <asm/irq.h> #include <asm/uaccess.h> +#include <linux/dmi.h> /* These identify the driver base version and may not be removed. */ static char version[] __devinitdata = @@ -1485,7 +1486,6 @@ static int rhine_rx(struct net_device *dev, int limit) copying to a minimally-sized skbuff. */ if (pkt_len < rx_copybreak && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) { - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ pci_dma_sync_single_for_cpu(rp->pdev, rp->rx_skbuff_dma[entry], @@ -1995,6 +1995,23 @@ static struct pci_driver rhine_driver = { .shutdown = rhine_shutdown, }; +static struct dmi_system_id __initdata rhine_dmi_table[] = { + { + .ident = "EPIA-M", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "Award Software International, Inc."), + DMI_MATCH(DMI_BIOS_VERSION, "6.00 PG"), + }, + }, + { + .ident = "KV7", + .matches = { + DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies, LTD"), + DMI_MATCH(DMI_BIOS_VERSION, "6.00 PG"), + }, + }, + { NULL } +}; static int __init rhine_init(void) { @@ -2002,6 +2019,16 @@ static int __init rhine_init(void) #ifdef MODULE printk(version); #endif + if (dmi_check_system(rhine_dmi_table)) { + /* these BIOSes fail at PXE boot if chip is in D3 */ + avoid_D3 = 1; + printk(KERN_WARNING "%s: Broken BIOS detected, avoid_D3 " + "enabled.\n", + DRV_NAME); + } + else if (avoid_D3) + printk(KERN_INFO "%s: avoid_D3 set.\n", DRV_NAME); + return pci_register_driver(&rhine_driver); } diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 8e5d820..25b75b6 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1339,7 +1339,8 @@ static inline int velocity_rx_copy(struct sk_buff **rx_skb, int pkt_size, if (vptr->flags & VELOCITY_FLAGS_IP_ALIGN) skb_reserve(new_skb, 2); - memcpy(new_skb->data, rx_skb[0]->data, pkt_size); + skb_copy_from_linear_data(rx_skb[0], new_skb->data, + pkt_size); *rx_skb = new_skb; ret = 0; } @@ -1398,7 +1399,6 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) vptr->stats.multicast++; skb = rd_info->skb; - skb->dev = vptr->dev; pci_dma_sync_single_for_cpu(vptr->pdev, rd_info->skb_dma, vptr->rx_buf_sz, PCI_DMA_FROMDEVICE); @@ -1428,7 +1428,7 @@ static int velocity_receive_frame(struct velocity_info *vptr, int idx) PCI_DMA_FROMDEVICE); skb_put(skb, pkt_len - 4); - skb->protocol = eth_type_trans(skb, skb->dev); + skb->protocol = eth_type_trans(skb, vptr->dev); stats->rx_bytes += pkt_len; netif_rx(skb); @@ -1928,7 +1928,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) if (pktlen < ETH_ZLEN) { /* Cannot occur until ZC support */ pktlen = ETH_ZLEN; - memcpy(tdinfo->buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, tdinfo->buf, skb->len); memset(tdinfo->buf + skb->len, 0, ETH_ZLEN - skb->len); tdinfo->skb = skb; tdinfo->skb_dma[0] = tdinfo->buf_dma; @@ -1944,7 +1944,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) int nfrags = skb_shinfo(skb)->nr_frags; tdinfo->skb = skb; if (nfrags > 6) { - memcpy(tdinfo->buf, skb->data, skb->len); + skb_copy_from_linear_data(skb, tdinfo->buf, skb->len); tdinfo->skb_dma[0] = tdinfo->buf_dma; td_ptr->tdesc0.pktsize = td_ptr->td_buf[0].pa_low = cpu_to_le32(tdinfo->skb_dma[0]); @@ -2007,7 +2007,7 @@ static int velocity_xmit(struct sk_buff *skb, struct net_device *dev) */ if ((vptr->flags & VELOCITY_FLAGS_TX_CSUM) && (skb->ip_summed == CHECKSUM_PARTIAL)) { - struct iphdr *ip = skb->nh.iph; + const struct iphdr *ip = ip_hdr(skb); if (ip->protocol == IPPROTO_TCP) td_ptr->tdesc1.TCR |= TCR0_TCPCK; else if (ip->protocol == IPPROTO_UDP) diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig index 61708cf..8897f53 100644 --- a/drivers/net/wan/Kconfig +++ b/drivers/net/wan/Kconfig @@ -26,7 +26,7 @@ config WAN # There is no way to detect a comtrol sv11 - force it modular for now. config HOSTESS_SV11 tristate "Comtrol Hostess SV-11 support" - depends on WAN && ISA && m && ISA_DMA_API + depends on WAN && ISA && m && ISA_DMA_API && INET help Driver for Comtrol Hostess SV-11 network card which operates on low speed synchronous serial links at up to diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index e91b5a8..2346473 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -94,7 +94,6 @@ #include <linux/device.h> #undef COSA_SLOW_IO /* for testing purposes only */ -#undef REALLY_SLOW_IO #include <asm/io.h> #include <asm/dma.h> @@ -774,7 +773,7 @@ static int sppp_rx_done(struct channel_data *chan) } chan->rx_skb->protocol = htons(ETH_P_WAN_PPP); chan->rx_skb->dev = chan->pppdev.dev; - chan->rx_skb->mac.raw = chan->rx_skb->data; + skb_reset_mac_header(chan->rx_skb); chan->stats.rx_packets++; chan->stats.rx_bytes += chan->cosa->rxsize; netif_rx(chan->rx_skb); diff --git a/drivers/net/wan/cycx_x25.c b/drivers/net/wan/cycx_x25.c index a631d1c..016b3ff 100644 --- a/drivers/net/wan/cycx_x25.c +++ b/drivers/net/wan/cycx_x25.c @@ -834,7 +834,7 @@ static void cycx_x25_irq_rx(struct cycx_device *card, struct cycx_x25_cmd *cmd) ++chan->ifstats.rx_packets; chan->ifstats.rx_bytes += pktlen; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); netif_rx(skb); dev->last_rx = jiffies; /* timestamp */ } diff --git a/drivers/net/wan/dlci.c b/drivers/net/wan/dlci.c index 7369875..66be20c 100644 --- a/drivers/net/wan/dlci.c +++ b/drivers/net/wan/dlci.c @@ -176,7 +176,7 @@ static void dlci_receive(struct sk_buff *skb, struct net_device *dev) if (process) { /* we've set up the protocol, so discard the header */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, header); dlp->stats.rx_bytes += skb->len; netif_rx(skb); diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c index 25021a7..dca0244 100644 --- a/drivers/net/wan/dscc4.c +++ b/drivers/net/wan/dscc4.c @@ -1904,7 +1904,8 @@ static struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv) struct TxFD *tx_fd = dpriv->tx_fd + last; skb->len = DUMMY_SKB_SIZE; - memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE); + skb_copy_to_linear_data(skb, version, + strlen(version) % DUMMY_SKB_SIZE); tx_fd->state = FrameEnd | TO_STATE_TX(DUMMY_SKB_SIZE); tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, DUMMY_SKB_SIZE, PCI_DMA_TODEVICE); diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index c45d6a8..58a53b6 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -864,7 +864,7 @@ fst_tx_dma_complete(struct fst_card_info *card, struct fst_port_info *port, static __be16 farsync_type_trans(struct sk_buff *skb, struct net_device *dev) { skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; return htons(ETH_P_CUST); } diff --git a/drivers/net/wan/hdlc.c b/drivers/net/wan/hdlc.c index 9040d7c..65ad2e2 100644 --- a/drivers/net/wan/hdlc.c +++ b/drivers/net/wan/hdlc.c @@ -38,7 +38,7 @@ #include <linux/hdlc.h> -static const char* version = "HDLC support module revision 1.20"; +static const char* version = "HDLC support module revision 1.21"; #undef DEBUG_LINK @@ -222,19 +222,31 @@ int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EINVAL; } +static void hdlc_setup_dev(struct net_device *dev) +{ + /* Re-init all variables changed by HDLC protocol drivers, + * including ether_setup() called from hdlc_raw_eth.c. + */ + dev->get_stats = hdlc_get_stats; + dev->flags = IFF_POINTOPOINT | IFF_NOARP; + dev->mtu = HDLC_MAX_MTU; + dev->type = ARPHRD_RAWHDLC; + dev->hard_header_len = 16; + dev->addr_len = 0; + dev->hard_header = NULL; + dev->rebuild_header = NULL; + dev->set_mac_address = NULL; + dev->hard_header_cache = NULL; + dev->header_cache_update = NULL; + dev->change_mtu = hdlc_change_mtu; + dev->hard_header_parse = NULL; +} + static void hdlc_setup(struct net_device *dev) { hdlc_device *hdlc = dev_to_hdlc(dev); - dev->get_stats = hdlc_get_stats; - dev->change_mtu = hdlc_change_mtu; - dev->mtu = HDLC_MAX_MTU; - - dev->type = ARPHRD_RAWHDLC; - dev->hard_header_len = 16; - - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - + hdlc_setup_dev(dev); hdlc->carrier = 1; hdlc->open = 0; spin_lock_init(&hdlc->state_lock); @@ -294,6 +306,7 @@ void detach_hdlc_protocol(struct net_device *dev) } kfree(hdlc->state); hdlc->state = NULL; + hdlc_setup_dev(dev); } diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c index b0bc5dd..9ec6cf2 100644 --- a/drivers/net/wan/hdlc_cisco.c +++ b/drivers/net/wan/hdlc_cisco.c @@ -37,16 +37,16 @@ struct hdlc_header { u8 address; u8 control; - u16 protocol; + __be16 protocol; }__attribute__ ((packed)); struct cisco_packet { - u32 type; /* code */ - u32 par1; - u32 par2; - u16 rel; /* reliability */ - u32 time; + __be32 type; /* code */ + __be32 par1; + __be32 par2; + __be16 rel; /* reliability */ + __be32 time; }__attribute__ ((packed)); #define CISCO_PACKET_LEN 18 #define CISCO_BIG_PACKET_LEN 20 @@ -97,7 +97,7 @@ static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev, static void cisco_keepalive_send(struct net_device *dev, u32 type, - u32 par1, u32 par2) + __be32 par1, __be32 par2) { struct sk_buff *skb; struct cisco_packet *data; @@ -115,16 +115,16 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type, data = (struct cisco_packet*)(skb->data + 4); data->type = htonl(type); - data->par1 = htonl(par1); - data->par2 = htonl(par2); - data->rel = 0xFFFF; + data->par1 = par1; + data->par2 = par2; + data->rel = __constant_htons(0xFFFF); /* we will need do_div here if 1000 % HZ != 0 */ data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ)); skb_put(skb, sizeof(struct cisco_packet)); skb->priority = TC_PRIO_CONTROL; skb->dev = dev; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev_queue_xmit(skb); } @@ -193,7 +193,7 @@ static int cisco_rx(struct sk_buff *skb) case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */ in_dev = dev->ip_ptr; addr = 0; - mask = ~0; /* is the mask correct? */ + mask = __constant_htonl(~0); /* is the mask correct? */ if (in_dev != NULL) { struct in_ifaddr **ifap = &in_dev->ifa_list; @@ -245,7 +245,7 @@ static int cisco_rx(struct sk_buff *skb) } /* switch(protocol) */ printk(KERN_INFO "%s: Unsupported protocol %x\n", dev->name, - data->protocol); + ntohs(data->protocol)); dev_kfree_skb_any(skb); return NET_RX_DROP; @@ -270,8 +270,9 @@ static void cisco_timer(unsigned long arg) netif_dormant_on(dev); } - cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq, - state(hdlc)->rxseq); + cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, + htonl(++state(hdlc)->txseq), + htonl(state(hdlc)->rxseq)); state(hdlc)->request_sent = 1; state(hdlc)->timer.expires = jiffies + state(hdlc)->settings.interval * HZ; @@ -365,10 +366,7 @@ static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr) memcpy(&state(hdlc)->settings, &new_settings, size); dev->hard_start_xmit = hdlc->xmit; dev->hard_header = cisco_hard_header; - dev->hard_header_cache = NULL; dev->type = ARPHRD_CISCO; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->addr_len = 0; netif_dormant_on(dev); return 0; } diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c index b45ab68..15b6e07 100644 --- a/drivers/net/wan/hdlc_fr.c +++ b/drivers/net/wan/hdlc_fr.c @@ -288,31 +288,31 @@ static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) struct sk_buff *skb = *skb_p; switch (skb->protocol) { - case __constant_ntohs(NLPID_CCITT_ANSI_LMI): + case __constant_htons(NLPID_CCITT_ANSI_LMI): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_CCITT_ANSI_LMI; break; - case __constant_ntohs(NLPID_CISCO_LMI): + case __constant_htons(NLPID_CISCO_LMI): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_CISCO_LMI; break; - case __constant_ntohs(ETH_P_IP): + case __constant_htons(ETH_P_IP): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IP; break; - case __constant_ntohs(ETH_P_IPV6): + case __constant_htons(ETH_P_IPV6): head_len = 4; skb_push(skb, head_len); skb->data[3] = NLPID_IPV6; break; - case __constant_ntohs(ETH_P_802_3): + case __constant_htons(ETH_P_802_3): head_len = 10; if (skb_headroom(skb) < head_len) { struct sk_buff *skb2 = skb_realloc_headroom(skb, @@ -340,7 +340,7 @@ static int fr_hard_header(struct sk_buff **skb_p, u16 dlci) skb->data[5] = FR_PAD; skb->data[6] = FR_PAD; skb->data[7] = FR_PAD; - *(u16*)(skb->data + 8) = skb->protocol; + *(__be16*)(skb->data + 8) = skb->protocol; } dlci_to_q922(skb->data, dlci); @@ -533,7 +533,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep) skb->protocol = __constant_htons(NLPID_CCITT_ANSI_LMI); fr_hard_header(&skb, LMI_CCITT_ANSI_DLCI); } - data = skb->tail; + data = skb_tail_pointer(skb); data[i++] = LMI_CALLREF; data[i++] = dce ? LMI_STATUS : LMI_STATUS_ENQUIRY; if (lmi == LMI_ANSI) @@ -590,7 +590,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep) skb_put(skb, i); skb->priority = TC_PRIO_CONTROL; skb->dev = dev; - skb->nh.raw = skb->data; + skb_reset_network_header(skb); dev_queue_xmit(skb); } @@ -974,8 +974,8 @@ static int fr_rx(struct sk_buff *skb) } else if (skb->len > 10 && data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) { - u16 oui = ntohs(*(u16*)(data + 6)); - u16 pid = ntohs(*(u16*)(data + 8)); + u16 oui = ntohs(*(__be16*)(data + 6)); + u16 pid = ntohs(*(__be16*)(data + 8)); skb_pull(skb, 10); switch ((((u32)oui) << 16) | pid) { @@ -1011,7 +1011,6 @@ static int fr_rx(struct sk_buff *skb) stats->rx_bytes += skb->len; if (pvc->state.becn) stats->rx_compressed++; - skb->dev = dev; netif_rx(skb); return NET_RX_SUCCESS; } else { @@ -1128,7 +1127,7 @@ static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type) memcpy(dev->dev_addr, "\x00\x01", 2); get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2); } else { - *(u16*)dev->dev_addr = htons(dlci); + *(__be16*)dev->dev_addr = htons(dlci); dlci_to_q922(dev->broadcast, dlci); } dev->hard_start_xmit = pvc_xmit; @@ -1289,10 +1288,7 @@ static int fr_ioctl(struct net_device *dev, struct ifreq *ifr) memcpy(&state(hdlc)->settings, &new_settings, size); dev->hard_start_xmit = hdlc->xmit; - dev->hard_header = NULL; dev->type = ARPHRD_FRAD; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->addr_len = 0; return 0; case IF_PROTO_FR_ADD_PVC: diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c index e9f7170..4591437 100644 --- a/drivers/net/wan/hdlc_ppp.c +++ b/drivers/net/wan/hdlc_ppp.c @@ -127,9 +127,7 @@ static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr) if (result) return result; dev->hard_start_xmit = hdlc->xmit; - dev->hard_header = NULL; dev->type = ARPHRD_PPP; - dev->addr_len = 0; netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c index fe3cae5..e23bc66 100644 --- a/drivers/net/wan/hdlc_raw.c +++ b/drivers/net/wan/hdlc_raw.c @@ -88,10 +88,7 @@ static int raw_ioctl(struct net_device *dev, struct ifreq *ifr) return result; memcpy(hdlc->state, &new_settings, size); dev->hard_start_xmit = hdlc->xmit; - dev->hard_header = NULL; dev->type = ARPHRD_RAWHDLC; - dev->flags = IFF_POINTOPOINT | IFF_NOARP; - dev->addr_len = 0; netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c index e4bb9f8..cd7b22f 100644 --- a/drivers/net/wan/hdlc_x25.c +++ b/drivers/net/wan/hdlc_x25.c @@ -215,9 +215,7 @@ static int x25_ioctl(struct net_device *dev, struct ifreq *ifr) x25_rx, 0)) != 0) return result; dev->hard_start_xmit = x25_xmit; - dev->hard_header = NULL; dev->type = ARPHRD_X25; - dev->addr_len = 0; netif_dormant_off(dev); return 0; } diff --git a/drivers/net/wan/hostess_sv11.c b/drivers/net/wan/hostess_sv11.c index a02c5fb..9ba3e4e 100644 --- a/drivers/net/wan/hostess_sv11.c +++ b/drivers/net/wan/hostess_sv11.c @@ -59,7 +59,7 @@ static void hostess_input(struct z8530_channel *c, struct sk_buff *skb) /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ skb_trim(skb, skb->len-2); skb->protocol=__constant_htons(ETH_P_WAN_PPP); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->dev=c->netdevice; /* * Send it to the PPP layer. We don't have time to process diff --git a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c index 2b54f1b..ae132c1 100644 --- a/drivers/net/wan/lmc/lmc_main.c +++ b/drivers/net/wan/lmc/lmc_main.c @@ -1636,7 +1636,7 @@ static int lmc_rx (struct net_device *dev) /*fold00*/ if (nsb) { sc->lmc_rxq[i] = nsb; nsb->dev = dev; - sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail); + sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb)); } sc->failed_recv_alloc = 1; goto skip_packet; @@ -1667,8 +1667,8 @@ static int lmc_rx (struct net_device *dev) /*fold00*/ skb_put (skb, len); skb->protocol = lmc_proto_type(sc, skb); skb->protocol = htons(ETH_P_WAN_PPP); - skb->mac.raw = skb->data; -// skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + /* skb_reset_network_header(skb); */ skb->dev = dev; lmc_proto_netif(sc, skb); @@ -1679,7 +1679,7 @@ static int lmc_rx (struct net_device *dev) /*fold00*/ if (nsb) { sc->lmc_rxq[i] = nsb; nsb->dev = dev; - sc->lmc_rxring[i].buffer1 = virt_to_bus (nsb->tail); + sc->lmc_rxring[i].buffer1 = virt_to_bus(skb_tail_pointer(nsb)); /* Transferred to 21140 below */ } else { @@ -1702,11 +1702,11 @@ static int lmc_rx (struct net_device *dev) /*fold00*/ if(!nsb) { goto give_it_anyways; } - memcpy(skb_put(nsb, len), skb->data, len); + skb_copy_from_linear_data(skb, skb_put(nsb, len), len); nsb->protocol = lmc_proto_type(sc, skb); - nsb->mac.raw = nsb->data; -// nsb->nh.raw = nsb->data; + skb_reset_mac_header(nsb); + /* skb_reset_network_header(nsb); */ nsb->dev = dev; lmc_proto_netif(sc, nsb); } @@ -1932,7 +1932,7 @@ static void lmc_softreset (lmc_softc_t * const sc) /*fold00*/ sc->lmc_rxring[i].status = 0x80000000; /* used to be PKT_BUF_SZ now uses skb since we lose some to head room */ - sc->lmc_rxring[i].length = skb->end - skb->data; + sc->lmc_rxring[i].length = skb_tailroom(skb); /* use to be tail which is dumb since you're thinking why write * to the end of the packj,et but since there's nothing there tail == data diff --git a/drivers/net/wan/lmc/lmc_media.h b/drivers/net/wan/lmc/lmc_media.h deleted file mode 100644 index ddcc004..0000000 --- a/drivers/net/wan/lmc/lmc_media.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef _LMC_MEDIA_H_ -#define _LMC_MEDIA_H_ - -lmc_media_t lmc_ds3_media = { - lmc_ds3_init, /* special media init stuff */ - lmc_ds3_default, /* reset to default state */ - lmc_ds3_set_status, /* reset status to state provided */ - lmc_dummy_set_1, /* set clock source */ - lmc_dummy_set2_1, /* set line speed */ - lmc_ds3_set_100ft, /* set cable length */ - lmc_ds3_set_scram, /* set scrambler */ - lmc_ds3_get_link_status, /* get link status */ - lmc_dummy_set_1, /* set link status */ - lmc_ds3_set_crc_length, /* set CRC length */ - lmc_dummy_set_1, /* set T1 or E1 circuit type */ - lmc_ds3_watchdog -}; - -lmc_media_t lmc_hssi_media = { - lmc_hssi_init, /* special media init stuff */ - lmc_hssi_default, /* reset to default state */ - lmc_hssi_set_status, /* reset status to state provided */ - lmc_hssi_set_clock, /* set clock source */ - lmc_dummy_set2_1, /* set line speed */ - lmc_dummy_set_1, /* set cable length */ - lmc_dummy_set_1, /* set scrambler */ - lmc_hssi_get_link_status, /* get link status */ - lmc_hssi_set_link_status, /* set link status */ - lmc_hssi_set_crc_length, /* set CRC length */ - lmc_dummy_set_1, /* set T1 or E1 circuit type */ - lmc_hssi_watchdog -}; - -lmc_media_t lmc_ssi_media = { lmc_ssi_init, /* special media init stuff */ - lmc_ssi_default, /* reset to default state */ - lmc_ssi_set_status, /* reset status to state provided */ - lmc_ssi_set_clock, /* set clock source */ - lmc_ssi_set_speed, /* set line speed */ - lmc_dummy_set_1, /* set cable length */ - lmc_dummy_set_1, /* set scrambler */ - lmc_ssi_get_link_status, /* get link status */ - lmc_ssi_set_link_status, /* set link status */ - lmc_ssi_set_crc_length, /* set CRC length */ - lmc_dummy_set_1, /* set T1 or E1 circuit type */ - lmc_ssi_watchdog -}; - -lmc_media_t lmc_t1_media = { - lmc_t1_init, /* special media init stuff */ - lmc_t1_default, /* reset to default state */ - lmc_t1_set_status, /* reset status to state provided */ - lmc_t1_set_clock, /* set clock source */ - lmc_dummy_set2_1, /* set line speed */ - lmc_dummy_set_1, /* set cable length */ - lmc_dummy_set_1, /* set scrambler */ - lmc_t1_get_link_status, /* get link status */ - lmc_dummy_set_1, /* set link status */ - lmc_t1_set_crc_length, /* set CRC length */ - lmc_t1_set_circuit_type, /* set T1 or E1 circuit type */ - lmc_t1_watchdog -}; - - -#endif - diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c index 62184de..999bf71 100644 --- a/drivers/net/wan/pc300_drv.c +++ b/drivers/net/wan/pc300_drv.c @@ -1755,17 +1755,17 @@ cpc_trace(struct net_device *dev, struct sk_buff *skb_main, char rx_tx) skb->dev = dev; skb->protocol = htons(ETH_P_CUST); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; skb->len = 10 + skb_main->len; - memcpy(skb->data, dev->name, 5); + skb_copy_to_linear_data(skb, dev->name, 5); skb->data[5] = '['; skb->data[6] = rx_tx; skb->data[7] = ']'; skb->data[8] = ':'; skb->data[9] = ' '; - memcpy(&skb->data[10], skb_main->data, skb_main->len); + skb_copy_from_linear_data(skb_main, &skb->data[10], skb_main->len); netif_rx(skb); } diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c index 5873c34..07dbdfb 100644 --- a/drivers/net/wan/pc300_tty.c +++ b/drivers/net/wan/pc300_tty.c @@ -1003,17 +1003,17 @@ static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx) skb_put (skb, 10 + len); skb->dev = dev->dev; skb->protocol = htons(ETH_P_CUST); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_HOST; skb->len = 10 + len; - memcpy(skb->data,dev->dev->name,5); + skb_copy_to_linear_data(skb, dev->dev->name, 5); skb->data[5] = '['; skb->data[6] = rxtx; skb->data[7] = ']'; skb->data[8] = ':'; skb->data[9] = ' '; - memcpy(&skb->data[10], buf, len); + skb_copy_to_linear_data_offset(skb, 10, buf, len); netif_rx(skb); } diff --git a/drivers/net/wan/pc300too.c b/drivers/net/wan/pc300too.c index bc156b5..aff05db 100644 --- a/drivers/net/wan/pc300too.c +++ b/drivers/net/wan/pc300too.c @@ -542,7 +542,7 @@ static int __init pc300_init_module(void) CLOCK_BASE = use_crystal_clock ? 24576000 : pci_clock_freq; - return pci_module_init(&pc300_pci_driver); + return pci_register_driver(&pc300_pci_driver); } diff --git a/drivers/net/wan/sbni.c b/drivers/net/wan/sbni.c index fc5c0c6..35eded7 100644 --- a/drivers/net/wan/sbni.c +++ b/drivers/net/wan/sbni.c @@ -999,11 +999,6 @@ get_rx_buf( struct net_device *dev ) if( !skb ) return NULL; -#ifdef CONFIG_SBNI_MULTILINE - skb->dev = ((struct net_local *) dev->priv)->master; -#else - skb->dev = dev; -#endif skb_reserve( skb, 2 ); /* Align IP on longword boundaries */ return skb; } diff --git a/drivers/net/wan/sealevel.c b/drivers/net/wan/sealevel.c index 70fb1b9..1313581 100644 --- a/drivers/net/wan/sealevel.c +++ b/drivers/net/wan/sealevel.c @@ -61,7 +61,7 @@ static void sealevel_input(struct z8530_channel *c, struct sk_buff *skb) /* Drop the CRC - it's not a good idea to try and negotiate it ;) */ skb_trim(skb, skb->len-2); skb->protocol=htons(ETH_P_WAN_PPP); - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); skb->dev=c->netdevice; /* * Send it to the PPP layer. We don't have time to process diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c index 218f7b5..67fc67c 100644 --- a/drivers/net/wan/syncppp.c +++ b/drivers/net/wan/syncppp.c @@ -227,7 +227,7 @@ static void sppp_input (struct net_device *dev, struct sk_buff *skb) unsigned long flags; skb->dev=dev; - skb->mac.raw=skb->data; + skb_reset_mac_header(skb); if (dev->flags & IFF_RUNNING) { diff --git a/drivers/net/wan/z85230.c b/drivers/net/wan/z85230.c index 8dbcf83..98ef400 100644 --- a/drivers/net/wan/z85230.c +++ b/drivers/net/wan/z85230.c @@ -407,7 +407,7 @@ static void z8530_tx(struct z8530_channel *c) while(c->txcount) { /* FIFO full ? */ if(!(read_zsreg(c, R0)&4)) - break; + return; c->txcount--; /* * Shovel out the byte @@ -1656,7 +1656,7 @@ static void z8530_rx_done(struct z8530_channel *c) else { skb_put(skb, ct); - memcpy(skb->data, rxb, ct); + skb_copy_to_linear_data(skb, rxb, ct); c->stats.rx_packets++; c->stats.rx_bytes+=ct; } @@ -1782,7 +1782,7 @@ int z8530_queue_xmit(struct z8530_channel *c, struct sk_buff *skb) */ c->tx_next_ptr=c->tx_dma_buf[c->tx_dma_used]; c->tx_dma_used^=1; /* Flip temp buffer */ - memcpy(c->tx_next_ptr, skb->data, skb->len); + skb_copy_from_linear_data(skb, c->tx_next_ptr, skb->len); } else c->tx_next_ptr=skb->data; diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig index ece3d9c..c4b3dc2 100644 --- a/drivers/net/wireless/Kconfig +++ b/drivers/net/wireless/Kconfig @@ -2,47 +2,21 @@ # Wireless LAN device configuration # -menu "Wireless LAN (non-hamradio)" - depends on NETDEVICES - -config NET_RADIO - bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" - select WIRELESS_EXT - ---help--- - Support for wireless LANs and everything having to do with radio, - but not with amateur radio or FM broadcasting. - - Saying Y here also enables the Wireless Extensions (creates - /proc/net/wireless and enables iwconfig access). The Wireless - Extension is a generic API allowing a driver to expose to the user - space configuration and statistics specific to common Wireless LANs. - The beauty of it is that a single set of tool can support all the - variations of Wireless LANs, regardless of their type (as long as - the driver supports Wireless Extension). Another advantage is that - these parameters may be changed on the fly without restarting the - driver (or Linux). If you wish to use Wireless Extensions with - wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch - the tools from - <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. +menu "Wireless LAN" -config NET_WIRELESS_RTNETLINK - bool "Wireless Extension API over RtNetlink" - depends on NET_RADIO +config WLAN_PRE80211 + bool "Wireless LAN (pre-802.11)" + depends on NETDEVICES ---help--- - Support the Wireless Extension API over the RtNetlink socket - in addition to the traditional ioctl interface (selected above). - - For now, few tools use this facility, but it might grow in the - future. The only downside is that it adds 4.5 kB to your kernel. + Say Y if you have any pre-802.11 wireless LAN hardware. -# Note : the cards are obsolete (can't buy them anymore), but the drivers -# are not, as people are still using them... -comment "Obsolete Wireless cards support (pre-802.11)" - depends on NET_RADIO && (INET || ISA || PCMCIA) + This option does not affect the kernel build, it only + lets you choose drivers. config STRIP tristate "STRIP (Metricom starmode radio IP)" - depends on NET_RADIO && INET + depends on INET && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Say Y if you have a Metricom radio and intend to use Starmode Radio IP. STRIP is a radio protocol developed for the MosquitoNet project @@ -65,7 +39,8 @@ config STRIP config ARLAN tristate "Aironet Arlan 655 & IC2200 DS support" - depends on NET_RADIO && ISA && !64BIT + depends on ISA && !64BIT && WLAN_PRE80211 + select WIRELESS_EXT ---help--- Aironet makes Arlan, a class of wireless LAN adapters. These use the www.Telxon.com chip, which is also used on several similar cards. @@ -80,7 +55,8 @@ config ARLAN config WAVELAN tristate "AT&T/Lucent old WaveLAN & DEC RoamAbout DS ISA support" - depends on NET_RADIO && ISA + depends on ISA && WLAN_PRE80211 + select WIRELESS_EXT ---help--- The Lucent WaveLAN (formerly NCR and AT&T; or DEC RoamAbout DS) is a Radio LAN (wireless Ethernet-like Local Area Network) using the @@ -107,7 +83,8 @@ config WAVELAN config PCMCIA_WAVELAN tristate "AT&T/Lucent old WaveLAN Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach an AT&T/Lucent Wavelan PCMCIA (PC-card) wireless Ethernet networking card to your computer. This @@ -118,7 +95,8 @@ config PCMCIA_WAVELAN config PCMCIA_NETWAVE tristate "Xircom Netwave AirSurfer Pcmcia wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_PRE80211 + select WIRELESS_EXT help Say Y here if you intend to attach this type of PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -126,12 +104,20 @@ config PCMCIA_NETWAVE To compile this driver as a module, choose M here: the module will be called netwave_cs. If unsure, say N. -comment "Wireless 802.11 Frequency Hopping cards support" - depends on NET_RADIO && PCMCIA + +config WLAN_80211 + bool "Wireless LAN (IEEE 802.11)" + depends on NETDEVICES + ---help--- + Say Y if you have any 802.11 wireless LAN hardware. + + This option does not affect the kernel build, it only + lets you choose drivers. config PCMCIA_RAYCS tristate "Aviator/Raytheon 2.4MHz wireless support" - depends on NET_RADIO && PCMCIA + depends on PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- Say Y here if you intend to attach an Aviator/Raytheon PCMCIA (PC-card) wireless Ethernet networking card to your computer. @@ -141,12 +127,10 @@ config PCMCIA_RAYCS To compile this driver as a module, choose M here: the module will be called ray_cs. If unsure, say N. -comment "Wireless 802.11b ISA/PCI cards support" - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - config IPW2100 tristate "Intel PRO/Wireless 2100 Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -200,7 +184,8 @@ config IPW2100_DEBUG config IPW2200 tristate "Intel PRO/Wireless 2200BG and 2915ABG Network Connection" - depends on NET_RADIO && PCI + depends on PCI && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select IEEE80211 ---help--- @@ -280,9 +265,23 @@ config IPW2200_DEBUG If you are not sure, say N here. +config LIBERTAS_USB + tristate "Marvell Libertas 8388 802.11a/b/g cards" + depends on NET_RADIO && USB + select FW_LOADER + ---help--- + A driver for Marvell Libertas 8388 USB devices. + +config LIBERTAS_USB_DEBUG + bool "Enable full debugging output in the Libertas USB module." + depends on LIBERTAS_USB + ---help--- + Debugging support. + config AIRO tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards" - depends on NET_RADIO && ISA_DMA_API && (PCI || BROKEN) + depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN) + select WIRELESS_EXT select CRYPTO ---help--- This is the standard Linux driver to support Cisco/Aironet ISA and @@ -299,7 +298,8 @@ config AIRO config HERMES tristate "Hermes chipset 802.11b support (Orinoco/Prism2/Symbol)" - depends on NET_RADIO && (PPC_PMAC || PCI || PCMCIA) + depends on (PPC_PMAC || PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for 802.11b wireless cards based on the "Hermes" or Intersil HFA384x (Prism 2) MAC controller. This includes the vast @@ -373,7 +373,8 @@ config PCI_HERMES config ATMEL tristate "Atmel at76c50x chipset 802.11b support" - depends on NET_RADIO && (PCI || PCMCIA) + depends on (PCI || PCMCIA) && WLAN_80211 + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -394,13 +395,9 @@ config PCI_ATMEL Enable support for PCI and mini-PCI cards containing the Atmel at76c506 chip. -# If Pcmcia is compiled in, offer Pcmcia cards... -comment "Wireless 802.11b Pcmcia/Cardbus cards support" - depends on NET_RADIO && PCMCIA - config PCMCIA_HERMES tristate "Hermes PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES ---help--- A driver for "Hermes" chipset based PCMCIA wireless adaptors, such as the Lucent WavelanIEEE/Orinoco cards and their OEM (Cabletron/ @@ -420,7 +417,7 @@ config PCMCIA_HERMES config PCMCIA_SPECTRUM tristate "Symbol Spectrum24 Trilogy PCMCIA card support" - depends on NET_RADIO && PCMCIA && HERMES + depends on PCMCIA && HERMES select FW_LOADER ---help--- @@ -434,7 +431,8 @@ config PCMCIA_SPECTRUM config AIRO_CS tristate "Cisco/Aironet 34X/35X/4500/4800 PCMCIA cards" - depends on NET_RADIO && PCMCIA && (BROKEN || !M32R) + depends on PCMCIA && (BROKEN || !M32R) && WLAN_80211 + select WIRELESS_EXT select CRYPTO select CRYPTO_AES ---help--- @@ -458,7 +456,8 @@ config AIRO_CS config PCMCIA_ATMEL tristate "Atmel at76c502/at76c504 PCMCIA cards" - depends on NET_RADIO && ATMEL && PCMCIA + depends on ATMEL && PCMCIA + select WIRELESS_EXT select FW_LOADER select CRC32 ---help--- @@ -467,17 +466,17 @@ config PCMCIA_ATMEL config PCMCIA_WL3501 tristate "Planet WL3501 PCMCIA cards" - depends on NET_RADIO && EXPERIMENTAL && PCMCIA + depends on EXPERIMENTAL && PCMCIA && WLAN_80211 + select WIRELESS_EXT ---help--- A driver for WL3501 PCMCIA 802.11 wireless cards made by Planet. It has basic support for Linux wireless extensions and initial micro support for ethtool. -comment "Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support" - depends on NET_RADIO && PCI config PRISM54 tristate 'Intersil Prism GT/Duette/Indigo PCI/Cardbus' - depends on PCI && NET_RADIO && EXPERIMENTAL + depends on PCI && EXPERIMENTAL && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Enable PCI and Cardbus support for the following chipset based cards: @@ -523,7 +522,8 @@ config PRISM54 config USB_ZD1201 tristate "USB ZD1201 based Wireless device support" - depends on USB && NET_RADIO + depends on USB && WLAN_80211 + select WIRELESS_EXT select FW_LOADER ---help--- Say Y if you want to use wireless LAN adapters based on the ZyDAS @@ -542,11 +542,4 @@ source "drivers/net/wireless/hostap/Kconfig" source "drivers/net/wireless/bcm43xx/Kconfig" source "drivers/net/wireless/zd1211rw/Kconfig" -# yes, this works even when no drivers are selected -config NET_WIRELESS - bool - depends on NET_RADIO && (ISA || PCI || PPC_PMAC || PCMCIA) - default y - endmenu - diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile index c613af1..d212460 100644 --- a/drivers/net/wireless/Makefile +++ b/drivers/net/wireless/Makefile @@ -43,3 +43,4 @@ obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o obj-$(CONFIG_PCMCIA_WL3501) += wl3501_cs.o obj-$(CONFIG_USB_ZD1201) += zd1201.o +obj-$(CONFIG_LIBERTAS_USB) += libertas/ diff --git a/drivers/net/wireless/README b/drivers/net/wireless/README deleted file mode 100644 index 0c274bf..0000000 --- a/drivers/net/wireless/README +++ /dev/null @@ -1,25 +0,0 @@ - README - ------ - - This directory is mostly for Wireless LAN drivers, in their -various incarnations (ISA, PCI, Pcmcia...). - This separate directory is needed because a lot of driver work -on different bus (typically PCI + Pcmcia) and share 95% of the -code. This allow the code and the config options to be in one single -place instead of scattered all over the driver tree, which is never -100% satisfactory. - - Note : if you want more info on the topic of Wireless LANs, -you are kindly invited to have a look at the Wireless Howto : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/ - Some Wireless LAN drivers, like orinoco_cs, require the use of -Wireless Tools to be configured : - http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html - - Special notes for distribution maintainers : - 1) wvlan_cs will be discontinued soon in favor of orinoco_cs - 2) Please add Wireless Tools support in your scripts - - Have fun... - - Jean diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c index b08055a..f21bbaf 100644 --- a/drivers/net/wireless/airo.c +++ b/drivers/net/wireless/airo.c @@ -1145,6 +1145,7 @@ static void airo_networks_free(struct airo_info *ai); struct airo_info { struct net_device_stats stats; struct net_device *dev; + struct list_head dev_list; /* Note, we can have MAX_FIDS outstanding. FIDs are 16-bits, so we use the high bit to mark whether it is in use. */ #define MAX_FIDS 6 @@ -1623,7 +1624,7 @@ static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, crypto_cipher_setkey(tfm, pkey, 16); counter = 0; - for (i = 0; i < (sizeof(context->coeff)/sizeof(context->coeff[0])); ) { + for (i = 0; i < ARRAY_SIZE(context->coeff); ) { aes_counter[15] = (u8)(counter >> 0); aes_counter[14] = (u8)(counter >> 8); aes_counter[13] = (u8)(counter >> 16); @@ -1632,7 +1633,7 @@ static void emmh32_setseed(emmh32_context *context, u8 *pkey, int keylen, memcpy (plain, aes_counter, 16); crypto_cipher_encrypt_one(tfm, plain, plain); cipher = plain; - for (j=0; (j<16) && (i< (sizeof(context->coeff)/sizeof(context->coeff[0]))); ) { + for (j = 0; (j < 16) && (i < ARRAY_SIZE(context->coeff)); ) { context->coeff[i++] = ntohl(*(u32 *)&cipher[j]); j += 4; } @@ -2360,6 +2361,21 @@ static int airo_change_mtu(struct net_device *dev, int new_mtu) return 0; } +static LIST_HEAD(airo_devices); + +static void add_airo_dev(struct airo_info *ai) +{ + /* Upper layers already keep track of PCI devices, + * so we only need to remember our non-PCI cards. */ + if (!ai->pci) + list_add_tail(&ai->dev_list, &airo_devices); +} + +static void del_airo_dev(struct airo_info *ai) +{ + if (!ai->pci) + list_del(&ai->dev_list); +} static int airo_close(struct net_device *dev) { struct airo_info *ai = dev->priv; @@ -2381,8 +2397,6 @@ static int airo_close(struct net_device *dev) { return 0; } -static void del_airo_dev( struct net_device *dev ); - void stop_airo_card( struct net_device *dev, int freeres ) { struct airo_info *ai = dev->priv; @@ -2434,17 +2448,15 @@ void stop_airo_card( struct net_device *dev, int freeres ) } } crypto_free_cipher(ai->tfm); - del_airo_dev( dev ); + del_airo_dev(ai); free_netdev( dev ); } EXPORT_SYMBOL(stop_airo_card); -static int add_airo_dev( struct net_device *dev ); - static int wll_header_parse(struct sk_buff *skb, unsigned char *haddr) { - memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); return ETH_ALEN; } @@ -2740,8 +2752,6 @@ static int airo_networks_allocate(struct airo_info *ai) static void airo_networks_free(struct airo_info *ai) { - if (!ai->networks) - return; kfree(ai->networks); ai->networks = NULL; } @@ -2816,12 +2826,10 @@ static struct net_device *_init_airo_card( unsigned short irq, int port, if (IS_ERR(ai->airo_thread_task)) goto err_out_free; ai->tfm = NULL; - rc = add_airo_dev( dev ); - if (rc) - goto err_out_thr; + add_airo_dev(ai); if (airo_networks_allocate (ai)) - goto err_out_unlink; + goto err_out_thr; airo_networks_initialize (ai); /* The Airo-specific entries in the device structure. */ @@ -2852,7 +2860,7 @@ static struct net_device *_init_airo_card( unsigned short irq, int port, if (rc) { airo_print_err(dev->name, "register interrupt %d failed, rc %d", irq, rc); - goto err_out_unlink; + goto err_out_nets; } if (!is_pcmcia) { if (!request_region( dev->base_addr, 64, dev->name )) { @@ -2935,9 +2943,10 @@ err_out_res: release_region( dev->base_addr, 64 ); err_out_irq: free_irq(dev->irq, dev); -err_out_unlink: - del_airo_dev(dev); +err_out_nets: + airo_networks_free(ai); err_out_thr: + del_airo_dev(ai); set_bit(JOB_DIE, &ai->jobs); kthread_stop(ai->airo_thread_task); err_out_free: @@ -3409,14 +3418,12 @@ badrx: OUT4500( apriv, EVACK, EV_RX); if (test_bit(FLAG_802_11, &apriv->flags)) { - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->dev = apriv->wifidev; skb->protocol = htons(ETH_P_802_2); - } else { - skb->dev = dev; + } else skb->protocol = eth_type_trans(skb,dev); - } skb->dev->last_rx = jiffies; skb->ip_summed = CHECKSUM_NONE; @@ -3639,7 +3646,6 @@ badmic: } #endif /* WIRELESS_SPY */ - skb->dev = ai->dev; skb->ip_summed = CHECKSUM_NONE; skb->protocol = eth_type_trans(skb, ai->dev); skb->dev->last_rx = jiffies; @@ -3747,7 +3753,7 @@ void mpi_receive_802_11 (struct airo_info *ai) wireless_spy_update(ai->dev, sa, &wstats); } #endif /* IW_WIRELESS_SPY */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->dev = ai->wifidev; skb->protocol = htons(ETH_P_802_2); @@ -5536,11 +5542,6 @@ static int proc_close( struct inode *inode, struct file *file ) return 0; } -static struct net_device_list { - struct net_device *dev; - struct net_device_list *next; -} *airo_devices; - /* Since the card doesn't automatically switch to the right WEP mode, we will make it do it. If the card isn't associated, every secs we will switch WEP modes to see if that will help. If the card is @@ -5583,26 +5584,6 @@ static void timer_func( struct net_device *dev ) { apriv->expires = RUN_AT(HZ*3); } -static int add_airo_dev( struct net_device *dev ) { - struct net_device_list *node = kmalloc( sizeof( *node ), GFP_KERNEL ); - if ( !node ) - return -ENOMEM; - - node->dev = dev; - node->next = airo_devices; - airo_devices = node; - - return 0; -} - -static void del_airo_dev( struct net_device *dev ) { - struct net_device_list **p = &airo_devices; - while( *p && ( (*p)->dev != dev ) ) - p = &(*p)->next; - if ( *p && (*p)->dev == dev ) - *p = (*p)->next; -} - #ifdef CONFIG_PCI static int __devinit airo_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pent) @@ -5626,6 +5607,10 @@ static int __devinit airo_pci_probe(struct pci_dev *pdev, static void __devexit airo_pci_remove(struct pci_dev *pdev) { + struct net_device *dev = pci_get_drvdata(pdev); + + airo_print_info(dev->name, "Unregistering..."); + stop_airo_card(dev, 1); } static int airo_pci_suspend(struct pci_dev *pdev, pm_message_t state) @@ -5751,9 +5736,11 @@ static int __init airo_init_module( void ) static void __exit airo_cleanup_module( void ) { - while( airo_devices ) { - airo_print_info(airo_devices->dev->name, "Unregistering...\n"); - stop_airo_card( airo_devices->dev, 1 ); + struct airo_info *ai; + while(!list_empty(&airo_devices)) { + ai = list_entry(airo_devices.next, struct airo_info, dev_list); + airo_print_info(ai->dev->name, "Unregistering..."); + stop_airo_card(ai->dev, 1); } #ifdef CONFIG_PCI pci_unregister_driver(&airo_driver); diff --git a/drivers/net/wireless/arlan-main.c b/drivers/net/wireless/arlan-main.c index 4688e56..498e848 100644 --- a/drivers/net/wireless/arlan-main.c +++ b/drivers/net/wireless/arlan-main.c @@ -1500,7 +1500,6 @@ static void arlan_rx_interrupt(struct net_device *dev, u_char rxStatus, u_short break; } skb_reserve(skb, 2); - skb->dev = dev; skbtmp = skb_put(skb, pkt_len); memcpy_fromio(skbtmp + ARLAN_FAKE_HDR_LEN, ((char __iomem *) arlan) + rxOffset, pkt_len - ARLAN_FAKE_HDR_LEN); diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c index 23eba69..51a7db5 100644 --- a/drivers/net/wireless/atmel.c +++ b/drivers/net/wireless/atmel.c @@ -827,14 +827,14 @@ static int start_tx(struct sk_buff *skb, struct net_device *dev) if (priv->wep_is_on) frame_ctl |= IEEE80211_FCTL_PROTECTED; if (priv->operating_mode == IW_MODE_ADHOC) { - memcpy(&header.addr1, skb->data, 6); + skb_copy_from_linear_data(skb, &header.addr1, 6); memcpy(&header.addr2, dev->dev_addr, 6); memcpy(&header.addr3, priv->BSSID, 6); } else { frame_ctl |= IEEE80211_FCTL_TODS; memcpy(&header.addr1, priv->CurrentBSSID, 6); memcpy(&header.addr2, dev->dev_addr, 6); - memcpy(&header.addr3, skb->data, 6); + skb_copy_from_linear_data(skb, &header.addr3, 6); } if (priv->use_wpa) @@ -920,7 +920,6 @@ static void fast_rx_path(struct atmel_private *priv, memcpy(&skbp[6], header->addr2, 6); /* source address */ priv->dev->last_rx = jiffies; - skb->dev = priv->dev; skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); @@ -1028,7 +1027,6 @@ static void frag_rx_path(struct atmel_private *priv, priv->rx_buf, priv->frag_len + 12); priv->dev->last_rx = jiffies; - skb->dev = priv->dev; skb->protocol = eth_type_trans(skb, priv->dev); skb->ip_summed = CHECKSUM_NONE; netif_rx(skb); diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig index 533993f..ce397e4 100644 --- a/drivers/net/wireless/bcm43xx/Kconfig +++ b/drivers/net/wireless/bcm43xx/Kconfig @@ -1,6 +1,7 @@ config BCM43XX tristate "Broadcom BCM43xx wireless support" - depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER select HW_RANDOM ---help--- diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h index 3a064de..f8483c1 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx.h @@ -21,7 +21,7 @@ #define PFX KBUILD_MODNAME ": " #define BCM43xx_SWITCH_CORE_MAX_RETRIES 50 -#define BCM43xx_IRQWAIT_MAX_RETRIES 50 +#define BCM43xx_IRQWAIT_MAX_RETRIES 100 #define BCM43xx_IO_SIZE 8192 @@ -277,11 +277,14 @@ #define BCM43xx_SBTMSTATELOW_REJECT 0x02 #define BCM43xx_SBTMSTATELOW_CLOCK 0x10000 #define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000 +#define BCM43xx_SBTMSTATELOW_G_MODE_ENABLE 0x20000000 /* sbtmstatehigh state flags */ #define BCM43xx_SBTMSTATEHIGH_SERROR 0x00000001 #define BCM43xx_SBTMSTATEHIGH_BUSY 0x00000004 #define BCM43xx_SBTMSTATEHIGH_TIMEOUT 0x00000020 +#define BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL 0x00010000 +#define BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL 0x00020000 #define BCM43xx_SBTMSTATEHIGH_COREFLAGS 0x1FFF0000 #define BCM43xx_SBTMSTATEHIGH_DMA64BIT 0x10000000 #define BCM43xx_SBTMSTATEHIGH_GATEDCLK 0x20000000 @@ -333,7 +336,7 @@ #define BCM43xx_SBF_PS2 0x04000000 #define BCM43xx_SBF_NO_SSID_BCAST 0x08000000 #define BCM43xx_SBF_TIME_UPDATE 0x10000000 -#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/ +#define BCM43xx_SBF_MODE_G 0x80000000 /* Microcode */ #define BCM43xx_UCODE_REVISION 0x0000 @@ -507,8 +510,6 @@ struct bcm43xx_sprominfo { u8 et1macaddr[6]; u8 et0phyaddr:5; u8 et1phyaddr:5; - u8 et0mdcport:1; - u8 et1mdcport:1; u8 boardrev; u8 locale:4; u8 antennas_aphy:2; @@ -542,7 +543,7 @@ struct bcm43xx_lopair { struct bcm43xx_phyinfo { /* Hardware Data */ - u8 version; + u8 analog; u8 type; u8 rev; u16 antenna_diversity; @@ -771,6 +772,7 @@ struct bcm43xx_private { * This is currently always BCM43xx_BUSTYPE_PCI */ u8 bustype; + u64 dma_mask; u16 board_vendor; u16 board_type; diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c index 978ed09..e3d2e61 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c @@ -145,16 +145,14 @@ dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring, int tx) { dma_addr_t dmaaddr; + int direction = PCI_DMA_FROMDEVICE; - if (tx) { - dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, - buf, len, - DMA_TO_DEVICE); - } else { - dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev, + if (tx) + direction = PCI_DMA_TODEVICE; + + dmaaddr = pci_map_single(ring->bcm->pci_dev, buf, len, - DMA_FROM_DEVICE); - } + direction); return dmaaddr; } @@ -166,13 +164,13 @@ void unmap_descbuffer(struct bcm43xx_dmaring *ring, int tx) { if (tx) { - dma_unmap_single(&ring->bcm->pci_dev->dev, + pci_unmap_single(ring->bcm->pci_dev, addr, len, - DMA_TO_DEVICE); + PCI_DMA_TODEVICE); } else { - dma_unmap_single(&ring->bcm->pci_dev->dev, + pci_unmap_single(ring->bcm->pci_dev, addr, len, - DMA_FROM_DEVICE); + PCI_DMA_FROMDEVICE); } } @@ -183,8 +181,8 @@ void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring, { assert(!ring->tx); - dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev, - addr, len, DMA_FROM_DEVICE); + pci_dma_sync_single_for_cpu(ring->bcm->pci_dev, + addr, len, PCI_DMA_FROMDEVICE); } static inline @@ -194,8 +192,8 @@ void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring, { assert(!ring->tx); - dma_sync_single_for_device(&ring->bcm->pci_dev->dev, - addr, len, DMA_FROM_DEVICE); + pci_dma_sync_single_for_cpu(ring->bcm->pci_dev, + addr, len, PCI_DMA_TODEVICE); } /* Unmap and free a descriptor buffer. */ @@ -214,17 +212,53 @@ void free_descriptor_buffer(struct bcm43xx_dmaring *ring, static int alloc_ringmemory(struct bcm43xx_dmaring *ring) { - struct device *dev = &(ring->bcm->pci_dev->dev); - - ring->descbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE, - &(ring->dmabase), GFP_KERNEL); + ring->descbase = pci_alloc_consistent(ring->bcm->pci_dev, BCM43xx_DMA_RINGMEMSIZE, + &(ring->dmabase)); if (!ring->descbase) { - printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); - return -ENOMEM; + /* Allocation may have failed due to pci_alloc_consistent + insisting on use of GFP_DMA, which is more restrictive + than necessary... */ + struct dma_desc *rx_ring; + dma_addr_t rx_ring_dma; + + rx_ring = kzalloc(BCM43xx_DMA_RINGMEMSIZE, GFP_KERNEL); + if (!rx_ring) + goto out_err; + + rx_ring_dma = pci_map_single(ring->bcm->pci_dev, rx_ring, + BCM43xx_DMA_RINGMEMSIZE, + PCI_DMA_BIDIRECTIONAL); + + if (pci_dma_mapping_error(rx_ring_dma) || + rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) { + /* Sigh... */ + if (!pci_dma_mapping_error(rx_ring_dma)) + pci_unmap_single(ring->bcm->pci_dev, + rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE, + PCI_DMA_BIDIRECTIONAL); + rx_ring_dma = pci_map_single(ring->bcm->pci_dev, + rx_ring, BCM43xx_DMA_RINGMEMSIZE, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(rx_ring_dma) || + rx_ring_dma + BCM43xx_DMA_RINGMEMSIZE > ring->bcm->dma_mask) { + assert(0); + if (!pci_dma_mapping_error(rx_ring_dma)) + pci_unmap_single(ring->bcm->pci_dev, + rx_ring_dma, BCM43xx_DMA_RINGMEMSIZE, + PCI_DMA_BIDIRECTIONAL); + goto out_err; + } + } + + ring->descbase = rx_ring; + ring->dmabase = rx_ring_dma; } memset(ring->descbase, 0, BCM43xx_DMA_RINGMEMSIZE); return 0; +out_err: + printk(KERN_ERR PFX "DMA ringmemory allocation failed\n"); + return -ENOMEM; } static void free_ringmemory(struct bcm43xx_dmaring *ring) @@ -407,6 +441,29 @@ static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring, if (unlikely(!skb)) return -ENOMEM; dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0); + /* This hardware bug work-around adapted from the b44 driver. + The chip may be unable to do PCI DMA to/from anything above 1GB */ + if (pci_dma_mapping_error(dmaaddr) || + dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) { + /* This one has 30-bit addressing... */ + if (!pci_dma_mapping_error(dmaaddr)) + pci_unmap_single(ring->bcm->pci_dev, + dmaaddr, ring->rx_buffersize, + PCI_DMA_FROMDEVICE); + dev_kfree_skb_any(skb); + skb = __dev_alloc_skb(ring->rx_buffersize,GFP_DMA); + if (skb == NULL) + return -ENOMEM; + dmaaddr = pci_map_single(ring->bcm->pci_dev, + skb->data, ring->rx_buffersize, + PCI_DMA_FROMDEVICE); + if (pci_dma_mapping_error(dmaaddr) || + dmaaddr + ring->rx_buffersize > ring->bcm->dma_mask) { + assert(0); + dev_kfree_skb_any(skb); + return -ENOMEM; + } + } meta->skb = skb; meta->dmaaddr = dmaaddr; skb->dev = ring->bcm->net_dev; @@ -636,8 +693,10 @@ struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm, err = dmacontroller_setup(ring); if (err) goto err_free_ringmemory; + return ring; out: + printk(KERN_ERR PFX "Error in bcm43xx_setup_dmaring\n"); return ring; err_free_ringmemory: @@ -705,30 +764,16 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) struct bcm43xx_dmaring *ring; int err = -ENOMEM; int dma64 = 0; - u64 mask = bcm43xx_get_supported_dma_mask(bcm); - int nobits; - if (mask == DMA_64BIT_MASK) { + bcm->dma_mask = bcm43xx_get_supported_dma_mask(bcm); + if (bcm->dma_mask == DMA_64BIT_MASK) dma64 = 1; - nobits = 64; - } else if (mask == DMA_32BIT_MASK) - nobits = 32; - else - nobits = 30; - err = pci_set_dma_mask(bcm->pci_dev, mask); - err |= pci_set_consistent_dma_mask(bcm->pci_dev, mask); - if (err) { -#ifdef CONFIG_BCM43XX_PIO - printk(KERN_WARNING PFX "DMA not supported on this device." - " Falling back to PIO.\n"); - bcm->__using_pio = 1; - return -ENOSYS; -#else - printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " - "Please recompile the driver with PIO support.\n"); - return -ENODEV; -#endif /* CONFIG_BCM43XX_PIO */ - } + err = pci_set_dma_mask(bcm->pci_dev, bcm->dma_mask); + if (err) + goto no_dma; + err = pci_set_consistent_dma_mask(bcm->pci_dev, bcm->dma_mask); + if (err) + goto no_dma; /* setup TX DMA channels. */ ring = bcm43xx_setup_dmaring(bcm, 0, 1, dma64); @@ -774,7 +819,9 @@ int bcm43xx_dma_init(struct bcm43xx_private *bcm) dma->rx_ring3 = ring; } - dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", nobits); + dprintk(KERN_INFO PFX "%d-bit DMA initialized\n", + (bcm->dma_mask == DMA_64BIT_MASK) ? 64 : + (bcm->dma_mask == DMA_32BIT_MASK) ? 32 : 30); err = 0; out: return err; @@ -800,7 +847,17 @@ err_destroy_tx1: err_destroy_tx0: bcm43xx_destroy_dmaring(dma->tx_ring0); dma->tx_ring0 = NULL; - goto out; +no_dma: +#ifdef CONFIG_BCM43XX_PIO + printk(KERN_WARNING PFX "DMA not supported on this device." + " Falling back to PIO.\n"); + bcm->__using_pio = 1; + return -ENOSYS; +#else + printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. " + "Please recompile the driver with PIO support.\n"); + return -ENODEV; +#endif /* CONFIG_BCM43XX_PIO */ } /* Generate a cookie for the TX header. */ @@ -905,6 +962,7 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, struct bcm43xx_dmadesc_generic *desc; struct bcm43xx_dmadesc_meta *meta; dma_addr_t dmaaddr; + struct sk_buff *bounce_skb; assert(skb_shinfo(skb)->nr_frags == 0); @@ -924,9 +982,29 @@ static void dma_tx_fragment(struct bcm43xx_dmaring *ring, skb->len - sizeof(struct bcm43xx_txhdr), (cur_frag == 0), generate_cookie(ring, slot)); + dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); + if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) { + /* chip cannot handle DMA to/from > 1GB, use bounce buffer (copied from b44 driver) */ + if (!dma_mapping_error(dmaaddr)) + unmap_descbuffer(ring, dmaaddr, skb->len, 1); + bounce_skb = __dev_alloc_skb(skb->len, GFP_ATOMIC|GFP_DMA); + if (!bounce_skb) + return; + dmaaddr = map_descbuffer(ring, bounce_skb->data, bounce_skb->len, 1); + if (dma_mapping_error(dmaaddr) || dmaaddr + skb->len > ring->bcm->dma_mask) { + if (!dma_mapping_error(dmaaddr)) + unmap_descbuffer(ring, dmaaddr, skb->len, 1); + dev_kfree_skb_any(bounce_skb); + assert(0); + return; + } + skb_copy_from_linear_data(skb, skb_put(bounce_skb, skb->len), + skb->len); + dev_kfree_skb_any(skb); + skb = bounce_skb; + } meta->skb = skb; - dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1); meta->dmaaddr = dmaaddr; fill_descriptor(ring, desc, dmaaddr, diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c index c947025..d2df6a0 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c @@ -32,7 +32,7 @@ #include <linux/netdevice.h> #include <linux/pci.h> #include <linux/string.h> -#include <linux/utsrelease.h> +#include <linux/utsname.h> static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -40,7 +40,7 @@ static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo * struct bcm43xx_private *bcm = bcm43xx_priv(dev); strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); - strncpy(info->version, UTS_RELEASE, sizeof(info->version)); + strncpy(info->version, utsname()->release, sizeof(info->version)); strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN); } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c index ad8e569..f2b8dba 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c @@ -325,6 +325,21 @@ void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val) } } +void bcm43xx_ilt_write32(struct bcm43xx_private *bcm, u16 offset, u32 val) +{ + if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA2, (val & 0xFFFF0000) >> 16); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val & 0x0000FFFF); + } else { + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset); + mmiowb(); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA2, (val & 0xFFFF0000) >> 16); + bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val & 0x0000FFFF); + } +} + u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset) { if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) { diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h index 464521a..d7eaf5f 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h @@ -27,6 +27,7 @@ extern const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE]; void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val); +void bcm43xx_ilt_write32(struct bcm43xx_private *bcm, u16 offset, u32 val); u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset); #endif /* BCM43xx_ILT_H_ */ diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c index 23aaf1e..5e96bca 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c @@ -95,13 +95,9 @@ static int modparam_noleds; module_param_named(noleds, modparam_noleds, int, 0444); MODULE_PARM_DESC(noleds, "Turn off all LED activity"); -#ifdef CONFIG_BCM43XX_DEBUG static char modparam_fwpostfix[64]; module_param_string(fwpostfix, modparam_fwpostfix, 64, 0444); -MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging."); -#else -# define modparam_fwpostfix "" -#endif /* CONFIG_BCM43XX_DEBUG*/ +MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for using multiple firmware image versions."); /* If you want to debug with just a single device, enable this, @@ -855,8 +851,6 @@ static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm) value = sprom[BCM43xx_SPROM_ETHPHY]; bcm->sprom.et0phyaddr = (value & 0x001F); bcm->sprom.et1phyaddr = (value & 0x03E0) >> 5; - bcm->sprom.et0mdcport = (value & (1 << 14)) >> 14; - bcm->sprom.et1mdcport = (value & (1 << 15)) >> 15; /* boardrev, antennas, locale */ value = sprom[BCM43xx_SPROM_BOARDREV]; @@ -952,6 +946,7 @@ static int bcm43xx_geo_init(struct bcm43xx_private *bcm) u8 channel; struct bcm43xx_phyinfo *phy; const char *iso_country; + u8 max_bg_channel; geo = kzalloc(sizeof(*geo), GFP_KERNEL); if (!geo) @@ -973,6 +968,23 @@ static int bcm43xx_geo_init(struct bcm43xx_private *bcm) } iso_country = bcm43xx_locale_iso(bcm->sprom.locale); +/* set the maximum channel based on locale set in sprom or witle locale option */ + switch (bcm->sprom.locale) { + case BCM43xx_LOCALE_THAILAND: + case BCM43xx_LOCALE_ISRAEL: + case BCM43xx_LOCALE_JORDAN: + case BCM43xx_LOCALE_USA_CANADA_ANZ: + case BCM43xx_LOCALE_USA_LOW: + max_bg_channel = 11; + break; + case BCM43xx_LOCALE_JAPAN: + case BCM43xx_LOCALE_JAPAN_HIGH: + max_bg_channel = 14; + break; + default: + max_bg_channel = 13; + } + if (have_a) { for (i = 0, channel = IEEE80211_52GHZ_MIN_CHANNEL; channel <= IEEE80211_52GHZ_MAX_CHANNEL; channel++) { @@ -984,7 +996,7 @@ static int bcm43xx_geo_init(struct bcm43xx_private *bcm) } if (have_bg) { for (i = 0, channel = IEEE80211_24GHZ_MIN_CHANNEL; - channel <= IEEE80211_24GHZ_MAX_CHANNEL; channel++) { + channel <= max_bg_channel; channel++) { chan = &geo->bg[i++]; chan->freq = bcm43xx_channel_to_freq_bg(channel); chan->channel = channel; @@ -1395,7 +1407,7 @@ void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy) & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002)); } else { if (connect_phy) - flags |= 0x20000000; + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_phy_connect(bcm, connect_phy); bcm43xx_core_enable(bcm, flags); bcm43xx_write16(bcm, 0x03E6, 0x0000); @@ -1453,12 +1465,10 @@ static void handle_irq_transmit_status(struct bcm43xx_private *bcm) bcm43xx_debugfs_log_txstat(bcm, &stat); - if (stat.flags & BCM43xx_TXSTAT_FLAG_IGNORE) + if (stat.flags & BCM43xx_TXSTAT_FLAG_AMPDU) + continue; + if (stat.flags & BCM43xx_TXSTAT_FLAG_INTER) continue; - if (!(stat.flags & BCM43xx_TXSTAT_FLAG_ACK)) { - //TODO: packet was not acked (was lost) - } - //TODO: There are more (unknown) flags to test. see bcm43xx_main.h if (bcm43xx_using_pio(bcm)) bcm43xx_pio_handle_xmitstatus(bcm, &stat); @@ -1866,9 +1876,6 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id) spin_lock(&bcm->irq_lock); - assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); - assert(bcm->current_core->id == BCM43xx_COREID_80211); - reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); if (reason == 0xffffffff) { /* irq not for us (shared irq) */ @@ -1879,6 +1886,9 @@ static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id) if (!reason) goto out; + assert(bcm43xx_status(bcm) == BCM43xx_STAT_INITIALIZED); + assert(bcm->current_core->id == BCM43xx_COREID_80211); + bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA0_REASON) & 0x0001DC00; bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON) @@ -2741,8 +2751,9 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm) * dangling pins on the second core. Be careful * and ignore these cores here. */ - if (bcm->pci_dev->device != 0x4324) { - dprintk(KERN_INFO PFX "Ignoring additional 802.11 core.\n"); + if (1 /*bcm->pci_dev->device != 0x4324*/ ) { + /* TODO: A PHY */ + dprintk(KERN_INFO PFX "Ignoring additional 802.11a core.\n"); continue; } } @@ -2983,8 +2994,10 @@ static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm) err = bcm43xx_pctl_set_crystal(bcm, 1); if (err) goto out; - bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status); - bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT); + err = bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status); + if (err) + goto out; + err = bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT); out: return err; @@ -3591,7 +3604,7 @@ int bcm43xx_select_wireless_core(struct bcm43xx_private *bcm, u32 sbtmstatelow; sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - sbtmstatelow |= 0x20000000; + sbtmstatelow |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow); } err = wireless_core_up(bcm, 1); @@ -3698,7 +3711,7 @@ static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); u16 value; - u8 phy_version; + u8 phy_analog; u8 phy_type; u8 phy_rev; int phy_rev_ok = 1; @@ -3706,12 +3719,12 @@ static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm) value = bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); - phy_version = (value & 0xF000) >> 12; + phy_analog = (value & 0xF000) >> 12; phy_type = (value & 0x0F00) >> 8; phy_rev = (value & 0x000F); - dprintk(KERN_INFO PFX "Detected PHY: Version: %x, Type %x, Revision %x\n", - phy_version, phy_type, phy_rev); + dprintk(KERN_INFO PFX "Detected PHY: Analog: %x, Type %x, Revision %x\n", + phy_analog, phy_type, phy_rev); switch (phy_type) { case BCM43xx_PHYTYPE_A: @@ -3754,7 +3767,7 @@ static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm) phy_rev); } - phy->version = phy_version; + phy->analog = phy_analog; phy->type = phy_type; phy->rev = phy_rev; if ((phy_type == BCM43xx_PHYTYPE_B) || (phy_type == BCM43xx_PHYTYPE_G)) { @@ -3796,12 +3809,18 @@ static int bcm43xx_attach_board(struct bcm43xx_private *bcm) } net_dev->base_addr = (unsigned long)bcm->mmio_addr; - bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID, + err = bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID, &bcm->board_vendor); - bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID, + if (err) + goto err_iounmap; + err = bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID, &bcm->board_type); - bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID, + if (err) + goto err_iounmap; + err = bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID, &bcm->board_revision); + if (err) + goto err_iounmap; err = bcm43xx_chipset_attach(bcm); if (err) @@ -3892,6 +3911,7 @@ err_pci_release: pci_release_regions(pci_dev); err_pci_disable: pci_disable_device(pci_dev); + printk(KERN_ERR PFX "Unable to attach board\n"); goto out; } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c index 52ce2a9..b37f1e3 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c @@ -168,16 +168,16 @@ int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect) flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH); if (connect) { - if (!(flags & 0x00010000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_G_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags |= (0x800 << 18); + flags |= BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } else { - if (!(flags & 0x00020000)) + if (!(flags & BCM43xx_SBTMSTATEHIGH_A_PHY_AVAIL)) return -ENODEV; flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW); - flags &= ~(0x800 << 18); + flags &= ~BCM43xx_SBTMSTATELOW_G_MODE_ENABLE; bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags); } out: @@ -205,8 +205,8 @@ static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm) (bcm->board_type == 0x0416)) return; - bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF); bcm43xx_phy_write(bcm, 0x0028, 0x8018); + bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF); if (phy->type == BCM43xx_PHYTYPE_G) { if (!phy->connected) @@ -300,16 +300,20 @@ static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm) if (phy->rev > 2) { bcm43xx_phy_write(bcm, 0x0422, 0x287A); - bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000); + bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) + & 0x0FFF) | 0x3000); } - bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874); + bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) + | 0x7874); bcm43xx_phy_write(bcm, 0x048E, 0x1C00); if (phy->rev == 1) { - bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600); + bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) + & 0xF0FF) | 0x0600); bcm43xx_phy_write(bcm, 0x048B, 0x005E); - bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E); + bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) + & 0xFF00) | 0x001E); bcm43xx_phy_write(bcm, 0x048D, 0x0002); } @@ -317,6 +321,13 @@ static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm) bcm43xx_ilt_write(bcm, offset + 0x0801, 7); bcm43xx_ilt_write(bcm, offset + 0x0802, 16); bcm43xx_ilt_write(bcm, offset + 0x0803, 28); + + if (phy->rev >= 6) { + bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426) + & 0xFFFC)); + bcm43xx_phy_write(bcm, 0x0426, (bcm43xx_phy_read(bcm, 0x0426) + & 0xEFFF)); + } } static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) @@ -328,7 +339,8 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) if (phy->rev == 1) { bcm43xx_phy_write(bcm, 0x0406, 0x4F19); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, - (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340); + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) + & 0xFC3F) | 0x0340); bcm43xx_phy_write(bcm, 0x042C, 0x005A); bcm43xx_phy_write(bcm, 0x0427, 0x001A); @@ -337,7 +349,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++) bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]); for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) - bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); + bcm43xx_ilt_write32(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); } else { /* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */ bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654); @@ -377,7 +389,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm) if (phy->rev == 1) { for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) - bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); + bcm43xx_ilt_write32(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); for (i = 0; i < 4; i++) { bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020); bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020); @@ -500,10 +512,10 @@ static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm) for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++) bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]); for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++) - bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); + bcm43xx_ilt_write32(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]); bcm43xx_phy_init_noisescaletbl(bcm); for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++) - bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); + bcm43xx_ilt_write32(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]); break; case 3: for (i = 0; i < 64; i++) @@ -729,19 +741,19 @@ static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); u16 offset; + u16 value; + u8 old_channel; - if (phy->version == 1 && - radio->version == 0x2050) { + if (phy->analog == 1) bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0050); - } if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) && (bcm->board_type != 0x0416)) { + value = 0x2120; for (offset = 0x00A8 ; offset < 0x00C7; offset++) { - bcm43xx_phy_write(bcm, offset, - (bcm43xx_phy_read(bcm, offset) + 0x2020) - & 0x3F3F); + bcm43xx_phy_write(bcm, offset, value); + value += 0x0202; } } bcm43xx_phy_write(bcm, 0x0035, @@ -776,7 +788,7 @@ static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11)); } - if (phy->version == 1 && radio->version == 0x2050) { + if (phy->analog == 1) { bcm43xx_phy_write(bcm, 0x0026, 0xCE00); bcm43xx_phy_write(bcm, 0x0021, 0x3763); bcm43xx_phy_write(bcm, 0x0022, 0x1BC3); @@ -787,14 +799,15 @@ static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0030, 0x00C6); bcm43xx_write16(bcm, 0x03EC, 0x3F22); - if (phy->version == 1 && radio->version == 0x2050) + if (phy->analog == 1) bcm43xx_phy_write(bcm, 0x0020, 0x3E1C); else bcm43xx_phy_write(bcm, 0x0020, 0x301C); - if (phy->version == 0) + if (phy->analog == 0) bcm43xx_write16(bcm, 0x03E4, 0x3000); + old_channel = radio->channel; /* Force to channel 7, even if not supported. */ bcm43xx_radio_selectchannel(bcm, 7, 0); @@ -816,11 +829,11 @@ static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm) bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007); - bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + bcm43xx_radio_selectchannel(bcm, old_channel, 0); bcm43xx_phy_write(bcm, 0x0014, 0x0080); bcm43xx_phy_write(bcm, 0x0032, 0x00CA); - bcm43xx_phy_write(bcm, 0x88A3, 0x002A); + bcm43xx_phy_write(bcm, 0x002A, 0x88A3); bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); @@ -835,61 +848,29 @@ static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm) struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); u16 offset, val; + u8 old_channel; bcm43xx_phy_write(bcm, 0x003E, 0x817A); bcm43xx_radio_write16(bcm, 0x007A, (bcm43xx_radio_read16(bcm, 0x007A) | 0x0058)); - if ((radio->manufact == 0x17F) && - (radio->version == 0x2050) && - (radio->revision == 3 || - radio->revision == 4 || - radio->revision == 5)) { - bcm43xx_radio_write16(bcm, 0x0051, 0x001F); - bcm43xx_radio_write16(bcm, 0x0052, 0x0040); - bcm43xx_radio_write16(bcm, 0x0053, 0x005B); - bcm43xx_radio_write16(bcm, 0x0054, 0x0098); + if (radio->revision == 4 || + radio->revision == 5) { + bcm43xx_radio_write16(bcm, 0x0051, 0x0037); + bcm43xx_radio_write16(bcm, 0x0052, 0x0070); + bcm43xx_radio_write16(bcm, 0x0053, 0x00B3); + bcm43xx_radio_write16(bcm, 0x0054, 0x009B); bcm43xx_radio_write16(bcm, 0x005A, 0x0088); bcm43xx_radio_write16(bcm, 0x005B, 0x0088); bcm43xx_radio_write16(bcm, 0x005D, 0x0088); bcm43xx_radio_write16(bcm, 0x005E, 0x0088); bcm43xx_radio_write16(bcm, 0x007D, 0x0088); + bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET, + (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, + BCM43xx_UCODEFLAGS_OFFSET) + | 0x00000200)); } - if ((radio->manufact == 0x17F) && - (radio->version == 0x2050) && - (radio->revision == 6)) { - bcm43xx_radio_write16(bcm, 0x0051, 0x0000); - bcm43xx_radio_write16(bcm, 0x0052, 0x0040); - bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); - bcm43xx_radio_write16(bcm, 0x0054, 0x0098); - bcm43xx_radio_write16(bcm, 0x005A, 0x0088); - bcm43xx_radio_write16(bcm, 0x005B, 0x008B); - bcm43xx_radio_write16(bcm, 0x005C, 0x00B5); - bcm43xx_radio_write16(bcm, 0x005D, 0x0088); - bcm43xx_radio_write16(bcm, 0x005E, 0x0088); - bcm43xx_radio_write16(bcm, 0x007D, 0x0088); - bcm43xx_radio_write16(bcm, 0x007C, 0x0001); - bcm43xx_radio_write16(bcm, 0x007E, 0x0008); - } - if ((radio->manufact == 0x17F) && - (radio->version == 0x2050) && - (radio->revision == 7)) { - bcm43xx_radio_write16(bcm, 0x0051, 0x0000); - bcm43xx_radio_write16(bcm, 0x0052, 0x0040); - bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); - bcm43xx_radio_write16(bcm, 0x0054, 0x0098); - bcm43xx_radio_write16(bcm, 0x005A, 0x0088); - bcm43xx_radio_write16(bcm, 0x005B, 0x00A8); - bcm43xx_radio_write16(bcm, 0x005C, 0x0075); - bcm43xx_radio_write16(bcm, 0x005D, 0x00F5); - bcm43xx_radio_write16(bcm, 0x005E, 0x00B8); - bcm43xx_radio_write16(bcm, 0x007D, 0x00E8); - bcm43xx_radio_write16(bcm, 0x007C, 0x0001); - bcm43xx_radio_write16(bcm, 0x007E, 0x0008); - bcm43xx_radio_write16(bcm, 0x007B, 0x0000); - } - if ((radio->manufact == 0x17F) && - (radio->version == 0x2050) && - (radio->revision == 8)) { + if (radio->revision == 8) { bcm43xx_radio_write16(bcm, 0x0051, 0x0000); bcm43xx_radio_write16(bcm, 0x0052, 0x0040); bcm43xx_radio_write16(bcm, 0x0053, 0x00B7); @@ -933,20 +914,26 @@ static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm) bcm43xx_phy_read(bcm, 0x0802) | 0x0100); bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000); + bcm43xx_phy_write(bcm, 0x5B, 0x0000); + bcm43xx_phy_write(bcm, 0x5C, 0x0000); } - /* Force to channel 7, even if not supported. */ - bcm43xx_radio_selectchannel(bcm, 7, 0); + old_channel = radio->channel; + if (old_channel >= 8) + bcm43xx_radio_selectchannel(bcm, 1, 0); + else + bcm43xx_radio_selectchannel(bcm, 13, 0); bcm43xx_radio_write16(bcm, 0x0050, 0x0020); bcm43xx_radio_write16(bcm, 0x0050, 0x0023); udelay(40); - bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C) | 0x0002)); - bcm43xx_radio_write16(bcm, 0x0050, 0x0020); - if (radio->manufact == 0x17F && - radio->version == 0x2050 && - radio->revision <= 2) { + if (radio->revision < 6 || radio-> revision == 8) { + bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C) + | 0x0002)); bcm43xx_radio_write16(bcm, 0x0050, 0x0020); + } + if (radio->revision <= 2) { + bcm43xx_radio_write16(bcm, 0x007C, 0x0020); bcm43xx_radio_write16(bcm, 0x005A, 0x0070); bcm43xx_radio_write16(bcm, 0x005B, 0x007B); bcm43xx_radio_write16(bcm, 0x005C, 0x00B0); @@ -954,53 +941,49 @@ static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm) bcm43xx_radio_write16(bcm, 0x007A, (bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007); - bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0); + bcm43xx_radio_selectchannel(bcm, old_channel, 0); bcm43xx_phy_write(bcm, 0x0014, 0x0200); - if (radio->version == 0x2050){ - if (radio->revision == 3 || - radio->revision == 4 || - radio->revision == 5) - bcm43xx_phy_write(bcm, 0x002A, 0x8AC0); - else - bcm43xx_phy_write(bcm, 0x002A, 0x88C2); - } + if (radio->revision >= 6) + bcm43xx_phy_write(bcm, 0x002A, 0x88C2); + else + bcm43xx_phy_write(bcm, 0x002A, 0x8AC0); bcm43xx_phy_write(bcm, 0x0038, 0x0668); bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF); - if (radio->version == 0x2050) { - if (radio->revision == 3 || - radio->revision == 4 || - radio->revision == 5) - bcm43xx_phy_write(bcm, 0x005D, bcm43xx_phy_read(bcm, 0x005D) | 0x0003); - else if (radio->revision <= 2) - bcm43xx_radio_write16(bcm, 0x005D, 0x000D); - } + if (radio->revision <= 5) + bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) + & 0xFF80) | 0x0003); + if (radio->revision <= 2) + bcm43xx_radio_write16(bcm, 0x005D, 0x000D); - if (phy->rev == 4) - bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004); - else + if (phy->analog == 4){ bcm43xx_write16(bcm, 0x03E4, 0x0009); + bcm43xx_phy_write(bcm, 0x61, bcm43xx_phy_read(bcm, 0x61) & 0xFFF); + } else { + bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004); + } + if (phy->type == BCM43xx_PHYTYPE_G) + bcm43xx_write16(bcm, 0x03E6, 0x0); if (phy->type == BCM43xx_PHYTYPE_B) { bcm43xx_write16(bcm, 0x03E6, 0x8140); bcm43xx_phy_write(bcm, 0x0016, 0x0410); bcm43xx_phy_write(bcm, 0x0017, 0x0820); bcm43xx_phy_write(bcm, 0x0062, 0x0007); - (void) bcm43xx_radio_calibrationvalue(bcm); - bcm43xx_phy_lo_b_measure(bcm); + bcm43xx_radio_init2050(bcm); + bcm43xx_phy_lo_g_measure(bcm); if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) { bcm43xx_calc_nrssi_slope(bcm); bcm43xx_calc_nrssi_threshold(bcm); } bcm43xx_phy_init_pctl(bcm); - } else - bcm43xx_write16(bcm, 0x03E6, 0x0); + } } static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); - u16 backup_phy[15]; + u16 backup_phy[15] = {0}; u16 backup_radio[3]; u16 backup_bband; u16 i; @@ -1011,8 +994,10 @@ static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001); backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811); backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812); - backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814); - backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815); + if (phy->rev != 1) { + backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814); + backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815); + } backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A); backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059); backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058); @@ -1040,14 +1025,16 @@ static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) bcm43xx_phy_read(bcm, 0x0811) | 0x0001); bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE); - bcm43xx_phy_write(bcm, 0x0814, - bcm43xx_phy_read(bcm, 0x0814) | 0x0001); - bcm43xx_phy_write(bcm, 0x0815, - bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); - bcm43xx_phy_write(bcm, 0x0814, - bcm43xx_phy_read(bcm, 0x0814) | 0x0002); - bcm43xx_phy_write(bcm, 0x0815, - bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD); + if (phy->rev != 1) { + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0001); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE); + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0002); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD); + } bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x000C); bcm43xx_phy_write(bcm, 0x0812, @@ -1063,17 +1050,19 @@ static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x005A, 0x0780); bcm43xx_phy_write(bcm, 0x0059, 0xC810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); - if (phy->version == 0) { + if (phy->analog == 0) { bcm43xx_phy_write(bcm, 0x0003, 0x0122); } else { bcm43xx_phy_write(bcm, 0x000A, bcm43xx_phy_read(bcm, 0x000A) | 0x2000); } - bcm43xx_phy_write(bcm, 0x0814, - bcm43xx_phy_read(bcm, 0x0814) | 0x0004); - bcm43xx_phy_write(bcm, 0x0815, - bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); + if (phy->rev != 1) { + bcm43xx_phy_write(bcm, 0x0814, + bcm43xx_phy_read(bcm, 0x0814) | 0x0004); + bcm43xx_phy_write(bcm, 0x0815, + bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB); + } bcm43xx_phy_write(bcm, 0x0003, (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F) | 0x0040); @@ -1160,8 +1149,10 @@ static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm) } } - bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]); - bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]); + if (phy->rev != 1) { + bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]); + bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]); + } bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]); bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]); bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]); @@ -1205,27 +1196,29 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) if (phy->rev >= 2) { bcm43xx_phy_write(bcm, 0x0814, 0x0000); bcm43xx_phy_write(bcm, 0x0815, 0x0000); - if (phy->rev == 2) - bcm43xx_phy_write(bcm, 0x0811, 0x0000); - else if (phy->rev >= 3) - bcm43xx_phy_write(bcm, 0x0811, 0x0400); + } + if (phy->rev == 2) { + bcm43xx_phy_write(bcm, 0x0811, 0x0000); bcm43xx_phy_write(bcm, 0x0015, 0x00C0); - if (phy->connected) { - tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF; - if (tmp < 6) { - bcm43xx_phy_write(bcm, 0x04C2, 0x1816); - bcm43xx_phy_write(bcm, 0x04C3, 0x8006); - if (tmp != 3) { - bcm43xx_phy_write(bcm, 0x04CC, - (bcm43xx_phy_read(bcm, 0x04CC) - & 0x00FF) | 0x1F00); - } + } + if (phy->rev > 5) { + bcm43xx_phy_write(bcm, 0x0811, 0x0400); + bcm43xx_phy_write(bcm, 0x0015, 0x00C0); + } + if (phy->rev >= 2 && phy->connected) { + tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF; + if (tmp ==3 || tmp == 5) { + bcm43xx_phy_write(bcm, 0x04C2, 0x1816); + bcm43xx_phy_write(bcm, 0x04C3, 0x8006); + if (tmp == 5) { + bcm43xx_phy_write(bcm, 0x04CC, + (bcm43xx_phy_read(bcm, 0x04CC) + & 0x00FF) | 0x1F00); } } - } - if (phy->rev < 3 && phy->connected) bcm43xx_phy_write(bcm, 0x047E, 0x0078); - if (phy->rev >= 6 && phy->rev <= 8) { + } + if (radio->revision == 8) { bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080); bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004); } @@ -1251,7 +1244,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) if (phy->rev >= 6) { bcm43xx_phy_write(bcm, 0x0036, (bcm43xx_phy_read(bcm, 0x0036) - & 0xF000) | (radio->txctl2 << 12)); + & 0x0FFF) | (radio->txctl2 << 12)); } if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) bcm43xx_phy_write(bcm, 0x002E, 0x8075); @@ -1262,7 +1255,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) else bcm43xx_phy_write(bcm, 0x002F, 0x0202); } - if (phy->connected) { + if (phy->connected || phy->rev >= 2) { bcm43xx_phy_lo_adjust(bcm, 0); bcm43xx_phy_write(bcm, 0x080F, 0x8078); } @@ -1276,7 +1269,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm) */ bcm43xx_nrssi_hw_update(bcm, 0xFFFF); bcm43xx_calc_nrssi_threshold(bcm); - } else if (phy->connected) { + } else if (phy->connected || phy->rev >= 2) { if (radio->nrssi[0] == -1000) { assert(radio->nrssi[1] == -1000); bcm43xx_calc_nrssi_slope(bcm); @@ -1638,14 +1631,14 @@ void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm, struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); u16 value; - if (phy->version == 0) { + if (phy->analog == 0) { value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0); value |= (baseband_attenuation & 0x000F); bcm43xx_write16(bcm, 0x03E6, value); return; } - if (phy->version > 1) { + if (phy->analog > 1) { value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C; value |= (baseband_attenuation << 2) & 0x003C; } else { diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h index 1f321ef..7311836 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h @@ -48,6 +48,10 @@ void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm); local_irq_restore(flags); \ } while (0) +/* Card uses the loopback gain stuff */ +#define has_loopback_gain(phy) \ + (((phy)->rev > 1) || ((phy)->connected)) + u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset); void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val); diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c index af19a07..6a109f4 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c @@ -458,7 +458,7 @@ static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x005A, 0x0480); bcm43xx_phy_write(bcm, 0x0059, 0x0810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); - if (phy->rev == 0) { + if (phy->analog == 0) { bcm43xx_phy_write(bcm, 0x0003, 0x0122); } else { bcm43xx_phy_write(bcm, 0x000A, @@ -570,9 +570,9 @@ void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm) nrssi0 = (s16)bcm43xx_phy_read(bcm, 0x0027); bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); - if (phy->rev >= 2) { + if (phy->analog >= 2) { bcm43xx_write16(bcm, 0x03E6, 0x0040); - } else if (phy->rev == 0) { + } else if (phy->analog == 0) { bcm43xx_write16(bcm, 0x03E6, 0x0122); } else { bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, @@ -596,7 +596,7 @@ void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0015, backup[5]); bcm43xx_phy_write(bcm, 0x002A, backup[6]); bcm43xx_synth_pu_workaround(bcm, radio->channel); - if (phy->rev != 0) + if (phy->analog != 0) bcm43xx_write16(bcm, 0x03F4, backup[13]); bcm43xx_phy_write(bcm, 0x0020, backup[7]); @@ -692,7 +692,7 @@ void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm) bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) & 0x007F); - if (phy->rev >= 2) { + if (phy->analog >= 2) { bcm43xx_phy_write(bcm, 0x0003, (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F) | 0x0040); @@ -882,10 +882,10 @@ static void _stack_save(u32 *_stackptr, size_t *stackidx, { u32 *stackptr = &(_stackptr[*stackidx]); - assert((offset & 0xF000) == 0x0000); - assert((id & 0xF0) == 0x00); + assert((offset & 0xE000) == 0x0000); + assert((id & 0xF8) == 0x00); *stackptr = offset; - *stackptr |= ((u32)id) << 12; + *stackptr |= ((u32)id) << 13; *stackptr |= ((u32)value) << 16; (*stackidx)++; assert(*stackidx < BCM43xx_INTERFSTACK_SIZE); @@ -896,12 +896,12 @@ static u16 _stack_restore(u32 *stackptr, { size_t i; - assert((offset & 0xF000) == 0x0000); - assert((id & 0xF0) == 0x00); + assert((offset & 0xE000) == 0x0000); + assert((id & 0xF8) == 0x00); for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) { - if ((*stackptr & 0x00000FFF) != offset) + if ((*stackptr & 0x00001FFF) != offset) continue; - if (((*stackptr & 0x0000F000) >> 12) != id) + if (((*stackptr & 0x00007000) >> 13) != id) continue; return ((*stackptr & 0xFFFF0000) >> 16); } @@ -1343,11 +1343,110 @@ u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm) return ret; } +#define LPD(L, P, D) (((L) << 2) | ((P) << 1) | ((D) << 0)) +static u16 bcm43xx_get_812_value(struct bcm43xx_private *bcm, u8 lpd) +{ + struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); + struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); + u16 loop_or = 0; + u16 adj_loopback_gain = phy->loopback_gain[0]; + u8 loop; + u16 extern_lna_control; + + if (!phy->connected) + return 0; + if (!has_loopback_gain(phy)) { + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0FB2; + case LPD(0, 0, 1): + return 0x00B2; + case LPD(1, 0, 1): + return 0x30B2; + case LPD(1, 0, 0): + return 0x30B3; + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x8FB2; + case LPD(0, 0, 1): + return 0x80B2; + case LPD(1, 0, 1): + return 0x20B2; + case LPD(1, 0, 0): + return 0x20B3; + default: + assert(0); + } + } + } else { + if (radio->revision == 8) + adj_loopback_gain += 0x003E; + else + adj_loopback_gain += 0x0026; + if (adj_loopback_gain >= 0x46) { + adj_loopback_gain -= 0x46; + extern_lna_control = 0x3000; + } else if (adj_loopback_gain >= 0x3A) { + adj_loopback_gain -= 0x3A; + extern_lna_control = 0x2000; + } else if (adj_loopback_gain >= 0x2E) { + adj_loopback_gain -= 0x2E; + extern_lna_control = 0x1000; + } else { + adj_loopback_gain -= 0x10; + extern_lna_control = 0x0000; + } + for (loop = 0; loop < 16; loop++) { + u16 tmp = adj_loopback_gain - 6 * loop; + if (tmp < 6) + break; + } + + loop_or = (loop << 8) | extern_lna_control; + if (phy->rev >= 7 && bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA) { + if (extern_lna_control) + loop_or |= 0x8000; + switch (lpd) { + case LPD(0, 1, 1): + return 0x8F92; + case LPD(0, 0, 1): + return (0x8092 | loop_or); + case LPD(1, 0, 1): + return (0x2092 | loop_or); + case LPD(1, 0, 0): + return (0x2093 | loop_or); + default: + assert(0); + } + } else { + switch (lpd) { + case LPD(0, 1, 1): + return 0x0F92; + case LPD(0, 0, 1): + case LPD(1, 0, 1): + return (0x0092 | loop_or); + case LPD(1, 0, 0): + return (0x0093 | loop_or); + default: + assert(0); + } + } + } + return 0; +} + u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) { struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm); struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm); - u16 backup[19] = { 0 }; + u16 backup[21] = { 0 }; u16 ret; u16 i, j; u32 tmp1 = 0, tmp2 = 0; @@ -1373,19 +1472,36 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS); backup[9] = bcm43xx_phy_read(bcm, 0x0802); bcm43xx_phy_write(bcm, 0x0814, - (bcm43xx_phy_read(bcm, 0x0814) | 0x0003)); + (bcm43xx_phy_read(bcm, 0x0814) + | 0x0003)); bcm43xx_phy_write(bcm, 0x0815, - (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC)); + (bcm43xx_phy_read(bcm, 0x0815) + & 0xFFFC)); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, - (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF)); + (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) + & 0x7FFF)); bcm43xx_phy_write(bcm, 0x0802, (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC)); - bcm43xx_phy_write(bcm, 0x0811, 0x01B3); - bcm43xx_phy_write(bcm, 0x0812, 0x0FB2); + if (phy->rev > 1) { /* loopback gain enabled */ + backup[19] = bcm43xx_phy_read(bcm, 0x080F); + backup[20] = bcm43xx_phy_read(bcm, 0x0810); + if (phy->rev >= 3) + bcm43xx_phy_write(bcm, 0x080F, 0xC020); + else + bcm43xx_phy_write(bcm, 0x080F, 0x8020); + bcm43xx_phy_write(bcm, 0x0810, 0x0000); + } + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); + if (phy->rev < 7 || !(bcm->sprom.boardflags + & BCM43xx_BFL_EXTLNA)) + bcm43xx_phy_write(bcm, 0x0811, 0x01B3); + else + bcm43xx_phy_write(bcm, 0x0811, 0x09B3); } - bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, - (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); } + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000)); backup[10] = bcm43xx_phy_read(bcm, 0x0035); bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F)); @@ -1393,30 +1509,42 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) backup[12] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT); // Initialization - if (phy->version == 0) { + if (phy->analog == 0) { bcm43xx_write16(bcm, 0x03E6, 0x0122); } else { - if (phy->version >= 2) - bcm43xx_write16(bcm, 0x03E6, 0x0040); + if (phy->analog >= 2) + bcm43xx_phy_write(bcm, 0x0003, + (bcm43xx_phy_read(bcm, 0x0003) + & 0xFFBF) | 0x0040); bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, - (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000)); + (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) + | 0x2000)); } ret = bcm43xx_radio_calibrationvalue(bcm); if (phy->type == BCM43xx_PHYTYPE_B) - bcm43xx_radio_write16(bcm, 0x0078, 0x0003); + bcm43xx_radio_write16(bcm, 0x0078, 0x0026); + if (phy->connected) + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 1, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xBFAF); bcm43xx_phy_write(bcm, 0x002B, 0x1403); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x00B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(0, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xBFA0); bcm43xx_radio_write16(bcm, 0x0051, (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004)); - bcm43xx_radio_write16(bcm, 0x0052, 0x0000); - bcm43xx_radio_write16(bcm, 0x0043, - bcm43xx_radio_read16(bcm, 0x0043) | 0x0009); + if (radio->revision == 8) + bcm43xx_radio_write16(bcm, 0x0043, 0x001F); + else { + bcm43xx_radio_write16(bcm, 0x0052, 0x0000); + bcm43xx_radio_write16(bcm, 0x0043, + (bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0) + | 0x0009); + } bcm43xx_phy_write(bcm, 0x0058, 0x0000); for (i = 0; i < 16; i++) { @@ -1424,21 +1552,25 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0059, 0xC810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 0))); bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); - udelay(10); + udelay(20); tmp1 += bcm43xx_phy_read(bcm, 0x002D); bcm43xx_phy_write(bcm, 0x0058, 0x0000); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } @@ -1456,21 +1588,29 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0059, 0xC810); bcm43xx_phy_write(bcm, 0x0058, 0x000D); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xEFB0); udelay(10); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */ + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 0))); bcm43xx_phy_write(bcm, 0x0015, 0xFFF0); udelay(10); tmp2 += bcm43xx_phy_read(bcm, 0x002D); bcm43xx_phy_write(bcm, 0x0058, 0x0000); if (phy->connected) - bcm43xx_phy_write(bcm, 0x0812, 0x30B2); + bcm43xx_phy_write(bcm, 0x0812, + bcm43xx_get_812_value(bcm, + LPD(1, 0, 1))); bcm43xx_phy_write(bcm, 0x0015, 0xAFB0); } tmp2++; @@ -1488,7 +1628,7 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0059, backup[17]); bcm43xx_phy_write(bcm, 0x0058, backup[18]); bcm43xx_write16(bcm, 0x03E6, backup[11]); - if (phy->version != 0) + if (phy->analog != 0) bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[12]); bcm43xx_phy_write(bcm, 0x0035, backup[10]); bcm43xx_radio_selectchannel(bcm, radio->channel, 1); @@ -1496,15 +1636,20 @@ u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm) bcm43xx_phy_write(bcm, 0x0030, backup[2]); bcm43xx_write16(bcm, 0x03EC, backup[3]); } else { - bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, - (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); if (phy->connected) { + bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, + (bcm43xx_read16(bcm, + BCM43xx_MMIO_PHY_RADIO) & 0x7FFF)); bcm43xx_phy_write(bcm, 0x0811, backup[4]); bcm43xx_phy_write(bcm, 0x0812, backup[5]); bcm43xx_phy_write(bcm, 0x0814, backup[6]); bcm43xx_phy_write(bcm, 0x0815, backup[7]); bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]); bcm43xx_phy_write(bcm, 0x0802, backup[9]); + if (phy->rev > 1) { + bcm43xx_phy_write(bcm, 0x080F, backup[19]); + bcm43xx_phy_write(bcm, 0x0810, backup[20]); + } } } if (i >= 15) @@ -1578,7 +1723,7 @@ void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm) for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { - if (tmp == (data_high[i] << 4 | data_low[j])) { + if (tmp == (data_high[i] | data_low[j])) { bcm43xx_phy_write(bcm, 0x0069, (i - j) << 8 | 0x00C0); return; } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c index d2ca949..d6d9413 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c +++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c @@ -105,18 +105,24 @@ static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev, struct bcm43xx_private *bcm = bcm43xx_priv(net_dev); unsigned long flags; u8 channel; + s8 expon; int freq; int err = -EINVAL; mutex_lock(&bcm->mutex); spin_lock_irqsave(&bcm->irq_lock, flags); - if ((data->freq.m >= 0) && (data->freq.m <= 1000)) { + if ((data->freq.e == 0) && + (data->freq.m >= 0) && (data->freq.m <= 1000)) { channel = data->freq.m; freq = bcm43xx_channel_to_freq(bcm, channel); } else { - channel = bcm43xx_freq_to_channel(bcm, data->freq.m); freq = data->freq.m; + expon = 6 - data->freq.e; + while (--expon >= 0) /* scale down the frequency to MHz */ + freq /= 10; + assert(freq > 1000); + channel = bcm43xx_freq_to_channel(bcm, freq); } if (!ieee80211_is_valid_channel(bcm->ieee, channel)) goto out_unlock; @@ -260,22 +266,22 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, if (phy->type == BCM43xx_PHYTYPE_A || phy->type == BCM43xx_PHYTYPE_G) { range->num_bitrates = 8; - range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB; - range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB; + range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB * 500000; + range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB * 500000; } if (phy->type == BCM43xx_PHYTYPE_B || phy->type == BCM43xx_PHYTYPE_G) { range->num_bitrates += 4; - range->bitrate[i++] = IEEE80211_CCK_RATE_1MB; - range->bitrate[i++] = IEEE80211_CCK_RATE_2MB; - range->bitrate[i++] = IEEE80211_CCK_RATE_5MB; - range->bitrate[i++] = IEEE80211_CCK_RATE_11MB; + range->bitrate[i++] = IEEE80211_CCK_RATE_1MB * 500000; + range->bitrate[i++] = IEEE80211_CCK_RATE_2MB * 500000; + range->bitrate[i++] = IEEE80211_CCK_RATE_5MB * 500000; + range->bitrate[i++] = IEEE80211_CCK_RATE_11MB * 500000; } geo = ieee80211_get_geo(bcm->ieee); @@ -285,7 +291,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, if (j == IW_MAX_FREQUENCIES) break; range->freq[j].i = j + 1; - range->freq[j].m = geo->a[i].freq;//FIXME? + range->freq[j].m = geo->a[i].freq * 100000; range->freq[j].e = 1; j++; } @@ -293,7 +299,7 @@ static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev, if (j == IW_MAX_FREQUENCIES) break; range->freq[j].i = j + 1; - range->freq[j].m = geo->bg[i].freq;//FIXME? + range->freq[j].m = geo->bg[i].freq * 100000; range->freq[j].e = 1; j++; } diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h index 2aed19e..9ecf2bf 100644 --- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h +++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h @@ -137,14 +137,8 @@ struct bcm43xx_xmitstatus { u16 unknown; //FIXME }; -#define BCM43xx_TXSTAT_FLAG_ACK 0x01 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x02 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x04 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x08 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x10 -#define BCM43xx_TXSTAT_FLAG_IGNORE 0x20 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x40 -//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x80 +#define BCM43xx_TXSTAT_FLAG_AMPDU 0x10 +#define BCM43xx_TXSTAT_FLAG_INTER 0x20 u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate); u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate); diff --git a/drivers/net/wireless/hostap/Kconfig b/drivers/net/wireless/hostap/Kconfig index 308f773..1fef331 100644 --- a/drivers/net/wireless/hostap/Kconfig +++ b/drivers/net/wireless/hostap/Kconfig @@ -1,6 +1,7 @@ config HOSTAP tristate "IEEE 802.11 for Host AP (Prism2/2.5/3 and WEP/TKIP/CCMP)" - depends on NET_RADIO + depends on WLAN_80211 + select WIRELESS_EXT select IEEE80211 select IEEE80211_CRYPT_WEP ---help--- diff --git a/drivers/net/wireless/hostap/hostap.h b/drivers/net/wireless/hostap/hostap.h index e89c890..ef37a75 100644 --- a/drivers/net/wireless/hostap/hostap.h +++ b/drivers/net/wireless/hostap/hostap.h @@ -2,13 +2,14 @@ #define HOSTAP_H #include <linux/ethtool.h> +#include <linux/kernel.h> #include "hostap_wlan.h" #include "hostap_ap.h" static const long freq_list[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; -#define FREQ_COUNT (sizeof(freq_list) / sizeof(freq_list[0])) +#define FREQ_COUNT ARRAY_SIZE(freq_list) /* hostap.c */ diff --git a/drivers/net/wireless/hostap/hostap_80211_rx.c b/drivers/net/wireless/hostap/hostap_80211_rx.c index 7e04dc9..cbedc9e 100644 --- a/drivers/net/wireless/hostap/hostap_80211_rx.c +++ b/drivers/net/wireless/hostap/hostap_80211_rx.c @@ -167,7 +167,7 @@ hdr->f.status = s; hdr->f.len = l; hdr->f.data = d ret = skb->len - phdrlen; skb->dev = dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, hdrlen); if (prism_header) skb_pull(skb, phdrlen); @@ -933,12 +933,14 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, if (frag == 0) { /* copy first fragment (including full headers) into * beginning of the fragment cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data, flen); + skb_copy_from_linear_data(skb, skb_put(frag_skb, flen), + flen); } else { /* append frame payload to the end of the fragment * cache skb */ - memcpy(skb_put(frag_skb, flen), skb->data + hdrlen, - flen); + skb_copy_from_linear_data_offset(skb, hdrlen, + skb_put(frag_skb, + flen), flen); } dev_kfree_skb(skb); skb = NULL; @@ -1044,8 +1046,9 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, skb->len >= ETH_HLEN + ETH_ALEN) { /* Non-standard frame: get addr4 from its bogus location after * the payload */ - memcpy(skb->data + ETH_ALEN, - skb->data + skb->len - ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, skb->len - ETH_ALEN, + skb->data + ETH_ALEN, + ETH_ALEN); skb_trim(skb, skb->len - ETH_ALEN); } @@ -1073,17 +1076,17 @@ void hostap_80211_rx(struct net_device *dev, struct sk_buff *skb, if (skb2 != NULL) { /* send to wireless media */ - skb2->protocol = __constant_htons(ETH_P_802_3); - skb2->mac.raw = skb2->nh.raw = skb2->data; - /* skb2->nh.raw = skb2->data + ETH_HLEN; */ skb2->dev = dev; + skb2->protocol = __constant_htons(ETH_P_802_3); + skb_reset_mac_header(skb2); + skb_reset_network_header(skb2); + /* skb2->network_header += ETH_HLEN; */ dev_queue_xmit(skb2); } if (skb) { skb->protocol = eth_type_trans(skb, dev); memset(skb->cb, 0, sizeof(skb->cb)); - skb->dev = dev; netif_rx(skb); } diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c index 4a5be70..246fac0 100644 --- a/drivers/net/wireless/hostap/hostap_80211_tx.c +++ b/drivers/net/wireless/hostap/hostap_80211_tx.c @@ -146,7 +146,8 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS; /* From&To DS: Addr1 = RA, Addr2 = TA, Addr3 = DA, * Addr4 = SA */ - memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); hdr_len += ETH_ALEN; } else { /* bogus 4-addr format to workaround Prism2 station @@ -159,7 +160,8 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) /* SA from skb->data + ETH_ALEN will be added after * frame payload; use hdr.addr4 as a temporary buffer */ - memcpy(&hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, + &hdr.addr4, ETH_ALEN); need_tailroom += ETH_ALEN; } @@ -174,24 +176,27 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) else memcpy(&hdr.addr1, local->bssid, ETH_ALEN); memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&hdr.addr3, skb->data, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); } else if (local->iw_mode == IW_MODE_MASTER && !to_assoc_ap) { fc |= IEEE80211_FCTL_FROMDS; /* From DS: Addr1 = DA, Addr2 = BSSID, Addr3 = SA */ - memcpy(&hdr.addr1, skb->data, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); memcpy(&hdr.addr2, dev->dev_addr, ETH_ALEN); - memcpy(&hdr.addr3, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr3, + ETH_ALEN); } else if (local->iw_mode == IW_MODE_INFRA || to_assoc_ap) { fc |= IEEE80211_FCTL_TODS; /* To DS: Addr1 = BSSID, Addr2 = SA, Addr3 = DA */ memcpy(&hdr.addr1, to_assoc_ap ? local->assoc_ap_addr : local->bssid, ETH_ALEN); - memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); - memcpy(&hdr.addr3, skb->data, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr3, ETH_ALEN); } else if (local->iw_mode == IW_MODE_ADHOC) { /* not From/To DS: Addr1 = DA, Addr2 = SA, Addr3 = BSSID */ - memcpy(&hdr.addr1, skb->data, ETH_ALEN); - memcpy(&hdr.addr2, skb->data + ETH_ALEN, ETH_ALEN); + skb_copy_from_linear_data(skb, &hdr.addr1, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, ETH_ALEN, &hdr.addr2, + ETH_ALEN); memcpy(&hdr.addr3, local->bssid, ETH_ALEN); } @@ -237,7 +242,7 @@ int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev) iface->stats.tx_packets++; iface->stats.tx_bytes += skb->len; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); meta = (struct hostap_skb_tx_data *) skb->cb; memset(meta, 0, sizeof(*meta)); meta->magic = HOSTAP_SKB_TX_DATA_MAGIC; diff --git a/drivers/net/wireless/hostap/hostap_ap.c b/drivers/net/wireless/hostap/hostap_ap.c index efb8cf3..5b3abd5 100644 --- a/drivers/net/wireless/hostap/hostap_ap.c +++ b/drivers/net/wireless/hostap/hostap_ap.c @@ -1,8 +1,8 @@ /* * Intersil Prism2 driver with Host AP (software access point) support * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * This file is to be included into hostap.c when S/W AP functionality is * compiled. @@ -982,7 +982,8 @@ static void prism2_send_mgmt(struct net_device *dev, meta->tx_cb_idx = tx_cb_idx; skb->dev = dev; - skb->mac.raw = skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); dev_queue_xmit(skb); } #endif /* PRISM2_NO_KERNEL_IEEE80211_MGMT */ @@ -1276,8 +1277,8 @@ static char * ap_auth_make_challenge(struct ap_data *ap) return NULL; } - memcpy(tmpbuf, skb->data + ap->crypt->extra_mpdu_prefix_len, - WLAN_AUTH_CHALLENGE_LEN); + skb_copy_from_linear_data_offset(skb, ap->crypt->extra_mpdu_prefix_len, + tmpbuf, WLAN_AUTH_CHALLENGE_LEN); dev_kfree_skb(skb); return tmpbuf; diff --git a/drivers/net/wireless/hostap/hostap_common.h b/drivers/net/wireless/hostap/hostap_common.h index 0162400..b31e6a0 100644 --- a/drivers/net/wireless/hostap/hostap_common.h +++ b/drivers/net/wireless/hostap/hostap_common.h @@ -368,9 +368,9 @@ enum { #define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 #define PRISM2_HOSTAPD_RID_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +offsetof(struct prism2_hostapd_param, u.rid.data) #define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ -((int) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) +offsetof(struct prism2_hostapd_param, u.generic_elem.data) /* Maximum length for algorithm names (-1 for nul termination) used in ioctl() */ diff --git a/drivers/net/wireless/hostap/hostap_cs.c b/drivers/net/wireless/hostap/hostap_cs.c index 8d8f4b9..ee1532b 100644 --- a/drivers/net/wireless/hostap/hostap_cs.c +++ b/drivers/net/wireless/hostap/hostap_cs.c @@ -22,7 +22,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static dev_info_t dev_info = "hostap_cs"; MODULE_AUTHOR("Jouni Malinen"); @@ -838,6 +838,8 @@ static struct pcmcia_device_id hostap_cs_ids[] = { PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), + PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0xd601, 0x0005, "ADLINK 345 CF", + 0x2d858104), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", 0x74c5e40d), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "Intersil", @@ -848,6 +850,11 @@ static struct pcmcia_device_id hostap_cs_ids[] = { "Intersil", "PRISM 2_5 PCMCIA ADAPTER", "ISL37300P", "Eval-RevA", 0x4b801a17, 0x6345a0bf, 0xc9049a39, 0xc23adc0e), + /* D-Link DWL-650 Rev. P1; manfid 0x000b, 0x7110 */ + PCMCIA_DEVICE_PROD_ID1234( + "D-Link", "DWL-650 Wireless PC Card RevP", "ISL37101P-10", + "A3", + 0x1a424a1c, 0x6ea57632, 0xdd97a26b, 0x56b21f52), PCMCIA_DEVICE_PROD_ID123( "Addtron", "AWP-100 Wireless PCMCIA", "Version 01.02", 0xe6ec52ce, 0x08649af2, 0x4b74baa0), diff --git a/drivers/net/wireless/hostap/hostap_hw.c b/drivers/net/wireless/hostap/hostap_hw.c index 3079378..959887b 100644 --- a/drivers/net/wireless/hostap/hostap_hw.c +++ b/drivers/net/wireless/hostap/hostap_hw.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3. * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * 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 @@ -1838,13 +1838,14 @@ static int prism2_tx_80211(struct sk_buff *skb, struct net_device *dev) /* skb->data starts with txdesc->frame_control */ hdr_len = 24; - memcpy(&txdesc.frame_control, skb->data, hdr_len); + skb_copy_from_linear_data(skb, &txdesc.frame_control, hdr_len); fc = le16_to_cpu(txdesc.frame_control); if (WLAN_FC_GET_TYPE(fc) == IEEE80211_FTYPE_DATA && (fc & IEEE80211_FCTL_FROMDS) && (fc & IEEE80211_FCTL_TODS) && skb->len >= 30) { /* Addr4 */ - memcpy(txdesc.addr4, skb->data + hdr_len, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, hdr_len, txdesc.addr4, + ETH_ALEN); hdr_len += ETH_ALEN; } @@ -2217,7 +2218,7 @@ static void hostap_tx_callback(local_info_t *local, memcpy(skb_put(skb, len), payload, len); skb->dev = local->dev; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); cb->func(skb, ok, cb->data); } diff --git a/drivers/net/wireless/hostap/hostap_main.c b/drivers/net/wireless/hostap/hostap_main.c index 9077e6e..4743426 100644 --- a/drivers/net/wireless/hostap/hostap_main.c +++ b/drivers/net/wireless/hostap/hostap_main.c @@ -3,8 +3,8 @@ * Intersil Prism2/2.5/3 - hostap.o module, common routines * * Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - * <jkmaline@cc.hut.fi> - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * <j@w1.fi> + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> * * 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 @@ -590,20 +590,20 @@ void hostap_dump_tx_header(const char *name, const struct hfa384x_tx_frame *tx) int hostap_80211_header_parse(struct sk_buff *skb, unsigned char *haddr) { - memcpy(haddr, skb->mac.raw + 10, ETH_ALEN); /* addr2 */ + memcpy(haddr, skb_mac_header(skb) + 10, ETH_ALEN); /* addr2 */ return ETH_ALEN; } int hostap_80211_prism_header_parse(struct sk_buff *skb, unsigned char *haddr) { - if (*(u32 *)skb->mac.raw == LWNG_CAP_DID_BASE) { - memcpy(haddr, skb->mac.raw + - sizeof(struct linux_wlan_ng_prism_hdr) + 10, + const unsigned char *mac = skb_mac_header(skb); + + if (*(u32 *)mac == LWNG_CAP_DID_BASE) { + memcpy(haddr, mac + sizeof(struct linux_wlan_ng_prism_hdr) + 10, ETH_ALEN); /* addr2 */ - } else { /* (*(u32 *)skb->mac.raw == htonl(LWNG_CAPHDR_VERSION)) */ - memcpy(haddr, skb->mac.raw + - sizeof(struct linux_wlan_ng_cap_hdr) + 10, + } else { /* (*(u32 *)mac == htonl(LWNG_CAPHDR_VERSION)) */ + memcpy(haddr, mac + sizeof(struct linux_wlan_ng_cap_hdr) + 10, ETH_ALEN); /* addr2 */ } return ETH_ALEN; @@ -1063,7 +1063,8 @@ int prism2_sta_send_mgmt(local_info_t *local, u8 *dst, u16 stype, meta->iface = netdev_priv(dev); skb->dev = dev; - skb->mac.raw = skb->nh.raw = skb->data; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); dev_queue_xmit(skb); return 0; diff --git a/drivers/net/wireless/hostap/hostap_pci.c b/drivers/net/wireless/hostap/hostap_pci.c index c4f6020..db4899e 100644 --- a/drivers/net/wireless/hostap/hostap_pci.c +++ b/drivers/net/wireless/hostap/hostap_pci.c @@ -20,7 +20,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static char *dev_info = "hostap_pci"; diff --git a/drivers/net/wireless/hostap/hostap_plx.c b/drivers/net/wireless/hostap/hostap_plx.c index e235e06..f0fd5ec 100644 --- a/drivers/net/wireless/hostap/hostap_plx.c +++ b/drivers/net/wireless/hostap/hostap_plx.c @@ -23,7 +23,7 @@ #include "hostap_wlan.h" -static char *version = PRISM2_VERSION " (Jouni Malinen <jkmaline@cc.hut.fi>)"; +static char *version = PRISM2_VERSION " (Jouni Malinen <j@w1.fi>)"; static char *dev_info = "hostap_plx"; diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c index b85857a..d51daf8 100644 --- a/drivers/net/wireless/ipw2100.c +++ b/drivers/net/wireless/ipw2100.c @@ -28,8 +28,8 @@ Portions of this file are based on the Host AP project, Copyright (c) 2001-2002, SSH Communications Security Corp and Jouni Malinen - <jkmaline@cc.hut.fi> - Copyright (c) 2002-2003, Jouni Malinen <jkmaline@cc.hut.fi> + <j@w1.fi> + Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> Portions of ipw2100_mod_firmware_load, ipw2100_do_mod_firmware_load, and ipw2100_fw_load are loosely based on drivers/sound/sound_firmware.c @@ -175,7 +175,7 @@ that only one external action is invoked at a time. /* Debugging stuff */ #ifdef CONFIG_IPW2100_DEBUG -#define CONFIG_IPW2100_RX_DEBUG /* Reception debugging */ +#define IPW2100_RX_DEBUG /* Reception debugging */ #endif MODULE_DESCRIPTION(DRV_DESCRIPTION); @@ -2239,7 +2239,7 @@ static void ipw2100_snapshot_free(struct ipw2100_priv *priv) priv->snapshot[0] = NULL; } -#ifdef CONFIG_IPW2100_DEBUG_C3 +#ifdef IPW2100_DEBUG_C3 static int ipw2100_snapshot_alloc(struct ipw2100_priv *priv) { int i; @@ -2314,13 +2314,13 @@ static u32 ipw2100_match_buf(struct ipw2100_priv *priv, u8 * in_buf, * The size of the constructed ethernet * */ -#ifdef CONFIG_IPW2100_RX_DEBUG +#ifdef IPW2100_RX_DEBUG static u8 packet_data[IPW_RX_NIC_BUFFER_LENGTH]; #endif static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) { -#ifdef CONFIG_IPW2100_DEBUG_C3 +#ifdef IPW2100_DEBUG_C3 struct ipw2100_status *status = &priv->status_queue.drv[i]; u32 match, reg; int j; @@ -2342,7 +2342,7 @@ static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) } #endif -#ifdef CONFIG_IPW2100_DEBUG_C3 +#ifdef IPW2100_DEBUG_C3 /* Halt the fimrware so we can get a good image */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); @@ -2413,15 +2413,16 @@ static void isr_rx(struct ipw2100_priv *priv, int i, skb_put(packet->skb, status->frame_size); -#ifdef CONFIG_IPW2100_RX_DEBUG +#ifdef IPW2100_RX_DEBUG /* Make a copy of the frame so we can dump it to the logs if * ieee80211_rx fails */ - memcpy(packet_data, packet->skb->data, - min_t(u32, status->frame_size, IPW_RX_NIC_BUFFER_LENGTH)); + skb_copy_from_linear_data(packet->skb, packet_data, + min_t(u32, status->frame_size, + IPW_RX_NIC_BUFFER_LENGTH)); #endif if (!ieee80211_rx(priv->ieee, packet->skb, stats)) { -#ifdef CONFIG_IPW2100_RX_DEBUG +#ifdef IPW2100_RX_DEBUG IPW_DEBUG_DROP("%s: Non consumed packet:\n", priv->net_dev->name); printk_buf(IPW_DL_DROP, packet_data, status->frame_size); @@ -2888,7 +2889,7 @@ static int __ipw2100_tx_process(struct ipw2100_priv *priv) #ifdef CONFIG_IPW2100_DEBUG if (packet->info.c_struct.cmd->host_command_reg < - sizeof(command_types) / sizeof(*command_types)) + ARRAY_SIZE(command_types)) IPW_DEBUG_TX("Command '%s (%d)' processed: %d.\n", command_types[packet->info.c_struct.cmd-> host_command_reg], @@ -3736,7 +3737,7 @@ static ssize_t show_registers(struct device *d, struct device_attribute *attr, out += sprintf(out, "%30s [Address ] : Hex\n", "Register"); - for (i = 0; i < (sizeof(hw_data) / sizeof(*hw_data)); i++) { + for (i = 0; i < ARRAY_SIZE(hw_data); i++) { read_register(dev, hw_data[i].addr, &val); out += sprintf(out, "%30s [%08X] : %08X\n", hw_data[i].name, hw_data[i].addr, val); @@ -3757,7 +3758,7 @@ static ssize_t show_hardware(struct device *d, struct device_attribute *attr, out += sprintf(out, "%30s [Address ] : Hex\n", "NIC entry"); - for (i = 0; i < (sizeof(nic_data) / sizeof(*nic_data)); i++) { + for (i = 0; i < ARRAY_SIZE(nic_data); i++) { u8 tmp8; u16 tmp16; u32 tmp32; @@ -3894,13 +3895,11 @@ static ssize_t show_ordinals(struct device *d, struct device_attribute *attr, if (priv->status & STATUS_RF_KILL_MASK) return 0; - if (loop >= sizeof(ord_data) / sizeof(*ord_data)) + if (loop >= ARRAY_SIZE(ord_data)) loop = 0; /* sysfs provides us PAGE_SIZE buffer */ - while (len < PAGE_SIZE - 128 && - loop < (sizeof(ord_data) / sizeof(*ord_data))) { - + while (len < PAGE_SIZE - 128 && loop < ARRAY_SIZE(ord_data)) { val_len = sizeof(u32); if (ipw2100_get_ordinal(priv, ord_data[loop].index, &val, @@ -4912,7 +4911,7 @@ static int ipw2100_set_power_mode(struct ipw2100_priv *priv, int power_level) else priv->power_mode = IPW_POWER_ENABLED | power_level; -#ifdef CONFIG_IPW2100_TX_POWER +#ifdef IPW2100_TX_POWER if (priv->port_type == IBSS && priv->adhoc_power != DFTL_IBSS_TX_POWER) { /* Set beacon interval */ cmd.host_command = TX_POWER_INDEX; @@ -6589,7 +6588,7 @@ static const long ipw2100_rates_11b[] = { 11000000 }; -#define RATE_COUNT (sizeof(ipw2100_rates_11b) / sizeof(ipw2100_rates_11b[0])) +#define RATE_COUNT ARRAY_SIZE(ipw2100_rates_11b) static int ipw2100_wx_get_name(struct net_device *dev, struct iw_request_info *info, diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c index c878a2f..7cb2052 100644 --- a/drivers/net/wireless/ipw2200.c +++ b/drivers/net/wireless/ipw2200.c @@ -1847,6 +1847,52 @@ static ssize_t store_net_stats(struct device *d, struct device_attribute *attr, static DEVICE_ATTR(net_stats, S_IWUSR | S_IRUGO, show_net_stats, store_net_stats); +static ssize_t show_channels(struct device *d, + struct device_attribute *attr, + char *buf) +{ + struct ipw_priv *priv = dev_get_drvdata(d); + const struct ieee80211_geo *geo = ieee80211_get_geo(priv->ieee); + int len = 0, i; + + len = sprintf(&buf[len], + "Displaying %d channels in 2.4Ghz band " + "(802.11bg):\n", geo->bg_channels); + + for (i = 0; i < geo->bg_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s, Band %s.\n", + geo->bg[i].channel, + geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->bg[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->bg[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->bg[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive", + geo->bg[i].flags & IEEE80211_CH_B_ONLY ? + "B" : "B/G"); + } + + len += sprintf(&buf[len], + "Displaying %d channels in 5.2Ghz band " + "(802.11a):\n", geo->a_channels); + for (i = 0; i < geo->a_channels; i++) { + len += sprintf(&buf[len], "%d: BSS%s%s, %s.\n", + geo->a[i].channel, + geo->a[i].flags & IEEE80211_CH_RADAR_DETECT ? + " (radar spectrum)" : "", + ((geo->a[i].flags & IEEE80211_CH_NO_IBSS) || + (geo->a[i].flags & IEEE80211_CH_RADAR_DETECT)) + ? "" : ", IBSS", + geo->a[i].flags & IEEE80211_CH_PASSIVE_ONLY ? + "passive only" : "active/passive"); + } + + return len; +} + +static DEVICE_ATTR(channels, S_IRUSR, show_channels, NULL); + static void notify_wx_assoc_event(struct ipw_priv *priv) { union iwreq_data wrqu; @@ -8133,7 +8179,7 @@ static void ipw_handle_mgmt_packet(struct ipw_priv *priv, skb->dev = priv->ieee->dev; /* Point raw at the ieee80211_stats */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->pkt_type = PACKET_OTHERHOST; skb->protocol = __constant_htons(ETH_P_80211_STATS); @@ -10355,7 +10401,7 @@ static void ipw_handle_promiscuous_tx(struct ipw_priv *priv, rt_hdr->it_len = dst->len; - memcpy(skb_put(dst, len), src->data, len); + skb_copy_from_linear_data(src, skb_put(dst, len), len); if (!ieee80211_rx(priv->prom_priv->ieee, dst, &dummystats)) dev_kfree_skb_any(dst); @@ -11383,6 +11429,7 @@ static struct attribute *ipw_sysfs_entries[] = { &dev_attr_led.attr, &dev_attr_speed_scan.attr, &dev_attr_net_stats.attr, + &dev_attr_channels.attr, #ifdef CONFIG_IPW2200_PROMISCUOUS &dev_attr_rtap_iface.attr, &dev_attr_rtap_filter.attr, diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c new file mode 100644 index 0000000..e0ecc4d --- /dev/null +++ b/drivers/net/wireless/libertas/11d.c @@ -0,0 +1,754 @@ +/** + * This file contains functions for 802.11D. + */ +#include <linux/ctype.h> +#include <linux/kernel.h> +#include <linux/wireless.h> + +#include "host.h" +#include "decl.h" +#include "11d.h" +#include "dev.h" +#include "wext.h" + +#define TX_PWR_DEFAULT 10 + +static struct region_code_mapping region_code_mapping[] = { + {"US ", 0x10}, /* US FCC */ + {"CA ", 0x10}, /* IC Canada */ + {"SG ", 0x10}, /* Singapore */ + {"EU ", 0x30}, /* ETSI */ + {"AU ", 0x30}, /* Australia */ + {"KR ", 0x30}, /* Republic Of Korea */ + {"ES ", 0x31}, /* Spain */ + {"FR ", 0x32}, /* France */ + {"JP ", 0x40}, /* Japan */ +}; + +/* Following 2 structure defines the supported channels */ +static struct chan_freq_power channel_freq_power_UN_BG[] = { + {1, 2412, TX_PWR_DEFAULT}, + {2, 2417, TX_PWR_DEFAULT}, + {3, 2422, TX_PWR_DEFAULT}, + {4, 2427, TX_PWR_DEFAULT}, + {5, 2432, TX_PWR_DEFAULT}, + {6, 2437, TX_PWR_DEFAULT}, + {7, 2442, TX_PWR_DEFAULT}, + {8, 2447, TX_PWR_DEFAULT}, + {9, 2452, TX_PWR_DEFAULT}, + {10, 2457, TX_PWR_DEFAULT}, + {11, 2462, TX_PWR_DEFAULT}, + {12, 2467, TX_PWR_DEFAULT}, + {13, 2472, TX_PWR_DEFAULT}, + {14, 2484, TX_PWR_DEFAULT} +}; + +static u8 wlan_region_2_code(u8 * region) +{ + u8 i; + u8 size = sizeof(region_code_mapping)/ + sizeof(struct region_code_mapping); + + for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++) + region[i] = toupper(region[i]); + + for (i = 0; i < size; i++) { + if (!memcmp(region, region_code_mapping[i].region, + COUNTRY_CODE_LEN)) + return (region_code_mapping[i].code); + } + + /* default is US */ + return (region_code_mapping[0].code); +} + +static u8 *wlan_code_2_region(u8 code) +{ + u8 i; + u8 size = sizeof(region_code_mapping) + / sizeof(struct region_code_mapping); + for (i = 0; i < size; i++) { + if (region_code_mapping[i].code == code) + return (region_code_mapping[i].region); + } + /* default is US */ + return (region_code_mapping[0].region); +} + +/** + * @brief This function finds the nrchan-th chan after the firstchan + * @param band band + * @param firstchan first channel number + * @param nrchan number of channels + * @return the nrchan-th chan number +*/ +static u8 wlan_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 * chan) +/*find the nrchan-th chan after the firstchan*/ +{ + u8 i; + struct chan_freq_power *cfp; + u8 cfp_no; + + cfp = channel_freq_power_UN_BG; + cfp_no = sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cfp_no; i++) { + if ((cfp + i)->channel == firstchan) { + lbs_pr_debug(1, "firstchan found\n"); + break; + } + } + + if (i < cfp_no) { + /*if beyond the boundary */ + if (i + nrchan < cfp_no) { + *chan = (cfp + i + nrchan)->channel; + return 1; + } + } + + return 0; +} + +/** + * @brief This function Checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return TRUE; FALSE +*/ +static u8 wlan_channel_known_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr; + u8 nr_chan = parsed_region_chan->nr_chan; + u8 i = 0; + + lbs_dbg_hex("11D:parsed_region_chan:", (char *)chanpwr, + sizeof(struct chan_power_11d) * nr_chan); + + for (i = 0; i < nr_chan; i++) { + if (chan == chanpwr[i].chan) { + lbs_pr_debug(1, "11D: Found Chan:%d\n", chan); + return 1; + } + } + + lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan); + return 0; +} + +u32 libertas_chan_2_freq(u8 chan, u8 band) +{ + struct chan_freq_power *cf; + u16 cnt; + u16 i; + u32 freq = 0; + + cf = channel_freq_power_UN_BG; + cnt = + sizeof(channel_freq_power_UN_BG) / + sizeof(struct chan_freq_power); + + for (i = 0; i < cnt; i++) { + if (chan == cf[i].channel) + freq = cf[i].freq; + } + + return freq; +} + +static int generate_domain_info_11d(struct parsed_region_chan_11d + *parsed_region_chan, + struct wlan_802_11d_domain_reg * domaininfo) +{ + u8 nr_subband = 0; + + u8 nr_chan = parsed_region_chan->nr_chan; + u8 nr_parsedchan = 0; + + u8 firstchan = 0, nextchan = 0, maxpwr = 0; + + u8 i, flag = 0; + + memcpy(domaininfo->countrycode, parsed_region_chan->countrycode, + COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D:nrchan=%d\n", nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (char *)parsed_region_chan, + sizeof(struct parsed_region_chan_11d)); + + for (i = 0; i < nr_chan; i++) { + if (!flag) { + flag = 1; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + nr_parsedchan = 1; + continue; + } + + if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 && + parsed_region_chan->chanpwr[i].pwr == maxpwr) { + nextchan++; + nr_parsedchan++; + } else { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = + nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + nextchan = firstchan = + parsed_region_chan->chanpwr[i].chan; + maxpwr = parsed_region_chan->chanpwr[i].pwr; + } + } + + if (flag) { + domaininfo->subband[nr_subband].firstchan = firstchan; + domaininfo->subband[nr_subband].nrchan = nr_parsedchan; + domaininfo->subband[nr_subband].maxtxpwr = maxpwr; + nr_subband++; + } + domaininfo->nr_subband = nr_subband; + + lbs_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband); + lbs_dbg_hex("11D:domaininfo:", (char *)domaininfo, + COUNTRY_CODE_LEN + 1 + + sizeof(struct ieeetypes_subbandset) * nr_subband); + return 0; +} + +/** + * @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS + * @param region_chan pointer to struct region_channel + * @param *parsed_region_chan pointer to parsed_region_chan_11d + * @return N/A +*/ +static void wlan_generate_parsed_region_chan_11d(struct region_channel * region_chan, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 i; + struct chan_freq_power *cfp; + + if (region_chan == NULL) { + lbs_pr_debug(1, "11D: region_chan is NULL\n"); + return; + } + + cfp = region_chan->CFP; + if (cfp == NULL) { + lbs_pr_debug(1, "11D: cfp equal NULL \n"); + return; + } + + parsed_region_chan->band = region_chan->band; + parsed_region_chan->region = region_chan->region; + memcpy(parsed_region_chan->countrycode, + wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN); + + lbs_pr_debug(1, "11D: region[0x%x] band[%d]\n", parsed_region_chan->region, + parsed_region_chan->band); + + for (i = 0; i < region_chan->nrcfp; i++, cfp++) { + parsed_region_chan->chanpwr[i].chan = cfp->channel; + parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower; + lbs_pr_debug(1, "11D: Chan[%d] Pwr[%d]\n", + parsed_region_chan->chanpwr[i].chan, + parsed_region_chan->chanpwr[i].pwr); + } + parsed_region_chan->nr_chan = region_chan->nrcfp; + + lbs_pr_debug(1, "11D: nrchan[%d]\n", parsed_region_chan->nr_chan); + + return; +} + +/** + * @brief generate parsed_region_chan from Domain Info learned from AP/IBSS + * @param region region ID + * @param band band + * @param chan chan + * @return TRUE;FALSE +*/ +static u8 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan) +{ + struct chan_freq_power *cfp; + int cfp_no; + u8 idx; + + ENTER(); + + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp == NULL) + return 0; + + for (idx = 0; idx < cfp_no; idx++) { + if (chan == (cfp + idx)->channel) { + /* If Mrvl Chip Supported? */ + if ((cfp + idx)->unsupported) { + return 0; + } else { + return 1; + } + } + } + + /*chan is not in the region table */ + LEAVE(); + return 0; +} + +/** + * @brief This function checks if chan txpwr is learned from AP/IBSS + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return 0 +*/ +static int parse_domain_info_11d(struct ieeetypes_countryinfofullset* + countryinfo, + u8 band, + struct parsed_region_chan_11d * + parsed_region_chan) +{ + u8 nr_subband, nrchan; + u8 lastchan, firstchan; + u8 region; + u8 curchan = 0; + + u8 idx = 0; /*chan index in parsed_region_chan */ + + u8 j, i; + + ENTER(); + + /*validation Rules: + 1. valid region Code + 2. First Chan increment + 3. channel range no overlap + 4. channel is valid? + 5. channel is supported by region? + 6. Others + */ + + lbs_dbg_hex("CountryInfo:", (u8 *) countryinfo, 30); + + if ((*(countryinfo->countrycode)) == 0 + || (countryinfo->len <= COUNTRY_CODE_LEN)) { + /* No region Info or Wrong region info: treat as No 11D info */ + LEAVE(); + return 0; + } + + /*Step1: check region_code */ + parsed_region_chan->region = region = + wlan_region_2_code(countryinfo->countrycode); + + lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region); + lbs_dbg_hex("CountryCode:", (char *)countryinfo->countrycode, + COUNTRY_CODE_LEN); + + parsed_region_chan->band = band; + + memcpy(parsed_region_chan->countrycode, countryinfo->countrycode, + COUNTRY_CODE_LEN); + + nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) / + sizeof(struct ieeetypes_subbandset); + + for (j = 0, lastchan = 0; j < nr_subband; j++) { + + if (countryinfo->subband[j].firstchan <= lastchan) { + /*Step2&3. Check First Chan Num increment and no overlap */ + lbs_pr_debug(1, "11D: Chan[%d>%d] Overlap\n", + countryinfo->subband[j].firstchan, lastchan); + continue; + } + + firstchan = countryinfo->subband[j].firstchan; + nrchan = countryinfo->subband[j].nrchan; + + for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) { + /*step4: channel is supported? */ + + if (!wlan_get_chan_11d(band, firstchan, i, &curchan)) { + /* Chan is not found in UN table */ + lbs_pr_debug(1, "chan is not supported: %d \n", i); + break; + } + + lastchan = curchan; + + if (wlan_region_chan_supported_11d + (region, band, curchan)) { + /*step5: Check if curchan is supported by mrvl in region */ + parsed_region_chan->chanpwr[idx].chan = curchan; + parsed_region_chan->chanpwr[idx].pwr = + countryinfo->subband[j].maxtxpwr; + idx++; + } else { + /*not supported and ignore the chan */ + lbs_pr_debug(1, + "11D:i[%d] chan[%d] unsupported in region[%x] band[%d]\n", + i, curchan, region, band); + } + } + + /*Step6: Add other checking if any */ + + } + + parsed_region_chan->nr_chan = idx; + + lbs_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan); + lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan, + 2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx); + + LEAVE(); + return 0; +} + +/** + * @brief This function calculates the scan type for channels + * @param chan chan number + * @param parsed_region_chan pointer to parsed_region_chan_11d + * @return PASSIVE if chan is unknown; ACTIVE if chan is known +*/ +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d * parsed_region_chan) +{ + u8 scan_type = cmd_scan_type_passive; + + ENTER(); + + if (wlan_channel_known_11d(chan, parsed_region_chan)) { + lbs_pr_debug(1, "11D: Found and do Active Scan\n"); + scan_type = cmd_scan_type_active; + } else { + lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n"); + } + + LEAVE(); + return scan_type; + +} + +void libertas_init_11d(wlan_private * priv) +{ + priv->adapter->enable11d = 0; + memset(&(priv->adapter->parsed_region_chan), 0, + sizeof(struct parsed_region_chan_11d)); + return; +} + +static int wlan_enable_11d(wlan_private * priv, u8 flag) +{ + int ret; + + priv->adapter->enable11d = flag; + + /* send cmd to FW to enable/disable 11D function in FW */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11D_ENABLE, + &priv->adapter->enable11d); + if (ret) + lbs_pr_debug(1, "11D: Fail to enable 11D \n"); + + return 0; +} + +/** + * @brief This function sets DOMAIN INFO to FW + * @param priv pointer to wlan_private + * @return 0; -1 +*/ +static int set_domain_info_11d(wlan_private * priv) +{ + int ret; + + if (!priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n"); + return 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + if (ret) + lbs_pr_debug(1, "11D: Fail to dnld domain Info\n"); + + return ret; +} + +/** + * @brief This function setups scan channels + * @param priv pointer to wlan_private + * @param band band + * @return 0 +*/ +int libertas_set_universaltable(wlan_private * priv, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + u16 size = sizeof(struct chan_freq_power); + u16 i = 0; + + memset(adapter->universal_channel, 0, + sizeof(adapter->universal_channel)); + + adapter->universal_channel[i].nrcfp = + sizeof(channel_freq_power_UN_BG) / size; + lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n", + adapter->universal_channel[i].nrcfp); + + adapter->universal_channel[i].CFP = channel_freq_power_UN_BG; + adapter->universal_channel[i].valid = 1; + adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE; + adapter->universal_channel[i].band = band; + i++; + + return 0; +} + +/** + * @brief This function implements command CMD_802_11D_DOMAIN_INFO + * @param priv pointer to wlan_private + * @param cmd pointer to cmd buffer + * @param cmdno cmd ID + * @param cmdOption cmd action + * @return 0 +*/ +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdoption) +{ + struct cmd_ds_802_11d_domain_info *pdomaininfo = + &cmd->params.domaininfo; + struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain; + wlan_adapter *adapter = priv->adapter; + u8 nr_subband = adapter->domainreg.nr_subband; + + ENTER(); + + lbs_pr_debug(1, "nr_subband=%x\n", nr_subband); + + cmd->command = cpu_to_le16(cmdno); + pdomaininfo->action = cpu_to_le16(cmdoption); + if (cmdoption == cmd_act_get) { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + lbs_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd, + (int)(cmd->size)); + LEAVE(); + return 0; + } + + domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN); + memcpy(domain->countrycode, adapter->domainreg.countrycode, + sizeof(domain->countrycode)); + + domain->header.len = + cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) + + sizeof(domain->countrycode)); + + if (nr_subband) { + memcpy(domain->subband, adapter->domainreg.subband, + nr_subband * sizeof(struct ieeetypes_subbandset)); + + cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) + + domain->header.len + + sizeof(struct mrvlietypesheader) + + S_DS_GEN); + } else { + cmd->size = + cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN); + } + + lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size)); + + LEAVE(); + + return 0; +} + +/** + * @brief This function implements private cmd: enable/disable 11D + * @param priv pointer to wlan_private + * @param wrq pointer to user data + * @return 0 or -1 + */ +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq) +{ + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + + lbs_pr_debug(1, "enable 11D: %s\n", + (data == 1) ? "enable" : "Disable"); + + wlan_enable_11d(priv, data); + val = (int *)wrq->u.name; + *val = priv->adapter->enable11d; + + LEAVE(); + return 0; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @param resp pointer to command response buffer + * @return 0; -1 + */ +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11d_domain_info + *domaininfo = &resp->params.domaininforesp; + struct mrvlietypes_domainparamset *domain = &domaininfo->domain; + u16 action = le16_to_cpu(domaininfo->action); + s16 ret = 0; + u8 nr_subband = 0; + + ENTER(); + + lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp, + (int)le16_to_cpu(resp->size)); + + nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset); + /* countrycode 3 bytes */ + + lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband); + + if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) { + lbs_pr_debug(1, "Invalid Numrer of Subband returned!!\n"); + return -1; + } + + switch (action) { + case cmd_act_set: /*Proc Set action */ + break; + + case cmd_act_get: + break; + default: + lbs_pr_debug(1, "Invalid action:%d\n", domaininfo->action); + ret = -1; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief This function parses countryinfo from AP and download country info to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + if (priv->adapter->enable11d) { + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + ret = parse_domain_info_11d(&adapter->pattemptedbssdesc-> + countryinfo, 0, + &adapter->parsed_region_chan); + + if (ret == -1) { + lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n"); + LEAVE(); + return ret; + } + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + } + LEAVE(); + return 0; +} + +/** + * @brief This function generates 11D info from user specified regioncode and download to FW + * @param priv pointer to wlan_private + * @return 0; -1 + */ +int libertas_create_dnld_countryinfo_11d(wlan_private * priv) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + struct region_channel *region_chan; + u8 j; + + ENTER(); + lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band); + + if (priv->adapter->enable11d) { + /* update parsed_region_chan_11; dnld domaininf to FW */ + + for (j = 0; j < sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); j++) { + region_chan = &adapter->region_channel[j]; + + lbs_pr_debug(1, "11D:[%d] region_chan->band[%d]\n", j, + region_chan->band); + + if (!region_chan || !region_chan->valid + || !region_chan->CFP) + continue; + if (region_chan->band != adapter->curbssparams.band) + continue; + break; + } + + if (j >= sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0])) { + lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n", + adapter->curbssparams.band); + LEAVE(); + return -1; + } + + memset(&adapter->parsed_region_chan, 0, + sizeof(struct parsed_region_chan_11d)); + wlan_generate_parsed_region_chan_11d(region_chan, + &adapter-> + parsed_region_chan); + + memset(&adapter->domainreg, 0, + sizeof(struct wlan_802_11d_domain_reg)); + generate_domain_info_11d(&adapter->parsed_region_chan, + &adapter->domainreg); + + ret = set_domain_info_11d(priv); + + if (ret) { + lbs_pr_debug(1, "11D: Err set domainInfo to FW\n"); + LEAVE(); + return ret; + } + + } + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h new file mode 100644 index 0000000..db2ebea9 --- /dev/null +++ b/drivers/net/wireless/libertas/11d.h @@ -0,0 +1,105 @@ +/** + * This header file contains data structures and + * function declarations of 802.11d + */ +#ifndef _WLAN_11D_ +#define _WLAN_11D_ + +#include "types.h" +#include "defs.h" + +#define UNIVERSAL_REGION_CODE 0xff + +/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr) + */ +#define MRVDRV_MAX_SUBBAND_802_11D 83 + +#define COUNTRY_CODE_LEN 3 +#define MAX_NO_OF_CHAN 40 + +struct cmd_ds_command; + +/** Data structure for Country IE*/ +struct ieeetypes_subbandset { + u8 firstchan; + u8 nrchan; + u8 maxtxpwr; +} __attribute__ ((packed)); + +struct ieeetypes_countryinfoset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +}; + +struct ieeetypes_countryinfofullset { + u8 element_id; + u8 len; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +} __attribute__ ((packed)); + +struct mrvlietypes_domainparamset { + struct mrvlietypesheader header; + u8 countrycode[COUNTRY_CODE_LEN]; + struct ieeetypes_subbandset subband[1]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11d_domain_info { + u16 action; + struct mrvlietypes_domainparamset domain; +} __attribute__ ((packed)); + +/** domain regulatory information */ +struct wlan_802_11d_domain_reg { + /** country Code*/ + u8 countrycode[COUNTRY_CODE_LEN]; + /** No. of subband*/ + u8 nr_subband; + struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D]; +}; + +struct chan_power_11d { + u8 chan; + u8 pwr; +} __attribute__ ((packed)); + +struct parsed_region_chan_11d { + u8 band; + u8 region; + s8 countrycode[COUNTRY_CODE_LEN]; + struct chan_power_11d chanpwr[MAX_NO_OF_CHAN]; + u8 nr_chan; +} __attribute__ ((packed)); + +struct region_code_mapping { + u8 region[COUNTRY_CODE_LEN]; + u8 code; +}; + +u8 libertas_get_scan_type_11d(u8 chan, + struct parsed_region_chan_11d *parsed_region_chan); + +u32 libertas_chan_2_freq(u8 chan, u8 band); + +enum state_11d libertas_get_state_11d(wlan_private * priv); + +void libertas_init_11d(wlan_private * priv); + +int libertas_set_universaltable(wlan_private * priv, u8 band); + +int libertas_cmd_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *cmd, u16 cmdno, + u16 cmdOption); + +int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq); + +int libertas_ret_802_11d_domain_info(wlan_private * priv, + struct cmd_ds_command *resp); + +int libertas_parse_dnld_countryinfo_11d(wlan_private * priv); + +int libertas_create_dnld_countryinfo_11d(wlan_private * priv); + +#endif /* _WLAN_11D_ */ diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/LICENSE new file mode 100644 index 0000000..8862742 --- /dev/null +++ b/drivers/net/wireless/libertas/LICENSE @@ -0,0 +1,16 @@ + Copyright (c) 2003-2006, Marvell International Ltd. + All Rights Reserved + + This program is free software; you can redistribute it and/or modify it + under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along with + this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place - Suite 330, Boston, MA 02111-1307, USA. + diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile new file mode 100644 index 0000000..19c9350 --- /dev/null +++ b/drivers/net/wireless/libertas/Makefile @@ -0,0 +1,21 @@ +# EXTRA_CFLAGS += -Wpacked + +usb8xxx-objs := main.o fw.o wext.o \ + rx.o tx.o cmd.o \ + cmdresp.o scan.o \ + join.o 11d.o \ + ioctl.o debugfs.o \ + ethtool.o assoc.o + +ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y) +EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG +endif + + +# This is needed to support the newer boot2 bootloader (v >= 3104) +EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND +usb8xxx-objs += if_bootcmd.o +usb8xxx-objs += if_usb.o + +obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o + diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README new file mode 100644 index 0000000..688da4c --- /dev/null +++ b/drivers/net/wireless/libertas/README @@ -0,0 +1,1044 @@ +================================================================================ + README for USB8388 + + (c) Copyright © 2003-2006, Marvell International Ltd. + All Rights Reserved + + This software file (the "File") is distributed by Marvell International + Ltd. under the terms of the GNU General Public License Version 2, June 1991 + (the "License"). You may use, redistribute and/or modify this File in + accordance with the terms and conditions of the License, a copy of which + is available along with the File in the license.txt file or by writing to + the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt. + + THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE + IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE + ARE EXPRESSLY DISCLAIMED. The License provides additional details about + this warranty disclaimer. +================================================================================ + +===================== +DRIVER LOADING +===================== + + o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/ + + o. Load driver by using the following command: + + insmod usb8388.ko [fw_name=usb8388.bin] + +===================== +IWPRIV COMMAND +===================== + +NAME + This manual describes the usage of private commands used in Marvell WLAN + Linux Driver. All the commands available in Wlanconfig will not be available + in the iwpriv. + +SYNOPSIS + iwpriv <ethX> <command> [sub-command] ... + + iwpriv ethX version + iwpriv ethX scantype [sub-command] + iwpriv ethX getSNR <n> + iwpriv ethX getNF <n> + iwpriv ethX getRSSI <n> + iwpriv ethX setrxant <n> + iwpriv ethX getrxant + iwpriv ethX settxant <n> + iwpriv ethX gettxant + iwpriv ethX authalgs <n> + iwpriv ethX pre-TBTT <n> + iwpriv ethX 8021xauthalgs <n> + iwpriv ethX encryptionmode <n> + iwpriv ethX setregioncode <n> + iwpriv ethX getregioncode + iwpriv ethX setbcnavg <n> + iwpriv ethX getbcnavg + iwpriv ethX setdataavg <n> + iwpriv ethX setlisteninter <n> + iwpriv ethX getlisteninter + iwpriv ethX setmultipledtim <n> + iwpriv ethX getmultipledtim + iwpriv ethX atimwindow <n> + iwpriv ethX deauth + iwpriv ethX adhocstop + iwpriv ethX radioon + iwpriv ethX radiooff + iwpriv ethX reasso-on + iwpriv ethX reasso-off + iwpriv ethX scanmode [sub-command] + iwpriv ethX setwpaie <n> + iwpriv ethX wlanidle-off + iwpriv ethX wlanidle-on + iwpriv ethX getcis + iwpriv ethX getlog + iwpriv ethX getadhocstatus + iwpriv ethX adhocgrate <n> + +Version 4 Command: + iwpriv ethX inactvityto <n> + iwpriv ethX sleeppd <n> + iwpriv ethX enable11d <n> + iwpriv ethX tpccfg <n> + iwpriv ethX powercfg <n> + iwpriv ethX setafc <n> + iwpriv ethX getafc + +Version 5 Command: + iwpriv ethX ledgpio <n> + iwpriv ethX scanprobes <n> + iwpriv ethX lolisteninter <n> + iwpriv ethX rateadapt <n> <m> + iwpriv ethX txcontrol <n> + iwpriv ethX psnullinterval <n> + iwpriv ethX prescan <n> + iwpriv ethX getrxinfo + iwpriv ethX gettxrate + iwpriv ethX beaconinterval + +BT Commands: + The blinding table (BT) contains a list of mac addresses that should be + ignored by the firmware. It is primarily used for debugging and + testing networks. It can be edited and inspected with the following + commands: + + iwpriv ethX bt_reset + iwpriv ethX bt_add <mac_address> + iwpriv ethX bt_del <mac_address> + iwpriv ethX bt_list <id> + +FWT Commands: + The forwarding table (FWT) is a feature used to manage mesh network + routing in the firmware. The FWT is essentially a routing table that + associates a destination mac address (da) with a next hop receiver + address (ra). The FWT can be inspected and edited with the following + iwpriv commands, which are described in greater detail below. + Eventually, the table will be automatically maintained by a custom + routing protocol. + + NOTE: FWT commands replace the previous DFT commands. What were the DFT + commands?, you might ask. They were an earlier API to the firmware that + implemented a simple MAC-layer forwarding mechanism. In the unlikely + event that you were using these commands, you must migrate to the new + FWT commands which can be used to achieve the same functionality. + + iwpriv ethX fwt_add [parameters] + iwpriv ethX fwt_del [parameters] + iwpriv ethX fwt_lookup [parameters] + iwpriv ethX fwt_list [parameters] + iwpriv ethX fwt_list_route [parameters] + iwpriv ethX fwt_list_neigh [parameters] + iwpriv ethX fwt_reset [parameters] + iwpriv ethX fwt_cleanup + iwpriv ethX fwt_time + +MESH Commands: + + The MESH commands are used to configure various features of the mesh + routing protocol. The following commands are supported: + + iwpriv ethX mesh_get_ttl + iwpriv ethX mesh_set_ttl ttl + +DESCRIPTION + Those commands are used to send additional commands to the Marvell WLAN + card via the Linux device driver. + + The ethX parameter specifies the network device that is to be used to + perform this command on. it could be eth0, eth1 etc. + +version + This is used to get the current version of the driver and the firmware. + +scantype + This command is used to set the scan type to be used by the driver in + the scan command. This setting will not be used while performing a scan + for a specific SSID, as it is always done with scan type being active. + + where the sub-commands are: - + active -- to set the scan type to active + passive -- to set the scan type to passive + get -- to get the scan type set in the driver + +getSNR + This command gets the average and non average value of Signal to Noise + Ratio of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + If no value is given, all four values are returned in the order mentioned + above. + + Note: This command is available only when STA is connected. + +getRSSI + This command gets the average and non average value os Receive Signal + Strength of Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +getNF + This command gets the average and non average value of Noise Floor of + Beacon and Data. + + where value is:- + 0 -- Beacon non-average. + 1 -- Beacon average. + 2 -- Data non-average. + 3 -- Data average. + + Note: This command is available only when STA is connected. + +setrxant + This command is used to set the mode for Rx antenna. + + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + + Usage: + iwpriv ethX setrxant 0x01: select Antenna 1. + +getrxant + This command is used to get the mode for Rx antenna. + + +settxant + This command is used to set the mode for Tx antenna. + The options that can be sent are:- + 1 -- Antenna 1. + 2 -- Antenna 2. + 0xFFFF -- Diversity. + Usage: + iwpriv ethX settxant 0x01: select Antenna 1. + +gettxant + This command is used to get the mode for Tx antenna. + +authalgs + This command is used by the WPA supplicant to set the authentication + algorithms in the station. + +8021xauthalgs + This command is used by the WPA supplicant to set the 8021.x authentication algorithm type + station. + + where values can be:- + 1 -- None + 2 -- LEAP + 4 -- TLS + 8 -- TTLs + 16 -- MD5 + + +encryptionmode + This command is used by the WPA supplicant to set the encryption algorithm. + + where values can be:- + 0 -- NONE + 1 -- WEP40 + 2 -- TKIP + 3 -- CCMP + 4 -- WEP104 + +pre-TBTT + This command is used to set pre-TBTT time period where value is in microseconds. + +setregioncode + This command is used to set the region code in the station. + where value is 'region code' for various regions like + USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ... + + Usage: + iwpriv ethX setregioncode 0x10: set region code to USA (0x10). + +getregioncode + This command is used to get the region code information set in the + station. + +setbcnavg + Set the weighting factor for calculating RSSI. + +getbcnavg + Get weighting factor for calculating RSSI. + +setdataavg + Set the weighting factor for calculating SNR. + +setlisteninter + This command is used to set the listen interval in the + station. + + where the value ranges between 1 - 255 + +getlisteninter + This command is used to get the listen interval value set in the + station. + +setmultipledtim + This command is used to set the multiple dtim value in the + station. + where the value is 1,2,3,4,5,0xfffe + 0xfffe means the firmware will use listen interval in association + command for waking up + +getmultipledtim + This command is used to get the multiple dtim value set in the station. + +atimwindow + This command is used to set the atim value in the + station. + + where the value ranges between 0 - 50 + +deauth + This command is used to send the de-authentication to the AP with which + the station is associated. This command is valid only when + station is in Infrastructure mode. + + Note: This command is available only when STA is connected. + +adhocstop + This command is used to stop beacon transmission from the station and + go into idle state in ad-hoc mode. + + Note: This command is available only when STA is connected. + +radioon + This command is used to turn on the RF antenna. + +radiooff + This command is sued to turn off the RF antenna. + +scanmode + This command is used to set the station to scan for either IBSS + networks or BSS networks or both BSS and IBSS networks. This + command can be used with sub commands, + + where the value for + bss -- Scan All the BSS networks. + ibss -- Scan All the IBSS networks. + any -- Scan both BSS and IBSS networks. + + + +setwpaie + This command is used by WPA supplicant to send the WPA-IE to the driver. + +wlanidle-off + This command is used to get into idle state. + + Note: This command is available only when STA is connected. + +wlanidle-on + This command is used to get off the idle state. + + Note: This command is available only when STA is connected. + + +getlog + This command is used to get the 802.11 statistics available in the + station. + + Note: This command is available only when STA is connected. + +getadhocstatus + This command is used to get the ad-hoc Network Status. + + The various status codes are: + AdhocStarted + AdhocJoined + AdhocIdle + InfraMode + AutoUnknownMode + + Note: This command is available only when STA is connected. + +adhocgrate + This command is used to enable(1) g_rate, Disable(0) g_rate + and request(2) the status which g_rate is disabled/enabled, + for Ad-hoc creator. + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +ledgpio + This command is used to set/get LEDs. + + iwpriv ethX ledgpio <LEDs> + will set the corresponding LED for the GPIO Line. + + iwpriv ethX ledgpio + will give u which LEDs are Enabled. + + Usage: + iwpriv eth1 ledgpio 1 0 2 1 3 4 + will enable + LED 1 -> GPIO 0 + LED 2 -> GPIO 1 + LED 3 -> GPIO 4 + + iwpriv eth1 ledgpio + shows LED information in the format as mentioned above. + + Note: LED0 is invalid + Note: Maximum Number of LEDs are 16. + +inactivityto + This command is used by the host to set/get the inactivity timeout value, + which specifies when WLAN device is put to sleep. + + Usage: + iwpriv ethX inactivityto [<timeout>] + + where the parameter are: + timeout: timeout value in milliseconds. + + Example: + iwpriv eth1 inactivityto + "get the timeout value" + + iwpriv eth1 inactivityto X + "set timeout value to X ms" + + +sleeppd + This command is used to configure the sleep period of the WLAN device. + + Usage: + iwpriv ethX sleeppd [<sleep period>] + + where the parameter are: + Period: sleep period in milliseconds. Range 10~60. + + Example: + iwpriv eth1 sleeppd 10 + "set period as 10 ms" + iwpriv eth1 sleeppd + "get the sleep period configuration" + +enable11d + This command is used to control 11d + where value is:- + 1 -- Enabled + 0 -- Disabled + 2 -- Get + + + + +tpccfg + Enables or disables automatic transmit power control. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply four more parameters in the following + order: + -UseSNR (Use SNR (in addition to PER) for TPC algorithm), + -P0 (P0 power level for TPC), + -P1 (P1 power level for TPC), + -P2 (P2 power level for TPC). + + Usage: + iwpriv ethX tpccfg: Get current configuration + iwpriv ethX tpccfg 0: disable auto TPC + iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR; + P0=0x05; P1=0x0a; P2=0x0d; + iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR; + P0=0x05; P1=0x0a; P2=0x0d. + +powercfg + Enables or disables power adaptation. + + The first parameter turns this feature on (1) or off (0). When turning + on, the user must also supply three more parameters in the following + order: + -P0 (P0 power level for Power Adaptation), + -P1 (P1 power level for Power Adaptation), + -P2 (P2 power level for Power Adaptation). + + Usage: + iwpriv ethX powercfg: Get current configuration + iwpriv ethX powercfg 0: disable power adaptation + iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation; + P0=0x0d; P1=0x0f; P2=0x12. + +getafc + This command returns automatic frequency control parameters. It returns + three integers: + -P0: automatic is on (1), or off (0), + -P1: current timing offset in PPM (part per million), and + -P2: current frequency offset in PPM. + +setafc + Set automatic frequency control options. + + The first parameter turns automatic on (1) or off (0). + The user must supply two more parameters in either case, in the following + order: + + When auto is on: + + -P0 (automatic adjustment frequency threshold in PPM), + -P1 (automatic adjustment period in beacon period), + + When auto is off: + + -P0 (manual adjustment timing offset in PPM), and + -P1 (manual adjustment frequency offset in PPM). + + Usage: + iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy + offset are 10 PPM. + + iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment, + frequency threshold 10 PPM, for every 10 beacon periods. + + + +scanprobes + This command sets number of probe requests per channel. + + Usage: + iwpriv ethX scanprobes 3 (set scan probes to 3) + iwpriv ethX scanprobes (get scan probes) + +lolisteninter + This command sets the value of listen interval. + + Usage: + iwpriv ethX lolisteninter 234 (set the lolisteninter to 234) + iwpriv ethX lolisteninter (get the lolisteninter value) + +rateadapt + This command sets the data rates bitmap. + Where <n> + 0: Disable auto rate adapt + 1: Enable auto rate adapt + + <m> + data rate bitmap + Bit Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 12-15 Reserved + + Usage: + iwpriv ethX rateadapt + read the currect data rate setting + iwpriv ethX rateadapt 1 0x07 + enable auto data rate adapt and + data rates are 1Mbps, 2Mbsp and 5.5Mbps + + +txcontrol + This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis. + + Where value <n> is: + if bit[4] == 1: + bit[3:0] -- 0 1 2 3 4 5 6 7 8 9 10 11 12 13-16 + Data Rate(Mbps) -- 1 2 5.5 11 Rsv 6 9 12 18 24 36 48 54 Rsv + + bit[12:8] + if bit[12] == 1, bit[11:8] specifies the Tx retry limit. + + bit[14:13] specifies per packet ack policy: + bit[14:13] + 1 0 use immediate ack policy for this packet + 1 1 use no ack policy for this packet + 0 x use the per-packet ack policy setting + + Usage: + iwpriv ethX txcontrol 0x7513 + Use no-ack policy, 5 retires for Tx, 11Mbps rate + + + +psnullinterval + This command is used to set/request NULL package interval for Power Save + under infrastructure mode. + + where value is:- + -1 -- Disabled + n>0 -- Set interval as n (seconds) + +prescan + This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap + + where value is:- + 0 -- Disabled + 1 -- Enabled + 2 -- Get + +getrxinfo + This command gets non average value of Signal to Noise Ratio of Data and rate index. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +gettxrate + This command gets current Tx rate index of the first packet associated with Rate Adaptation. + + The following table shows RateIndex and Rate + + RateIndex Data rate + 0 1 Mbps + 1 2 Mbps + 2 5.5 Mbps + 3 11 Mbps + 4 Reserved + 5 6 Mbps + 6 9 Mbps + 7 12 Mbps + 8 18 Mbps + 9 24 Mbps + 10 36 Mbps + 11 48 Mbps + 12 54 Mbps + 13-15 Reserved + +bcninterval + This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc + beacon interval when no argument is given. The valid beacon interval is between 20 - 1000, + default beacon interval is 100. + + Usage: + iwpriv ethX bcninterval 100 (set adhoc beacon interval to 100) + iwpriv ethX bcninterval (get adhoc beacon interval) + +fwt_add + This command is used to insert an entry into the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr] + + The parameters between brackets are optional, but they must appear in + the order specified. For example, if you want to specify the metric, + you must also specify the dir, ssn, and dsn but you need not specify the + hopcount, expiration, sleepmode, or snr. Any unspecified parameters + will be assigned the defaults specified below. + + The different parameters are:- + da -- DA MAC address in the form 00:11:22:33:44:55 + ra -- RA MAC address in the form 00:11:22:33:44:55 + metric -- route metric (cost: smaller-metric routes are + preferred, default is 0) + dir -- direction (1 for direct, 0 for reverse, + default is 1) + ssn -- Source Sequence Number (time at the RA for + reverse routes. Default is 0) + dsn -- Destination Sequence Number (time at the DA + for direct routes. Default is 0) + hopcount -- hop count (currently unused, default is 0) + ttl -- TTL (Only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is + 1024us, or ~ 1ms. Use 0 for an indefinite + entry, default is 0) + sleepmode -- RA's sleep mode (currently unused, default is + 0) + snr -- SNR in the link to RA (currently unused, + default is 0) + + The command does not return anything. + +fwt_del + This command is used to remove an entry to the FWT table. The list of + parameters must follow the following structure: + + iwpriv ethX fwt_del da ra [dir] + + where the different parameters are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + dir -- direction (1 for direct, 0 for reverse, + default is 1) + + The command does not return anything. + +fwt_lookup + This command is used to get the best route in the FWT table to a given + host. The only parameter is the MAC address of the host that is being + looked for. + + iwpriv ethX fwt_lookup da + + where:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + + The command returns an output string identical to the one returned by + fwt_list described below. + + +fwt_list + This command is used to list a route from the FWT table. The only + parameter is the index into the table. If you want to list all the + routes in a table, start with index=0, and keep listing until you get a + "(null)" string. Note that the indicies may change as the fwt is + updated. It is expected that most users will not use fwt_list directly, + but that a utility similar to the traditional route command will be used + to invoke fwt_list over and over. + + iwpriv ethX fwt_list index + + The output is a string of the following form: + + da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + ra -- RA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + +fwt_list_route + This command is used to list a route from the FWT table. The only + parameter is the route ID. If you want to list all the routes in a + table, start with rid=0, and keep incrementing rid until you get a + "(null)" string. This function is similar to fwt_list. The only + difference is the output format. Also note that this command is meant + for debugging. It is expected that users will use fwt_lookup and + fwt_list. One important reason for this is that the route id may change + as the route table is altered. + + iwpriv ethX fwt_list_route rid + + The output is a string of the following form: + + da metric dir nid ssn dsn hopcount ttl expiration + + where the different fields are:- + da -- DA MAC address (in the form "00:11:22:33:44:55") + metric -- route metric (cost: smaller-metric routes are preferred) + dir -- direction (1 for direct, 0 for reverse) + nid -- Next-hop (neighbor) host ID (nid) + ssn -- Source Sequence Number (time at the RA for reverse routes) + dsn -- Destination Sequence Number (time at the DA for direct routes) + hopcount -- hop count (currently unused) + ttl -- TTL count (only used in reverse entries) + expiration -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry) + +fwt_list_neigh + This command is used to list a neighbor from the FWT table. The only + parameter is the neighbor ID. If you want to list all the neighbors in a + table, start with nid=0, and keep incrementing nid until you get a + "(null)" string. Note that the nid from a fwt_list_route command can be + used as an input to this command. Also note that this command is meant + mostly for debugging. It is expected that users will use fwt_lookup. + One important reason for this is that the neighbor id may change as the + neighbor table is altered. + + iwpriv ethX fwt_list_neigh nid + + The output is a string of the following form: + + ra sleepmode snr references + + where the different fields are:- + ra -- RA MAC address (in the form "00:11:22:33:44:55") + sleepmode -- RA's sleep mode (currently unused) + snr -- SNR in the link to RA (currently unused) + references -- RA's reference counter + +fwt_reset + This command is used to reset the FWT table, getting rid of all the + entries. There are no input parameters. + + iwpriv ethX fwt_reset + + The command does not return anything. + +fwt_cleanup + This command is used to perform user-based garbage recollection. The + FWT table is checked, and all the entries that are expired or invalid + are cleaned. Note that this is exported to the driver for debugging + purposes, as garbage collection is also fired by the firmware when in + space problems. There are no input parameters. + + iwpriv ethX fwt_cleanup + + The command does returns the number of invalid/expired routes deleted. + +fwt_time + This command returns a card's internal time representation. It is this + time that is used to represent the expiration times of FWT entries. The + number is not consistent from card to card; it is simply a timer count. + The fwt_time command is used to inspect the timer so that expiration + times reported by fwt_list can be properly interpreted. + + iwpriv ethX fwt_time + +mesh_get_ttl + + The mesh ttl is the number of hops a mesh packet can traverse before it + is dropped. This parameter is used to prevent infinite loops in the + mesh network. The value returned by this function is the ttl assigned + to all mesh packets. Currently there is no way to control the ttl on a + per packet or per socket basis. + + iwpriv ethX mesh_get_ttl + +mesh_set_ttl ttl + + Set the ttl. The argument must be between 0 and 255. + + iwpriv ethX mesh_set_ttl <ttl> + +========================= +ETHTOOL +========================= + + +Use the -i option to retrieve version information from the driver. + +# ethtool -i eth0 +driver: libertas +version: COMM-USB8388-318.p4 +firmware-version: 5.110.7 +bus-info: + +Use the -e option to read the EEPROM contents of the card. + + Usage: + ethtool -e ethX [raw on|off] [offset N] [length N] + + -e retrieves and prints an EEPROM dump for the specified ethernet + device. When raw is enabled, then it dumps the raw EEPROM data + to stdout. The length and offset parameters allow dumping cer- + tain portions of the EEPROM. Default is to dump the entire EEP- + ROM. + +# ethtool -e eth0 offset 0 length 16 +Offset Values +------ ------ +0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00 + +======================== +DEBUGFS COMMANDS +======================== + +those commands are used via debugfs interface + +=========== +rdmac +rdbbp +rdrf + These commands are used to read the MAC, BBP and RF registers from the + card. These commands take one parameter that specifies the offset + location that is to be read. This parameter must be specified in + hexadecimal (its possible to preceed preceding the number with a "0x"). + + Path: /debugfs/libertas_wireless/ethX/registers/ + + Usage: + echo "0xa123" > rdmac ; cat rdmac + echo "0xa123" > rdbbp ; cat rdbbp + echo "0xa123" > rdrf ; cat rdrf +wrmac +wrbbp +wrrf + These commands are used to write the MAC, BBP and RF registers in the + card. These commands take two parameters that specify the offset + location and the value that is to be written. This parameters must + be specified in hexadecimal (its possible to preceed the number + with a "0x"). + + Usage: + echo "0xa123 0xaa" > wrmac + echo "0xa123 0xaa" > wrbbp + echo "0xa123 0xaa" > wrrf + +sleepparams + This command is used to set the sleepclock configurations + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat sleepparams: reads the current sleepclock configuration + + echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration. + + where: + p1 is Sleep clock error in ppm (0-65535) + p2 is Wakeup offset in usec (0-65535) + p3 is Clock stabilization time in usec (0-65535) + p4 is Control periodic calibration (0-2) + p5 is Control the use of external sleep clock (0-2) + p6 is reserved for debug (0-65535) + +subscribed_events + + The subscribed_events directory contains the interface for the + subscribed events API. + + Path: /debugfs/libertas_wireless/ethX/subscribed_events/ + + Each event is represented by a filename. Each filename consists of the + following three fields: + Value Frequency Subscribed + + To read the current values for a given event, do: + cat event + To set the current values, do: + echo "60 2 1" > event + + Frequency field specifies the reporting frequency for this event. + If it is set to 0, then the event is reported only once, and then + automatically unsubscribed. If it is set to 1, then the event is + reported every time it occurs. If it is set to N, then the event is + reported every Nth time it occurs. + + beacon_missed + Value field specifies the number of consecutive missing beacons which + triggers the LINK_LOSS event. This event is generated only once after + which the firmware resets its state. At initialization, the LINK_LOSS + event is subscribed by default. The default value of MissedBeacons is + 60. + + failure_count + Value field specifies the consecutive failure count threshold which + triggers the generation of the MAX_FAIL event. Once this event is + generated, the consecutive failure count is reset to 0. + At initialization, the MAX_FAIL event is NOT subscribed by + default. + + high_rssi + This event is generated when the average received RSSI in beacons goes + above a threshold, specified by Value. + + low_rssi + This event is generated when the average received RSSI in beacons goes + below a threshold, specified by Value. + + high_snr + This event is generated when the average received SNR in beacons goes + above a threshold, specified by Value. + + low_snr + This event is generated when the average received SNR in beacons goes + below a threshold, specified by Value. + +extscan + This command is used to do a specific scan. + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: echo "SSID" > extscan + + Example: + echo "LINKSYS-AP" > extscan + + To see the results of use getscantable command. + +getscantable + + Display the current contents of the driver scan table (ie. get the + scan results). + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + cat getscantable + +setuserscan + Initiate a customized scan and retrieve the results + + + Path: /debugfs/libertas_wireless/ethX/ + + Usage: + echo "[ARGS]" > setuserscan + + where [ARGS]: + + chan=[chan#][band][mode] where band is [a,b,g] and mode is + blank for active or 'p' for passive + bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan + ssid="[SSID]" specify a SSID filter for the scan + keep=[0 or 1] keep the previous scan results (1), discard (0) + dur=[scan time] time to scan for each channel in milliseconds + probes=[#] number of probe requests to send on each chan + type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any) + + Any combination of the above arguments can be supplied on the command line. + If the chan token is absent, a full channel scan will be completed by + the driver. If the dur or probes tokens are absent, the driver default + setting will be used. The bssid and ssid fields, if blank, + will produce an unfiltered scan. The type field will default to 3 (Any) + and the keep field will default to 0 (Discard). + + Examples: + 1) Perform an active scan on channels 1, 6, and 11 in the 'g' band: + echo "chan=1g,6g,11g" > setuserscan + + 2) Perform a passive scan on channel 11 for 20 ms: + echo "chan=11gp dur=20" > setuserscan + + 3) Perform an active scan on channels 1, 6, and 11; and a passive scan on + channel 36 in the 'a' band: + + echo "chan=1g,6g,11g,36ap" > setuserscan + + 4) Perform an active scan on channel 6 and 36 for a specific SSID: + echo "chan=6g,36a ssid="TestAP"" > setuserscan + + 5) Scan all available channels (B/G, A bands) for a specific BSSID, keep + the current scan table intact, update existing or append new scan data: + echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan + + 6) Scan channel 6, for all infrastructure networks, sending two probe + requests. Keep the previous scan table intact. Update any duplicate + BSSID/SSID matches with the new scan data: + echo "chan=6g type=1 probes=2 keep=1" > setuserscan + + All entries in the scan table (not just the new scan data when keep=1) + will be displayed upon completion by use of the getscantable ioctl. + +============================================================================== diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c new file mode 100644 index 0000000..b55c7f5 --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.c @@ -0,0 +1,588 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#include <linux/bitops.h> +#include <net/ieee80211.h> + +#include "assoc.h" +#include "join.h" +#include "decl.h" +#include "hostcmd.h" +#include "host.h" + + +static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static int assoc_helper_essid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid); + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->prescan) { + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1); + } + + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, + NULL, wlan802_11infrastructure); + if (i >= 0) { + lbs_pr_debug(1, + "SSID found in scan list ... associating...\n"); + + ret = wlan_associate(priv, &adapter->scantable[i]); + if (ret == 0) { + memcpy(&assoc_req->bssid, + &adapter->scantable[i].macaddress, + ETH_ALEN); + } + } else { + lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n", + assoc_req->ssid.ssid); + } + } else if (assoc_req->mode == wlan802_11ibss) { + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0); + + /* Search for the requested SSID in the scan table */ + i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL, + wlan802_11ibss); + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + /* else send START command */ + lbs_pr_debug(1, "SSID not found in list, so creating adhoc" + " with SSID '%s'\n", assoc_req->ssid.ssid); + libertas_start_adhoc_network(priv, &assoc_req->ssid); + } + memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN); + } + + LEAVE(); + return ret; +} + + +static int assoc_helper_bssid(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i, ret = 0; + + ENTER(); + + lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n", + MAC_ARG(assoc_req->bssid)); + + /* Search for index position in list for requested MAC */ + i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid, + assoc_req->mode); + if (i < 0) { + lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, " + "cannot associate.\n", MAC_ARG(assoc_req->bssid)); + goto out; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + ret = wlan_associate(priv, &adapter->scantable[i]); + lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret); + } else if (assoc_req->mode == wlan802_11ibss) { + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } + memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid, + sizeof(struct WLAN_802_11_SSID)); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_associate(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0, done = 0; + + /* If we're given and 'any' BSSID, try associating based on SSID */ + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN) + && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) { + ret = assoc_helper_bssid(priv, assoc_req); + done = 1; + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + } + + if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + ret = assoc_helper_essid(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret); + } + } + + return ret; +} + + +static int assoc_helper_mode(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->mode == adapter->inframode) { + LEAVE(); + return 0; + } + + if (assoc_req->mode == wlan802_11infrastructure) { + if (adapter->psstate != PS_STATE_FULL_POWER) + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + adapter->psmode = wlan802_11powermodecam; + } + + adapter->inframode = assoc_req->mode; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + 0, cmd_option_waitforrsp, + OID_802_11_INFRASTRUCTURE_MODE, + (void *) assoc_req->mode); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wep_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int i; + int ret = 0; + + ENTER(); + + /* Set or remove WEP keys */ + if ( assoc_req->wep_keys[0].len + || assoc_req->wep_keys[1].len + || assoc_req->wep_keys[2].len + || assoc_req->wep_keys[3].len) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, assoc_req); + } else { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_remove, + cmd_option_waitforrsp, + 0, NULL); + } + + if (ret) + goto out; + + /* enable/disable the MAC's WEP packet filter */ + if (assoc_req->secinfo.WEPstatus == wlan802_11WEPenabled) + adapter->currentpacketfilter |= cmd_act_mac_wep_enable; + else + adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable; + ret = libertas_set_mac_packet_filter(priv); + if (ret) + goto out; + + mutex_lock(&adapter->lock); + + /* Copy WEP keys into adapter wep key fields */ + for (i = 0; i < 4; i++) { + memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx; + + mutex_unlock(&adapter->lock); + +out: + LEAVE(); + return ret; +} + +static int assoc_helper_secinfo(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + memcpy(&adapter->secinfo, &assoc_req->secinfo, + sizeof(struct wlan_802_11_security)); + + ret = libertas_set_mac_packet_filter(priv); + + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_keys(wlan_private *priv, + struct assoc_request * assoc_req) +{ + int ret = 0; + + ENTER(); + + /* enable/Disable RSN */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_enable_rsn, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + if (ret) + goto out; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_key_material, + cmd_act_set, + cmd_option_waitforrsp, + 0, assoc_req); + +out: + LEAVE(); + return ret; +} + + +static int assoc_helper_wpa_ie(wlan_private *priv, + struct assoc_request * assoc_req) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) { + memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len); + adapter->wpa_ie_len = assoc_req->wpa_ie_len; + } else { + memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN); + adapter->wpa_ie_len = 0; + } + + LEAVE(); + return ret; +} + + +static int should_deauth_infrastructure(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new SSID in " + " configuration request.\n"); + return 1; + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + if (adapter->secinfo.authmode != + assoc_req->secinfo.authmode) { + lbs_pr_debug(1, "Deauthenticating due to updated security " + "info in configuration request.\n"); + return 1; + } + } + + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + lbs_pr_debug(1, "Deauthenticating due to new BSSID in " + " configuration request.\n"); + return 1; + } + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11infrastructure) + return 1; + } + + return 0; +} + + +static int should_stop_adhoc(wlan_adapter *adapter, + struct assoc_request * assoc_req) +{ + if (adapter->connect_status != libertas_connected) + return 0; + + if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength) + return 1; + if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid, + sizeof(struct WLAN_802_11_SSID))) + return 1; + + /* FIXME: deal with 'auto' mode somehow */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + if (assoc_req->mode != wlan802_11ibss) + return 1; + } + + return 0; +} + + +void wlan_association_worker(struct work_struct *work) +{ + wlan_private *priv = container_of(work, wlan_private, assoc_work.work); + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req = NULL; + int ret = 0; + int find_any_ssid = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = adapter->assoc_req; + adapter->assoc_req = NULL; + mutex_unlock(&adapter->lock); + + if (!assoc_req) { + LEAVE(); + return; + } + + lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n", + assoc_req->flags); + + /* If 'any' SSID was specified, find an SSID to associate with */ + if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags) + && !assoc_req->ssid.ssidlength) + find_any_ssid = 1; + + /* But don't use 'any' SSID if there's a valid locked BSSID to use */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + if (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN) + && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN)) + find_any_ssid = 0; + } + + if (find_any_ssid) { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid, + assoc_req->mode, &new_mode); + if (ret) { + lbs_pr_debug(1, "Could not find best network\n"); + ret = -ENETUNREACH; + goto out; + } + + /* Ensure we switch to the mode of the AP */ + if (assoc_req->mode == wlan802_11autounknown) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + assoc_req->mode = new_mode; + } + } + + /* + * Check if the attributes being changing require deauthentication + * from the currently associated infrastructure access point. + */ + if (adapter->inframode == wlan802_11infrastructure) { + if (should_deauth_infrastructure(adapter, assoc_req)) { + ret = libertas_send_deauthentication(priv); + if (ret) { + lbs_pr_debug(1, "Deauthentication due to new " + "configuration request failed: %d\n", + ret); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + if (should_stop_adhoc(adapter, assoc_req)) { + ret = libertas_stop_adhoc_network(priv); + if (ret) { + lbs_pr_debug(1, "Teardown of AdHoc network due to " + "new configuration request failed: %d\n", + ret); + } + + } + } + + /* Send the various configuration bits to the firmware */ + if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) { + ret = assoc_helper_mode(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if ( test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) { + ret = assoc_helper_wep_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + ret = assoc_helper_secinfo(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + ret = assoc_helper_wpa_ie(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, ret); + goto out; + } + } + + if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags) + || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + ret = assoc_helper_wpa_keys(priv, assoc_req); + if (ret) { +lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, ret); + goto out; + } + } + + /* SSID/BSSID should be the _last_ config option set, because they + * trigger the association attempt. + */ + if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags) + || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + int success = 1; + + ret = assoc_helper_associate(priv, assoc_req); + if (ret) { + lbs_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n", + ret); + success = 0; + } + + if (adapter->connect_status != libertas_connected) { + lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, " + "not connected.\n"); + success = 0; + } + + if (success) { + lbs_pr_debug(1, "ASSOC: association attempt successful. " + "Associated to '%s' (" MAC_FMT ")\n", + assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid)); + libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, 0, NULL); + + libertas_prepare_and_send_command(priv, + cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + } else { + + ret = -1; + } + } + +out: + if (ret) { + lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n", + ret); + } + kfree(assoc_req); + LEAVE(); +} + + +/* + * Caller MUST hold any necessary locks + */ +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter) +{ + struct assoc_request * assoc_req; + + if (!adapter->assoc_req) { + adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL); + if (!adapter->assoc_req) { + lbs_pr_info("Not enough memory to allocate association" + " request!\n"); + return NULL; + } + } + + /* Copy current configuration attributes to the association request, + * but don't overwrite any that are already set. + */ + assoc_req = adapter->assoc_req; + if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) { + memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + } + + if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) + assoc_req->channel = adapter->curbssparams.channel; + + if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) + assoc_req->mode = adapter->inframode; + + if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) { + memcpy(&assoc_req->bssid, adapter->curbssparams.bssid, + ETH_ALEN); + } + + if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) { + int i; + for (i = 0; i < 4; i++) { + memcpy(&assoc_req->wep_keys[i], &adapter->wep_keys[i], + sizeof(struct WLAN_802_11_KEY)); + } + } + + if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) + assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx; + + if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key, + sizeof(struct WLAN_802_11_KEY)); + } + + if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) { + memcpy(&assoc_req->secinfo, &adapter->secinfo, + sizeof(struct wlan_802_11_security)); + } + + if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) { + memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie, + MAX_WPA_IE_LEN); + assoc_req->wpa_ie_len = adapter->wpa_ie_len; + } + + return assoc_req; +} + + diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h new file mode 100644 index 0000000..2ffd82d --- /dev/null +++ b/drivers/net/wireless/libertas/assoc.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2006, Red Hat, Inc. */ + +#ifndef _WLAN_ASSOC_H_ +#define _WLAN_ASSOC_H_ + +#include "dev.h" + +void wlan_association_worker(struct work_struct *work); + +struct assoc_request * wlan_get_association_request(wlan_adapter *adapter); + +#define ASSOC_DELAY (HZ / 2) +static inline void wlan_postpone_association_work(wlan_private *priv) +{ + if (priv->adapter->surpriseremoved) + return; + cancel_delayed_work(&priv->assoc_work); + queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY); +} + +static inline void wlan_cancel_association_work(wlan_private *priv) +{ + cancel_delayed_work(&priv->assoc_work); + if (priv->adapter->assoc_req) { + kfree(priv->adapter->assoc_req); + priv->adapter->assoc_req = NULL; + } +} + +#endif /* _WLAN_ASSOC_H */ diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c new file mode 100644 index 0000000..bfdac58 --- /dev/null +++ b/drivers/net/wireless/libertas/cmd.c @@ -0,0 +1,1958 @@ +/** + * This file contains the handling of command. + * It prepares command and sends it to firmware when it is ready. + */ + +#include <net/iw_handler.h> +#include "host.h" +#include "hostcmd.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode); + +static u16 commands_allowed_in_ps[] = { + cmd_802_11_rssi, +}; + +/** + * @brief This function checks if the commans is allowed + * in PS mode not. + * + * @param command the command ID + * @return TRUE or FALSE + */ +static u8 is_command_allowed_in_ps(u16 command) +{ + int count = sizeof(commands_allowed_in_ps) + / sizeof(commands_allowed_in_ps[0]); + int i; + + for (i = 0; i < count; i++) { + if (command == cpu_to_le16(commands_allowed_in_ps[i])) + return 1; + } + + return 0; +} + +static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd) +{ + struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_get_hw_spec); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN); + memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_ps_mode(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode; + u16 action = cmd_action; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_ps_mode); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) + + S_DS_GEN); + psm->action = cpu_to_le16(cmd_action); + psm->multipledtim = 0; + switch (action) { + case cmd_subcmd_enter_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n"); + lbs_pr_debug(1, "locallisteninterval = %d\n", + adapter->locallisteninterval); + + psm->locallisteninterval = + cpu_to_le16(adapter->locallisteninterval); + psm->nullpktinterval = + cpu_to_le16(adapter->nullpktinterval); + psm->multipledtim = + cpu_to_le16(priv->adapter->multipledtim); + break; + + case cmd_subcmd_exit_ps: + lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n"); + break; + + case cmd_subcmd_sleep_confirmed: + lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n"); + break; + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + u16 *timeout = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_inactivity_timeout); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout) + + S_DS_GEN); + + cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action); + + if (cmd_action) + cmd->params.inactivity_timeout.timeout = + cpu_to_le16(*timeout); + else + cmd->params.inactivity_timeout.timeout = 0; + + return 0; +} + +static int wlan_cmd_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_sleep_params); + + if (cmd_action == cmd_act_get) { + memset(&adapter->sp, 0, sizeof(struct sleep_params)); + memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params)); + sp->action = cpu_to_le16(cmd_action); + } else if (cmd_action == cmd_act_set) { + sp->action = cpu_to_le16(cmd_action); + sp->error = cpu_to_le16(adapter->sp.sp_error); + sp->offset = cpu_to_le16(adapter->sp.sp_offset); + sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime); + sp->calcontrol = (u8) adapter->sp.sp_calcontrol; + sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk; + sp->reserved = cpu_to_le16(adapter->sp.sp_reserved); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_set_wep(wlan_private * priv, + struct cmd_ds_command *cmd, + u32 cmd_act, + void * pdata_buf) +{ + struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_set_wep); + cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep)) + + S_DS_GEN); + + if (cmd_act == cmd_act_add) { + int i; + + if (!assoc_req) { + lbs_pr_debug(1, "Invalid association request!"); + ret = -1; + goto done; + } + + wep->action = cpu_to_le16(cmd_act_add); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (assoc_req->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + + lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex); + + /* Copy key types and material to host command structure */ + for (i = 0; i < 4; i++) { + struct WLAN_802_11_KEY * pkey = &assoc_req->wep_keys[i]; + + switch (pkey->len) { + case KEY_LEN_WEP_40: + wep->keytype[i] = cmd_type_wep_40_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case KEY_LEN_WEP_104: + wep->keytype[i] = cmd_type_wep_104_bit; + memmove(&wep->keymaterial[i], pkey->key, + pkey->len); + break; + case 0: + break; + default: + lbs_pr_debug(1, "Invalid WEP key %d length of %d\n", + i, pkey->len); + ret = -1; + goto done; + break; + } + } + } else if (cmd_act == cmd_act_remove) { + /* ACT_REMOVE clears _all_ WEP keys */ + wep->action = cpu_to_le16(cmd_act_remove); + + /* default tx key index */ + wep->keyindex = cpu_to_le16((u16) + (adapter->wep_tx_keyidx & + (u32)cmd_WEP_KEY_INDEX_MASK)); + } + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_enable_rsn(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn; + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_enable_rsn); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) + + S_DS_GEN); + penableRSN->action = cpu_to_le16(cmd_action); + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + penableRSN->enable = cpu_to_le16(cmd_enable_rsn); + } else { + penableRSN->enable = cpu_to_le16(cmd_disable_rsn); + } + + return 0; +} + + +static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset, + struct WLAN_802_11_KEY * pkey) +{ + pkeyparamset->keytypeid = cpu_to_le16(pkey->type); + + if (pkey->flags & KEY_INFO_WPA_ENABLED) { + pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED); + } else { + pkeyparamset->keyinfo = cpu_to_le16(!KEY_INFO_WPA_ENABLED); + } + + if (pkey->flags & KEY_INFO_WPA_UNICAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST); + } else if (pkey->flags & KEY_INFO_WPA_MCAST) { + pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST); + } + + pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL); + pkeyparamset->keylen = cpu_to_le16(pkey->len); + memcpy(pkeyparamset->key, pkey->key, pkey->len); + pkeyparamset->length = cpu_to_le16( sizeof(pkeyparamset->keytypeid) + + sizeof(pkeyparamset->keyinfo) + + sizeof(pkeyparamset->keylen) + + sizeof(pkeyparamset->key)); +} + +static int wlan_cmd_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, + u32 cmd_oid, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_key_material *pkeymaterial = + &cmd->params.keymaterial; + int ret = 0; + int index = 0; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_key_material); + pkeymaterial->action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_get) { + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action)); + ret = 0; + goto done; + } + + memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet)); + + if (adapter->wpa_unicast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_unicast_key); + index++; + } + + if (adapter->wpa_mcast_key.len) { + set_one_wpa_key(&pkeymaterial->keyParamSet[index], + &adapter->wpa_mcast_key); + index++; + } + + cmd->size = cpu_to_le16( S_DS_GEN + + sizeof (pkeymaterial->action) + + index * sizeof(struct MrvlIEtype_keyParamSet)); + + ret = 0; + +done: + LEAVE(); + return ret; +} + +static int wlan_cmd_802_11_reset(wlan_private * priv, + struct cmd_ds_command *cmd, int cmd_action) +{ + struct cmd_ds_802_11_reset *reset = &cmd->params.reset; + + cmd->command = cpu_to_le16(cmd_802_11_reset); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN); + reset->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_802_11_get_log(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_log); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_get_stat(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_get_stat); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + + S_DS_GEN); + + return 0; +} + +static int wlan_cmd_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, + int cmd_oid, void *pdata_buf) +{ + struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib; + wlan_adapter *adapter = priv->adapter; + u8 ucTemp; + + ENTER(); + + lbs_pr_debug(1, "SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid); + + cmd->command = cpu_to_le16(cmd_802_11_snmp_mib); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_snmp_mib) + + S_DS_GEN); + + switch (cmd_oid) { + case OID_802_11_INFRASTRUCTURE_MODE: + { + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode = + (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf; + pSNMPMIB->querytype = cpu_to_le16(cmd_act_set); + pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i); + pSNMPMIB->bufsize = sizeof(u8); + if (mode == wlan802_11infrastructure) + ucTemp = SNMP_MIB_VALUE_INFRA; + else + ucTemp = SNMP_MIB_VALUE_ADHOC; + + memmove(pSNMPMIB->value, &ucTemp, sizeof(u8)); + + break; + } + + case OID_802_11D_ENABLE: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) dot11d_i); + + if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = cmd_act_set; + pSNMPMIB->bufsize = sizeof(u16); + ulTemp = *(u32 *)pdata_buf; + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + } + break; + } + + case OID_802_11_FRAGMENTATION_THRESHOLD: + { + u32 ulTemp; + + pSNMPMIB->oid = cpu_to_le16((u16) fragthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) pdata_buf); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) ulTemp); + + } + + break; + } + + case OID_802_11_RTS_THRESHOLD: + { + + u32 ulTemp; + pSNMPMIB->oid = le16_to_cpu((u16) rtsthresh_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = + cpu_to_le16(sizeof(u16)); + ulTemp = *((u32 *) + pdata_buf); + *(unsigned short *)(pSNMPMIB->value) = + cpu_to_le16((u16) ulTemp); + + } + break; + } + case OID_802_11_TX_RETRYCOUNT: + pSNMPMIB->oid = cpu_to_le16((u16) short_retrylim_i); + + if (cmd_action == cmd_act_get) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_get); + } else if (cmd_action == cmd_act_set) { + pSNMPMIB->querytype = + cpu_to_le16(cmd_act_set); + pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16)); + *((unsigned short *)(pSNMPMIB->value)) = + cpu_to_le16((u16) adapter->txretrycount); + } + + break; + default: + break; + } + + lbs_pr_debug(1, + "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n", + cmd->command, cmd->size, cmd->seqnum, cmd->result); + + lbs_pr_debug(1, + "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n", + pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize, + *(u16 *) pSNMPMIB->value); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_radio_control(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_radio_control *pradiocontrol = + &cmd->params.radio; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_radio_control); + + pradiocontrol->action = cpu_to_le16(cmd_action); + + switch (adapter->preamble) { + case cmd_type_short_preamble: + pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE); + break; + + case cmd_type_long_preamble: + pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE); + break; + + case cmd_type_auto_preamble: + default: + pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE); + break; + } + + if (adapter->radioon) + pradiocontrol->control |= cpu_to_le16(TURN_ON_RF); + else + pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF); + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + + struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp; + + ENTER(); + + cmd->size = + cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rf_tx_power); + prtp->action = cmd_action; + + lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size, + cmd->command, prtp->action); + + switch (cmd_action) { + case cmd_act_tx_power_opt_get: + prtp->action = cpu_to_le16(cmd_act_get); + prtp->currentlevel = 0; + break; + + case cmd_act_tx_power_opt_set_high: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_high); + break; + + case cmd_act_tx_power_opt_set_mid: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = + cpu_to_le16(cmd_act_tx_power_index_mid); + break; + + case cmd_act_tx_power_opt_set_low: + prtp->action = cpu_to_le16(cmd_act_set); + prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf)); + break; + } + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant; + + cmd->command = cpu_to_le16(cmd_802_11_rf_antenna); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) + + S_DS_GEN); + + rant->action = cpu_to_le16(cmd_action); + if ((cmd_action == cmd_act_set_rx) || + (cmd_action == cmd_act_set_tx)) { + rant->antennamode = + cpu_to_le16((u16) (*(u32 *) pdata_buf)); + } + + return 0; +} + +static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_rate_adapt_rateset + *rateadapt = &cmd->params.rateset; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_802_11_rate_adapt_rateset); + + ENTER(); + + rateadapt->action = cmd_action; + rateadapt->enablehwauto = adapter->enablehwauto; + rateadapt->bitmap = adapter->ratebitmap; + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate; + wlan_adapter *adapter = priv->adapter; + u16 action = cmd_action; + + ENTER(); + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) + + S_DS_GEN); + + cmd->command = cpu_to_le16(cmd_802_11_data_rate); + + memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate)); + + pdatarate->action = cpu_to_le16(cmd_action); + + if (action == cmd_act_set_tx_fix_rate) { + pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate); + lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n", + adapter->datarate); + } else if (action == cmd_act_set_tx_auto) { + lbs_pr_debug(1, "Setting FW for AUTO rate\n"); + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_mac_multicast_adr(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr; + wlan_adapter *adapter = priv->adapter; + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) + + S_DS_GEN); + cmd->command = cpu_to_le16(cmd_mac_multicast_adr); + + pMCastAdr->action = cpu_to_le16(cmd_action); + pMCastAdr->nr_of_adrs = + cpu_to_le16((u16) adapter->nr_of_multicastmacaddr); + memcpy(pMCastAdr->maclist, adapter->multicastlist, + adapter->nr_of_multicastmacaddr * ETH_ALEN); + + return 0; +} + +static int wlan_cmd_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *cmd, + int option, void *pdata_buf) +{ + struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel; + + cmd->command = cpu_to_le16(cmd_802_11_rf_channel); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel) + + S_DS_GEN); + + if (option == cmd_opt_802_11_rf_channel_set) { + rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf)); + } + + rfchan->action = cpu_to_le16(option); + + return 0; +} + +static int wlan_cmd_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_rssi); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN); + cmd->params.rssi.N = priv->adapter->bcn_avg_factor; + + /* reset Beacon SNR/NF/RSSI values */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->NF[TYPE_BEACON][TYPE_AVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0; + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0; + + return 0; +} + +static int wlan_cmd_reg_access(wlan_private * priv, + struct cmd_ds_command *cmdptr, + u8 cmd_action, void *pdata_buf) +{ + struct wlan_offset_value *offval; + + ENTER(); + + offval = (struct wlan_offset_value *)pdata_buf; + + switch (cmdptr->command) { + case cmd_mac_reg_access: + { + struct cmd_ds_mac_reg_access *macreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_mac_reg_access) + + S_DS_GEN); + macreg = + (struct cmd_ds_mac_reg_access *)&cmdptr->params. + macreg; + + macreg->action = cpu_to_le16(cmd_action); + macreg->offset = cpu_to_le16((u16) offval->offset); + macreg->value = cpu_to_le32(offval->value); + + break; + } + + case cmd_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *bbpreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_bbp_reg_access) + + S_DS_GEN); + bbpreg = + (struct cmd_ds_bbp_reg_access *)&cmdptr->params. + bbpreg; + + bbpreg->action = cpu_to_le16(cmd_action); + bbpreg->offset = cpu_to_le16((u16) offval->offset); + bbpreg->value = (u8) offval->value; + + break; + } + + case cmd_rf_reg_access: + { + struct cmd_ds_rf_reg_access *rfreg; + + cmdptr->size = + cpu_to_le16(sizeof + (struct cmd_ds_rf_reg_access) + + S_DS_GEN); + rfreg = + (struct cmd_ds_rf_reg_access *)&cmdptr->params. + rfreg; + + rfreg->action = cpu_to_le16(cmd_action); + rfreg->offset = cpu_to_le16((u16) offval->offset); + rfreg->value = (u8) offval->value; + + break; + } + + default: + break; + } + + LEAVE(); + return 0; +} + +static int wlan_cmd_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action) +{ + wlan_adapter *adapter = priv->adapter; + + cmd->command = cpu_to_le16(cmd_802_11_mac_address); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.macadd.action = cpu_to_le16(cmd_action); + + if (cmd_action == cmd_act_set) { + memcpy(cmd->params.macadd.macadd, + adapter->current_addr, ETH_ALEN); + lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6); + } + + return 0; +} + +static int wlan_cmd_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *cmd, + int cmd_action, void *pdata_buf) +{ + struct wlan_ioctl_regrdwr *ea = pdata_buf; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_eeprom_access); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) + + S_DS_GEN); + cmd->result = 0; + + cmd->params.rdeeprom.action = cpu_to_le16(ea->action); + cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset); + cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB); + cmd->params.rdeeprom.value = 0; + + return 0; +} + +static int wlan_cmd_bt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_bt_access *bt_access = &cmd->params.bt; + lbs_pr_debug(1, "BT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_bt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + + S_DS_GEN); + cmd->result = 0; + bt_access->action = cpu_to_le16(cmd_action); + + switch (cmd_action) { + case cmd_act_bt_access_add: + memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN); + lbs_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_del: + memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN); + lbs_dbg_hex("BT_DEL: blinded mac address-", bt_access->addr1, 6); + break; + case cmd_act_bt_access_list: + bt_access->id = cpu_to_le32(*(u32 *) pdata_buf); + break; + case cmd_act_bt_access_reset: + break; + default: + break; + } + return 0; +} + +static int wlan_cmd_fwt_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_fwt_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(fwt_access, pdata_buf, sizeof(*fwt_access)); + else + memset(fwt_access, 0, sizeof(*fwt_access)); + + fwt_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +static int wlan_cmd_mesh_access(wlan_private * priv, + struct cmd_ds_command *cmd, + u16 cmd_action, void *pdata_buf) +{ + struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh; + lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action); + + cmd->command = cpu_to_le16(cmd_mesh_access); + cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + + S_DS_GEN); + cmd->result = 0; + + if (pdata_buf) + memcpy(mesh_access, pdata_buf, sizeof(*mesh_access)); + else + memset(mesh_access, 0, sizeof(*mesh_access)); + + mesh_access->action = cpu_to_le16(cmd_action); + + return 0; +} + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + + ENTER(); + + if (!cmdnode) { + lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n"); + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + if (!cmdptr) { + lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n"); + goto done; + } + + /* Exit_PS command needs to be queued in the header always. */ + if (cmdptr->command == cmd_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode; + if (psm->action == cmd_subcmd_exit_ps) { + if (adapter->psstate != PS_STATE_FULL_POWER) + addtail = 0; + } + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (addtail) + list_add_tail((struct list_head *)cmdnode, + &adapter->cmdpendingq); + else + list_add((struct list_head *)cmdnode, &adapter->cmdpendingq); + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n", + (u32) cmdnode, + ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command); + +done: + LEAVE(); + return; +} + +/* + * TODO: Fix the issue when DownloadcommandToStation is being called the + * second time when the command timesout. All the cmdptr->xxx are in little + * endian and therefore all the comparissions will fail. + * For now - we are not performing the endian conversion the second time - but + * for PS and DEEP_SLEEP we need to worry + */ +static int DownloadcommandToStation(wlan_private * priv, + struct cmd_ctrl_node *cmdnode) +{ + unsigned long flags; + struct cmd_ds_command *cmdptr; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 cmdsize; + u16 command; + + ENTER(); + + if (!adapter || !cmdnode) { + lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n", + (int)adapter, (int)cmdnode); + if (cmdnode) { + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + } + ret = -1; + goto done; + } + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!cmdptr || !cmdptr->size) { + lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, " + "Not sending\n"); + __libertas_cleanup_and_insert_cmd(priv, cmdnode); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + adapter->cur_cmd = cmdnode; + adapter->cur_cmd_retcode = 0; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n", + cmdptr->size); + + cmdsize = cmdptr->size; + + command = cpu_to_le16(cmdptr->command); + + cmdnode->cmdwaitqwoken = 0; + cmdsize = cpu_to_le16(cmdsize); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize); + + if (ret != 0) { + lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n"); + spin_lock_irqsave(&adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies); + lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize); + + /* Setup the timer after transmit command */ + if (command == cmd_802_11_scan + || command == cmd_802_11_authenticate + || command == cmd_802_11_associate) + mod_timer(&adapter->command_timer, jiffies + (10*HZ)); + else + mod_timer(&adapter->command_timer, jiffies + (5*HZ)); + + ret = 0; + + done: + LEAVE(); + return ret; +} + +static int wlan_cmd_mac_control(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + struct cmd_ds_mac_control *mac = &cmd->params.macctrl; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_mac_control); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN); + mac->action = cpu_to_le16(priv->adapter->currentpacketfilter); + + lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n", + mac->action, cmd->size); + + LEAVE(); + return 0; +} + +/** + * This function inserts command node to cmdfreeq + * after cleans it. Requires adapter->driver_lock held. + */ +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + wlan_adapter *adapter = priv->adapter; + + if (!ptempcmd) + goto done; + + cleanup_cmdnode(ptempcmd); + list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq); +done: + return; +} + +void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd) +{ + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + __libertas_cleanup_and_insert_cmd(priv, ptempcmd); + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + +int libertas_set_radio_control(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n", + priv->adapter->radioon, priv->adapter->preamble); + + LEAVE(); + return ret; +} + +int libertas_set_mac_packet_filter(wlan_private * priv) +{ + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n", + priv->adapter->currentpacketfilter); + + /* Send MAC control command to station */ + ret = libertas_prepare_and_send_command(priv, + cmd_mac_control, 0, 0, 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief This function prepare the command before send to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd_no command number + * @param cmd_action command action: GET or SET + * @param wait_option wait option: wait response or not + * @param cmd_oid cmd oid: treated as sub command + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode; + struct cmd_ds_command *cmdptr; + unsigned long flags; + + ENTER(); + + if (!adapter) { + lbs_pr_debug(1, "PREP_CMD: adapter is Null\n"); + ret = -1; + goto done; + } + + if (adapter->surpriseremoved) { + lbs_pr_debug(1, "PREP_CMD: Card is Removed\n"); + ret = -1; + goto done; + } + + cmdnode = libertas_get_free_cmd_ctrl_node(priv); + + if (cmdnode == NULL) { + lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n"); + + /* Wake up main thread to execute next command */ + wake_up_interruptible(&priv->mainthread.waitq); + ret = -1; + goto done; + } + + libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf); + + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n", + (u32) cmdptr, cmd_no); + + if (!cmdptr) { + lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + /* Set sequence number, command and INT option */ + adapter->seqnum++; + cmdptr->seqnum = cpu_to_le16(adapter->seqnum); + + cmdptr->command = cmd_no; + cmdptr->result = 0; + + switch (cmd_no) { + case cmd_get_hw_spec: + ret = wlan_cmd_hw_spec(priv, cmdptr); + break; + case cmd_802_11_ps_mode: + ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_scan: + ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf); + break; + + case cmd_mac_control: + ret = wlan_cmd_mac_control(priv, cmdptr); + break; + + case cmd_802_11_associate: + case cmd_802_11_reassociate: + ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_deauthenticate: + ret = libertas_cmd_80211_deauthenticate(priv, cmdptr); + break; + + case cmd_802_11_set_wep: + ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_ad_hoc_start: + ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf); + break; + case cmd_code_dnld: + break; + + case cmd_802_11_reset: + ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_get_log: + ret = wlan_cmd_802_11_get_log(priv, cmdptr); + break; + + case cmd_802_11_authenticate: + ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_get_stat: + ret = wlan_cmd_802_11_get_stat(priv, cmdptr); + break; + + case cmd_802_11_snmp_mib: + ret = wlan_cmd_802_11_snmp_mib(priv, cmdptr, + cmd_action, cmd_oid, pdata_buf); + break; + + case cmd_mac_reg_access: + case cmd_bbp_reg_access: + case cmd_rf_reg_access: + ret = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_channel: + ret = wlan_cmd_802_11_rf_channel(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_rf_tx_power: + ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_radio_control: + ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_rf_antenna: + ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_data_rate: + ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action); + break; + case cmd_802_11_rate_adapt_rateset: + ret = wlan_cmd_802_11_rate_adapt_rateset(priv, + cmdptr, cmd_action); + break; + + case cmd_mac_multicast_adr: + ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_ad_hoc_join: + ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf); + break; + + case cmd_802_11_rssi: + ret = wlan_cmd_802_11_rssi(priv, cmdptr); + break; + + case cmd_802_11_ad_hoc_stop: + ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr); + break; + + case cmd_802_11_enable_rsn: + ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_key_material: + ret = wlan_cmd_802_11_key_material(priv, cmdptr, + cmd_action, cmd_oid, + pdata_buf); + break; + + case cmd_802_11_pairwise_tsc: + break; + case cmd_802_11_group_tsc: + break; + + case cmd_802_11_mac_address: + ret = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action); + break; + + case cmd_802_11_eeprom_access: + ret = wlan_cmd_802_11_eeprom_access(priv, cmdptr, + cmd_action, pdata_buf); + break; + + case cmd_802_11_set_afc: + case cmd_802_11_get_afc: + + cmdptr->command = cpu_to_le16(cmd_no); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) + + S_DS_GEN); + + memmove(&cmdptr->params.afc, + pdata_buf, sizeof(struct cmd_ds_802_11_afc)); + + ret = 0; + goto done; + + case cmd_802_11d_domain_info: + ret = libertas_cmd_802_11d_domain_info(priv, cmdptr, + cmd_no, cmd_action); + break; + + case cmd_802_11_sleep_params: + ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action); + break; + case cmd_802_11_inactivity_timeout: + ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr, + cmd_action, pdata_buf); + libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 0, pdata_buf); + break; + + case cmd_802_11_tpc_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_tpc_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) + + S_DS_GEN); + + memmove(&cmdptr->params.tpccfg, + pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg)); + + ret = 0; + break; + case cmd_802_11_led_gpio_ctrl: + { + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio*) + cmdptr->params.ledgpio.data; + + memmove(&cmdptr->params.ledgpio, + pdata_buf, + sizeof(struct cmd_ds_802_11_led_ctrl)); + + cmdptr->command = + cpu_to_le16(cmd_802_11_led_gpio_ctrl); + +#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8 + cmdptr->size = + cpu_to_le16(gpio->header.len + S_DS_GEN + + ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN); + gpio->header.len = cpu_to_le16(gpio->header.len); + + ret = 0; + break; + } + case cmd_802_11_pwr_cfg: + cmdptr->command = cpu_to_le16(cmd_802_11_pwr_cfg); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) + + S_DS_GEN); + memmove(&cmdptr->params.pwrcfg, pdata_buf, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + + ret = 0; + break; + case cmd_bt_access: + ret = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_fwt_access: + ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_mesh_access: + ret = wlan_cmd_mesh_access(priv, cmdptr, cmd_action, pdata_buf); + break; + + case cmd_get_tsf: + cmdptr->command = cpu_to_le16(cmd_get_tsf); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_get_tsf) + + S_DS_GEN); + ret = 0; + break; + case cmd_802_11_tx_rate_query: + cmdptr->command = + cpu_to_le16(cmd_802_11_tx_rate_query); + cmdptr->size = + cpu_to_le16(sizeof(struct cmd_tx_rate_query) + + S_DS_GEN); + adapter->txrate = 0; + ret = 0; + break; + default: + lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no); + ret = -1; + break; + } + + /* return error, since the command preparation failed */ + if (ret != 0) { + lbs_pr_debug(1, "PREP_CMD: command preparation failed\n"); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + ret = -1; + goto done; + } + + cmdnode->cmdwaitqwoken = 0; + + libertas_queue_cmd(adapter, cmdnode, 1); + adapter->nr_cmd_pending++; + wake_up_interruptible(&priv->mainthread.waitq); + + if (wait_option & cmd_option_waitforrsp) { + lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n"); + might_sleep(); + wait_event_interruptible(cmdnode->cmdwait_q, + cmdnode->cmdwaitqwoken); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd_retcode) { + lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n", + adapter->cur_cmd_retcode); + adapter->cur_cmd_retcode = 0; + ret = -1; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + LEAVE(); + return ret; +} + +/** + * @brief This function allocates the command buffer and link + * it to command free queue. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_allocate_cmd_buffer(wlan_private * priv) +{ + int ret = 0; + u32 ulbufsize; + u32 i; + struct cmd_ctrl_node *tempcmd_array; + u8 *ptempvirtualaddr; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate and initialize cmdCtrlNode */ + ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER; + + if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n"); + ret = -1; + goto done; + } + + adapter->cmd_array = tempcmd_array; + memset(adapter->cmd_array, 0, ulbufsize); + + /* Allocate and initialize command buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) { + lbs_pr_debug(1, + "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n"); + ret = -1; + goto done; + } + + memset(ptempvirtualaddr, 0, ulbufsize); + + /* Update command buffer virtual */ + tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr; + } + + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + init_waitqueue_head(&tempcmd_array[i].cmdwait_q); + libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]); + } + + ret = 0; + done: + LEAVE(); + return ret; +} + +/** + * @brief This function frees the command buffer. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_free_cmd_buffer(wlan_private * priv) +{ + u32 ulbufsize; + unsigned int i; + struct cmd_ctrl_node *tempcmd_array; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* need to check if cmd array is allocated or not */ + if (adapter->cmd_array == NULL) { + lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n"); + goto done; + } + + tempcmd_array = adapter->cmd_array; + + /* Release shared memory buffers */ + ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER; + for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) { + if (tempcmd_array[i].bufvirtualaddr) { + lbs_pr_debug(1, "Free all the array\n"); + kfree(tempcmd_array[i].bufvirtualaddr); + tempcmd_array[i].bufvirtualaddr = NULL; + } + } + + /* Release cmd_ctrl_node */ + if (adapter->cmd_array) { + lbs_pr_debug(1, "Free cmd_array\n"); + kfree(adapter->cmd_array); + adapter->cmd_array = NULL; + } + +done: + LEAVE(); + return 0; +} + +/** + * @brief This function gets a free command node if available in + * command free queue. + * + * @param priv A pointer to wlan_private structure + * @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL + */ +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv) +{ + struct cmd_ctrl_node *tempnode; + wlan_adapter *adapter = priv->adapter; + unsigned long flags; + + if (!adapter) + return NULL; + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!list_empty(&adapter->cmdfreeq)) { + tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next; + list_del((struct list_head *)tempnode); + } else { + lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n"); + tempnode = NULL; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (tempnode) { + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n"); + lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n", + tempnode); + cleanup_cmdnode(tempnode); + } + + return tempnode; +} + +/** + * @brief This function cleans command node. + * + * @param ptempnode A pointer to cmdCtrlNode structure + * @return n/a + */ +static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode) +{ + if (!ptempnode) + return; + ptempnode->cmdwaitqwoken = 1; + wake_up_interruptible(&ptempnode->cmdwait_q); + ptempnode->status = 0; + ptempnode->cmd_oid = (u32) 0; + ptempnode->wait_option = 0; + ptempnode->pdata_buf = NULL; + + if (ptempnode->bufvirtualaddr != NULL) + memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER); + return; +} + +/** + * @brief This function initializes the command node. + * + * @param priv A pointer to wlan_private structure + * @param ptempnode A pointer to cmd_ctrl_node structure + * @param cmd_oid cmd oid: treated as sub command + * @param wait_option wait option: wait response or not + * @param pdata_buf A pointer to informaion buffer + * @return 0 or -1 + */ +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf) +{ + ENTER(); + + if (!ptempnode) + return; + + ptempnode->cmd_oid = cmd_oid; + ptempnode->wait_option = wait_option; + ptempnode->pdata_buf = pdata_buf; + + LEAVE(); +} + +/** + * @brief This function executes next command in command + * pending queue. It will put fimware back to PS mode + * if applicable. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +int libertas_execute_next_command(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *cmdnode = NULL; + struct cmd_ds_command *cmdptr; + unsigned long flags; + int ret = 0; + + lbs_pr_debug(1, "libertas_execute_next_command\n"); + + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (adapter->cur_cmd) { + lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n"); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + if (!list_empty(&adapter->cmdpendingq)) { + cmdnode = (struct cmd_ctrl_node *) + adapter->cmdpendingq.next; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (cmdnode) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Got next command from cmdpendingq\n"); + cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr; + + if (is_command_allowed_in_ps(cmdptr->command)) { + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + ret = -1; + goto done; + } + lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command " + "0x%x in psstate %d\n", + cmdptr->command, adapter->psstate); + } else if (adapter->psstate != PS_STATE_FULL_POWER) { + /* + * 1. Non-PS command: + * Queue it. set needtowakeup to TRUE if current state + * is SLEEP, otherwise call libertas_ps_wakeup to send Exit_PS. + * 2. PS command but not Exit_PS: + * Ignore it. + * 3. PS command Exit_PS: + * Set needtowakeup to TRUE if current state is SLEEP, + * otherwise send this command down to firmware + * immediately. + */ + if (cmdptr->command != + cpu_to_le16(cmd_802_11_ps_mode)) { + /* Prepare to send Exit PS, + * this non PS command will be sent later */ + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + /* w/ new scheme, it will not reach here. + since it is blocked in main_thread. */ + adapter->needtowakeup = 1; + } else + libertas_ps_wakeup(priv, 0); + + ret = 0; + goto done; + } else { + /* + * PS command. Ignore it if it is not Exit_PS. + * otherwise send it down immediately. + */ + struct cmd_ds_802_11_ps_mode *psm = + &cmdptr->params.psmode; + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: PS cmd- action=0x%x\n", + psm->action); + if (psm->action != + cpu_to_le16(cmd_subcmd_exit_ps)) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore Enter PS cmd\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + + ret = 0; + goto done; + } + + if ((adapter->psstate == PS_STATE_SLEEP) + || (adapter->psstate == PS_STATE_PRE_SLEEP) + ) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n"); + list_del((struct list_head *)cmdnode); + libertas_cleanup_and_insert_cmd(priv, cmdnode); + adapter->needtowakeup = 1; + + ret = 0; + goto done; + } + + lbs_pr_debug(1, + "EXEC_NEXT_CMD: Sending Exit_PS down...\n"); + } + } + list_del((struct list_head *)cmdnode); + lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n", + cmdptr->command); + DownloadcommandToStation(priv, cmdnode); + } else { + /* + * check if in power save mode, if yes, put the device back + * to PS mode + */ + if ((adapter->psmode != wlan802_11powermodecam) && + (adapter->psstate == PS_STATE_FULL_POWER) && + (adapter->connect_status == libertas_connected)) { + if (adapter->secinfo.WPAenabled + || adapter->secinfo.WPA2enabled) { + /* check for valid WPA group keys */ + if (adapter->wpa_mcast_key.len + || adapter->wpa_unicast_key.len) { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: WPA enabled and GTK_SET" + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } else { + lbs_pr_debug(1, + "EXEC_NEXT_CMD: command PendQ is empty," + " go back to PS_SLEEP"); + libertas_ps_sleep(priv, 0); + } + } + } + + ret = 0; +done: + return ret; +} + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str) +{ + union iwreq_data iwrq; + u8 buf[50]; + + ENTER(); + + memset(&iwrq, 0, sizeof(union iwreq_data)); + memset(buf, 0, sizeof(buf)); + + snprintf(buf, sizeof(buf) - 1, "%s", str); + + iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN; + + /* Send Event to upper layer */ + lbs_pr_debug(1, "Event Indication string = %s\n", + (char *)buf); + lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length); + + lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str); + wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf); + + LEAVE(); + return; +} + +static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size) +{ + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n", + size); + + lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size); + + ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->intcounter || adapter->currenttxskb) + lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n", + adapter->intcounter, adapter->currenttxskb); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (ret) { + lbs_pr_alert( + "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n"); + } else { + spin_lock_irqsave(&adapter->driver_lock, flags); + if (!adapter->intcounter) { + adapter->psstate = PS_STATE_SLEEP; + } else { + lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n", + adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n"); + lbs_pr_debug(1, "+"); + } + + LEAVE(); + return ret; +} + +void libertas_ps_sleep(wlan_private * priv, int wait_option) +{ + + ENTER(); + + /* + * PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_enter_ps, wait_option, 0, NULL); + + LEAVE(); + return; +} + +/** + * @brief This function sends Eixt_PS command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param wait_option wait response or not + * @return n/a + */ +void libertas_ps_wakeup(wlan_private * priv, int wait_option) +{ + enum WLAN_802_11_POWER_MODE Localpsmode; + + ENTER(); + + Localpsmode = wlan802_11powermodecam; + + lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode); + + libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode, + cmd_subcmd_exit_ps, + wait_option, 0, &Localpsmode); + + LEAVE(); + return; +} + +/** + * @brief This function checks condition and prepares to + * send sleep confirm command to firmware if ok. + * + * @param priv A pointer to wlan_private structure + * @param psmode Power Saving mode + * @return n/a + */ +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode) +{ + unsigned long flags =0; + wlan_adapter *adapter = priv->adapter; + u8 allowed = 1; + + ENTER(); + + if (priv->wlan_dev.dnld_sent) { + allowed = 0; + lbs_pr_debug(1, "D"); + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + allowed = 0; + lbs_pr_debug(1, "C"); + } + if (adapter->intcounter > 0) { + allowed = 0; + lbs_pr_debug(1, "I%d", adapter->intcounter); + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + if (allowed) { + lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n"); + sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep, + sizeof(struct PS_CMD_ConfirmSleep)); + } else { + lbs_pr_debug(1, "Sleep Confirm has been delayed\n"); + } + + LEAVE(); +} diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c new file mode 100644 index 0000000..cdb012c --- /dev/null +++ b/drivers/net/wireless/libertas/cmdresp.c @@ -0,0 +1,1031 @@ +/** + * This file contains the handling of command + * responses as well as events generated by firmware. + */ +#include <linux/delay.h> +#include <linux/if_arp.h> +#include <linux/netdevice.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +/** + * @brief This function handles disconnect event. it + * reports disconnect to upper layer, clean tx/rx packets, + * reset link state etc. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +void libertas_mac_event_disconnected(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + if (adapter->connect_status != libertas_connected) + return; + + lbs_pr_debug(1, "Handles disconnect event.\n"); + + memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + + /* + * Cisco AP sends EAP failure and de-auth in less than 0.5 ms. + * It causes problem in the Supplicant + */ + + msleep_interruptible(1000); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + /* Free Tx and Rx packets */ + kfree_skb(priv->adapter->currenttxskb); + priv->adapter->currenttxskb = NULL; + + /* report disconnect to upper layer */ + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + /* reset SNR/NF/RSSI values */ + memset(adapter->SNR, 0x00, sizeof(adapter->SNR)); + memset(adapter->NF, 0x00, sizeof(adapter->NF)); + memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI)); + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + adapter->rxpd_rate = 0; + lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n", + adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n", + adapter->previousssid.ssid, adapter->previousssid.ssidlength); + + /* reset internal flags */ + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + adapter->wpa_ie_len = 0; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + + adapter->connect_status = libertas_disconnected; + + /* + * memorize the previous SSID and BSSID + * it could be used for re-assoc + */ + memcpy(&adapter->previousssid, + &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID)); + memcpy(adapter->previousbssid, + adapter->curbssparams.bssid, ETH_ALEN); + + /* need to erase the current SSID and BSSID info */ + adapter->pattemptedbssdesc = NULL; + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + if (adapter->psstate != PS_STATE_FULL_POWER) { + /* make firmware to exit PS mode */ + lbs_pr_debug(1, "Disconnected, so exit PS mode.\n"); + libertas_ps_wakeup(priv, 0); + } +} + +/** + * @brief This function handles MIC failure event. + * + * @param priv A pointer to wlan_private structure + * @para event the event id + * @return n/a + */ +static void handle_mic_failureevent(wlan_private * priv, u32 event) +{ + char buf[50]; + + memset(buf, 0, sizeof(buf)); + + sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication "); + + if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) { + strcat(buf, "unicast "); + } else { + strcat(buf, "multicast "); + } + + libertas_send_iwevcustom_event(priv, buf); +} + +static int wlan_ret_reg_access(wlan_private * priv, + u16 type, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (type) { + case cmd_ret_mac_reg_access: + { + struct cmd_ds_mac_reg_access *reg; + + reg = + (struct cmd_ds_mac_reg_access *)&resp->params. + macreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_bbp_reg_access: + { + struct cmd_ds_bbp_reg_access *reg; + reg = + (struct cmd_ds_bbp_reg_access *)&resp->params. + bbpreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + case cmd_ret_rf_reg_access: + { + struct cmd_ds_rf_reg_access *reg; + reg = + (struct cmd_ds_rf_reg_access *)&resp->params. + rfreg; + + adapter->offsetvalue.offset = reg->offset; + adapter->offsetvalue.value = reg->value; + break; + } + + default: + LEAVE(); + return -1; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_get_hw_spec(wlan_private * priv, + struct cmd_ds_command *resp) +{ + u32 i; + struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo); + + adapter->fwreleasenumber = hwspec->fwreleasenumber; + + lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n", + adapter->fwreleasenumber); + lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n", + hwspec->permanentaddr[0], hwspec->permanentaddr[1], + hwspec->permanentaddr[2], hwspec->permanentaddr[3], + hwspec->permanentaddr[4], hwspec->permanentaddr[5]); + lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X version=0x%X\n", + hwspec->hwifversion, hwspec->version); + + adapter->regioncode = le16_to_cpu(hwspec->regioncode); + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + /* use the region code to search for the index */ + if (adapter->regioncode == libertas_region_code_to_index[i]) { + adapter->regiontableindex = (u16) i; + break; + } + } + + /* if it's unidentified region code, use the default (USA) */ + if (i >= MRVDRV_MAX_REGION_CODE) { + adapter->regioncode = 0x10; + adapter->regiontableindex = 0; + lbs_pr_info( + "unidentified region code, use the default (USA)\n"); + } + + if (adapter->current_addr[0] == 0xff) { + memmove(adapter->current_addr, hwspec->permanentaddr, + ETH_ALEN); + } + + memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + + if (libertas_set_regiontable(priv, adapter->regioncode, 0)) { + ret = -1; + goto done; + } + + if (libertas_set_universaltable(priv, 0)) { + ret = -1; + goto done; + } + + done: + LEAVE(); + return ret; +} + +static int wlan_ret_802_11_sleep_params(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n" + " extsleepclk=%x\n", sp->error, sp->offset, + sp->stabletime, sp->calcontrol, sp->externalsleepclk); + adapter->sp.sp_error = le16_to_cpu(sp->error); + adapter->sp.sp_offset = le16_to_cpu(sp->offset); + adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime); + adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol); + adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk); + adapter->sp.sp_reserved = le16_to_cpu(sp->reserved); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_stat(wlan_private * priv, + struct cmd_ds_command *resp) +{ +/* currently adapter->wlan802_11Stat is unused + + struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat; + wlan_adapter *adapter = priv->adapter; + + // TODO Convert it to Big endian befor copy + memcpy(&adapter->wlan802_11Stat, + p11Stat, sizeof(struct cmd_ds_802_11_get_stat)); +*/ + return 0; +} + +static int wlan_ret_802_11_snmp_mib(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib; + u16 oid = le16_to_cpu(smib->oid); + u16 querytype = le16_to_cpu(smib->querytype); + + ENTER(); + + lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid, + querytype); + lbs_pr_debug(1, "SNMP_RESP: Buf size = %x\n", + le16_to_cpu(smib->bufsize)); + + if (querytype == cmd_act_get) { + switch (oid) { + case fragthresh_i: + priv->adapter->fragthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n", + priv->adapter->fragthsd); + break; + case rtsthresh_i: + priv->adapter->rtsthsd = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n", + priv->adapter->rtsthsd); + break; + case short_retrylim_i: + priv->adapter->txretrycount = + le16_to_cpu(* + ((unsigned short *)(smib->value))); + lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n", + priv->adapter->rtsthsd); + break; + default: + break; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_key_material(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_key_material *pkeymaterial = + &resp->params.keymaterial; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pkeymaterial->action); + + ENTER(); + + /* Copy the returned key to driver private data */ + if (action == cmd_act_get) { + u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet; + u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size)); + + while (buf_ptr < resp_end) { + struct MrvlIEtype_keyParamSet * pkeyparamset = + (struct MrvlIEtype_keyParamSet *) buf_ptr; + struct WLAN_802_11_KEY * pkey; + u16 key_info = le16_to_cpu(pkeyparamset->keyinfo); + u16 param_set_len = le16_to_cpu(pkeyparamset->length); + u8 * end; + u16 key_len = le16_to_cpu(pkeyparamset->keylen); + + end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type) + + sizeof (pkeyparamset->length) + + param_set_len; + /* Make sure we don't access past the end of the IEs */ + if (end > resp_end) + break; + + if (key_info & KEY_INFO_WPA_UNICAST) + pkey = &adapter->wpa_unicast_key; + else if (key_info & KEY_INFO_WPA_MCAST) + pkey = &adapter->wpa_mcast_key; + else + break; + + /* Copy returned key into driver */ + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + if (key_len > sizeof(pkey->key)) + break; + pkey->type = le16_to_cpu(pkeyparamset->keytypeid); + pkey->flags = le16_to_cpu(pkeyparamset->keyinfo); + pkey->len = le16_to_cpu(pkeyparamset->keylen); + memcpy(pkey->key, pkeyparamset->key, pkey->len); + + buf_ptr = end + 1; + } + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_mac_address(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_tx_power(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel); + + lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_antenna(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(pAntenna->action); + + if (action == cmd_act_get_rx) + adapter->rxantennamode = + le16_to_cpu(pAntenna->antennamode); + + if (action == cmd_act_get_tx) + adapter->txantennamode = + le16_to_cpu(pAntenna->antennamode); + + lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n", + action, le16_to_cpu(pAntenna->antennamode)); + + return 0; +} + +static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rate_adapt_rateset *rates = + &resp->params.rateset; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (rates->action == cmd_act_get) { + adapter->enablehwauto = rates->enablehwauto; + adapter->ratebitmap = rates->bitmap; + } + + LEAVE(); + + return 0; +} + +static int wlan_ret_802_11_data_rate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate; + wlan_adapter *adapter = priv->adapter; + u8 dot11datarate; + + ENTER(); + + lbs_dbg_hex("DATA_RATE_RESP: data_rate- ", + (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate)); + + dot11datarate = pdatarate->datarate[0]; + if (pdatarate->action == cmd_act_get_tx_rate) { + memcpy(adapter->libertas_supported_rates, pdatarate->datarate, + sizeof(adapter->libertas_supported_rates)); + } + adapter->datarate = libertas_index_to_data_rate(dot11datarate); + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rf_channel(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rf_channel *rfchannel = + &resp->params.rfchannel; + wlan_adapter *adapter = priv->adapter; + u16 action = le16_to_cpu(rfchannel->action); + u16 newchannel = le16_to_cpu(rfchannel->currentchannel); + + ENTER(); + + if (action == cmd_opt_802_11_rf_channel_get + && adapter->curbssparams.channel != newchannel) { + lbs_pr_debug(1, "channel Switch: %d to %d\n", + adapter->curbssparams.channel, newchannel); + + /* Update the channel again */ + adapter->curbssparams.channel = newchannel; + } + + LEAVE(); + return 0; +} + +static int wlan_ret_802_11_rssi(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp; + wlan_adapter *adapter = priv->adapter; + + /* store the non average value */ + adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR); + adapter->NF[TYPE_BEACON][TYPE_NOAVG] = + le16_to_cpu(rssirsp->noisefloor); + + adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR); + adapter->NF[TYPE_BEACON][TYPE_AVG] = + le16_to_cpu(rssirsp->avgnoisefloor); + + adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + adapter->RSSI[TYPE_BEACON][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE); + + lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n", + adapter->RSSI[TYPE_BEACON][TYPE_AVG]); + + return 0; +} + +static int wlan_ret_802_11_eeprom_access(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr *pbuf; + pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom; + + lbs_pr_debug(1, "eeprom read len=%x\n", + le16_to_cpu(resp->params.rdeeprom.bytecount)); + if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) { + pbuf->NOB = 0; + lbs_pr_debug(1, "eeprom read return length is too big\n"); + return -1; + } + pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount); + if (pbuf->NOB > 0) { + + memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + lbs_dbg_hex("adapter", (char *)&pbuf->value, + le16_to_cpu(resp->params.rdeeprom.bytecount)); + } + return 0; +} + +static int wlan_ret_get_log(wlan_private * priv, + struct cmd_ds_command *resp) +{ + struct cmd_ds_802_11_get_log *logmessage = + (struct cmd_ds_802_11_get_log *)&resp->params.glog; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* TODO Convert it to Big Endian before copy */ + memcpy(&adapter->logmsg, logmessage, + sizeof(struct cmd_ds_802_11_get_log)); + + LEAVE(); + return 0; +} + +static inline int handle_cmd_response(u16 respcmd, + struct cmd_ds_command *resp, + wlan_private *priv) +{ + int ret = 0; + unsigned long flags; + wlan_adapter *adapter = priv->adapter; + + switch (respcmd) { + case cmd_ret_mac_reg_access: + case cmd_ret_bbp_reg_access: + case cmd_ret_rf_reg_access: + ret = wlan_ret_reg_access(priv, respcmd, resp); + break; + + case cmd_ret_hw_spec_info: + ret = wlan_ret_get_hw_spec(priv, resp); + break; + + case cmd_ret_802_11_scan: + ret = libertas_ret_80211_scan(priv, resp); + break; + + case cmd_ret_802_11_get_log: + ret = wlan_ret_get_log(priv, resp); + break; + + case cmd_ret_802_11_associate: + case cmd_ret_802_11_reassociate: + ret = libertas_ret_80211_associate(priv, resp); + break; + + case cmd_ret_802_11_disassociate: + case cmd_ret_802_11_deauthenticate: + ret = libertas_ret_80211_disassociate(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_start: + case cmd_ret_802_11_ad_hoc_join: + ret = libertas_ret_80211_ad_hoc_start(priv, resp); + break; + + case cmd_ret_802_11_stat: + ret = wlan_ret_802_11_stat(priv, resp); + break; + + case cmd_ret_802_11_snmp_mib: + ret = wlan_ret_802_11_snmp_mib(priv, resp); + break; + + case cmd_ret_802_11_rf_tx_power: + ret = wlan_ret_802_11_rf_tx_power(priv, resp); + break; + + case cmd_ret_802_11_set_afc: + case cmd_ret_802_11_get_afc: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.afc, + sizeof(struct cmd_ds_802_11_afc)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + case cmd_ret_802_11_rf_antenna: + ret = wlan_ret_802_11_rf_antenna(priv, resp); + break; + + case cmd_ret_mac_multicast_adr: + case cmd_ret_mac_control: + case cmd_ret_802_11_set_wep: + case cmd_ret_802_11_reset: + case cmd_ret_802_11_authenticate: + case cmd_ret_802_11_radio_control: + case cmd_ret_802_11_beacon_stop: + case cmd_ret_802_11_enable_rsn: + break; + + case cmd_ret_802_11_data_rate: + ret = wlan_ret_802_11_data_rate(priv, resp); + break; + case cmd_ret_802_11_rate_adapt_rateset: + ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp); + break; + case cmd_ret_802_11_rf_channel: + ret = wlan_ret_802_11_rf_channel(priv, resp); + break; + + case cmd_ret_802_11_rssi: + ret = wlan_ret_802_11_rssi(priv, resp); + break; + + case cmd_ret_802_11_mac_address: + ret = wlan_ret_802_11_mac_address(priv, resp); + break; + + case cmd_ret_802_11_ad_hoc_stop: + ret = libertas_ret_80211_ad_hoc_stop(priv, resp); + break; + + case cmd_ret_802_11_key_material: + lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n"); + ret = wlan_ret_802_11_key_material(priv, resp); + break; + + case cmd_ret_802_11_eeprom_access: + ret = wlan_ret_802_11_eeprom_access(priv, resp); + break; + + case cmd_ret_802_11d_domain_info: + ret = libertas_ret_802_11d_domain_info(priv, resp); + break; + + case cmd_ret_802_11_sleep_params: + ret = wlan_ret_802_11_sleep_params(priv, resp); + break; + case cmd_ret_802_11_inactivity_timeout: + spin_lock_irqsave(&adapter->driver_lock, flags); + *((u16 *) adapter->cur_cmd->pdata_buf) = + le16_to_cpu(resp->params.inactivity_timeout.timeout); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + + case cmd_ret_802_11_tpc_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.tpccfg, + sizeof(struct cmd_ds_802_11_tpc_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_led_gpio_ctrl: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.ledgpio, + sizeof(struct cmd_ds_802_11_led_ctrl)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_802_11_pwr_cfg: + spin_lock_irqsave(&adapter->driver_lock, flags); + memmove(adapter->cur_cmd->pdata_buf, + &resp->params.pwrcfg, + sizeof(struct cmd_ds_802_11_pwr_cfg)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + break; + + case cmd_ret_get_tsf: + spin_lock_irqsave(&adapter->driver_lock, flags); + memcpy(priv->adapter->cur_cmd->pdata_buf, + &resp->params.gettsf.tsfvalue, sizeof(u64)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_bt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.bt.addr1, 2 * ETH_ALEN); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_fwt_access: + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.fwt, + sizeof(resp->params.fwt)); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + break; + case cmd_ret_mesh_access: + if (adapter->cur_cmd->pdata_buf) + memcpy(adapter->cur_cmd->pdata_buf, + &resp->params.mesh, + sizeof(resp->params.mesh)); + break; + case cmd_rte_802_11_tx_rate_query: + priv->adapter->txrate = resp->params.txrate.txrate; + break; + default: + lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n", + resp->command); + break; + } + return ret; +} + +int libertas_process_rx_command(wlan_private * priv) +{ + u16 respcmd; + struct cmd_ds_command *resp; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + ulong flags; + u16 result; + + ENTER(); + + lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies); + + /* Now we got response from FW, cancel the command timer */ + del_timer(&adapter->command_timer); + + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + + if (!adapter->cur_cmd) { + lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd); + ret = -1; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + goto done; + } + resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr); + + lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr, + priv->wlan_dev.upld_len); + + respcmd = le16_to_cpu(resp->command); + + result = le16_to_cpu(resp->result); + + lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd, + result, priv->wlan_dev.upld_len); + + if (!(respcmd & 0x8000)) { + lbs_pr_debug(1, "Invalid response to command!"); + adapter->cur_cmd_retcode = -1; + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + ret = -1; + goto done; + } + + /* Store the response code to cur_cmd_retcode. */ + adapter->cur_cmd_retcode = le16_to_cpu(resp->result); + + if (respcmd == cmd_ret_802_11_ps_mode) { + struct cmd_ds_802_11_ps_mode *psmode; + + psmode = &resp->params.psmode; + lbs_pr_debug(1, + "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n", + resp->result, psmode->action); + psmode->action = cpu_to_le16(psmode->action); + + if (result) { + lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n", + resp->result); + if (adapter->inframode == wlan802_11ibss) { + /* + * We should not re-try enter-ps command in + * ad-hoc mode. It takes place in + * libertas_execute_next_command(). + */ + if (psmode->action == cmd_subcmd_enter_ps) + adapter->psmode = + wlan802_11powermodecam; + } + } else if (psmode->action == cmd_subcmd_enter_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_AWAKE; + + lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n"); + if (adapter->connect_status != libertas_connected) { + /* + * When Deauth Event received before Enter_PS command + * response, We need to wake up the firmware. + */ + lbs_pr_debug(1, + "Disconnected, Going to invoke libertas_ps_wakeup\n"); + + mutex_unlock(&adapter->lock); + spin_unlock_irqrestore(&adapter->driver_lock, flags); + libertas_ps_wakeup(priv, 0); + mutex_lock(&adapter->lock); + spin_lock_irqsave(&adapter->driver_lock, flags); + } + } else if (psmode->action == cmd_subcmd_exit_ps) { + adapter->needtowakeup = 0; + adapter->psstate = PS_STATE_FULL_POWER; + lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n"); + } else { + lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n", + psmode->action); + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = 0; + goto done; + } + + if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) { + /* Copy the response back to response buffer */ + memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size); + + adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD; + } + + /* If the command is not successful, cleanup and return failure */ + if ((result != 0 || !(respcmd & 0x8000))) { + lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n", + resp->command, resp->result); + /* + * Handling errors here + */ + switch (respcmd) { + case cmd_ret_hw_spec_info: + case cmd_ret_802_11_reset: + lbs_pr_debug(1, "CMD_RESP: Reset command failed\n"); + break; + + } + + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = -1; + goto done; + } + + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + ret = handle_cmd_response(respcmd, resp, priv); + + spin_lock_irqsave(&adapter->driver_lock, flags); + if (adapter->cur_cmd) { + /* Clean up and Put current command back to cmdfreeq */ + __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd); + adapter->nr_cmd_pending--; + WARN_ON(adapter->nr_cmd_pending > 128); + adapter->cur_cmd = NULL; + } + spin_unlock_irqrestore(&adapter->driver_lock, flags); + +done: + mutex_unlock(&adapter->lock); + LEAVE(); + return ret; +} + +int libertas_process_event(wlan_private * priv) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + u32 eventcause; + + spin_lock_irq(&adapter->driver_lock); + eventcause = adapter->eventcause; + spin_unlock_irq(&adapter->driver_lock); + + ENTER(); + + lbs_pr_debug(1, "EVENT Cause %x\n", eventcause); + + switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) { + case MACREG_INT_CODE_LINK_SENSED: + lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n"); + break; + + case MACREG_INT_CODE_DEAUTHENTICATED: + lbs_pr_debug(1, "EVENT: Deauthenticated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_DISASSOCIATED: + lbs_pr_debug(1, "EVENT: Disassociated\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_LINK_LOSE_NO_SCAN: + lbs_pr_debug(1, "EVENT: Link lost\n"); + libertas_mac_event_disconnected(priv); + break; + + case MACREG_INT_CODE_PS_SLEEP: + lbs_pr_debug(1, "EVENT: SLEEP\n"); + lbs_pr_debug(1, "_"); + + /* handle unexpected PS SLEEP event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS SLEEP\n"); + break; + } + adapter->psstate = PS_STATE_PRE_SLEEP; + + libertas_ps_confirm_sleep(priv, (u16) adapter->psmode); + + break; + + case MACREG_INT_CODE_PS_AWAKE: + lbs_pr_debug(1, "EVENT: AWAKE \n"); + lbs_pr_debug(1, "|"); + + /* handle unexpected PS AWAKE event */ + if (adapter->psstate == PS_STATE_FULL_POWER) { + lbs_pr_debug(1, + "EVENT: In FULL POWER mode - ignore PS AWAKE\n"); + break; + } + + adapter->psstate = PS_STATE_AWAKE; + + if (adapter->needtowakeup) { + /* + * wait for the command processing to finish + * before resuming sending + * adapter->needtowakeup will be set to FALSE + * in libertas_ps_wakeup() + */ + lbs_pr_debug(1, "Waking up...\n"); + libertas_ps_wakeup(priv, 0); + } + break; + + case MACREG_INT_CODE_MIC_ERR_UNICAST: + lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST); + break; + + case MACREG_INT_CODE_MIC_ERR_MULTICAST: + lbs_pr_debug(1, "EVENT: MULTICAST MIC ERROR\n"); + handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST); + break; + case MACREG_INT_CODE_MIB_CHANGED: + case MACREG_INT_CODE_INIT_DONE: + break; + + case MACREG_INT_CODE_ADHOC_BCN_LOST: + lbs_pr_debug(1, "EVENT: HWAC - ADHOC BCN LOST\n"); + break; + + case MACREG_INT_CODE_RSSI_LOW: + lbs_pr_alert( "EVENT: RSSI_LOW\n"); + break; + case MACREG_INT_CODE_SNR_LOW: + lbs_pr_alert( "EVENT: SNR_LOW\n"); + break; + case MACREG_INT_CODE_MAX_FAIL: + lbs_pr_alert( "EVENT: MAX_FAIL\n"); + break; + case MACREG_INT_CODE_RSSI_HIGH: + lbs_pr_alert( "EVENT: RSSI_HIGH\n"); + break; + case MACREG_INT_CODE_SNR_HIGH: + lbs_pr_alert( "EVENT: SNR_HIGH\n"); + break; + + default: + lbs_pr_alert( "EVENT: unknown event id: %#x\n", + eventcause >> SBI_EVENT_CAUSE_SHIFT); + break; + } + + spin_lock_irq(&adapter->driver_lock); + adapter->eventcause = 0; + spin_unlock_irq(&adapter->driver_lock); + LEAVE(); + return ret; +} diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c new file mode 100644 index 0000000..51dfd20 --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.c @@ -0,0 +1,1935 @@ +#include <linux/module.h> +#include <linux/dcache.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/mm.h> +#include <net/iw_handler.h> +#include "dev.h" +#include "decl.h" +#include "host.h" + +static struct dentry *libertas_dir = NULL; +static char *szStates[] = { + "Connected", + "Disconnected" +}; + +void libertas_debug_init(wlan_private * priv, struct net_device *dev); + +static int open_file_generic(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +static ssize_t write_file_dummy(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static const size_t len = PAGE_SIZE; + +static ssize_t libertas_dev_info(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + ssize_t res; + + pos += snprintf(buf+pos, len-pos, "state = %s\n", + szStates[priv->adapter->connect_status]); + pos += snprintf(buf+pos, len-pos, "region_code = %02x\n", + (u32) priv->adapter->regioncode); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + + +static ssize_t libertas_getscantable(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + size_t pos = 0; + int numscansdone = 0, res; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + pos += snprintf(buf+pos, len-pos, + "# | ch | ss | bssid | cap | TSF | Qual | SSID \n"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------"); + pos += snprintf(buf+pos, len-pos, + "---------------------------------------\n"); + + while (numscansdone < priv->adapter->numinscantable) { + struct bss_descriptor *pbssinfo; + u16 cap; + + pbssinfo = &priv->adapter->scantable[numscansdone]; + memcpy(&cap, &pbssinfo->cap, sizeof(cap)); + pos += snprintf(buf+pos, len-pos, + "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |", + numscansdone, pbssinfo->channel, pbssinfo->rssi, + pbssinfo->macaddress[0], pbssinfo->macaddress[1], + pbssinfo->macaddress[2], pbssinfo->macaddress[3], + pbssinfo->macaddress[4], pbssinfo->macaddress[5]); + pos += snprintf(buf+pos, len-pos, " %04x-", cap); + pos += snprintf(buf+pos, len-pos, "%c%c%c |", + pbssinfo->cap.ibss ? 'A' : 'I', + pbssinfo->cap.privacy ? 'P' : ' ', + pbssinfo->cap.spectrummgmt ? 'S' : ' '); + pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf); + pos += snprintf(buf+pos, len-pos, " %d |", + SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi)); + + pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid); + + numscansdone++; + } + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t buf_size, res; + int p1, p2, p3, p4, p5, p6; + struct sleep_params sp; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, user_buf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6); + if (res != 6) { + res = -EFAULT; + goto out_unlock; + } + sp.sp_error = p1; + sp.sp_offset = p2; + sp.sp_stabletime = p3; + sp.sp_calcontrol = p4; + sp.sp_extsleepclk = p5; + sp.sp_reserved = p6; + + memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params)); + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (!res) + res = count; + else + res = -EINVAL; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_sleepparams_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res; + size_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_prepare_and_send_command(priv, + cmd_802_11_sleep_params, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + if (res) { + res = -EFAULT; + goto out_unlock; + } + + pos += snprintf(buf, len, "%d %d %d %d %d %d\n", adapter->sp.sp_error, + adapter->sp.sp_offset, adapter->sp.sp_stabletime, + adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk, + adapter->sp.sp_reserved); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_extscan(struct file *file, const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct WLAN_802_11_SSID extscan_ssid; + union iwreq_data wrqu; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1); + extscan_ssid.ssidlength = strlen(buf)-1; + + libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1); + + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + return count; +} + +static int libertas_parse_chan(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur) +{ + char *start, *end, *hold, *str; + int i = 0; + + start = strstr(buf, "chan="); + if (!start) + return -EINVAL; + start += 5; + end = strstr(start, " "); + if (!end) + end = buf + count; + hold = kzalloc((end - start)+1, GFP_KERNEL); + if (!hold) + return -ENOMEM; + strncpy(hold, start, end - start); + hold[(end-start)+1] = '\0'; + while(hold && (str = strsep(&hold, ","))) { + int chan; + char band, passive = 0; + sscanf(str, "%d%c%c", &chan, &band, &passive); + scan_cfg->chanlist[i].channumber = chan; + scan_cfg->chanlist[i].scantype = passive ? 1 : 0; + if (band == 'b' || band == 'g') + scan_cfg->chanlist[i].radiotype = 0; + else if (band == 'a') + scan_cfg->chanlist[i].radiotype = 1; + + scan_cfg->chanlist[i].scantime = dur; + i++; + } + + kfree(hold); + return i; +} + +static void libertas_parse_bssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + unsigned int mac[ETH_ALEN]; + int i; + + hold = strstr(buf, "bssid="); + if (!hold) + return; + hold += 6; + sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, + mac+4, mac+5); + for(i=0;i<ETH_ALEN;i++) + scan_cfg->specificBSSID[i] = mac[i]; +} + +static void libertas_parse_ssid(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold, *end; + ssize_t size; + + hold = strstr(buf, "ssid="); + if (!hold) + return; + hold += 5; + end = strstr(hold, " "); + if (!end) + end = buf + count - 1; + + size = min(IW_ESSID_MAX_SIZE, end - hold); + strncpy(scan_cfg->specificSSID, hold, size); + + return; +} + +static void libertas_parse_keep(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "keep="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + if (val != 0) + val = 1; + + scan_cfg->keeppreviousscan = val; + return; +} + +static int libertas_parse_dur(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "dur="); + if (!hold) + return 0; + hold += 4; + sscanf(hold, "%d", &val); + + return val; +} + +static void libertas_parse_probes(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "probes="); + if (!hold) + return; + hold += 7; + sscanf(hold, "%d", &val); + + scan_cfg->numprobes = val; + + return; +} + +static void libertas_parse_type(char *buf, size_t count, + struct wlan_ioctl_user_scan_cfg *scan_cfg) +{ + char *hold; + int val; + + hold = strstr(buf, "type="); + if (!hold) + return; + hold += 5; + sscanf(hold, "%d", &val); + + /* type=1,2 or 3 */ + if (val < 1 || val > 3) + return; + + scan_cfg->bsstype = val; + + return; +} + +static ssize_t libertas_setuserscan(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + struct wlan_ioctl_user_scan_cfg *scan_cfg; + union iwreq_data wrqu; + int dur; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL); + if (!scan_cfg) + return -ENOMEM; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + + scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY; + + dur = libertas_parse_dur(buf, count, scan_cfg); + libertas_parse_chan(buf, count, scan_cfg, dur); + libertas_parse_bssid(buf, count, scan_cfg); + libertas_parse_ssid(buf, count, scan_cfg); + libertas_parse_keep(buf, count, scan_cfg); + libertas_parse_probes(buf, count, scan_cfg); + libertas_parse_type(buf, count, scan_cfg); + + wlan_scan_networks(priv, scan_cfg); + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + memset(&wrqu, 0x00, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL); + +out_unlock: + free_page(addr); + kfree(scan_cfg); + return count; +} + +static int libertas_event_initcmd(wlan_private *priv, void **response_buf, + struct cmd_ctrl_node **cmdnode, + struct cmd_ds_command **cmd) +{ + u16 wait_option = cmd_option_waitforrsp; + + if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) { + lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n"); + return -ENOMEM; + } + if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) { + lbs_pr_debug(1, "failed to allocate response buffer!\n"); + return -ENOMEM; + } + libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL); + init_waitqueue_head(&(*cmdnode)->cmdwait_q); + (*cmdnode)->pdata_buf = *response_buf; + (*cmdnode)->cmdflags |= CMD_F_HOSTCMD; + (*cmdnode)->cmdwaitqwoken = 0; + *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr; + (*cmd)->command = cmd_802_11_subscribe_event; + (*cmd)->seqnum = ++priv->adapter->seqnum; + (*cmd)->result = 0; + return 0; +} + +static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Lowrssi; + case TLV_TYPE_RSSI_LOW: + Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Lowrssi->rssivalue, + Lowrssi->rssifreq, + (event->events & 0x0001)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static u16 libertas_get_events_bitmap(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res; + u16 event_bitmap; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + return res; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + event_bitmap = event->events; + kfree(response_buf); + return event_bitmap; +} + +static ssize_t libertas_lowrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(0x0104); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0001 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *LowSnr; + case TLV_TYPE_SNR_LOW: + LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + LowSnr->snrvalue, + LowSnr->snrfreq, + (event->events & 0x0002)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_lowsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0002 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; + +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_failurecount *failcount; + case TLV_TYPE_FAILCOUNT: + failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + failcount->failvalue, + failcount->Failfreq, + (event->events & 0x0004)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_failurecount); + break; + } + } + + kfree(response_buf); + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_failcount_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_failurecount *failcount; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_failurecount)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + failcount = (struct mrvlietypes_failurecount *)(ptr); + failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT); + failcount->header.len = 2; + failcount->failvalue = cpu_to_le16(value); + failcount->Failfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0004 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = (struct cmd_ds_command *)response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + free_page(addr); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_beaconsmissed *bcnmiss; + case TLV_TYPE_BCNMISS: + bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d N/A %d\n", + bcnmiss->beaconmissed, + (event->events & 0x0008)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_beaconsmissed); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_bcnmiss_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_beaconsmissed *bcnmiss; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_beaconsmissed)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr); + bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS); + bcnmiss->header.len = 2; + bcnmiss->beaconmissed = cpu_to_le16(value); + event_bitmap |= subscribed ? 0x0008 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + free_page(addr); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_rssithreshold *Highrssi; + case TLV_TYPE_RSSI_HIGH: + Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + Highrssi->rssivalue, + Highrssi->rssifreq, + (event->events & 0x0010)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highrssi_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_rssithreshold *rssi_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_rssithreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr); + rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH); + rssi_threshold->header.len = 2; + rssi_threshold->rssivalue = cpu_to_le16(value); + rssi_threshold->rssifreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0010 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + void *response_buf; + int res, cmd_len; + ssize_t pos = 0; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) { + free_page(addr); + return res; + } + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_get; + pcmdptr->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN); + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN); + while (cmd_len < pcmdptr->size) { + struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len); + switch(header->type) { + struct mrvlietypes_snrthreshold *HighSnr; + case TLV_TYPE_SNR_HIGH: + HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len); + pos += snprintf(buf+pos, len-pos, "%d %d %d\n", + HighSnr->snrvalue, + HighSnr->snrfreq, + (event->events & 0x0020)?1:0); + default: + cmd_len += sizeof(struct mrvlietypes_snrthreshold); + break; + } + } + + kfree(response_buf); + + res = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return res; +} + +static ssize_t libertas_highsnr_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + ssize_t res, buf_size; + int value, freq, subscribed, cmd_len; + struct cmd_ctrl_node *pcmdnode; + struct cmd_ds_command *pcmdptr; + struct cmd_ds_802_11_subscribe_event *event; + struct mrvlietypes_snrthreshold *snr_threshold; + void *response_buf; + u16 event_bitmap; + u8 *ptr; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%d %d %d", &value, &freq, &subscribed); + if (res != 3) { + res = -EFAULT; + goto out_unlock; + } + + event_bitmap = libertas_get_events_bitmap(priv); + + res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr); + if (res < 0) + goto out_unlock; + + event = &pcmdptr->params.subscribe_event; + event->action = cmd_act_set; + pcmdptr->size = cpu_to_le16(S_DS_GEN + + sizeof(struct cmd_ds_802_11_subscribe_event) + + sizeof(struct mrvlietypes_snrthreshold)); + cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event); + ptr = (u8*) pcmdptr+cmd_len; + snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr); + snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH); + snr_threshold->header.len = 2; + snr_threshold->snrvalue = cpu_to_le16(value); + snr_threshold->snrfreq = cpu_to_le16(freq); + event_bitmap |= subscribed ? 0x0020 : 0x0; + event->events = event_bitmap; + + libertas_queue_cmd(adapter, pcmdnode, 1); + wake_up_interruptible(&priv->mainthread.waitq); + + /* Sleep until response is generated by FW */ + wait_event_interruptible(pcmdnode->cmdwait_q, + pcmdnode->cmdwaitqwoken); + + pcmdptr = response_buf; + + if (pcmdptr->result) { + lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__, + pcmdptr->result); + kfree(response_buf); + free_page(addr); + return 0; + } + + if (pcmdptr->command != cmd_ret_802_11_subscribe_event) { + lbs_pr_err("command response incorrect!\n"); + kfree(response_buf); + free_page(addr); + return 0; + } + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->mac_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n", + priv->mac_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + return ret; +} + +static ssize_t libertas_rdmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->mac_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrmac_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_mac_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdbbp_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->bbp_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n", + priv->bbp_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrbbp_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_bbp_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_rdrf_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + wlan_adapter *adapter = priv->adapter; + struct wlan_offset_value offval; + ssize_t pos = 0; + int ret; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + offval.offset = priv->rf_offset; + offval.value = 0; + + ret = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 0, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n", + priv->rf_offset, adapter->offsetvalue.value); + + ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos); + free_page(addr); + + return ret; +} + +static ssize_t libertas_rdrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + priv->rf_offset = simple_strtoul((char *)buf, NULL, 16); + res = count; +out_unlock: + free_page(addr); + return res; +} + +static ssize_t libertas_wrrf_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *ppos) +{ + + wlan_private *priv = file->private_data; + ssize_t res, buf_size; + u32 offset, value; + struct wlan_offset_value offval; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + buf_size = min(count, len - 1); + if (copy_from_user(buf, userbuf, buf_size)) { + res = -EFAULT; + goto out_unlock; + } + res = sscanf(buf, "%x %x", &offset, &value); + if (res != 2) { + res = -EFAULT; + goto out_unlock; + } + + offval.offset = offset; + offval.value = value; + res = libertas_prepare_and_send_command(priv, + cmd_rf_reg_access, 1, + cmd_option_waitforrsp, 0, &offval); + mdelay(10); + + res = count; +out_unlock: + free_page(addr); + return res; +} + +#define FOPS(fread, fwrite) { \ + .owner = THIS_MODULE, \ + .open = open_file_generic, \ + .read = (fread), \ + .write = (fwrite), \ +} + +struct libertas_debugfs_files { + char *name; + int perm; + struct file_operations fops; +}; + +struct libertas_debugfs_files debugfs_files[] = { + { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), }, + { "getscantable", 0444, FOPS(libertas_getscantable, + write_file_dummy), }, + { "sleepparams", 0644, FOPS(libertas_sleepparams_read, + libertas_sleepparams_write), }, + { "extscan", 0600, FOPS(NULL, libertas_extscan), }, + { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), }, +}; + +struct libertas_debugfs_files debugfs_events_files[] = { + {"low_rssi", 0644, FOPS(libertas_lowrssi_read, + libertas_lowrssi_write), }, + {"low_snr", 0644, FOPS(libertas_lowsnr_read, + libertas_lowsnr_write), }, + {"failure_count", 0644, FOPS(libertas_failcount_read, + libertas_failcount_write), }, + {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read, + libertas_bcnmiss_write), }, + {"high_rssi", 0644, FOPS(libertas_highrssi_read, + libertas_highrssi_write), }, + {"high_snr", 0644, FOPS(libertas_highsnr_read, + libertas_highsnr_write), }, +}; + +struct libertas_debugfs_files debugfs_regs_files[] = { + {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), }, + {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), }, + {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), }, + {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), }, + {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), }, + {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), }, +}; + +void libertas_debugfs_init(void) +{ + if (!libertas_dir) + libertas_dir = debugfs_create_dir("libertas_wireless", NULL); + + return; +} + +void libertas_debugfs_remove(void) +{ + if (libertas_dir) + debugfs_remove(libertas_dir); + return; +} + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev) +{ + int i; + struct libertas_debugfs_files *files; + if (!libertas_dir) + goto exit; + + priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_dir); + if (!priv->debugfs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_files); i++) { + files = &debugfs_files[i]; + priv->debugfs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->debugfs_dir, + priv, + &files->fops); + } + + priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir); + if (!priv->events_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) { + files = &debugfs_events_files[i]; + priv->debugfs_events_files[i] = debugfs_create_file(files->name, + files->perm, + priv->events_dir, + priv, + &files->fops); + } + + priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir); + if (!priv->regs_dir) + goto exit; + + for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) { + files = &debugfs_regs_files[i]; + priv->debugfs_regs_files[i] = debugfs_create_file(files->name, + files->perm, + priv->regs_dir, + priv, + &files->fops); + } + +#ifdef PROC_DEBUG + libertas_debug_init(priv, dev); +#endif +exit: + return; +} + +void libertas_debugfs_remove_one(wlan_private *priv) +{ + int i; + + for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) + debugfs_remove(priv->debugfs_regs_files[i]); + + debugfs_remove(priv->regs_dir); + + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_events_files[i]); + + debugfs_remove(priv->events_dir); +#ifdef PROC_DEBUG + debugfs_remove(priv->debugfs_debug); +#endif + for(i=0; i<ARRAY_SIZE(debugfs_files); i++) + debugfs_remove(priv->debugfs_files[i]); +} + +/* debug entry */ + +#define item_size(n) (FIELD_SIZEOF(wlan_adapter, n)) +#define item_addr(n) (offsetof(wlan_adapter, n)) + +struct debug_data { + char name[32]; + u32 size; + u32 addr; +}; + +/* To debug any member of wlan_adapter, simply add one line here. + */ +static struct debug_data items[] = { + {"intcounter", item_size(intcounter), item_addr(intcounter)}, + {"psmode", item_size(psmode), item_addr(psmode)}, + {"psstate", item_size(psstate), item_addr(psstate)}, +}; + +static int num_of_items = ARRAY_SIZE(items); + +/** + * @brief proc read function + * + * @param page pointer to buffer + * @param s read data starting position + * @param off offset + * @param cnt counter + * @param eof end of file flag + * @param data data to output + * @return number of output data + */ +static ssize_t wlan_debugfs_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) +{ + int val = 0; + size_t pos = 0; + ssize_t res; + char *p; + int i; + struct debug_data *d; + unsigned long addr = get_zeroed_page(GFP_KERNEL); + char *buf = (char *)addr; + + p = buf; + + d = (struct debug_data *)file->private_data; + + for (i = 0; i < num_of_items; i++) { + if (d[i].size == 1) + val = *((u8 *) d[i].addr); + else if (d[i].size == 2) + val = *((u16 *) d[i].addr); + else if (d[i].size == 4) + val = *((u32 *) d[i].addr); + + pos += sprintf(p + pos, "%s=%d\n", d[i].name, val); + } + + res = simple_read_from_buffer(userbuf, count, ppos, p, pos); + + free_page(addr); + return res; +} + +/** + * @brief proc write function + * + * @param f file pointer + * @param buf pointer to data buffer + * @param cnt data number to write + * @param data data to write + * @return number of data + */ +static int wlan_debugfs_write(struct file *f, const char __user *buf, + size_t cnt, loff_t *ppos) +{ + int r, i; + char *pdata; + char *p; + char *p0; + char *p1; + char *p2; + struct debug_data *d = (struct debug_data *)f->private_data; + + pdata = (char *)kmalloc(cnt, GFP_KERNEL); + if (pdata == NULL) + return 0; + + if (copy_from_user(pdata, buf, cnt)) { + lbs_pr_debug(1, "Copy from user failed\n"); + kfree(pdata); + return 0; + } + + p0 = pdata; + for (i = 0; i < num_of_items; i++) { + do { + p = strstr(p0, d[i].name); + if (p == NULL) + break; + p1 = strchr(p, '\n'); + if (p1 == NULL) + break; + p0 = p1++; + p2 = strchr(p, '='); + if (!p2) + break; + p2++; + r = simple_strtoul(p2, NULL, 0); + if (d[i].size == 1) + *((u8 *) d[i].addr) = (u8) r; + else if (d[i].size == 2) + *((u16 *) d[i].addr) = (u16) r; + else if (d[i].size == 4) + *((u32 *) d[i].addr) = (u32) r; + break; + } while (1); + } + kfree(pdata); + + return cnt; +} + +static struct file_operations libertas_debug_fops = { + .owner = THIS_MODULE, + .open = open_file_generic, + .write = wlan_debugfs_write, + .read = wlan_debugfs_read, +}; + +/** + * @brief create debug proc file + * + * @param priv pointer wlan_private + * @param dev pointer net_device + * @return N/A + */ +void libertas_debug_init(wlan_private * priv, struct net_device *dev) +{ + int i; + + if (!priv->debugfs_dir) + return; + + for (i = 0; i < num_of_items; i++) + items[i].addr += (u32) priv->adapter; + + priv->debugfs_debug = debugfs_create_file("debug", 0644, + priv->debugfs_dir, &items[0], + &libertas_debug_fops); +} + +/** + * @brief remove proc file + * + * @param priv pointer wlan_private + * @return N/A + */ +void libertas_debug_remove(wlan_private * priv) +{ + debugfs_remove(priv->debugfs_debug); +} diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h new file mode 100644 index 0000000..880a11b --- /dev/null +++ b/drivers/net/wireless/libertas/debugfs.h @@ -0,0 +1,6 @@ +void libertas_debugfs_init(void); +void libertas_debugfs_remove(void); + +void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev); +void libertas_debugfs_remove_one(wlan_private *priv); + diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h new file mode 100644 index 0000000..606bdd0 --- /dev/null +++ b/drivers/net/wireless/libertas/decl.h @@ -0,0 +1,83 @@ +/** + * This file contains declaration referring to + * functions defined in other source files + */ + +#ifndef _WLAN_DECL_H_ +#define _WLAN_DECL_H_ + +#include "defs.h" + +/** Function Prototype Declaration */ +struct wlan_private; +struct sk_buff; +struct net_device; + +extern char *libertas_fw_name; + +void libertas_free_adapter(wlan_private * priv); +int libertas_set_mac_packet_filter(wlan_private * priv); + +int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt); +void libertas_send_tx_feedback(wlan_private * priv); +u8 libertas_check_last_packet_indication(wlan_private * priv); + +int libertas_free_cmd_buffer(wlan_private * priv); +struct cmd_ctrl_node; +struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv); + +void libertas_set_cmd_ctrl_node(wlan_private * priv, + struct cmd_ctrl_node *ptempnode, + u32 cmd_oid, u16 wait_option, void *pdata_buf); + +int libertas_prepare_and_send_command(wlan_private * priv, + u16 cmd_no, + u16 cmd_action, + u16 wait_option, u32 cmd_oid, void *pdata_buf); + +void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail); + +int libertas_allocate_cmd_buffer(wlan_private * priv); +int libertas_execute_next_command(wlan_private * priv); +int libertas_process_event(wlan_private * priv); +void libertas_interrupt(struct net_device *); +int libertas_set_radio_control(wlan_private * priv); +u32 libertas_index_to_data_rate(u8 index); +u8 libertas_data_rate_to_index(u32 rate); +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen); + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb); + +/** The proc fs interface */ +int libertas_process_rx_command(wlan_private * priv); +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb); +void libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); +void __libertas_cleanup_and_insert_cmd(wlan_private * priv, + struct cmd_ctrl_node *ptempcmd); + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band); + +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *); + +void libertas_ps_sleep(wlan_private * priv, int wait_option); +void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode); +void libertas_ps_wakeup(wlan_private * priv, int wait_option); + +void libertas_tx_runqueue(wlan_private *priv); + +extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel( + wlan_adapter * adapter, u8 band, u16 channel); + +extern void libertas_mac_event_disconnected(wlan_private * priv); + +void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str); + +int reset_device(wlan_private *priv); +/* main.c */ +extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, + int *cfp_no); +wlan_private *wlan_add_card(void *card); +int wlan_remove_card(void *card); + +#endif /* _WLAN_DECL_H_ */ diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h new file mode 100644 index 0000000..fb1478c --- /dev/null +++ b/drivers/net/wireless/libertas/defs.h @@ -0,0 +1,369 @@ +/** + * This header file contains global constant/enum definitions, + * global variable declaration. + */ +#ifndef _WLAN_DEFS_H_ +#define _WLAN_DEFS_H_ + +#include <linux/spinlock.h> + +extern unsigned int libertas_debug; + +#define DRV_NAME "usb8xxx" + +#define lbs_pr_info(format, args...) \ + printk(KERN_INFO DRV_NAME": " format, ## args) +#define lbs_pr_err(format, args...) \ + printk(KERN_ERR DRV_NAME": " format, ## args) +#define lbs_pr_alert(format, args...) \ + printk(KERN_ALERT DRV_NAME": " format, ## args) + +#ifdef DEBUG +#define lbs_pr_debug(level, format, args...) \ + do { if (libertas_debug >= level) \ + printk(KERN_INFO DRV_NAME": " format, ##args); } while (0) +#define lbs_dev_dbg(level, device, format, args...) \ + lbs_pr_debug(level, "%s: " format, \ + (device)->bus_id , ## args) + +static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len) +{ + int i = 0; + + if (!libertas_debug) + return; + + printk(KERN_DEBUG "%s: ", prompt); + for (i = 1; i <= len; i++) { + printk(KERN_DEBUG "%02x ", (u8) * buf); + buf++; + } + printk("\n"); +} +#else +#define lbs_pr_debug(level, format, args...) do {} while (0) +#define lbs_dev_dbg(level, device, format, args...) do {} while (0) +#define lbs_dbg_hex(x,y,z) do {} while (0) +#endif + +#define ENTER() lbs_pr_debug(1, "Enter: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) +#define LEAVE() lbs_pr_debug(1, "Leave: %s, %s:%i\n", \ + __FUNCTION__, __FILE__, __LINE__) + +/** Buffer Constants */ + +/* The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical +* addresses of TxPD buffers. Station has only 8 TxPD available, Whereas +* driver has more local TxPDs. Each TxPD on the host memory is associated +* with a Tx control node. The driver maintains 8 RxPD descriptors for +* station firmware to store Rx packet information. +* +* Current version of MAC has a 32x6 multicast address buffer. +* +* 802.11b can have up to 14 channels, the driver keeps the +* BSSID(MAC address) of each APs or Ad hoc stations it has sensed. +*/ + +#define MRVDRV_MAX_MULTICAST_LIST_SIZE 32 +#define MRVDRV_NUM_OF_CMD_BUFFER 10 +#define MRVDRV_SIZE_OF_CMD_BUFFER (2 * 1024) +#define MRVDRV_MAX_CHANNEL_SIZE 14 +#define MRVDRV_MAX_BSSID_LIST 64 +#define MRVDRV_ASSOCIATION_TIME_OUT 255 +#define MRVDRV_SNAP_HEADER_LEN 8 + +#define WLAN_UPLD_SIZE 2312 +#define DEV_NAME_LEN 32 + +/** Misc constants */ +/* This section defines 802.11 specific contants */ + +#define MRVDRV_MAX_BSS_DESCRIPTS 16 +#define MRVDRV_MAX_REGION_CODE 6 + +#define MRVDRV_IGNORE_MULTIPLE_DTIM 0xfffe +#define MRVDRV_MIN_MULTIPLE_DTIM 1 +#define MRVDRV_MAX_MULTIPLE_DTIM 5 +#define MRVDRV_DEFAULT_MULTIPLE_DTIM 1 + +#define MRVDRV_DEFAULT_LISTEN_INTERVAL 10 + +#define MRVDRV_CHANNELS_PER_SCAN 4 +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +#define MRVDRV_DEBUG_RX_PATH 0x00000001 +#define MRVDRV_DEBUG_TX_PATH 0x00000002 + +#define MRVDRV_MIN_BEACON_INTERVAL 20 +#define MRVDRV_MAX_BEACON_INTERVAL 1000 +#define MRVDRV_BEACON_INTERVAL 100 + +/** TxPD status */ + +/* Station firmware use TxPD status field to report final Tx transmit +* result, Bit masks are used to present combined situations. +*/ + +#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01 +#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08 + +/** Tx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define TxPD_CONTROL_WDS_FRAME (1<<17) +#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME + +/** RxPD status */ + +#define MRVDRV_RXPD_STATUS_OK 0x0001 + +/** RxPD status - Received packet types */ +/** Rx mesh flag */ +/* Currently we are using normal WDS flag as mesh flag. + * TODO: change to proper mesh flag when MAC understands it. + */ +#define RxPD_CONTROL_WDS_FRAME (0x40) +#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME + +/** RSSI-related defines */ +/* RSSI constants are used to implement 802.11 RSSI threshold +* indication. if the Rx packet signal got too weak for 5 consecutive +* times, miniport driver (driver) will report this event to wrapper +*/ + +#define MRVDRV_NF_DEFAULT_SCAN_VALUE (-96) + +/** RTS/FRAG related defines */ +#define MRVDRV_RTS_MIN_VALUE 0 +#define MRVDRV_RTS_MAX_VALUE 2347 +#define MRVDRV_FRAG_MIN_VALUE 256 +#define MRVDRV_FRAG_MAX_VALUE 2346 + +/* This is for firmware specific length */ +#define EXTRA_LEN 36 + +#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN) + +#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \ + (ETH_FRAME_LEN + sizeof(struct rxpd) \ + + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN) + +#define CMD_F_HOSTCMD (1 << 0) +#define FW_CAPINFO_WPA (1 << 0) + +/** WPA key LENGTH*/ +#define MRVL_MAX_KEY_WPA_KEY_LENGTH 32 + +#define KEY_LEN_WPA_AES 16 +#define KEY_LEN_WPA_TKIP 32 +#define KEY_LEN_WEP_104 13 +#define KEY_LEN_WEP_40 5 + +#define RF_ANTENNA_1 0x1 +#define RF_ANTENNA_2 0x2 +#define RF_ANTENNA_AUTO 0xFFFF + +#define BAND_B (0x01) +#define BAND_G (0x02) +#define ALL_802_11_BANDS (BAND_B | BAND_G) + +/** MACRO DEFINITIONS */ +#define CAL_NF(NF) ((s32)(-(s32)(NF))) +#define CAL_RSSI(SNR, NF) ((s32)((s32)(SNR) + CAL_NF(NF))) +#define SCAN_RSSI(RSSI) (0x100 - ((u8)(RSSI))) + +#define DEFAULT_BCN_AVG_FACTOR 8 +#define DEFAULT_DATA_AVG_FACTOR 8 +#define AVG_SCALE 100 +#define CAL_AVG_SNR_NF(AVG, SNRNF, N) \ + (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \ + ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \ + AVG_SCALE)) / N)) + +#define B_SUPPORTED_RATES 8 +#define G_SUPPORTED_RATES 14 + +#define WLAN_SUPPORTED_RATES 14 + +#define MAX_LEDS 8 + +#define IS_MESH_FRAME(x) (x->cb[6]) +#define SET_MESH_FRAME(x) (x->cb[6]=1) +#define UNSET_MESH_FRAME(x) (x->cb[6]=0) + +/** Global Variable Declaration */ +typedef struct _wlan_private wlan_private; +typedef struct _wlan_adapter wlan_adapter; +extern const char libertas_driver_version[]; +extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE]; + +extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES]; + +extern u8 libertas_supported_rates[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES]; + +extern u8 libertas_adhoc_rates_b[4]; + +/** ENUM definition*/ +/** SNRNF_TYPE */ +enum SNRNF_TYPE { + TYPE_BEACON = 0, + TYPE_RXPD, + MAX_TYPE_B +}; + +/** SNRNF_DATA*/ +enum SNRNF_DATA { + TYPE_NOAVG = 0, + TYPE_AVG, + MAX_TYPE_AVG +}; + +/** WLAN_802_11_AUTH_ALG*/ +enum WLAN_802_11_AUTH_ALG { + AUTH_ALG_OPEN_SYSTEM = 1, + AUTH_ALG_SHARED_KEY = 2, + AUTH_ALG_NETWORK_EAP = 8, +}; + +/** WLAN_802_1X_AUTH_ALG */ +enum WLAN_802_1X_AUTH_ALG { + WLAN_1X_AUTH_ALG_NONE = 1, + WLAN_1X_AUTH_ALG_LEAP = 2, + WLAN_1X_AUTH_ALG_TLS = 4, + WLAN_1X_AUTH_ALG_TTLS = 8, + WLAN_1X_AUTH_ALG_MD5 = 16, +}; + +/** WLAN_802_11_ENCRYPTION_MODE */ +enum WLAN_802_11_ENCRYPTION_MODE { + CIPHER_NONE, + CIPHER_WEP40, + CIPHER_TKIP, + CIPHER_CCMP, + CIPHER_WEP104, +}; + +/** WLAN_802_11_POWER_MODE */ +enum WLAN_802_11_POWER_MODE { + wlan802_11powermodecam, + wlan802_11powermodemax_psp, + wlan802_11Powermodefast_psp, + /*not a real mode, defined as an upper bound */ + wlan802_11powemodemax +}; + +/** PS_STATE */ +enum PS_STATE { + PS_STATE_FULL_POWER, + PS_STATE_AWAKE, + PS_STATE_PRE_SLEEP, + PS_STATE_SLEEP +}; + +/** DNLD_STATE */ +enum DNLD_STATE { + DNLD_RES_RECEIVED, + DNLD_DATA_SENT, + DNLD_CMD_SENT +}; + +/** WLAN_MEDIA_STATE */ +enum WLAN_MEDIA_STATE { + libertas_connected, + libertas_disconnected +}; + +/** WLAN_802_11_PRIVACY_FILTER */ +enum WLAN_802_11_PRIVACY_FILTER { + wlan802_11privfilteracceptall, + wlan802_11privfilter8021xWEP +}; + +/** mv_ms_type */ +enum mv_ms_type { + MVMS_DAT = 0, + MVMS_CMD = 1, + MVMS_TXDONE = 2, + MVMS_EVENT +}; + +/** WLAN_802_11_NETWORK_INFRASTRUCTURE */ +enum WLAN_802_11_NETWORK_INFRASTRUCTURE { + wlan802_11ibss, + wlan802_11infrastructure, + wlan802_11autounknown, + /*defined as upper bound */ + wlan802_11infrastructuremax +}; + +/** WLAN_802_11_AUTHENTICATION_MODE */ +enum WLAN_802_11_AUTHENTICATION_MODE { + wlan802_11authmodeopen = 0x00, + wlan802_11authmodeshared = 0x01, + wlan802_11authmodenetworkEAP = 0x80, +}; + +/** WLAN_802_11_WEP_STATUS */ +enum WLAN_802_11_WEP_STATUS { + wlan802_11WEPenabled, + wlan802_11WEPdisabled, +}; + +/** SNMP_MIB_INDEX_e */ +enum SNMP_MIB_INDEX_e { + desired_bsstype_i = 0, + op_rateset_i, + bcnperiod_i, + dtimperiod_i, + assocrsp_timeout_i, + rtsthresh_i, + short_retrylim_i, + long_retrylim_i, + fragthresh_i, + dot11d_i, + dot11h_i, + manufid_i, + prodID_i, + manuf_oui_i, + manuf_name_i, + manuf_prodname_i, + manuf_prodver_i, +}; + +/** KEY_TYPE_ID */ +enum KEY_TYPE_ID { + KEY_TYPE_ID_WEP = 0, + KEY_TYPE_ID_TKIP, + KEY_TYPE_ID_AES +}; + +/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */ +enum KEY_INFO_WPA { + KEY_INFO_WPA_MCAST = 0x01, + KEY_INFO_WPA_UNICAST = 0x02, + KEY_INFO_WPA_ENABLED = 0x04 +}; + +/** SNMP_MIB_VALUE_e */ +enum SNMP_MIB_VALUE_e { + SNMP_MIB_VALUE_INFRA = 1, + SNMP_MIB_VALUE_ADHOC +}; + +/* Default values for fwt commands. */ +#define FWT_DEFAULT_METRIC 0 +#define FWT_DEFAULT_DIR 1 +#define FWT_DEFAULT_SSN 0xffffffff +#define FWT_DEFAULT_DSN 0 +#define FWT_DEFAULT_HOPCOUNT 0 +#define FWT_DEFAULT_TTL 0 +#define FWT_DEFAULT_EXPIRATION 0 +#define FWT_DEFAULT_SLEEPMODE 0 +#define FWT_DEFAULT_SNR 0 + +#endif /* _WLAN_DEFS_H_ */ diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h new file mode 100644 index 0000000..b1f876f --- /dev/null +++ b/drivers/net/wireless/libertas/dev.h @@ -0,0 +1,403 @@ +/** + * This file contains definitions and data structures specific + * to Marvell 802.11 NIC. It contains the Device Information + * structure wlan_adapter. + */ +#ifndef _WLAN_DEV_H_ +#define _WLAN_DEV_H_ + +#include <linux/netdevice.h> +#include <linux/wireless.h> +#include <linux/ethtool.h> +#include <linux/debugfs.h> + +#include "defs.h" +#include "scan.h" +#include "thread.h" + +extern struct ethtool_ops libertas_ethtool_ops; + +#define MAX_BSSID_PER_CHANNEL 16 + +#define NR_TX_QUEUE 3 + +/* For the extended Scan */ +#define MAX_EXTENDED_SCAN_BSSID_LIST MAX_BSSID_PER_CHANNEL * \ + MRVDRV_MAX_CHANNEL_SIZE + 1 + +#define MAX_REGION_CHANNEL_NUM 2 + +/** Chan-freq-TxPower mapping table*/ +struct chan_freq_power { + /** channel Number */ + u16 channel; + /** frequency of this channel */ + u32 freq; + /** Max allowed Tx power level */ + u16 maxtxpower; + /** TRUE:channel unsupported; FLASE:supported*/ + u8 unsupported; +}; + +/** region-band mapping table*/ +struct region_channel { + /** TRUE if this entry is valid */ + u8 valid; + /** region code for US, Japan ... */ + u8 region; + /** band B/G/A, used for BAND_CONFIG cmd */ + u8 band; + /** Actual No. of elements in the array below */ + u8 nrcfp; + /** chan-freq-txpower mapping table*/ + struct chan_freq_power *CFP; +}; + +struct wlan_802_11_security { + u8 WPAenabled; + u8 WPA2enabled; + enum WLAN_802_11_WEP_STATUS WEPstatus; + enum WLAN_802_11_AUTHENTICATION_MODE authmode; + enum WLAN_802_1X_AUTH_ALG auth1xalg; + enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode; +}; + +/** Current Basic Service Set State Structure */ +struct current_bss_params { + struct bss_descriptor bssdescriptor; + /** bssid */ + u8 bssid[ETH_ALEN]; + /** ssid */ + struct WLAN_802_11_SSID ssid; + + /** band */ + u8 band; + /** channel */ + u8 channel; + /** number of rates supported */ + int numofrates; + /** supported rates*/ + u8 datarates[WLAN_SUPPORTED_RATES]; +}; + +/** sleep_params */ +struct sleep_params { + u16 sp_error; + u16 sp_offset; + u16 sp_stabletime; + u8 sp_calcontrol; + u8 sp_extsleepclk; + u16 sp_reserved; +}; + +/** Data structure for the Marvell WLAN device */ +typedef struct _wlan_dev { + /** device name */ + char name[DEV_NAME_LEN]; + /** card pointer */ + void *card; + /** IO port */ + u32 ioport; + /** Upload received */ + u32 upld_rcv; + /** Upload type */ + u32 upld_typ; + /** Upload length */ + u32 upld_len; + /** netdev pointer */ + struct net_device *netdev; + /* Upload buffer */ + u8 upld_buf[WLAN_UPLD_SIZE]; + /* Download sent: + bit0 1/0=data_sent/data_tx_done, + bit1 1/0=cmd_sent/cmd_tx_done, + all other bits reserved 0 */ + u8 dnld_sent; +} wlan_dev_t, *pwlan_dev_t; + +/* Mesh statistics */ +struct wlan_mesh_stats { + u32 fwd_bcast_cnt; /* Fwd: Broadcast counter */ + u32 fwd_unicast_cnt; /* Fwd: Unicast counter */ + u32 fwd_drop_ttl; /* Fwd: TTL zero */ + u32 fwd_drop_rbt; /* Fwd: Recently Broadcasted */ + u32 fwd_drop_noroute; /* Fwd: No route to Destination */ + u32 fwd_drop_nobuf; /* Fwd: Run out of internal buffers */ + u32 drop_blind; /* Rx: Dropped by blinding table */ +}; + +/** Private structure for the MV device */ +struct _wlan_private { + int open; + int mesh_open; + int infra_open; + + wlan_adapter *adapter; + wlan_dev_t wlan_dev; + + struct net_device_stats stats; + struct net_device *mesh_dev ; /* Virtual device */ + + struct iw_statistics wstats; + struct wlan_mesh_stats mstats; + struct dentry *debugfs_dir; + struct dentry *debugfs_debug; + struct dentry *debugfs_files[6]; + + struct dentry *events_dir; + struct dentry *debugfs_events_files[6]; + + struct dentry *regs_dir; + struct dentry *debugfs_regs_files[6]; + + u32 mac_offset; + u32 bbp_offset; + u32 rf_offset; + + const struct firmware *firmware; + struct device *hotplug_device; + + /** thread to service interrupts */ + struct wlan_thread mainthread; + + struct delayed_work assoc_work; + struct workqueue_struct *assoc_thread; +}; + +/** Association request + * + * Encapsulates all the options that describe a specific assocation request + * or configuration of the wireless card's radio, mode, and security settings. + */ +struct assoc_request { +#define ASSOC_FLAG_SSID 1 +#define ASSOC_FLAG_CHANNEL 2 +#define ASSOC_FLAG_MODE 3 +#define ASSOC_FLAG_BSSID 4 +#define ASSOC_FLAG_WEP_KEYS 5 +#define ASSOC_FLAG_WEP_TX_KEYIDX 6 +#define ASSOC_FLAG_WPA_MCAST_KEY 7 +#define ASSOC_FLAG_WPA_UCAST_KEY 8 +#define ASSOC_FLAG_SECINFO 9 +#define ASSOC_FLAG_WPA_IE 10 + unsigned long flags; + + struct WLAN_802_11_SSID ssid; + u8 channel; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode; + u8 bssid[ETH_ALEN]; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + struct wlan_802_11_security secinfo; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; +}; + +/** Wlan adapter data structure*/ +struct _wlan_adapter { + /** STATUS variables */ + u32 fwreleasenumber; + u32 fwcapinfo; + /* protected with big lock */ + + struct mutex lock; + + u8 tmptxbuf[WLAN_UPLD_SIZE]; + /* protected by hard_start_xmit serialization */ + + /** command-related variables */ + u16 seqnum; + /* protected by big lock */ + + struct cmd_ctrl_node *cmd_array; + /** Current command */ + struct cmd_ctrl_node *cur_cmd; + int cur_cmd_retcode; + /** command Queues */ + /** Free command buffers */ + struct list_head cmdfreeq; + /** Pending command buffers */ + struct list_head cmdpendingq; + + wait_queue_head_t cmd_pending; + u8 nr_cmd_pending; + /* command related variables protected by adapter->driver_lock */ + + /** Async and Sync Event variables */ + u32 intcounter; + u32 eventcause; + u8 nodename[16]; /* nickname */ + + /** spin locks */ + spinlock_t driver_lock; + + /** Timers */ + struct timer_list command_timer; + + /* TX queue used in PS mode */ + spinlock_t txqueue_lock; + struct sk_buff *tx_queue_ps[NR_TX_QUEUE]; + unsigned int tx_queue_idx; + + u8 hisregcpy; + + /** current ssid/bssid related parameters*/ + struct current_bss_params curbssparams; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + + struct bss_descriptor *pattemptedbssdesc; + + struct WLAN_802_11_SSID previousssid; + u8 previousbssid[ETH_ALEN]; + + struct bss_descriptor *scantable; + u32 numinscantable; + + u8 scantype; + u32 scanmode; + + u16 beaconperiod; + u8 adhoccreate; + + /** capability Info used in Association, start, join */ + struct ieeetypes_capinfo capinfo; + + /** MAC address information */ + u8 current_addr[ETH_ALEN]; + u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN]; + u32 nr_of_multicastmacaddr; + + /** 802.11 statistics */ +// struct cmd_DS_802_11_GET_STAT wlan802_11Stat; + + u16 enablehwauto; + u16 ratebitmap; + /** control G rates */ + u8 adhoc_grate_enabled; + + u32 txantenna; + u32 rxantenna; + + u8 adhocchannel; + u32 fragthsd; + u32 rtsthsd; + + u32 datarate; + u8 is_datarate_auto; + + u16 listeninterval; + u16 prescan; + u8 txretrycount; + + /** Tx-related variables (for single packet tx) */ + struct sk_buff *currenttxskb; + u16 TxLockFlag; + + /** NIC Operation characteristics */ + u16 currentpacketfilter; + u32 connect_status; + u16 regioncode; + u16 regiontableindex; + u16 txpowerlevel; + + /** POWER MANAGEMENT AND PnP SUPPORT */ + u8 surpriseremoved; + u16 atimwindow; + + u16 psmode; /* Wlan802_11PowermodeCAM=disable + Wlan802_11PowermodeMAX_PSP=enable */ + u16 multipledtim; + u32 psstate; + u8 needtowakeup; + + struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep; + u16 locallisteninterval; + u16 nullpktinterval; + + struct assoc_request * assoc_req; + + /** Encryption parameter */ + struct wlan_802_11_security secinfo; + + /** WEP keys */ + struct WLAN_802_11_KEY wep_keys[4]; + u16 wep_tx_keyidx; + + /** WPA keys */ + struct WLAN_802_11_KEY wpa_mcast_key; + struct WLAN_802_11_KEY wpa_unicast_key; + + /** WPA Information Elements*/ +#define MAX_WPA_IE_LEN 64 + u8 wpa_ie[MAX_WPA_IE_LEN]; + u8 wpa_ie_len; + + u16 rxantennamode; + u16 txantennamode; + + /** Requested Signal Strength*/ + u16 bcn_avg_factor; + u16 data_avg_factor; + u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG]; + u16 NF[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG]; + u8 rawSNR[DEFAULT_DATA_AVG_FACTOR]; + u8 rawNF[DEFAULT_DATA_AVG_FACTOR]; + u16 nextSNRNF; + u16 numSNRNF; + u16 rxpd_rate; + + u8 radioon; + u32 preamble; + + /** Multi bands Parameter*/ + u8 libertas_supported_rates[G_SUPPORTED_RATES]; + + /** Blue Tooth Co-existence Arbitration */ + + /** sleep_params */ + struct sleep_params sp; + + /** RF calibration data */ + +#define MAX_REGION_CHANNEL_NUM 2 + /** region channel data */ + struct region_channel region_channel[MAX_REGION_CHANNEL_NUM]; + + struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM]; + + /** 11D and Domain Regulatory Data */ + struct wlan_802_11d_domain_reg domainreg; + struct parsed_region_chan_11d parsed_region_chan; + + /** FSM variable for 11d support */ + u32 enable11d; + + /** MISCELLANEOUS */ + u8 *prdeeprom; + struct wlan_offset_value offsetvalue; + + struct cmd_ds_802_11_get_log logmsg; + u16 scanprobes; + + u32 pkttxctrl; + + u16 txrate; + u32 linkmode; + u32 radiomode; + u32 debugmode; + u8 fw_ready; +}; + +#endif /* _WLAN_DEV_H_ */ diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c new file mode 100644 index 0000000..0064de5 --- /dev/null +++ b/drivers/net/wireless/libertas/ethtool.c @@ -0,0 +1,184 @@ + +#include <linux/netdevice.h> +#include <linux/ethtool.h> +#include <linux/delay.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" +static const char * mesh_stat_strings[]= { + "drop_duplicate_bcast", + "drop_ttl_zero", + "drop_no_fwd_route", + "drop_no_buffers", + "fwded_unicast_cnt", + "fwded_bcast_cnt", + "drop_blind_table" +}; + +static void libertas_ethtool_get_drvinfo(struct net_device *dev, + struct ethtool_drvinfo *info) +{ + wlan_private *priv = (wlan_private *) dev->priv; + char fwver[32]; + + libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1); + + strcpy(info->driver, "libertas"); + strcpy(info->version, libertas_driver_version); + strcpy(info->fw_version, fwver); +} + +/* All 8388 parts have 16KiB EEPROM size at the time of writing. + * In case that changes this needs fixing. + */ +#define LIBERTAS_EEPROM_LEN 16384 + +static int libertas_ethtool_get_eeprom_len(struct net_device *dev) +{ + return LIBERTAS_EEPROM_LEN; +} + +static int libertas_ethtool_get_eeprom(struct net_device *dev, + struct ethtool_eeprom *eeprom, u8 * bytes) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_regrdwr regctrl; + char *ptr; + int ret; + + regctrl.action = 0; + regctrl.offset = eeprom->offset; + regctrl.NOB = eeprom->len; + + if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN) + return -EINVAL; + +// mutex_lock(&priv->mutex); + + adapter->prdeeprom = + (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL); + if (!adapter->prdeeprom) + return -ENOMEM; + memcpy(adapter->prdeeprom, ®ctrl, sizeof(regctrl)); + + /* +14 is for action, offset, and NOB in + * response */ + lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n", + regctrl.action, regctrl.offset, regctrl.NOB); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_eeprom_access, + regctrl.action, + cmd_option_waitforrsp, 0, + ®ctrl); + + if (ret) { + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); + LEAVE(); + return ret; + } + + mdelay(10); + + ptr = (char *)adapter->prdeeprom; + + /* skip the command header, but include the "value" u32 variable */ + ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4; + + /* + * Return the result back to the user + */ + memcpy(bytes, ptr, eeprom->len); + + if (adapter->prdeeprom) + kfree(adapter->prdeeprom); +// mutex_unlock(&priv->mutex); + + return 0; +} + +static void libertas_ethtool_get_stats(struct net_device * dev, + struct ethtool_stats * stats, u64 * data) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + stats->cmd = ETHTOOL_GSTATS; + BUG_ON(stats->n_stats != MESH_STATS_NUM); + + data[0] = priv->mstats.fwd_drop_rbt; + data[1] = priv->mstats.fwd_drop_ttl; + data[2] = priv->mstats.fwd_drop_noroute; + data[3] = priv->mstats.fwd_drop_nobuf; + data[4] = priv->mstats.fwd_unicast_cnt; + data[5] = priv->mstats.fwd_bcast_cnt; + data[6] = priv->mstats.drop_blind; + + LEAVE(); +} + +static int libertas_ethtool_get_stats_count(struct net_device * dev) +{ + int ret; + wlan_private *priv = dev->priv; + struct cmd_ds_mesh_access mesh_access; + + ENTER(); + /* Get Mesh Statistics */ + ret = libertas_prepare_and_send_command(priv, + cmd_mesh_access, cmd_act_mesh_get_stats, + cmd_option_waitforrsp, 0, &mesh_access); + + if (ret) { + LEAVE(); + return 0; + } + + priv->mstats.fwd_drop_rbt = mesh_access.data[0]; + priv->mstats.fwd_drop_ttl = mesh_access.data[1]; + priv->mstats.fwd_drop_noroute = mesh_access.data[2]; + priv->mstats.fwd_drop_nobuf = mesh_access.data[3]; + priv->mstats.fwd_unicast_cnt = mesh_access.data[4]; + priv->mstats.fwd_bcast_cnt = mesh_access.data[5]; + priv->mstats.drop_blind = mesh_access.data[6]; + + LEAVE(); + return MESH_STATS_NUM; +} + +static void libertas_ethtool_get_strings (struct net_device * dev, + u32 stringset, + u8 * s) +{ + int i; + + ENTER(); + switch (stringset) { + case ETH_SS_STATS: + for (i=0; i < MESH_STATS_NUM; i++) { + memcpy(s + i * ETH_GSTRING_LEN, + mesh_stat_strings[i], + ETH_GSTRING_LEN); + } + break; + } + LEAVE(); +} + +struct ethtool_ops libertas_ethtool_ops = { + .get_drvinfo = libertas_ethtool_get_drvinfo, + .get_eeprom = libertas_ethtool_get_eeprom, + .get_eeprom_len = libertas_ethtool_get_eeprom_len, + .get_stats_count = libertas_ethtool_get_stats_count, + .get_ethtool_stats = libertas_ethtool_get_stats, + .get_strings = libertas_ethtool_get_strings, +}; + diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c new file mode 100644 index 0000000..b194a45 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.c @@ -0,0 +1,361 @@ +/** + * This file contains the initialization for FW and HW + */ +#include <linux/module.h> +#include <linux/moduleparam.h> + +#include <linux/vmalloc.h> +#include <linux/firmware.h> +#include <linux/version.h> + +#include "host.h" +#include "sbi.h" +#include "defs.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "if_usb.h" + +char *libertas_fw_name = NULL; +module_param_named(fw_name, libertas_fw_name, charp, 0644); + +unsigned int libertas_debug = 0; +module_param(libertas_debug, int, 0); + +/** + * @brief This function checks the validity of Boot2/FW image. + * + * @param data pointer to image + * len image length + * @return 0 or -1 + */ +static int check_fwfile_format(u8 *data, u32 totlen) +{ + u8 bincmd, exit; + u32 blksize, offset, len; + int ret; + + ret = 1; + exit = len = 0; + + do { + bincmd = *data; + blksize = *(u32*)(data + offsetof(struct fwheader, datalength)); + switch (bincmd) { + case FW_HAS_DATA_TO_RECV: + offset = sizeof(struct fwheader) + blksize; + data += offset; + len += offset; + if (len >= totlen) + exit = 1; + break; + case FW_HAS_LAST_BLOCK: + exit = 1; + ret = 0; + break; + default: + exit = 1; + break; + } + } while (!exit); + + if (ret) + lbs_pr_err("bin file format check FAIL...\n"); + else + lbs_pr_debug(1, "bin file format check PASS...\n"); + + return ret; +} + +/** + * @brief This function downloads firmware image, gets + * HW spec from firmware and set basic parameters to + * firmware. + * + * @param priv A pointer to wlan_private structure + * @return 0 or -1 + */ +static int wlan_setup_station_hw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if ((ret = request_firmware(&priv->firmware, libertas_fw_name, + priv->hotplug_device)) < 0) { + lbs_pr_err("request_firmware() failed, error code = %#x\n", + ret); + lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name); + goto done; + } + + if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) { + release_firmware(priv->firmware); + goto done; + } + + ret = libertas_sbi_prog_firmware(priv); + + release_firmware(priv->firmware); + + if (ret) { + lbs_pr_debug(1, "Bootloader in invalid state!\n"); + ret = -1; + goto done; + } + + /* + * Read MAC address from HW + */ + memset(adapter->current_addr, 0xff, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + libertas_set_mac_packet_filter(priv); + + /* Get the supported Data rates */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + cmd_act_get_tx_rate, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + ret = -1; + goto done; + } + + ret = 0; +done: + LEAVE(); + + return (ret); +} + +static int wlan_allocate_adapter(wlan_private * priv) +{ + u32 ulbufsize; + wlan_adapter *adapter = priv->adapter; + + struct bss_descriptor *ptempscantable; + + /* Allocate buffer to store the BSSID list */ + ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST; + if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) { + libertas_free_adapter(priv); + return -1; + } + + adapter->scantable = ptempscantable; + memset(adapter->scantable, 0, ulbufsize); + + /* Allocate the command buffers */ + libertas_allocate_cmd_buffer(priv); + + memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum); + adapter->libertas_ps_confirm_sleep.command = + cpu_to_le16(cmd_802_11_ps_mode); + adapter->libertas_ps_confirm_sleep.size = + cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep)); + adapter->libertas_ps_confirm_sleep.result = 0; + adapter->libertas_ps_confirm_sleep.action = + cpu_to_le16(cmd_subcmd_sleep_confirmed); + + return 0; +} + +static void wlan_init_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + adapter->scanprobes = 0; + + adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR; + adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR; + + /* ATIM params */ + adapter->atimwindow = 0; + + adapter->connect_status = libertas_disconnected; + memset(adapter->current_addr, 0xff, ETH_ALEN); + + /* scan type */ + adapter->scantype = cmd_scan_type_active; + + /* scan mode */ + adapter->scanmode = cmd_bss_type_any; + + /* 802.11 specific */ + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]); + i++) + memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY)); + adapter->wep_tx_keyidx = 0; + adapter->secinfo.WEPstatus = wlan802_11WEPdisabled; + adapter->secinfo.authmode = wlan802_11authmodeopen; + adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE; + adapter->secinfo.Encryptionmode = CIPHER_NONE; + adapter->inframode = wlan802_11infrastructure; + + adapter->assoc_req = NULL; + + adapter->numinscantable = 0; + adapter->pattemptedbssdesc = NULL; + mutex_init(&adapter->lock); + + adapter->prescan = 1; + + memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams)); + + /* PnP and power profile */ + adapter->surpriseremoved = 0; + + adapter->currentpacketfilter = + cmd_act_mac_rx_on | cmd_act_mac_tx_on; + + adapter->radioon = RADIO_ON; + adapter->txantenna = RF_ANTENNA_2; + adapter->rxantenna = RF_ANTENNA_AUTO; + + adapter->is_datarate_auto = 1; + adapter->beaconperiod = MRVDRV_BEACON_INTERVAL; + + // set default value of capinfo. +#define SHORT_PREAMBLE_ALLOWED 1 + memset(&adapter->capinfo, 0, sizeof(adapter->capinfo)); + adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED; + + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + + adapter->psmode = wlan802_11powermodecam; + adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM; + + adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL; + + adapter->psstate = PS_STATE_FULL_POWER; + adapter->needtowakeup = 0; + adapter->locallisteninterval = 0; /* default value in firmware will be used */ + + adapter->datarate = 0; // Initially indicate the rate as auto + + adapter->adhoc_grate_enabled = 0; + + adapter->intcounter = 0; + + adapter->currenttxskb = NULL; + adapter->pkttxctrl = 0; + + memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*)); + adapter->tx_queue_idx = 0; + spin_lock_init(&adapter->txqueue_lock); + + return; +} + +static void command_timer_fn(unsigned long data); + +int libertas_init_fw(wlan_private * priv) +{ + int ret = -1; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* Allocate adapter structure */ + if ((ret = wlan_allocate_adapter(priv)) != 0) + goto done; + + /* init adapter structure */ + wlan_init_adapter(priv); + + /* init timer etc. */ + setup_timer(&adapter->command_timer, command_timer_fn, + (unsigned long)priv); + + /* download fimrware etc. */ + if ((ret = wlan_setup_station_hw(priv)) != 0) { + del_timer_sync(&adapter->command_timer); + goto done; + } + + /* init 802.11d */ + libertas_init_11d(priv); + + ret = 0; +done: + LEAVE(); + return ret; +} + +void libertas_free_adapter(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + + if (!adapter) { + lbs_pr_debug(1, "Why double free adapter?:)\n"); + return; + } + + lbs_pr_debug(1, "Free command buffer\n"); + libertas_free_cmd_buffer(priv); + + lbs_pr_debug(1, "Free commandTimer\n"); + del_timer(&adapter->command_timer); + + lbs_pr_debug(1, "Free scantable\n"); + if (adapter->scantable) { + kfree(adapter->scantable); + adapter->scantable = NULL; + } + + lbs_pr_debug(1, "Free adapter\n"); + + /* Free the adapter object itself */ + kfree(adapter); + priv->adapter = NULL; +} + +/** + * This function handles the timeout of command sending. + * It will re-send the same command again. + */ +static void command_timer_fn(unsigned long data) +{ + wlan_private *priv = (wlan_private *)data; + wlan_adapter *adapter = priv->adapter; + struct cmd_ctrl_node *ptempnode; + struct cmd_ds_command *cmd; + unsigned long flags; + + ptempnode = adapter->cur_cmd; + cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr; + + lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command); + + if (!adapter->fw_ready) + return; + + if (ptempnode == NULL) { + lbs_pr_debug(1, "PTempnode Empty\n"); + return; + } + + spin_lock_irqsave(&adapter->driver_lock, flags); + adapter->cur_cmd = NULL; + spin_unlock_irqrestore(&adapter->driver_lock, flags); + + lbs_pr_debug(1, "Re-sending same command as it timeout...!\n"); + libertas_queue_cmd(adapter, ptempnode, 0); + + wake_up_interruptible(&priv->mainthread.waitq); + + return; +} diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h new file mode 100644 index 0000000..1f9ae26 --- /dev/null +++ b/drivers/net/wireless/libertas/fw.h @@ -0,0 +1,13 @@ +/** + * This header file contains FW interface related definitions. + */ +#ifndef _WLAN_FW_H_ +#define _WLAN_FW_H_ + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +int libertas_init_fw(wlan_private * priv); + +#endif /* _WLAN_FW_H_ */ diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h new file mode 100644 index 0000000..c0faaec --- /dev/null +++ b/drivers/net/wireless/libertas/host.h @@ -0,0 +1,338 @@ +/** + * This file contains definitions of WLAN commands. + */ + +#ifndef _HOST_H_ +#define _HOST_H_ + +/** PUBLIC DEFINITIONS */ +#define DEFAULT_AD_HOC_CHANNEL 6 +#define DEFAULT_AD_HOC_CHANNEL_A 36 + +/** IEEE 802.11 oids */ +#define OID_802_11_SSID 0x00008002 +#define OID_802_11_INFRASTRUCTURE_MODE 0x00008008 +#define OID_802_11_FRAGMENTATION_THRESHOLD 0x00008009 +#define OID_802_11_RTS_THRESHOLD 0x0000800A +#define OID_802_11_TX_ANTENNA_SELECTED 0x0000800D +#define OID_802_11_SUPPORTED_RATES 0x0000800E +#define OID_802_11_STATISTICS 0x00008012 +#define OID_802_11_TX_RETRYCOUNT 0x0000801D +#define OID_802_11D_ENABLE 0x00008020 + +#define cmd_option_waitforrsp 0x0002 + +/** Host command ID */ +#define cmd_code_dnld 0x0002 +#define cmd_get_hw_spec 0x0003 +#define cmd_eeprom_update 0x0004 +#define cmd_802_11_reset 0x0005 +#define cmd_802_11_scan 0x0006 +#define cmd_802_11_get_log 0x000b +#define cmd_mac_multicast_adr 0x0010 +#define cmd_802_11_authenticate 0x0011 +#define cmd_802_11_eeprom_access 0x0059 +#define cmd_802_11_associate 0x0050 +#define cmd_802_11_set_wep 0x0013 +#define cmd_802_11_get_stat 0x0014 +#define cmd_802_3_get_stat 0x0015 +#define cmd_802_11_snmp_mib 0x0016 +#define cmd_mac_reg_map 0x0017 +#define cmd_bbp_reg_map 0x0018 +#define cmd_mac_reg_access 0x0019 +#define cmd_bbp_reg_access 0x001a +#define cmd_rf_reg_access 0x001b +#define cmd_802_11_radio_control 0x001c +#define cmd_802_11_rf_channel 0x001d +#define cmd_802_11_rf_tx_power 0x001e +#define cmd_802_11_rssi 0x001f +#define cmd_802_11_rf_antenna 0x0020 + +#define cmd_802_11_ps_mode 0x0021 + +#define cmd_802_11_data_rate 0x0022 +#define cmd_rf_reg_map 0x0023 +#define cmd_802_11_deauthenticate 0x0024 +#define cmd_802_11_reassociate 0x0025 +#define cmd_802_11_disassociate 0x0026 +#define cmd_mac_control 0x0028 +#define cmd_802_11_ad_hoc_start 0x002b +#define cmd_802_11_ad_hoc_join 0x002c + +#define cmd_802_11_query_tkip_reply_cntrs 0x002e +#define cmd_802_11_enable_rsn 0x002f +#define cmd_802_11_pairwise_tsc 0x0036 +#define cmd_802_11_group_tsc 0x0037 +#define cmd_802_11_key_material 0x005e + +#define cmd_802_11_set_afc 0x003c +#define cmd_802_11_get_afc 0x003d + +#define cmd_802_11_ad_hoc_stop 0x0040 + +#define cmd_802_11_beacon_stop 0x0049 + +#define cmd_802_11_mac_address 0x004D +#define cmd_802_11_eeprom_access 0x0059 + +#define cmd_802_11_band_config 0x0058 + +#define cmd_802_11d_domain_info 0x005b + +#define cmd_802_11_sleep_params 0x0066 + +#define cmd_802_11_inactivity_timeout 0x0067 + +#define cmd_802_11_tpc_cfg 0x0072 +#define cmd_802_11_pwr_cfg 0x0073 + +#define cmd_802_11_led_gpio_ctrl 0x004e + +#define cmd_802_11_subscribe_event 0x0075 + +#define cmd_802_11_rate_adapt_rateset 0x0076 + +#define cmd_802_11_tx_rate_query 0x007f + +#define cmd_get_tsf 0x0080 + +#define cmd_bt_access 0x0087 +#define cmd_ret_bt_access 0x8087 + +#define cmd_fwt_access 0x0088 +#define cmd_ret_fwt_access 0x8088 + +#define cmd_mesh_access 0x0090 +#define cmd_ret_mesh_access 0x8090 + +/* For the IEEE Power Save */ +#define cmd_subcmd_enter_ps 0x0030 +#define cmd_subcmd_exit_ps 0x0031 +#define cmd_subcmd_sleep_confirmed 0x0034 +#define cmd_subcmd_full_powerdown 0x0035 +#define cmd_subcmd_full_powerup 0x0036 + +/* command RET code, MSB is set to 1 */ +#define cmd_ret_hw_spec_info 0x8003 +#define cmd_ret_eeprom_update 0x8004 +#define cmd_ret_802_11_reset 0x8005 +#define cmd_ret_802_11_scan 0x8006 +#define cmd_ret_802_11_get_log 0x800b +#define cmd_ret_mac_control 0x8028 +#define cmd_ret_mac_multicast_adr 0x8010 +#define cmd_ret_802_11_authenticate 0x8011 +#define cmd_ret_802_11_deauthenticate 0x8024 +#define cmd_ret_802_11_associate 0x8012 +#define cmd_ret_802_11_reassociate 0x8025 +#define cmd_ret_802_11_disassociate 0x8026 +#define cmd_ret_802_11_set_wep 0x8013 +#define cmd_ret_802_11_stat 0x8014 +#define cmd_ret_802_3_stat 0x8015 +#define cmd_ret_802_11_snmp_mib 0x8016 +#define cmd_ret_mac_reg_map 0x8017 +#define cmd_ret_bbp_reg_map 0x8018 +#define cmd_ret_rf_reg_map 0x8023 +#define cmd_ret_mac_reg_access 0x8019 +#define cmd_ret_bbp_reg_access 0x801a +#define cmd_ret_rf_reg_access 0x801b +#define cmd_ret_802_11_radio_control 0x801c +#define cmd_ret_802_11_rf_channel 0x801d +#define cmd_ret_802_11_rssi 0x801f +#define cmd_ret_802_11_rf_tx_power 0x801e +#define cmd_ret_802_11_rf_antenna 0x8020 +#define cmd_ret_802_11_ps_mode 0x8021 +#define cmd_ret_802_11_data_rate 0x8022 + +#define cmd_ret_802_11_ad_hoc_start 0x802B +#define cmd_ret_802_11_ad_hoc_join 0x802C + +#define cmd_ret_802_11_query_tkip_reply_cntrs 0x802e +#define cmd_ret_802_11_enable_rsn 0x802f +#define cmd_ret_802_11_pairwise_tsc 0x8036 +#define cmd_ret_802_11_group_tsc 0x8037 +#define cmd_ret_802_11_key_material 0x805e + +#define cmd_enable_rsn 0x0001 +#define cmd_disable_rsn 0x0000 + +#define cmd_act_set 0x0001 +#define cmd_act_get 0x0000 + +#define cmd_act_get_AES (cmd_act_get + 2) +#define cmd_act_set_AES (cmd_act_set + 2) +#define cmd_act_remove_aes (cmd_act_set + 3) + +#define cmd_ret_802_11_set_afc 0x803c +#define cmd_ret_802_11_get_afc 0x803d + +#define cmd_ret_802_11_ad_hoc_stop 0x8040 + +#define cmd_ret_802_11_beacon_stop 0x8049 + +#define cmd_ret_802_11_mac_address 0x804D +#define cmd_ret_802_11_eeprom_access 0x8059 + +#define cmd_ret_802_11_band_config 0x8058 + +#define cmd_ret_802_11_sleep_params 0x8066 + +#define cmd_ret_802_11_inactivity_timeout 0x8067 + +#define cmd_ret_802_11d_domain_info (0x8000 | \ + cmd_802_11d_domain_info) + +#define cmd_ret_802_11_tpc_cfg (cmd_802_11_tpc_cfg | 0x8000) +#define cmd_ret_802_11_pwr_cfg (cmd_802_11_pwr_cfg | 0x8000) + +#define cmd_ret_802_11_led_gpio_ctrl 0x804e + +#define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000) + +#define cmd_ret_802_11_rate_adapt_rateset (cmd_802_11_rate_adapt_rateset | 0x8000) + +#define cmd_rte_802_11_tx_rate_query (cmd_802_11_tx_rate_query | 0x8000) + +#define cmd_ret_get_tsf 0x8080 + +/* Define action or option for cmd_802_11_set_wep */ +#define cmd_act_add 0x0002 +#define cmd_act_remove 0x0004 +#define cmd_act_use_default 0x0008 + +#define cmd_type_wep_40_bit 0x0001 +#define cmd_type_wep_104_bit 0x0002 + +#define cmd_NUM_OF_WEP_KEYS 4 + +#define cmd_WEP_KEY_INDEX_MASK 0x3fff + +/* Define action or option for cmd_802_11_reset */ +#define cmd_act_halt 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_bss_type_bss 0x0001 +#define cmd_bss_type_ibss 0x0002 +#define cmd_bss_type_any 0x0003 + +/* Define action or option for cmd_802_11_scan */ +#define cmd_scan_type_active 0x0000 +#define cmd_scan_type_passive 0x0001 + +#define cmd_scan_radio_type_bg 0 + +#define cmd_scan_probe_delay_time 0 + +/* Define action or option for cmd_mac_control */ +#define cmd_act_mac_rx_on 0x0001 +#define cmd_act_mac_tx_on 0x0002 +#define cmd_act_mac_loopback_on 0x0004 +#define cmd_act_mac_wep_enable 0x0008 +#define cmd_act_mac_int_enable 0x0010 +#define cmd_act_mac_multicast_enable 0x0020 +#define cmd_act_mac_broadcast_enable 0x0040 +#define cmd_act_mac_promiscuous_enable 0x0080 +#define cmd_act_mac_all_multicast_enable 0x0100 +#define cmd_act_mac_strict_protection_enable 0x0400 + +/* Define action or option for cmd_802_11_radio_control */ +#define cmd_type_auto_preamble 0x0001 +#define cmd_type_short_preamble 0x0002 +#define cmd_type_long_preamble 0x0003 + +#define TURN_ON_RF 0x01 +#define RADIO_ON 0x01 +#define RADIO_OFF 0x00 + +#define SET_AUTO_PREAMBLE 0x05 +#define SET_SHORT_PREAMBLE 0x03 +#define SET_LONG_PREAMBLE 0x01 + +/* Define action or option for CMD_802_11_RF_CHANNEL */ +#define cmd_opt_802_11_rf_channel_get 0x00 +#define cmd_opt_802_11_rf_channel_set 0x01 + +/* Define action or option for cmd_802_11_rf_tx_power */ +#define cmd_act_tx_power_opt_get 0x0000 +#define cmd_act_tx_power_opt_set_high 0x8007 +#define cmd_act_tx_power_opt_set_mid 0x8004 +#define cmd_act_tx_power_opt_set_low 0x8000 + +#define cmd_act_tx_power_index_high 0x0007 +#define cmd_act_tx_power_index_mid 0x0004 +#define cmd_act_tx_power_index_low 0x0000 + +/* Define action or option for cmd_802_11_data_rate */ +#define cmd_act_set_tx_auto 0x0000 +#define cmd_act_set_tx_fix_rate 0x0001 +#define cmd_act_get_tx_rate 0x0002 + +#define cmd_act_set_rx 0x0001 +#define cmd_act_set_tx 0x0002 +#define cmd_act_set_both 0x0003 +#define cmd_act_get_rx 0x0004 +#define cmd_act_get_tx 0x0008 +#define cmd_act_get_both 0x000c + +/* Define action or option for cmd_802_11_ps_mode */ +#define cmd_type_cam 0x0000 +#define cmd_type_max_psp 0x0001 +#define cmd_type_fast_psp 0x0002 + +/* Define action or option for cmd_bt_access */ +enum cmd_bt_access_opts { + /* The bt commands start at 5 instead of 1 because the old dft commands + * are mapped to 1-4. These old commands are no longer maintained and + * should not be called. + */ + cmd_act_bt_access_add = 5, + cmd_act_bt_access_del, + cmd_act_bt_access_list, + cmd_act_bt_access_reset +}; + +/* Define action or option for cmd_fwt_access */ +enum cmd_fwt_access_opts { + cmd_act_fwt_access_add = 1, + cmd_act_fwt_access_del, + cmd_act_fwt_access_lookup, + cmd_act_fwt_access_list, + cmd_act_fwt_access_list_route, + cmd_act_fwt_access_list_neighbor, + cmd_act_fwt_access_reset, + cmd_act_fwt_access_cleanup, + cmd_act_fwt_access_time, +}; + +/* Define action or option for cmd_mesh_access */ +enum cmd_mesh_access_opts { + cmd_act_mesh_get_ttl = 1, + cmd_act_mesh_set_ttl, + cmd_act_mesh_get_stats, + cmd_act_mesh_get_mpp, + cmd_act_mesh_set_mpp, +}; + +/** Card Event definition */ +#define MACREG_INT_CODE_TX_PPA_FREE 0x00000000 +#define MACREG_INT_CODE_TX_DMA_DONE 0x00000001 +#define MACREG_INT_CODE_LINK_LOSE_W_SCAN 0x00000002 +#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN 0x00000003 +#define MACREG_INT_CODE_LINK_SENSED 0x00000004 +#define MACREG_INT_CODE_CMD_FINISHED 0x00000005 +#define MACREG_INT_CODE_MIB_CHANGED 0x00000006 +#define MACREG_INT_CODE_INIT_DONE 0x00000007 +#define MACREG_INT_CODE_DEAUTHENTICATED 0x00000008 +#define MACREG_INT_CODE_DISASSOCIATED 0x00000009 +#define MACREG_INT_CODE_PS_AWAKE 0x0000000a +#define MACREG_INT_CODE_PS_SLEEP 0x0000000b +#define MACREG_INT_CODE_MIC_ERR_MULTICAST 0x0000000d +#define MACREG_INT_CODE_MIC_ERR_UNICAST 0x0000000e +#define MACREG_INT_CODE_WM_AWAKE 0x0000000f +#define MACREG_INT_CODE_ADHOC_BCN_LOST 0x00000011 +#define MACREG_INT_CODE_RSSI_LOW 0x00000019 +#define MACREG_INT_CODE_SNR_LOW 0x0000001a +#define MACREG_INT_CODE_MAX_FAIL 0x0000001b +#define MACREG_INT_CODE_RSSI_HIGH 0x0000001c +#define MACREG_INT_CODE_SNR_HIGH 0x0000001d + +#endif /* _HOST_H_ */ diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h new file mode 100644 index 0000000..f239e5d --- /dev/null +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -0,0 +1,693 @@ +/* + * This file contains the function prototypes, data structure + * and defines for all the host/station commands + */ +#ifndef __HOSTCMD__H +#define __HOSTCMD__H + +#include <linux/wireless.h> +#include "11d.h" +#include "types.h" + +/* 802.11-related definitions */ + +/* TxPD descriptor */ +struct txpd { + /* Current Tx packet status */ + u32 tx_status; + /* Tx control */ + u32 tx_control; + u32 tx_packet_location; + /* Tx packet length */ + u16 tx_packet_length; + /* First 2 byte of destination MAC address */ + u8 tx_dest_addr_high[2]; + /* Last 4 byte of destination MAC address */ + u8 tx_dest_addr_low[4]; + /* Pkt Priority */ + u8 priority; + /* Pkt Trasnit Power control */ + u8 powermgmt; + /* Amount of time the packet has been queued in the driver (units = 2ms) */ + u8 pktdelay_2ms; + /* reserved */ + u8 reserved1; +}; + +/* RxPD Descriptor */ +struct rxpd { + /* Current Rx packet status */ + u16 status; + + /* SNR */ + u8 snr; + + /* Tx control */ + u8 rx_control; + + /* Pkt length */ + u16 pkt_len; + + /* Noise Floor */ + u8 nf; + + /* Rx Packet Rate */ + u8 rx_rate; + + /* Pkt addr */ + u32 pkt_ptr; + + /* Next Rx RxPD addr */ + u32 next_rxpd_ptr; + + /* Pkt Priority */ + u8 priority; + u8 reserved[3]; +}; + +struct cmd_ctrl_node { + /* CMD link list */ + struct list_head list; + u32 status; + /* CMD ID */ + u32 cmd_oid; + /*CMD wait option: wait for finish or no wait */ + u16 wait_option; + /* command parameter */ + void *pdata_buf; + /*command data */ + u8 *bufvirtualaddr; + u16 cmdflags; + /* wait queue */ + u16 cmdwaitqwoken; + wait_queue_head_t cmdwait_q; +}; + +/* WLAN_802_11_KEY + * + * Generic structure to hold all key types. key type (WEP40, WEP104, TKIP, AES) + * is determined from the keylength field. + */ +struct WLAN_802_11_KEY { + u32 len; + u32 flags; /* KEY_INFO_* from wlan_defs.h */ + u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH]; + u16 type; /* KEY_TYPE_* from wlan_defs.h */ +}; + +struct IE_WPA { + u8 elementid; + u8 len; + u8 oui[4]; + u16 version; +}; + +struct WLAN_802_11_SSID { + /* SSID length */ + u32 ssidlength; + + /* SSID information field */ + u8 ssid[IW_ESSID_MAX_SIZE]; +}; + +struct WPA_SUPPLICANT { + u8 wpa_ie[256]; + u8 wpa_ie_len; +}; + +/* wlan_offset_value */ +struct wlan_offset_value { + u32 offset; + u32 value; +}; + +struct WLAN_802_11_FIXED_IEs { + u8 timestamp[8]; + u16 beaconinterval; + u16 capabilities; +}; + +struct WLAN_802_11_VARIABLE_IEs { + u8 elementid; + u8 length; + u8 data[1]; +}; + +/* Define general data structure */ +/* cmd_DS_GEN */ +struct cmd_ds_gen { + u16 command; + u16 size; + u16 seqnum; + u16 result; +}; + +#define S_DS_GEN sizeof(struct cmd_ds_gen) +/* + * Define data structure for cmd_get_hw_spec + * This structure defines the response for the GET_HW_SPEC command + */ +struct cmd_ds_get_hw_spec { + /* HW Interface version number */ + u16 hwifversion; + /* HW version number */ + u16 version; + /* Max number of TxPD FW can handle */ + u16 nr_txpd; + /* Max no of Multicast address */ + u16 nr_mcast_adr; + /* MAC address */ + u8 permanentaddr[6]; + + /* region Code */ + u16 regioncode; + + /* Number of antenna used */ + u16 nr_antenna; + + /* FW release number, example 0x1234=1.2.3.4 */ + u32 fwreleasenumber; + + /* Base Address of TxPD queue */ + u32 wcb_base; + /* Read Pointer of RxPd queue */ + u32 rxpd_rdptr; + + /* Write Pointer of RxPd queue */ + u32 rxpd_wrptr; + + /*FW/HW capability */ + u32 fwcapinfo; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_reset { + u16 action; +}; + +struct cmd_ds_802_11_subscribe_event { + u16 action; + u16 events; +}; + +/* + * This scan handle Country Information IE(802.11d compliant) + * Define data structure for cmd_802_11_scan + */ +struct cmd_ds_802_11_scan { + u8 bsstype; + u8 BSSID[ETH_ALEN]; + u8 tlvbuffer[1]; +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_chanlistparamset_t ChanListParamSet; + mrvlietypes_ratesparamset_t OpRateSet; +#endif +}; + +struct cmd_ds_802_11_scan_rsp { + u16 bssdescriptsize; + u8 nr_sets; + u8 bssdesc_and_tlvbuffer[1]; +}; + +struct cmd_ds_802_11_get_log { + u32 mcasttxframe; + u32 failed; + u32 retry; + u32 multiretry; + u32 framedup; + u32 rtssuccess; + u32 rtsfailure; + u32 ackfailure; + u32 rxfrag; + u32 mcastrxframe; + u32 fcserror; + u32 txframe; + u32 wepundecryptable; +}; + +struct cmd_ds_mac_control { + u16 action; + u16 reserved; +}; + +struct cmd_ds_mac_multicast_adr { + u16 action; + u16 nr_of_adrs; + u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE]; +}; + +struct cmd_ds_802_11_authenticate { + u8 macaddr[ETH_ALEN]; + u8 authtype; + u8 reserved[10]; +}; + +struct cmd_ds_802_11_deauthenticate { + u8 macaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate { + u8 peerstaaddr[6]; + struct ieeetypes_capinfo capinfo; + u16 listeninterval; + u16 bcnperiod; + u8 dtimperiod; + +#if 0 + mrvlietypes_ssidparamset_t ssidParamSet; + mrvlietypes_phyparamset_t phyparamset; + mrvlietypes_ssparamset_t ssparamset; + mrvlietypes_ratesparamset_t ratesParamSet; +#endif +} __attribute__ ((packed)); + +struct cmd_ds_802_11_disassociate { + u8 destmacaddr[6]; + u16 reasoncode; +}; + +struct cmd_ds_802_11_associate_rsp { + struct ieeetypes_assocrsp assocRsp; +}; + +struct cmd_ds_802_11_ad_hoc_result { + u8 PAD[3]; + u8 BSSID[ETH_ALEN]; +}; + +struct cmd_ds_802_11_set_wep { + /* ACT_ADD, ACT_REMOVE or ACT_ENABLE */ + u16 action; + + /* key Index selected for Tx */ + u16 keyindex; + + /* 40, 128bit or TXWEP */ + u8 keytype[4]; + u8 keymaterial[4][16]; +}; + +struct cmd_ds_802_3_get_stat { + u32 xmitok; + u32 rcvok; + u32 xmiterror; + u32 rcverror; + u32 rcvnobuffer; + u32 rcvcrcerror; +}; + +struct cmd_ds_802_11_get_stat { + u32 txfragmentcnt; + u32 mcasttxframecnt; + u32 failedcnt; + u32 retrycnt; + u32 Multipleretrycnt; + u32 rtssuccesscnt; + u32 rtsfailurecnt; + u32 ackfailurecnt; + u32 frameduplicatecnt; + u32 rxfragmentcnt; + u32 mcastrxframecnt; + u32 fcserrorcnt; + u32 bcasttxframecnt; + u32 bcastrxframecnt; + u32 txbeacon; + u32 rxbeacon; + u32 wepundecryptable; +}; + +struct cmd_ds_802_11_snmp_mib { + u16 querytype; + u16 oid; + u16 bufsize; + u8 value[128]; +}; + +struct cmd_ds_mac_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_bbp_reg_map { + u16 buffersize; + u8 regmap[128]; + u16 reserved; +}; + +struct cmd_ds_rf_reg_map { + u16 buffersize; + u8 regmap[64]; + u16 reserved; +}; + +struct cmd_ds_mac_reg_access { + u16 action; + u16 offset; + u32 value; +}; + +struct cmd_ds_bbp_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_rf_reg_access { + u16 action; + u16 offset; + u8 value; + u8 reserved[3]; +}; + +struct cmd_ds_802_11_radio_control { + u16 action; + u16 control; +}; + +struct cmd_ds_802_11_sleep_params { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Sleep clock error in ppm */ + u16 error; + + /* Wakeup offset in usec */ + u16 offset; + + /* Clock stabilization time in usec */ + u16 stabletime; + + /* control periodic calibration */ + u8 calcontrol; + + /* control the use of external sleep clock */ + u8 externalsleepclk; + + /* reserved field, should be set to zero */ + u16 reserved; +}; + +struct cmd_ds_802_11_inactivity_timeout { + /* ACT_GET/ACT_SET */ + u16 action; + + /* Inactivity timeout in msec */ + u16 timeout; +}; + +struct cmd_ds_802_11_rf_channel { + u16 action; + u16 currentchannel; + u16 rftype; + u16 reserved; + u8 channellist[32]; +}; + +struct cmd_ds_802_11_rssi { + /* weighting factor */ + u16 N; + + u16 reserved_0; + u16 reserved_1; + u16 reserved_2; +}; + +struct cmd_ds_802_11_rssi_rsp { + u16 SNR; + u16 noisefloor; + u16 avgSNR; + u16 avgnoisefloor; +}; + +struct cmd_ds_802_11_mac_address { + u16 action; + u8 macadd[ETH_ALEN]; +}; + +struct cmd_ds_802_11_rf_tx_power { + u16 action; + u16 currentlevel; +}; + +struct cmd_ds_802_11_rf_antenna { + u16 action; + + /* Number of antennas or 0xffff(diversity) */ + u16 antennamode; + +}; + +struct cmd_ds_802_11_ps_mode { + u16 action; + u16 nullpktinterval; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct PS_CMD_ConfirmSleep { + u16 command; + u16 size; + u16 seqnum; + u16 result; + + u16 action; + u16 reserved1; + u16 multipledtim; + u16 reserved; + u16 locallisteninterval; +}; + +struct cmd_ds_802_11_data_rate { + u16 action; + u16 reserverd; + u8 datarate[G_SUPPORTED_RATES]; +}; + +struct cmd_ds_802_11_rate_adapt_rateset { + u16 action; + u16 enablehwauto; + u16 bitmap; +}; + +struct cmd_ds_802_11_ad_hoc_start { + u8 SSID[IW_ESSID_MAX_SIZE]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + union IEEEtypes_ssparamset ssparamset; + union ieeetypes_phyparamset phyparamset; + u16 probedelay; + struct ieeetypes_capinfo cap; + u8 datarate[G_SUPPORTED_RATES]; + u8 tlv_memory_size_pad[100]; +} __attribute__ ((packed)); + +struct adhoc_bssdesc { + u8 BSSID[6]; + u8 SSID[32]; + u8 bsstype; + u16 beaconperiod; + u8 dtimperiod; + u8 timestamp[8]; + u8 localtime[8]; + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[G_SUPPORTED_RATES]; + + /* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the + * Adhoc join command and will cause a binary layout mismatch with + * the firmware + */ +} __attribute__ ((packed)); + +struct cmd_ds_802_11_ad_hoc_join { + struct adhoc_bssdesc bssdescriptor; + u16 failtimeout; + u16 probedelay; + +} __attribute__ ((packed)); + +struct cmd_ds_802_11_enable_rsn { + u16 action; + u16 enable; +}; + +struct MrvlIEtype_keyParamSet { + /* type ID */ + u16 type; + + /* length of Payload */ + u16 length; + + /* type of key: WEP=0, TKIP=1, AES=2 */ + u16 keytypeid; + + /* key control Info specific to a keytypeid */ + u16 keyinfo; + + /* length of key */ + u16 keylen; + + /* key material of size keylen */ + u8 key[32]; +}; + +struct cmd_ds_802_11_key_material { + u16 action; + struct MrvlIEtype_keyParamSet keyParamSet[2]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_eeprom_access { + u16 action; + + /* multiple 4 */ + u16 offset; + u16 bytecount; + u8 value; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_tpc_cfg { + u16 action; + u8 enable; + s8 P0; + s8 P1; + s8 P2; + u8 usesnr; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_led_ctrl { + u16 action; + u16 numled; + u8 data[256]; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_pwr_cfg { + u16 action; + u8 enable; + s8 PA_P0; + s8 PA_P1; + s8 PA_P2; +} __attribute__ ((packed)); + +struct cmd_ds_802_11_afc { + u16 afc_auto; + union { + struct { + u16 threshold; + u16 period; + }; + struct { + s16 timing_offset; + s16 carrier_offset; + }; + }; +} __attribute__ ((packed)); + +struct cmd_tx_rate_query { + u16 txrate; +} __attribute__ ((packed)); + +struct cmd_ds_get_tsf { + __le64 tsfvalue; +} __attribute__ ((packed)); + +struct cmd_ds_bt_access { + u16 action; + u32 id; + u8 addr1[ETH_ALEN]; + u8 addr2[ETH_ALEN]; +} __attribute__ ((packed)); + +struct cmd_ds_fwt_access { + u16 action; + u32 id; + u8 da[ETH_ALEN]; + u8 dir; + u8 ra[ETH_ALEN]; + u32 ssn; + u32 dsn; + u32 metric; + u8 hopcount; + u8 ttl; + u32 expiration; + u8 sleepmode; + u32 snr; + u32 references; +} __attribute__ ((packed)); + +#define MESH_STATS_NUM 7 +struct cmd_ds_mesh_access { + u16 action; + u32 data[MESH_STATS_NUM + 1]; /* last position reserved */ +} __attribute__ ((packed)); + +struct cmd_ds_command { + /* command header */ + u16 command; + u16 size; + u16 seqnum; + u16 result; + + /* command Body */ + union { + struct cmd_ds_get_hw_spec hwspec; + struct cmd_ds_802_11_ps_mode psmode; + struct cmd_ds_802_11_scan scan; + struct cmd_ds_802_11_scan_rsp scanresp; + struct cmd_ds_mac_control macctrl; + struct cmd_ds_802_11_associate associate; + struct cmd_ds_802_11_deauthenticate deauth; + struct cmd_ds_802_11_set_wep wep; + struct cmd_ds_802_11_ad_hoc_start ads; + struct cmd_ds_802_11_reset reset; + struct cmd_ds_802_11_ad_hoc_result result; + struct cmd_ds_802_11_get_log glog; + struct cmd_ds_802_11_authenticate auth; + struct cmd_ds_802_11_get_stat gstat; + struct cmd_ds_802_3_get_stat gstat_8023; + struct cmd_ds_802_11_snmp_mib smib; + struct cmd_ds_802_11_rf_tx_power txp; + struct cmd_ds_802_11_rf_antenna rant; + struct cmd_ds_802_11_data_rate drate; + struct cmd_ds_802_11_rate_adapt_rateset rateset; + struct cmd_ds_mac_multicast_adr madr; + struct cmd_ds_802_11_ad_hoc_join adj; + struct cmd_ds_802_11_radio_control radio; + struct cmd_ds_802_11_rf_channel rfchannel; + struct cmd_ds_802_11_rssi rssi; + struct cmd_ds_802_11_rssi_rsp rssirsp; + struct cmd_ds_802_11_disassociate dassociate; + struct cmd_ds_802_11_mac_address macadd; + struct cmd_ds_802_11_enable_rsn enbrsn; + struct cmd_ds_802_11_key_material keymaterial; + struct cmd_ds_mac_reg_access macreg; + struct cmd_ds_bbp_reg_access bbpreg; + struct cmd_ds_rf_reg_access rfreg; + struct cmd_ds_802_11_eeprom_access rdeeprom; + + struct cmd_ds_802_11d_domain_info domaininfo; + struct cmd_ds_802_11d_domain_info domaininforesp; + + struct cmd_ds_802_11_sleep_params sleep_params; + struct cmd_ds_802_11_inactivity_timeout inactivity_timeout; + struct cmd_ds_802_11_tpc_cfg tpccfg; + struct cmd_ds_802_11_pwr_cfg pwrcfg; + struct cmd_ds_802_11_afc afc; + struct cmd_ds_802_11_led_ctrl ledgpio; + + struct cmd_tx_rate_query txrate; + struct cmd_ds_bt_access bt; + struct cmd_ds_fwt_access fwt; + struct cmd_ds_mesh_access mesh; + struct cmd_ds_get_tsf gettsf; + struct cmd_ds_802_11_subscribe_event subscribe_event; + } params; +} __attribute__ ((packed)); + +#endif diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c new file mode 100644 index 0000000..567000c --- /dev/null +++ b/drivers/net/wireless/libertas/if_bootcmd.c @@ -0,0 +1,38 @@ +/** + * This file contains functions used in USB Boot command + * and Boot2/FW update + */ + +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +/** + * @brief This function issues Boot command to the Boot2 code + * @param ivalue 1:Boot from FW by USB-Download + * 2:Boot from FW in EEPROM + * @return 0 + */ +int if_usb_issue_boot_command(wlan_private *priv, int ivalue) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct bootcmdstr sbootcmd; + int i; + + /* Prepare command */ + sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER; + sbootcmd.u8cmd_tag = ivalue; + for (i=0; i<11; i++) + sbootcmd.au8dumy[i]=0x00; + memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr)); + + /* Issue command */ + usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr)); + + return 0; +} diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c new file mode 100644 index 0000000..695fb6a --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.c @@ -0,0 +1,952 @@ +/** + * This file contains functions used in USB interface module. + */ +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/netdevice.h> +#include <linux/usb.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "if_usb.h" + +#define MESSAGE_HEADER_LEN 4 + +static const char usbdriver_name[] = "usb8xxx"; + +static struct usb_device_id if_usb_table[] = { + /* Enter the device signature inside */ + { + USB_DEVICE(USB8388_VID_1, USB8388_PID_1), + }, + { + USB_DEVICE(USB8388_VID_2, USB8388_PID_2), + }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, if_usb_table); + +static void if_usb_receive(struct urb *urb); +static void if_usb_receive_fwload(struct urb *urb); + +/** + * @brief call back function to handle the status of the URB + * @param urb pointer to urb structure + * @return N/A + */ +static void if_usb_write_bulk_callback(struct urb *urb) +{ + wlan_private *priv = (wlan_private *) (urb->context); + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + /* handle the transmission complete validations */ + + if (urb->status != 0) { + /* print the failure status number for debug */ + lbs_pr_info("URB in failure status\n"); + } else { + lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n"); + lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n", + urb->actual_length); + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + /* Wake main thread if commands are pending */ + if (!adapter->cur_cmd) + wake_up_interruptible(&priv->mainthread.waitq); + if ((adapter->connect_status == libertas_connected)) + netif_wake_queue(dev); + } + + return; +} + +/** + * @brief free tx/rx urb, skb and rx buffer + * @param cardp pointer usb_card_rec + * @return N/A + */ +void if_usb_free(struct usb_card_rec *cardp) +{ + ENTER(); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + usb_free_urb(cardp->tx_urb); + cardp->tx_urb = NULL; + + usb_free_urb(cardp->rx_urb); + cardp->rx_urb = NULL; + + kfree(cardp->bulk_out_buffer); + cardp->bulk_out_buffer = NULL; + + LEAVE(); + return; +} + +/** + * @brief sets the configuration values + * @param ifnum interface number + * @param id pointer to usb_device_id + * @return 0 on success, error code on failure + */ +static int if_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + wlan_private *pwlanpriv; + struct usb_card_rec *usb_cardp; + int i; + + udev = interface_to_usbdev(intf); + + usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL); + if (!usb_cardp) { + lbs_pr_err("Out of memory allocating private data.\n"); + goto error; + } + + usb_cardp->udev = udev; + iface_desc = intf->cur_altsetting; + + lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X" + " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n", + udev->descriptor.bcdUSB, + udev->descriptor.bDeviceClass, + udev->descriptor.bDeviceSubClass, + udev->descriptor.bDeviceProtocol); + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* we found a bulk in endpoint */ + lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n", + endpoint->wMaxPacketSize); + if (! + (usb_cardp->rx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1, &udev->dev, + "Rx URB allocation failed\n"); + goto dealloc; + } + usb_cardp->rx_urb_recall = 0; + + usb_cardp->bulk_in_size = + endpoint->wMaxPacketSize; + usb_cardp->bulk_in_endpointAddr = + (endpoint-> + bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); + lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n", + endpoint->bEndpointAddress); + } + + if (((endpoint-> + bEndpointAddress & USB_ENDPOINT_DIR_MASK) == + USB_DIR_OUT) + && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_BULK)) { + /* We found bulk out endpoint */ + if (! + (usb_cardp->tx_urb = + usb_alloc_urb(0, GFP_KERNEL))) { + lbs_dev_dbg(1,&udev->dev, + "Tx URB allocation failed\n"); + goto dealloc; + } + + usb_cardp->bulk_out_size = + endpoint->wMaxPacketSize; + lbs_dev_dbg(1, &udev->dev, + "Bulk out size is %d\n", + endpoint->wMaxPacketSize); + usb_cardp->bulk_out_endpointAddr = + endpoint->bEndpointAddress; + lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n", + endpoint->bEndpointAddress); + usb_cardp->bulk_out_buffer = + kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE, + GFP_KERNEL); + + if (!usb_cardp->bulk_out_buffer) { + lbs_dev_dbg(1, &udev->dev, + "Could not allocate buffer\n"); + goto dealloc; + } + } + } + + + /* At this point wlan_add_card() will be called. Don't worry + * about keeping pwlanpriv around since it will be set on our + * usb device data in -> add() -> libertas_sbi_register_dev(). + */ + if (!(pwlanpriv = wlan_add_card(usb_cardp))) + goto dealloc; + + usb_get_dev(udev); + usb_set_intfdata(intf, usb_cardp); + + /* + * return card structure, which can be got back in the + * diconnect function as the ptr + * argument. + */ + return 0; + +dealloc: + if_usb_free(usb_cardp); + +error: + return -ENOMEM; +} + +/** + * @brief free resource and cleanup + * @param udev pointer to usb_device + * @param ptr pointer to usb_cardp + * @return N/A + */ +static void if_usb_disconnect(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = (wlan_private *) cardp->priv; + wlan_adapter *adapter = NULL; + + adapter = priv->adapter; + + /* + * Update Surprise removed to TRUE + */ + adapter->surpriseremoved = 1; + + /* card is removed and we can call wlan_remove_card */ + lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n"); + wlan_remove_card(cardp); + + /* Unlink and free urb */ + if_usb_free(cardp); + + usb_set_intfdata(intf, NULL); + usb_put_dev(interface_to_usbdev(intf)); + + return; +} + +/** + * @brief This function download FW + * @param priv pointer to wlan_private + * @return 0 + */ +static int if_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct FWData *fwdata; + struct fwheader *fwheader; + u8 *firmware = priv->firmware->data; + + fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC); + + if (!fwdata) + return -1; + + fwheader = &fwdata->fwheader; + + if (!cardp->CRC_OK) { + cardp->totalbytes = cardp->fwlastblksent; + cardp->fwseqnum = cardp->lastseqnum - 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n", + cardp->totalbytes); + + memcpy(fwheader, &firmware[cardp->totalbytes], + sizeof(struct fwheader)); + + cardp->fwlastblksent = cardp->totalbytes; + cardp->totalbytes += sizeof(struct fwheader); + + lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n"); + memcpy(fwdata->data, &firmware[cardp->totalbytes], + fwdata->fwheader.datalength); + + lbs_dev_dbg(2, &cardp->udev->dev, + "Data length = %d\n", fwdata->fwheader.datalength); + + cardp->fwseqnum = cardp->fwseqnum + 1; + + fwdata->seqnum = cardp->fwseqnum; + cardp->lastseqnum = fwdata->seqnum; + cardp->totalbytes += fwdata->fwheader.datalength; + + if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) { + lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "seqnum = %d totalbytes = %d\n", cardp->fwseqnum, + cardp->totalbytes); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + + } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) { + lbs_dev_dbg(2, &cardp->udev->dev, + "Host has finished FW downloading\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "Donwloading FW JUMP BLOCK\n"); + memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE); + usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE); + cardp->fwfinalblk = 1; + } + + lbs_dev_dbg(2, &cardp->udev->dev, + "The firmware download is done size is %d\n", + cardp->totalbytes); + + kfree(fwdata); + + return 0; +} + +static int libertas_do_reset(wlan_private *priv) +{ + int ret; + struct usb_card_rec *cardp = priv->wlan_dev.card; + + ret = usb_reset_device(cardp->udev); + if (!ret) { + msleep(10); + reset_device(priv); + msleep(10); + } + return ret; +} + +/** + * @brief This function transfer the data to the device. + * @param priv pointer to wlan_private + * @param payload pointer to payload data + * @param nb data length + * @return 0 or -1 + */ +int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb) +{ + /* pointer to card structure */ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int ret = -1; + + /* check if device is removed */ + if (priv->adapter->surpriseremoved) { + lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n"); + goto tx_ret; + } + + usb_fill_bulk_urb(cardp->tx_urb, cardp->udev, + usb_sndbulkpipe(cardp->udev, + cardp->bulk_out_endpointAddr), + payload, nb, if_usb_write_bulk_callback, priv); + + cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET; + + if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) { + /* transfer failed */ + lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n"); + ret = 0; + } + +tx_ret: + return ret; +} + +static int __if_usb_submit_rx_urb(wlan_private * priv, + void (*callbackfn) + (struct urb *urb)) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + struct sk_buff *skb; + struct read_cb_info *rinfo = &cardp->rinfo; + int ret = -1; + + if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) { + lbs_pr_err("No free skb\n"); + goto rx_ret; + } + + rinfo->skb = skb; + + /* Fill the receive configuration URB and initialise the Rx call back */ + usb_fill_bulk_urb(cardp->rx_urb, cardp->udev, + usb_rcvbulkpipe(cardp->udev, + cardp->bulk_in_endpointAddr), + skb->tail + IPFIELD_ALIGN_OFFSET, + MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn, + rinfo); + + cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET; + + lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); + if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) { + /* handle failure conditions */ + lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n"); + ret = -1; + } else { + lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n"); + ret = 0; + } + +rx_ret: + return ret; +} + +static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload); +} + +static inline int if_usb_submit_rx_urb(wlan_private * priv) +{ + return __if_usb_submit_rx_urb(priv, &if_usb_receive); +} + +static void if_usb_receive_fwload(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + struct fwsyncheader *syncfwheader; + struct bootcmdrespStr bootcmdresp; + + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed during fw load\n"); + kfree_skb(skb); + return; + } + + if (cardp->bootcmdresp == 0) { + memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(bootcmdresp)); + if (cardp->udev->descriptor.bcdDevice < 0x3106) { + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + return; + } + if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) { + lbs_pr_info( + "boot cmd response wrong magic number (0x%x)\n", + bootcmdresp.u32magicnumber); + } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) { + lbs_pr_info( + "boot cmd response cmd_tag error (%d)\n", + bootcmdresp.u8cmd_tag); + } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) { + lbs_pr_info( + "boot cmd response result error (%d)\n", + bootcmdresp.u8result); + } else { + cardp->bootcmdresp = 1; + lbs_dev_dbg(1, &cardp->udev->dev, + "Received valid boot command response\n"); + } + kfree_skb(skb); + if_usb_submit_rx_urb_fwload(priv); + return; + } + + syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC); + if (!syncfwheader) { + lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n"); + kfree_skb(skb); + return; + } + + memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET, + sizeof(struct fwsyncheader)); + + if (!syncfwheader->cmd) { + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk with correct CRC\n"); + lbs_dev_dbg(2, &cardp->udev->dev, + "FW received Blk seqnum = %d\n", + syncfwheader->seqnum); + cardp->CRC_OK = 1; + } else { + lbs_dev_dbg(1, &cardp->udev->dev, + "FW received Blk with CRC error\n"); + cardp->CRC_OK = 0; + } + + kfree_skb(skb); + + if (cardp->fwfinalblk) { + cardp->fwdnldover = 1; + goto exit; + } + + if_prog_firmware(priv); + + if_usb_submit_rx_urb_fwload(priv); +exit: + kfree(syncfwheader); + + return; + +} + +#define MRVDRV_MIN_PKT_LEN 30 + +static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + + MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) { + lbs_dev_dbg(1, &cardp->udev->dev, + "Packet length is Invalid\n"); + kfree_skb(skb); + return; + } + + skb_reserve(skb, IPFIELD_ALIGN_OFFSET); + skb_put(skb, recvlength); + skb_pull(skb, MESSAGE_HEADER_LEN); + libertas_process_rxed_packet(priv, skb); + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); +} + +static inline void process_cmdrequest(int recvlength, u8 *recvbuff, + struct sk_buff *skb, + struct usb_card_rec *cardp, + wlan_private *priv) +{ + u8 *cmdbuf; + if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) { + lbs_dev_dbg(1, &cardp->udev->dev, + "The receive buffer is too large\n"); + kfree_skb(skb); + return; + } + + if (!in_interrupt()) + BUG(); + + spin_lock(&priv->adapter->driver_lock); + /* take care of cur_cmd = NULL case by reading the + * data to clear the interrupt */ + if (!priv->adapter->cur_cmd) { + cmdbuf = priv->wlan_dev.upld_buf; + priv->adapter->hisregcpy &= ~his_cmdupldrdy; + } else + cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr; + + cardp->usb_int_cause |= his_cmdupldrdy; + priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN); + memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN, + priv->wlan_dev.upld_len); + + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + + lbs_dev_dbg(1, &cardp->udev->dev, + "Wake up main thread to handle cmd response\n"); + + return; +} + +/** + * @brief This function reads of the packet into the upload buff, + * wake up the main thread and initialise the Rx callack. + * + * @param urb pointer to struct urb + * @return N/A + */ +static void if_usb_receive(struct urb *urb) +{ + struct read_cb_info *rinfo = (struct read_cb_info *)urb->context; + wlan_private *priv = rinfo->priv; + struct sk_buff *skb = rinfo->skb; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + int recvlength = urb->actual_length; + u8 *recvbuff = NULL; + u32 recvtype; + + ENTER(); + + if (recvlength) { + if (urb->status) { + lbs_dev_dbg(1, &cardp->udev->dev, + "URB status is failed\n"); + kfree_skb(skb); + goto setup_for_next; + } + + recvbuff = skb->data + IPFIELD_ALIGN_OFFSET; + memcpy(&recvtype, recvbuff, sizeof(u32)); + lbs_dev_dbg(1, &cardp->udev->dev, + "Recv length = 0x%x\n", recvlength); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type = 0x%X\n", recvtype); + recvtype = le32_to_cpu(recvtype); + lbs_dev_dbg(1, &cardp->udev->dev, + "Receive type after = 0x%X\n", recvtype); + } else if (urb->status) + goto rx_exit; + + + switch (recvtype) { + case CMD_TYPE_DATA: + process_cmdtypedata(recvlength, skb, cardp, priv); + break; + + case CMD_TYPE_REQUEST: + process_cmdrequest(recvlength, recvbuff, skb, cardp, priv); + break; + + case CMD_TYPE_INDICATION: + /* Event cause handling */ + spin_lock(&priv->adapter->driver_lock); + cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN); + lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n", + cardp->usb_event_cause); + if (cardp->usb_event_cause & 0xffff0000) { + libertas_send_tx_feedback(priv); + break; + } + cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3; + cardp->usb_int_cause |= his_cardevent; + kfree_skb(skb); + libertas_interrupt(priv->wlan_dev.netdev); + spin_unlock(&priv->adapter->driver_lock); + goto rx_exit; + default: + kfree_skb(skb); + break; + } + +setup_for_next: + if_usb_submit_rx_urb(priv); +rx_exit: + LEAVE(); + return; +} + +/** + * @brief This function downloads data to FW + * @param priv pointer to wlan_private structure + * @param type type of data + * @param buf pointer to data buffer + * @param len number of bytes + * @return 0 or -1 + */ +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb) +{ + int ret = -1; + u32 tmp; + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + + lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type); + lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb); + + if (type == MVMS_CMD) { + tmp = cpu_to_le32(CMD_TYPE_REQUEST); + priv->wlan_dev.dnld_sent = DNLD_CMD_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + + } else { + tmp = cpu_to_le32(CMD_TYPE_DATA); + priv->wlan_dev.dnld_sent = DNLD_DATA_SENT; + memcpy(cardp->bulk_out_buffer, (u8 *) & tmp, + MESSAGE_HEADER_LEN); + } + + memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb); + + ret = + usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN); + + return ret; +} + +/* called with adapter->driver_lock held */ +int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + + *ireg = cardp->usb_int_cause; + cardp->usb_int_cause = 0; + + lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg); + + return 0; +} + +int libertas_sbi_read_event_cause(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + priv->adapter->eventcause = cardp->usb_event_cause; + /* Re-submit rx urb here to avoid event lost issue */ + if_usb_submit_rx_urb(priv); + return 0; +} + +int reset_device(wlan_private *priv) +{ + int ret; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset, + cmd_act_halt, 0, 0, NULL); + msleep_interruptible(10); + + return ret; +} + +int libertas_sbi_unregister_dev(wlan_private * priv) +{ + int ret = 0; + + /* Need to send a Reset command to device before USB resources freed + * and wlan_remove_card() called, then device can handle FW download + * again. + */ + if (priv) + reset_device(priv); + + return ret; +} + + +/** + * @brief This function register usb device and initialize parameter + * @param priv pointer to wlan_private + * @return 0 or -1 + */ +int libertas_sbi_register_dev(wlan_private * priv) +{ + + struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card; + ENTER(); + + cardp->priv = priv; + cardp->eth_dev = priv->wlan_dev.netdev; + priv->hotplug_device = &(cardp->udev->dev); + + SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev)); + + lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n", + cardp->udev); + + LEAVE(); + return 0; +} + + + +int libertas_sbi_prog_firmware(wlan_private * priv) +{ + struct usb_card_rec *cardp = priv->wlan_dev.card; + int i = 0; + static int reset_count = 10; + + ENTER(); + + cardp->rinfo.priv = priv; + +restart: + if (if_usb_submit_rx_urb_fwload(priv) < 0) { + lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n"); + LEAVE(); + return -1; + } + +#ifdef SUPPORT_BOOT_COMMAND + cardp->bootcmdresp = 0; + do { + int j = 0; + i++; + /* Issue Boot command = 1, Boot from Download-FW */ + if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB); + /* wait for command response */ + do { + j++; + msleep_interruptible(100); + } while (cardp->bootcmdresp == 0 && j < 10); + } while (cardp->bootcmdresp == 0 && i < 5); + + if (cardp->bootcmdresp == 0) { + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + return -1; + } +#endif + + i = 0; + priv->adapter->fw_ready = 0; + + cardp->totalbytes = 0; + cardp->fwlastblksent = 0; + cardp->CRC_OK = 1; + cardp->fwdnldover = 0; + cardp->fwseqnum = -1; + cardp->totalbytes = 0; + cardp->fwfinalblk = 0; + + if_prog_firmware(priv); + + do { + lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n"); + i++; + msleep_interruptible(100); + if (priv->adapter->surpriseremoved || i >= 20) + break; + } while (!cardp->fwdnldover); + + if (!cardp->fwdnldover) { + lbs_pr_info("failed to load fw, resetting device!\n"); + if (--reset_count >= 0) { + libertas_do_reset(priv); + goto restart; + } + + lbs_pr_info("FW download failure, time = %d ms\n", i * 100); + LEAVE(); + return -1; + } + + if_usb_submit_rx_urb(priv); + + /* Delay 200 ms to waiting for the FW ready */ + msleep_interruptible(200); + + priv->adapter->fw_ready = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Given a usb_card_rec return its wlan_private + * @param card pointer to a usb_card_rec + * @return pointer to wlan_private + */ +wlan_private *libertas_sbi_get_priv(void *card) +{ + struct usb_card_rec *cardp = card; + return cardp->priv; +} + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private * priv) +{ + return 0; +} + +int libertas_sbi_resume(wlan_private * priv) +{ + return 0; +} +#endif + +#ifdef CONFIG_PM +static int if_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + wlan_private *priv = cardp->priv; + + ENTER(); + + if (priv->adapter->psstate != PS_STATE_FULL_POWER) + return -1; + + netif_device_detach(cardp->eth_dev); + + /* Unlink tx & rx urb */ + usb_kill_urb(cardp->tx_urb); + usb_kill_urb(cardp->rx_urb); + + cardp->rx_urb_recall = 1; + + LEAVE(); + return 0; +} + +static int if_usb_resume(struct usb_interface *intf) +{ + struct usb_card_rec *cardp = usb_get_intfdata(intf); + + ENTER(); + + cardp->rx_urb_recall = 0; + + if_usb_submit_rx_urb(cardp->priv); + + netif_device_attach(cardp->eth_dev); + + LEAVE(); + return 0; +} +#else +#define if_usb_suspend NULL +#define if_usb_resume NULL +#endif + +static struct usb_driver if_usb_driver = { + /* driver name */ + .name = usbdriver_name, + /* probe function name */ + .probe = if_usb_probe, + /* disconnect function name */ + .disconnect = if_usb_disconnect, + /* device signature table */ + .id_table = if_usb_table, + .suspend = if_usb_suspend, + .resume = if_usb_resume, +}; + +/** + * @brief This function registers driver. + * @param add pointer to add_card callback function + * @param remove pointer to remove card callback function + * @param arg pointer to call back function parameter + * @return dummy success variable + */ +int libertas_sbi_register(void) +{ + /* + * API registers the Marvell USB driver + * to the USB system + */ + usb_register(&if_usb_driver); + + /* Return success to wlan layer */ + return 0; +} + +/** + * @brief This function removes usb driver. + * @return N/A + */ +void libertas_sbi_unregister(void) +{ + /* API unregisters the driver from USB subsystem */ + usb_deregister(&if_usb_driver); + return; +} diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h new file mode 100644 index 0000000..7851167 --- /dev/null +++ b/drivers/net/wireless/libertas/if_usb.h @@ -0,0 +1,109 @@ +/** + * This file contains definition for USB interface. + */ +#define CMD_TYPE_REQUEST 0xF00DFACE +#define CMD_TYPE_DATA 0xBEADC0DE +#define CMD_TYPE_INDICATION 0xBEEFFACE + +#define IPFIELD_ALIGN_OFFSET 2 + +#define USB8388_VID_1 0x1286 +#define USB8388_PID_1 0x2001 +#define USB8388_VID_2 0x05a3 +#define USB8388_PID_2 0x8388 + +#ifdef SUPPORT_BOOT_COMMAND +#define BOOT_CMD_FW_BY_USB 0x01 +#define BOOT_CMD_FW_IN_EEPROM 0x02 +#define BOOT_CMD_UPDATE_BOOT2 0x03 +#define BOOT_CMD_UPDATE_FW 0x04 +#define BOOT_CMD_MAGIC_NUMBER 0x4C56524D /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */ + +struct bootcmdstr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 au8dumy[11]; +}; + +#define BOOT_CMD_RESP_OK 0x0001 +#define BOOT_CMD_RESP_FAIL 0x0000 + +struct bootcmdrespStr +{ + u32 u32magicnumber; + u8 u8cmd_tag; + u8 u8result; + u8 au8dumy[2]; +}; +#endif /* SUPPORT_BOOT_COMMAND */ + +/* read callback private data */ +struct read_cb_info { + wlan_private *priv; + struct sk_buff *skb; +}; + +/** USB card description structure*/ +struct usb_card_rec { + struct net_device *eth_dev; + struct usb_device *udev; + struct urb *rx_urb, *tx_urb; + void *priv; + struct read_cb_info rinfo; + + int bulk_in_size; + u8 bulk_in_endpointAddr; + + u8 *bulk_out_buffer; + int bulk_out_size; + u8 bulk_out_endpointAddr; + + u8 CRC_OK; + u32 fwseqnum; + u32 lastseqnum; + u32 totalbytes; + u32 fwlastblksent; + u8 fwdnldover; + u8 fwfinalblk; + + u32 usb_event_cause; + u8 usb_int_cause; + + u8 rx_urb_recall; + + u8 bootcmdresp; +}; + +/** fwheader */ +struct fwheader { + u32 dnldcmd; + u32 baseaddr; + u32 datalength; + u32 CRC; +}; + +#define FW_MAX_DATA_BLK_SIZE 600 +/** FWData */ +struct FWData { + struct fwheader fwheader; + u32 seqnum; + u8 data[FW_MAX_DATA_BLK_SIZE]; +}; + +/** fwsyncheader */ +struct fwsyncheader { + u32 cmd; + u32 seqnum; +}; + +#define FW_HAS_DATA_TO_RECV 0x00000001 +#define FW_HAS_LAST_BLOCK 0x00000004 + +#define FW_DATA_XMIT_SIZE \ + sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32) + +int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb); +void if_usb_free(struct usb_card_rec *cardp); +int if_usb_issue_boot_command(wlan_private *priv, int ivalue); + diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c new file mode 100644 index 0000000..82b3964 --- /dev/null +++ b/drivers/net/wireless/libertas/ioctl.c @@ -0,0 +1,2500 @@ +/** + * This file contains ioctl functions + */ + +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> +#include <net/ieee80211.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "wext.h" + +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN + \ + IW_ESSID_MAX_SIZE + \ + IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \ + IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \ + IW_EV_PARAM_LEN + 40) /* 40 for WPAIE */ + +#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ) + +static int setrxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2 + && mode != RF_ANTENNA_AUTO) { + return -EINVAL; + } + + adapter->rxantennamode = mode; + + lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_rx, + cmd_option_waitforrsp, 0, + &adapter->rxantennamode); + return ret; +} + +static int settxantenna(wlan_private * priv, int mode) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2) + && (mode != RF_ANTENNA_AUTO)) { + return -EINVAL; + } + + adapter->txantennamode = mode; + + lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_set_tx, + cmd_option_waitforrsp, 0, + &adapter->txantennamode); + + return ret; +} + +static int getrxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->rxantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_rx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode); + + return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1; +} + +static int gettxantenna(wlan_private * priv, char *buf) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + // clear it, so we will know if the value + // returned below is correct or not. + adapter->txantennamode = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna, + cmd_act_get_tx, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode); + + return sprintf(buf, "0x%04x", adapter->txantennamode) + 1; +} + +static int wlan_set_region(wlan_private * priv, u16 region_code) +{ + int i; + + for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) { + // use the region code to search for the index + if (region_code == libertas_region_code_to_index[i]) { + priv->adapter->regiontableindex = (u16) i; + priv->adapter->regioncode = region_code; + break; + } + } + + // if it's unidentified region code + if (i >= MRVDRV_MAX_REGION_CODE) { + lbs_pr_debug(1, "region Code not identified\n"); + LEAVE(); + return -1; + } + + if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) { + LEAVE(); + return -EINVAL; + } + + return 0; +} + +/** + * @brief Get/Set Firmware wakeup method + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + if (copy_to_user + (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) { + lbs_pr_alert("copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert("ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_alert("Copy from user failed\n"); + return -EFAULT; + } + + adapter->pkttxctrl = (u32) data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +/** + * @brief Get/Set NULL Package generation interval + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data; + ENTER(); + + if ((int)wrq->u.data.length == 0) { + data = adapter->nullpktinterval; + + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_alert( "copy_to_user failed!\n"); + return -EFAULT; + } + } else { + if ((int)wrq->u.data.length > 1) { + lbs_pr_alert( "ioctl too many args!\n"); + return -EFAULT; + } + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->nullpktinterval = data; + } + + wrq->u.data.length = 1; + + LEAVE(); + return 0; +} + +static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data[2]; + ENTER(); + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[1] = adapter->rxpd_rate; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + wrq->u.data.length = 2; + LEAVE(); + return 0; +} + +static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int data[4]; + + ENTER(); + memset(data, 0, sizeof(data)); + if (wrq->u.data.length) { + if (copy_from_user(data, wrq->u.data.pointer, + min_t(size_t, wrq->u.data.length, 4) * sizeof(int))) + return -EFAULT; + } + if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) { + if (adapter->connect_status == libertas_connected) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + } + + if (wrq->u.data.length == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4)) + return -EFAULT; + wrq->u.data.length = 4; + } else if (data[0] == 0) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 1) { + data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 2) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG]; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else if (data[0] == 3) { + data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int))) + return -EFAULT; + wrq->u.data.length = 1; + } else + return -ENOTSUPP; + + LEAVE(); + return 0; +} + +static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.length > 0) { + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) + return -EFAULT; + + lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data); + if ((data > MRVDRV_MAX_BEACON_INTERVAL) + || (data < MRVDRV_MIN_BEACON_INTERVAL)) + return -ENOTSUPP; + adapter->beaconperiod = data; + } + data = adapter->beaconperiod; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) + return -EFAULT; + + wrq->u.data.length = 1; + + return 0; +} + +static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + ENTER(); + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + break; + case 1: + temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG], + adapter->NF[TYPE_BEACON][TYPE_AVG]); + break; + case 2: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + break; + case 3: + temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + break; + default: + return -ENOTSUPP; + } + val = (int *)wrq->u.name; + *val = temp; + + LEAVE(); + return 0; +} + +static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + int temp; + int data = 0; + int *val; + + data = SUBCMD_DATA(wrq); + if ((data == 0) || (data == 1)) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, cmd_option_waitforrsp, + 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } + + switch (data) { + case 0: + temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG]; + break; + case 1: + temp = adapter->NF[TYPE_BEACON][TYPE_AVG]; + break; + case 2: + temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG]; + break; + case 3: + temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE; + break; + default: + return -ENOTSUPP; + } + + temp = CAL_NF(temp); + + lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp); + val = (int *)wrq->u.name; + *val = temp; + return 0; +} + +static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req) +{ + wlan_adapter *adapter = priv->adapter; + int *pdata; + struct iwreq *wrq = (struct iwreq *)req; + int ret = 0; + adapter->txrate = 0; + lbs_pr_debug(1, "wlan_get_txrate_ioctl\n"); + ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query, + cmd_act_get, cmd_option_waitforrsp, + 0, NULL); + if (ret) + return ret; + + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->txrate; + return 0; +} + +static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + char status[64]; + wlan_adapter *adapter = priv->adapter; + + memset(status, 0, sizeof(status)); + + switch (adapter->inframode) { + case wlan802_11ibss: + if (adapter->connect_status == libertas_connected) { + if (adapter->adhoccreate) + memcpy(&status, "AdhocStarted", sizeof(status)); + else + memcpy(&status, "AdhocJoined", sizeof(status)); + } else { + memcpy(&status, "AdhocIdle", sizeof(status)); + } + break; + case wlan802_11infrastructure: + memcpy(&status, "Inframode", sizeof(status)); + break; + default: + memcpy(&status, "AutoUnknownmode", sizeof(status)); + break; + } + + lbs_pr_debug(1, "status = %s\n", status); + wrq->u.data.length = strlen(status) + 1; + + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, + &status, wrq->u.data.length)) + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Set/Get WPA IE + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + + if (wrq->u.data.length) { + if (wrq->u.data.length > sizeof(adapter->wpa_ie)) { + lbs_pr_debug(1, "failed to copy WPA IE, too big \n"); + return -EFAULT; + } + if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer, + wrq->u.data.length)) { + lbs_pr_debug(1, "failed to copy WPA IE \n"); + return -EFAULT; + } + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len, + adapter->wpa_ie[0]); + lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len); + if (adapter->wpa_ie[0] == WPA_IE) + adapter->secinfo.WPAenabled = 1; + else if (adapter->wpa_ie[0] == WPA2_IE) + adapter->secinfo.WPA2enabled = 1; + else { + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + } else { + memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie)); + adapter->wpa_ie_len = wrq->u.data.length; + lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n", + adapter->wpa_ie_len, adapter->wpa_ie[0]); + adapter->secinfo.WPAenabled = 0; + adapter->secinfo.WPA2enabled = 0; + } + + // enable/disable RSN in firmware if WPA is enabled/disabled + // depending on variable adapter->secinfo.WPAenabled is set or not + ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn, + cmd_act_set, cmd_option_waitforrsp, + 0, NULL); + + LEAVE(); + return ret; +} + +/** + * @brief Set Auto prescan + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int data; + wlan_adapter *adapter = priv->adapter; + int *val; + + data = SUBCMD_DATA(wrq); + lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data); + adapter->prescan = data; + + val = (int *)wrq->u.name; + *val = data; + return 0; +} + +static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + u32 mdtim; + int idata; + int ret = -EINVAL; + + ENTER(); + + idata = SUBCMD_DATA(wrq); + mdtim = (u32) idata; + if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM) + && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM)) + || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) { + priv->adapter->multipledtim = mdtim; + ret = 0; + } + if (ret) + lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n"); + + LEAVE(); + return ret; +} + +/** + * @brief Set authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + wlan_adapter *adapter = priv->adapter; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + + lbs_pr_debug(1, "auth alg is %#x\n", alg); + + switch (alg) { + case AUTH_ALG_SHARED_KEY: + adapter->secinfo.authmode = wlan802_11authmodeshared; + break; + case AUTH_ALG_NETWORK_EAP: + adapter->secinfo.authmode = + wlan802_11authmodenetworkEAP; + break; + case AUTH_ALG_OPEN_SYSTEM: + default: + adapter->secinfo.authmode = wlan802_11authmodeopen; + break; + } + return 0; +} + +/** + * @brief Set 802.1x authentication mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req) +{ + int alg; + struct iwreq *wrq = (struct iwreq *)req; + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + alg = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg); + priv->adapter->secinfo.auth1xalg = alg; + return 0; +} + +static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + if (wrq->u.data.flags == 0) { + //from iwpriv subcmd + mode = SUBCMD_DATA(wrq); + } else { + //from wpa_supplicant subcmd + if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + } + lbs_pr_debug(1, "encryption mode is %#x\n", mode); + priv->adapter->secinfo.Encryptionmode = mode; + + LEAVE(); + return 0; +} + +static void adjust_mtu(wlan_private * priv) +{ + int mtu_increment = 0; + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + mtu_increment += sizeof(struct ieee80211_hdr_4addr); + + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) + mtu_increment += max(sizeof(struct tx_radiotap_hdr), + sizeof(struct rx_radiotap_hdr)); + priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN + - sizeof(struct ethhdr) + + mtu_increment; +} + +/** + * @brief Set Link-Layer Layer mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_LINKMODE_802_3: + priv->adapter->linkmode = mode; + break; + case WLAN_LINKMODE_802_11: + priv->adapter->linkmode = mode; + break; + default: + lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n", + mode); + return -EINVAL; + break; + } + lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode); + + adjust_mtu(priv); + + return 0; +} + +/** + * @brief Set Radio header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req) +{ + int mode; + + mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data; + + switch (mode) { + case WLAN_RADIOMODE_NONE: + priv->adapter->radiomode = mode; + break; + case WLAN_RADIOMODE_RADIOTAP: + priv->adapter->radiomode = mode; + break; + default: + lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n", + mode); + return -EINVAL; + } + lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode); + + adjust_mtu(priv); + return 0; +} + +/** + * @brief Set Debug header mode + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req) +{ + priv->adapter->debugmode = (int)((struct ifreq *) + ((u8 *) req + 4))->ifr_data; + return 0; +} + +static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n"); + len = getrxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv, + struct ifreq *req) +{ + int len; + char buf[8]; + struct iwreq *wrq = (struct iwreq *)req; + + lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n"); + len = gettxantenna(priv, buf); + + wrq->u.data.length = len; + if (wrq->u.data.pointer) { + if (copy_to_user(wrq->u.data.pointer, &buf, len)) { + lbs_pr_debug(1, "CopyToUser failed\n"); + return -EFAULT; + } + } + return 0; +} + +/** + * @brief Get the MAC TSF value from the firmware + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure containing buffer + * space to store a TSF value retrieved from the firmware + * + * @return 0 if successful; IOCTL error code otherwise + */ +static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u64 tsfval; + int ret; + + ret = libertas_prepare_and_send_command(priv, + cmd_get_tsf, + 0, cmd_option_waitforrsp, 0, &tsfval); + + lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval); + + if (ret != 0) { + lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n"); + ret = -EFAULT; + } else { + if (copy_to_user(wrq->u.data.pointer, + &tsfval, + min_t(size_t, wrq->u.data.length, + sizeof(tsfval))) != 0) { + + lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n"); + ret = -EFAULT; + } else { + ret = 0; + } + } + return ret; +} + +/** + * @brief Get/Set adapt rate + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + wlan_adapter *adapter = priv->adapter; + int data[2]; + + memset(data, 0, sizeof(data)); + if (!wrq->u.data.length) { + lbs_pr_debug(1, "Get ADAPT RATE SET\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_get, + cmd_option_waitforrsp, 0, NULL); + data[0] = adapter->enablehwauto; + data[1] = adapter->ratebitmap; + if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } +#define GET_TWO_INT 2 + wrq->u.data.length = GET_TWO_INT; + } else { + lbs_pr_debug(1, "Set ADAPT RATE SET\n"); + if (wrq->u.data.length > 2) + return -EINVAL; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * wrq->u.data.length)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + adapter->enablehwauto = data[0]; + adapter->ratebitmap = data[1]; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rate_adapt_rateset, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + return ret; +} + +/** + * @brief Get/Set inactivity timeout + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to iwreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + int data = 0; + u16 timeout = 0; + + ENTER(); + if (wrq->u.data.length > 1) + return -ENOTSUPP; + + if (wrq->u.data.length == 0) { + /* Get */ + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_get, + cmd_option_waitforrsp, 0, + &timeout); + data = timeout; + if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } else { + /* Set */ + if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + timeout = data; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_inactivity_timeout, + cmd_act_set, + cmd_option_waitforrsp, 0, + &timeout); + } + + wrq->u.data.length = 1; + + LEAVE(); + return ret; +} + +static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + int ret; + char buf[GETLOG_BUFSIZE - 1]; + wlan_adapter *adapter = priv->adapter; + + lbs_pr_debug(1, " GET STATS\n"); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log, + 0, cmd_option_waitforrsp, 0, NULL); + + if (ret) { + return ret; + } + + if (wrq->u.data.pointer) { + sprintf(buf, "\n mcasttxframe %u failed %u retry %u " + "multiretry %u framedup %u " + "rtssuccess %u rtsfailure %u ackfailure %u\n" + "rxfrag %u mcastrxframe %u fcserror %u " + "txframe %u wepundecryptable %u ", + adapter->logmsg.mcasttxframe, + adapter->logmsg.failed, + adapter->logmsg.retry, + adapter->logmsg.multiretry, + adapter->logmsg.framedup, + adapter->logmsg.rtssuccess, + adapter->logmsg.rtsfailure, + adapter->logmsg.ackfailure, + adapter->logmsg.rxfrag, + adapter->logmsg.mcastrxframe, + adapter->logmsg.fcserror, + adapter->logmsg.txframe, + adapter->logmsg.wepundecryptable); + wrq->u.data.length = strlen(buf) + 1; + if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + } + + return 0; +} + +static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + u8 buf[12]; + u8 *option[] = { "active", "passive", "get", }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + if (priv->adapter->enable11d) { + lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n"); + return -EFAULT; + } + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) + return -EFAULT; + + lbs_pr_debug(1, "Scan type Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + case 0: + adapter->scantype = cmd_scan_type_active; + break; + case 1: + adapter->scantype = cmd_scan_type_passive; + break; + case 2: + wrq->u.data.length = strlen(option[adapter->scantype]) + 1; + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scantype], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + + break; + default: + lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n"); + ret = -EINVAL; + break; + } + + return ret; +} + +static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + u8 buf[12]; + u8 *option[] = { "bss", "ibss", "any", "get" }; + int i, max_options = (sizeof(option) / sizeof(option[0])); + int ret = 0; + + ENTER(); + + memset(buf, 0, sizeof(buf)); + + if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf), + wrq->u.data.length))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Scan mode Option = %s\n", buf); + + buf[sizeof(buf) - 1] = '\0'; + + for (i = 0; i < max_options; i++) { + if (!strcmp(buf, option[i])) + break; + } + + switch (i) { + + case 0: + adapter->scanmode = cmd_bss_type_bss; + break; + case 1: + adapter->scanmode = cmd_bss_type_ibss; + break; + case 2: + adapter->scanmode = cmd_bss_type_any; + break; + case 3: + + wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1; + + lbs_pr_debug(1, "Get Scan mode Option = %s\n", + option[adapter->scanmode - 1]); + + lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length); + + if (copy_to_user(wrq->u.data.pointer, + option[adapter->scanmode - 1], + wrq->u.data.length)) { + lbs_pr_debug(1, "Copy to user failed\n"); + ret = -EFAULT; + } + lbs_pr_debug(1, "GET Scan type Option after copy = %s\n", + (char *)wrq->u.data.pointer); + + break; + + default: + lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n"); + ret = -EINVAL; + break; + } + + LEAVE(); + return ret; +} + +/** + * @brief Get/Set Adhoc G Rate + * + * @param priv A pointer to wlan_private structure + * @param wrq A pointer to user data + * @return 0--success, otherwise fail + */ +static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq) +{ + wlan_adapter *adapter = priv->adapter; + int data, data1; + int *val; + + ENTER(); + + data1 = SUBCMD_DATA(wrq); + switch (data1) { + case 0: + adapter->adhoc_grate_enabled = 0; + break; + case 1: + adapter->adhoc_grate_enabled = 1; + break; + case 2: + break; + default: + return -EINVAL; + } + data = adapter->adhoc_grate_enabled; + val = (int *)wrq->u.name; + *val = data; + LEAVE(); + return 0; +} + +static inline int hex2int(char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + return -1; +} + +/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx") + into binary format (6 bytes). + + This function expects that each byte is represented with 2 characters + (e.g., 11:2:11:11:11:11 is invalid) + + */ +static char *eth_str2addr(char *ethstr, u8 * addr) +{ + int i, val, val2; + char *pos = ethstr; + + /* get rid of initial blanks */ + while (*pos == ' ' || *pos == '\t') + ++pos; + + for (i = 0; i < 6; i++) { + val = hex2int(*pos++); + if (val < 0) + return NULL; + val2 = hex2int(*pos++); + if (val2 < 0) + return NULL; + addr[i] = (val * 16 + val2) & 0xff; + + if (i < 5 && *pos++ != ':') + return NULL; + } + return pos; +} + +/* this writes xx:xx:xx:xx:xx:xx into ethstr + (ethstr must have space for 18 chars) */ +static int eth_addr2str(u8 * addr, char *ethstr) +{ + int i; + char *pos = ethstr; + + for (i = 0; i < 6; i++) { + sprintf(pos, "%02x", addr[i] & 0xff); + pos += 2; + if (i < 5) + *pos++ = ':'; + } + return 17; +} + +/** + * @brief Add an entry to the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + char *pos; + u8 ethaddr[ETH_ALEN]; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("BT_ADD: Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str); + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_add, + cmd_option_waitforrsp, 0, ethaddr)); +} + +/** + * @brief Delete an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char ethaddrs_str[18]; + u8 ethaddr[ETH_ALEN]; + char *pos; + + ENTER(); + if (copy_from_user(ethaddrs_str, wrq->u.data.pointer, + sizeof(ethaddrs_str))) + return -EFAULT; + + if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) { + lbs_pr_info("Invalid MAC address\n"); + return -EINVAL; + } + + lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_del, + cmd_option_waitforrsp, 0, ethaddr)); + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the BT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_reset_ioctl(wlan_private * priv) +{ + ENTER(); + + lbs_pr_alert( "BT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_bt_access, + cmd_act_bt_access_reset, + cmd_option_waitforrsp, 0, NULL)); + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the BT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + int pos; + char *addr1; + struct iwreq *wrq = (struct iwreq *)req; + /* used to pass id and store the bt entry returned by the FW */ + union { + int id; + char addr1addr2[2 * ETH_ALEN]; + } param; + static char outstr[64]; + char *pbuf = outstr; + int ret; + + ENTER(); + + if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -1; + } + param.id = simple_strtoul(outstr, NULL, 10); + pos = sprintf(pbuf, "%d: ", param.id); + pbuf += pos; + + ret = libertas_prepare_and_send_command(priv, cmd_bt_access, + cmd_act_bt_access_list, + cmd_option_waitforrsp, 0, + (char *)¶m); + + if (ret == 0) { + addr1 = param.addr1addr2; + + pos = sprintf(pbuf, "ignoring traffic from "); + pbuf += pos; + pos = eth_addr2str(addr1, pbuf); + pbuf += pos; + } else { + sprintf(pbuf, "(null)"); + pbuf += pos; + } + + wrq->u.data.length = strlen(outstr); + if (copy_to_user(wrq->u.data.pointer, (char *)outstr, + wrq->u.data.length)) { + lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Find the next parameter in an input string + * @param ptr A pointer to the input parameter string + * @return A pointer to the next parameter, or 0 if no parameters left. + */ +static char * next_param(char * ptr) +{ + if (!ptr) return NULL; + while (*ptr == ' ' || *ptr == '\t') ++ptr; + return (*ptr == '\0') ? NULL : ptr; +} + +/** + * @brief Add an entry to the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[128]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.metric = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.metric = FWT_DEFAULT_METRIC; + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + + if ((ptr = next_param(ptr))) + fwt_access.ssn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.ssn = FWT_DEFAULT_SSN; + + if ((ptr = next_param(ptr))) + fwt_access.dsn = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.dsn = FWT_DEFAULT_DSN; + + if ((ptr = next_param(ptr))) + fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT; + + if ((ptr = next_param(ptr))) + fwt_access.ttl = simple_strtoul(ptr, &ptr, 10); + else + fwt_access.ttl = FWT_DEFAULT_TTL; + + if ((ptr = next_param(ptr))) + fwt_access.expiration = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.expiration = FWT_DEFAULT_EXPIRATION; + + if ((ptr = next_param(ptr))) + fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE; + + if ((ptr = next_param(ptr))) + fwt_access.snr = + cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + else + fwt_access.snr = FWT_DEFAULT_SNR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str, + fwt_access.dir, ethaddr2_str); + lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n", + fwt_access.ssn, fwt_access.dsn, fwt_access.metric, + fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration, + fwt_access.sleepmode, fwt_access.snr); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_add, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + +/** + * @brief Delete an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n"); + return -EINVAL; + } + + if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) { + lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n"); + return -EINVAL; + } + + if ((ptr = next_param(ptr))) + fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10); + else + fwt_access.dir = FWT_DEFAULT_DIR; + +#ifdef DEBUG + { + char ethaddr1_str[18], ethaddr2_str[18]; + lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + eth_addr2str(fwt_access.ra, ethaddr2_str); + lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str, + ethaddr2_str, fwt_access.dir); + } +#endif + + LEAVE(); + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_del, + cmd_option_waitforrsp, 0, + (void *)&fwt_access)); +} + + +/** + * @brief Print route parameters + * @param fwt_access struct cmd_ds_fwt_access with route info + * @param buf destination buffer for route info + */ +static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf) +{ + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.da, buf); + buf += sprintf(buf, " "); + buf += eth_addr2str(fwt_access.ra, buf); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric)); + buf += sprintf(buf, " %u", fwt_access.dir); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn)); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn)); + buf += sprintf(buf, " %u", fwt_access.hopcount); + buf += sprintf(buf, " %u", fwt_access.ttl); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration)); + buf += sprintf(buf, " %u", fwt_access.sleepmode); + buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr)); +} + +/** + * @brief Lookup an entry in the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + char *ptr; + static struct cmd_ds_fwt_access fwt_access; + static char out_str[128]; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) { + lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n"); + return -EINVAL; + } + +#ifdef DEBUG + { + char ethaddr1_str[18]; + lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str); + eth_addr2str(fwt_access.da, ethaddr1_str); + lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str); + } +#endif + + ret = libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_lookup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, out_str); + else + sprintf(out_str, "(null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Reset all entries from the FWT table + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_reset_ioctl(wlan_private * priv) +{ + lbs_pr_debug(1, "FWT: resetting\n"); + + return (libertas_prepare_and_send_command(priv, + cmd_fwt_access, + cmd_act_fwt_access_reset, + cmd_option_waitforrsp, 0, NULL)); +} + +/** + * @brief List an entry from the FWT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) + print_route(fwt_access, pbuf); + else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FRT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[64]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_route, + cmd_option_waitforrsp, 0, (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " "); + pbuf += eth_addr2str(fwt_access.da, pbuf); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric)); + pbuf += sprintf(pbuf, " %u", fwt_access.dir); + /* note that the firmware returns the nid in the id field */ + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn)); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn)); + pbuf += sprintf(pbuf, " hop %u", fwt_access.hopcount); + pbuf += sprintf(pbuf, " ttl %u", fwt_access.ttl); + pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief List an entry from the FNT table + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct iwreq *wrq = (struct iwreq *)req; + char in_str[8]; + static struct cmd_ds_fwt_access fwt_access; + char *ptr = in_str; + static char out_str[128]; + char *pbuf = out_str; + int ret; + + ENTER(); + if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str))) + return -EFAULT; + + memset(&fwt_access, 0, sizeof(fwt_access)); + fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10)); + +#ifdef DEBUG + { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str); + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id)); + } +#endif + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_list_neighbor, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) { + pbuf += sprintf(pbuf, " ra "); + pbuf += eth_addr2str(fwt_access.ra, pbuf); + pbuf += sprintf(pbuf, " slp %u", fwt_access.sleepmode); + pbuf += sprintf(pbuf, " snr %u", le32_to_cpu(fwt_access.snr)); + pbuf += sprintf(pbuf, " ref %u", le32_to_cpu(fwt_access.references)); + } else + pbuf += sprintf(pbuf, " (null)"); + + wrq->u.data.length = strlen(out_str); + if (copy_to_user(wrq->u.data.pointer, (char *)out_str, + wrq->u.data.length)) { + lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n"); + return -EFAULT; + } + + LEAVE(); + return 0; +} + +/** + * @brief Cleans up the route (FRT) and neighbor (FNT) tables + * (Garbage Collection) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: cleaning up\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_cleanup, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets firmware internal time (debug purposes) + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req) +{ + static struct cmd_ds_fwt_access fwt_access; + int ret; + + ENTER(); + + lbs_pr_debug(1, "FWT: getting time\n"); + + memset(&fwt_access, 0, sizeof(fwt_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_fwt_access, + cmd_act_fwt_access_time, + cmd_option_waitforrsp, 0, + (void *)&fwt_access); + + if (ret == 0) + req->ifr_data = (char *)(le32_to_cpu(fwt_access.references)); + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param req A pointer to ifreq structure + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + memset(&mesh_access, 0, sizeof(mesh_access)); + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_get_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret == 0) { + req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0])); + } + else + return -EFAULT; + + LEAVE(); + return 0; +} + +/** + * @brief Gets mesh ttl from firmware + * @param priv A pointer to wlan_private structure + * @param ttl New ttl value + * @return 0 --success, otherwise fail + */ +static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl) +{ + struct cmd_ds_mesh_access mesh_access; + int ret; + + ENTER(); + + if( (ttl > 0xff) || (ttl < 0) ) + return -EINVAL; + + memset(&mesh_access, 0, sizeof(mesh_access)); + mesh_access.data[0] = ttl; + + ret = libertas_prepare_and_send_command(priv, cmd_mesh_access, + cmd_act_mesh_set_ttl, + cmd_option_waitforrsp, 0, + (void *)&mesh_access); + + if (ret != 0) + ret = -EFAULT; + + LEAVE(); + return ret; +} + +/** + * @brief ioctl function - entry point + * + * @param dev A pointer to net_device structure + * @param req A pointer to ifreq structure + * @param cmd command + * @return 0--success, otherwise fail + */ +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd) +{ + int subcmd = 0; + int idata = 0; + int *pdata; + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iwreq *wrq = (struct iwreq *)req; + + ENTER(); + + lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd); + switch (cmd) { + case WLANSCAN_TYPE: + lbs_pr_debug(1, "Scan type Ioctl\n"); + ret = wlan_scan_type_ioctl(priv, wrq); + break; + + case WLAN_SETNONE_GETNONE: /* set WPA mode on/off ioctl #20 */ + switch (wrq->u.data.flags) { + case WLANDEAUTH: + lbs_pr_debug(1, "Deauth\n"); + libertas_send_deauth(priv); + break; + + case WLANADHOCSTOP: + lbs_pr_debug(1, "Adhoc stop\n"); + ret = libertas_do_adhocstop_ioctl(priv); + break; + + case WLANRADIOON: + wlan_radio_ioctl(priv, 1); + break; + + case WLANRADIOOFF: + wlan_radio_ioctl(priv, 0); + break; + case WLANWLANIDLEON: + libertas_idle_on(priv); + break; + case WLANWLANIDLEOFF: + libertas_idle_off(priv); + break; + case WLAN_SUBCMD_BT_RESET: /* bt_reset */ + wlan_bt_reset_ioctl(priv); + break; + case WLAN_SUBCMD_FWT_RESET: /* fwt_reset */ + wlan_fwt_reset_ioctl(priv); + break; + } /* End of switch */ + break; + + case WLANSETWPAIE: + ret = wlan_setwpaie_ioctl(priv, req); + break; + case WLAN_SETINT_GETINT: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = (int)req->ifr_data; //from iwpriv subcmd + switch (subcmd) { + case WLANNF: + ret = wlan_get_nf(priv, wrq); + break; + case WLANRSSI: + ret = wlan_get_rssi(priv, wrq); + break; + case WLANENABLE11D: + ret = libertas_cmd_enable_11d(priv, wrq); + break; + case WLANADHOCGRATE: + ret = wlan_do_set_grate_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_SET_PRESCAN: + ret = wlan_subcmd_setprescan_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SETONEINT_GETONEINT: + switch (wrq->u.data.flags) { + case WLAN_BEACON_INTERVAL: + ret = wlan_beacon_interval(priv, wrq); + break; + + case WLAN_LISTENINTRVL: + if (!wrq->u.data.length) { + int data; + lbs_pr_debug(1, "Get locallisteninterval value\n"); +#define GET_ONE_INT 1 + data = adapter->locallisteninterval; + if (copy_to_user(wrq->u.data.pointer, + &data, sizeof(int))) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = GET_ONE_INT; + } else { + int data; + if (copy_from_user + (&data, wrq->u.data.pointer, sizeof(int))) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + + lbs_pr_debug(1, "Set locallisteninterval = %d\n", + data); +#define MAX_U16_VAL 65535 + if (data > MAX_U16_VAL) { + lbs_pr_debug(1, "Exceeds U16 value\n"); + return -EINVAL; + } + adapter->locallisteninterval = data; + } + break; + case WLAN_TXCONTROL: + ret = wlan_txcontrol(priv, wrq); //adds for txcontrol ioctl + break; + + case WLAN_NULLPKTINTERVAL: + ret = wlan_null_pkt_interval(priv, wrq); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + break; + + case WLAN_SETONEINT_GETNONE: + /* The first 4 bytes of req->ifr_data is sub-ioctl number + * after 4 bytes sits the payload. + */ + subcmd = wrq->u.data.flags; //from wpa_supplicant subcmd + + if (!subcmd) + subcmd = (int)req->ifr_data; //from iwpriv subcmd + + switch (subcmd) { + case WLAN_SUBCMD_SETRXANTENNA: /* SETRXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = setrxantenna(priv, idata); + break; + case WLAN_SUBCMD_SETTXANTENNA: /* SETTXANTENNA */ + idata = SUBCMD_DATA(wrq); + ret = settxantenna(priv, idata); + break; + case WLAN_SET_ATIM_WINDOW: + adapter->atimwindow = SUBCMD_DATA(wrq); + adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50); + break; + case WLANSETBCNAVG: + adapter->bcn_avg_factor = SUBCMD_DATA(wrq); + if (adapter->bcn_avg_factor == 0) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR) + adapter->bcn_avg_factor = + DEFAULT_BCN_AVG_FACTOR; + break; + case WLANSETDATAAVG: + adapter->data_avg_factor = SUBCMD_DATA(wrq); + if (adapter->data_avg_factor == 0) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR) + adapter->data_avg_factor = + DEFAULT_DATA_AVG_FACTOR; + break; + case WLANSETREGION: + idata = SUBCMD_DATA(wrq); + ret = wlan_set_region(priv, (u16) idata); + break; + + case WLAN_SET_LISTEN_INTERVAL: + idata = SUBCMD_DATA(wrq); + adapter->listeninterval = (u16) idata; + break; + + case WLAN_SET_MULTIPLE_DTIM: + ret = wlan_set_multiple_dtim_ioctl(priv, req); + break; + + case WLANSETAUTHALG: + ret = wlan_setauthalg_ioctl(priv, req); + break; + + case WLANSET8021XAUTHALG: + ret = wlan_set8021xauthalg_ioctl(priv, req); + break; + + case WLANSETENCRYPTIONMODE: + ret = wlan_setencryptionmode_ioctl(priv, req); + break; + + case WLAN_SET_LINKMODE: + ret = wlan_set_linkmode_ioctl(priv, req); + break; + + case WLAN_SET_RADIOMODE: + ret = wlan_set_radiomode_ioctl(priv, req); + break; + + case WLAN_SET_DEBUGMODE: + ret = wlan_set_debugmode_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_SET_TTL: + idata = SUBCMD_DATA(wrq); + ret = wlan_mesh_set_ttl_ioctl(priv, idata); + break; + + default: + ret = -EOPNOTSUPP; + break; + } + + break; + + case WLAN_SETNONE_GETTWELVE_CHAR: /* Get Antenna settings */ + /* + * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is + * in flags of iwreq structure, otherwise it will be in + * mode member of iwreq structure. + */ + switch ((int)wrq->u.data.flags) { + case WLAN_SUBCMD_GETRXANTENNA: /* Get Rx Antenna */ + ret = wlan_subcmd_getrxantenna_ioctl(priv, req); + break; + + case WLAN_SUBCMD_GETTXANTENNA: /* Get Tx Antenna */ + ret = wlan_subcmd_gettxantenna_ioctl(priv, req); + break; + + case WLAN_GET_TSF: + ret = wlan_get_tsf_ioctl(priv, wrq); + break; + } + break; + + case WLAN_SET128CHAR_GET128CHAR: + switch ((int)wrq->u.data.flags) { + + case WLANSCAN_MODE: + lbs_pr_debug(1, "Scan mode Ioctl\n"); + ret = wlan_scan_mode_ioctl(priv, wrq); + break; + + case WLAN_GET_ADHOC_STATUS: + ret = wlan_get_adhoc_status_ioctl(priv, wrq); + break; + case WLAN_SUBCMD_BT_ADD: + ret = wlan_bt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_DEL: + ret = wlan_bt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_BT_LIST: + ret = wlan_bt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_ADD: + ret = wlan_fwt_add_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_DEL: + ret = wlan_fwt_del_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LOOKUP: + ret = wlan_fwt_lookup_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_NEIGHBOR: + ret = wlan_fwt_list_neighbor_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST: + ret = wlan_fwt_list_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_LIST_ROUTE: + ret = wlan_fwt_list_route_ioctl(priv, req); + break; + } + break; + + case WLAN_SETNONE_GETONEINT: + switch ((int)req->ifr_data) { + case WLANGETBCNAVG: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->bcn_avg_factor; + break; + + case WLANGETREGION: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->regioncode; + break; + + case WLAN_GET_LISTEN_INTERVAL: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->listeninterval; + break; + + case WLAN_GET_LINKMODE: + req->ifr_data = (char *)((u32) adapter->linkmode); + break; + + case WLAN_GET_RADIOMODE: + req->ifr_data = (char *)((u32) adapter->radiomode); + break; + + case WLAN_GET_DEBUGMODE: + req->ifr_data = (char *)((u32) adapter->debugmode); + break; + + case WLAN_GET_MULTIPLE_DTIM: + pdata = (int *)wrq->u.name; + *pdata = (int)adapter->multipledtim; + break; + case WLAN_GET_TX_RATE: + ret = wlan_get_txrate_ioctl(priv, req); + break; + case WLAN_SUBCMD_FWT_CLEANUP: /* fwt_cleanup */ + ret = wlan_fwt_cleanup_ioctl(priv, req); + break; + + case WLAN_SUBCMD_FWT_TIME: /* fwt_time */ + ret = wlan_fwt_time_ioctl(priv, req); + break; + + case WLAN_SUBCMD_MESH_GET_TTL: + ret = wlan_mesh_get_ttl_ioctl(priv, req); + break; + + default: + ret = -EOPNOTSUPP; + + } + + break; + + case WLANGETLOG: + ret = wlan_do_getlog_ioctl(priv, wrq); + break; + + case WLAN_SET_GET_SIXTEEN_INT: + switch ((int)wrq->u.data.flags) { + case WLAN_TPCCFG: + { + int data[5]; + struct cmd_ds_802_11_tpc_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 5)) + return -1; + + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 5)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.usesnr = data[1]; + cfg.P0 = data[2]; + cfg.P1 = data[3]; + cfg.P2 = data[4]; + } + + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_tpc_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + + data[0] = cfg.enable; + data[1] = cfg.usesnr; + data[2] = cfg.P0; + data[3] = cfg.P1; + data[4] = cfg.P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 5)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 5; + } + break; + + case WLAN_POWERCFG: + { + int data[4]; + struct cmd_ds_802_11_pwr_cfg cfg; + memset(&cfg, 0, sizeof(cfg)); + if ((wrq->u.data.length > 1) + && (wrq->u.data.length != 4)) + return -1; + if (wrq->u.data.length == 0) { + cfg.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 4)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + cfg.action = + cpu_to_le16 + (cmd_act_set); + cfg.enable = data[0]; + cfg.PA_P0 = data[1]; + cfg.PA_P1 = data[2]; + cfg.PA_P2 = data[3]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_pwr_cfg, + 0, + cmd_option_waitforrsp, + 0, (void *)&cfg); + data[0] = cfg.enable; + data[1] = cfg.PA_P0; + data[2] = cfg.PA_P1; + data[3] = cfg.PA_P2; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 4)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 4; + } + break; + case WLAN_AUTO_FREQ_SET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + if (wrq->u.data.length != 3) + return -1; + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy from user failed\n"); + return -EFAULT; + } + afc.afc_auto = data[0]; + + if (afc.afc_auto != 0) { + afc.threshold = data[1]; + afc.period = data[2]; + } else { + afc.timing_offset = data[1]; + afc.carrier_offset = data[2]; + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_set_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + } + break; + case WLAN_AUTO_FREQ_GET: + { + int data[3]; + struct cmd_ds_802_11_afc afc; + memset(&afc, 0, sizeof(afc)); + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_get_afc, + 0, + cmd_option_waitforrsp, + 0, (void *)&afc); + data[0] = afc.afc_auto; + data[1] = afc.timing_offset; + data[2] = afc.carrier_offset; + if (copy_to_user + (wrq->u.data.pointer, data, + sizeof(int) * 3)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = 3; + } + break; + case WLAN_SCANPROBES: + { + int data; + if (wrq->u.data.length > 0) { + if (copy_from_user + (&data, wrq->u.data.pointer, + sizeof(int))) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + adapter->scanprobes = data; + } else { + data = adapter->scanprobes; + if (copy_to_user + (wrq->u.data.pointer, &data, + sizeof(int))) { + lbs_pr_debug(1, + "Copy to user failed\n"); + return -EFAULT; + } + } + wrq->u.data.length = 1; + } + break; + case WLAN_LED_GPIO_CTRL: + { + int i; + int data[16]; + + struct cmd_ds_802_11_led_ctrl ctrl; + struct mrvlietypes_ledgpio *gpio = + (struct mrvlietypes_ledgpio *) ctrl.data; + + memset(&ctrl, 0, sizeof(ctrl)); + if (wrq->u.data.length > MAX_LEDS * 2) + return -ENOTSUPP; + if ((wrq->u.data.length % 2) != 0) + return -ENOTSUPP; + if (wrq->u.data.length == 0) { + ctrl.action = + cpu_to_le16 + (cmd_act_get); + } else { + if (copy_from_user + (data, wrq->u.data.pointer, + sizeof(int) * + wrq->u.data.length)) { + lbs_pr_debug(1, + "Copy from user failed\n"); + return -EFAULT; + } + + ctrl.action = + cpu_to_le16 + (cmd_act_set); + ctrl.numled = cpu_to_le16(0); + gpio->header.type = + cpu_to_le16(TLV_TYPE_LED_GPIO); + gpio->header.len = wrq->u.data.length; + for (i = 0; i < wrq->u.data.length; + i += 2) { + gpio->ledpin[i / 2].led = + data[i]; + gpio->ledpin[i / 2].pin = + data[i + 1]; + } + } + ret = + libertas_prepare_and_send_command(priv, + cmd_802_11_led_gpio_ctrl, + 0, + cmd_option_waitforrsp, + 0, (void *)&ctrl); + for (i = 0; i < gpio->header.len; i += 2) { + data[i] = gpio->ledpin[i / 2].led; + data[i + 1] = gpio->ledpin[i / 2].pin; + } + if (copy_to_user(wrq->u.data.pointer, data, + sizeof(int) * + gpio->header.len)) { + lbs_pr_debug(1, "Copy to user failed\n"); + return -EFAULT; + } + + wrq->u.data.length = gpio->header.len; + } + break; + case WLAN_ADAPT_RATESET: + ret = wlan_adapt_rateset(priv, wrq); + break; + case WLAN_INACTIVITY_TIMEOUT: + ret = wlan_inactivity_timeout(priv, wrq); + break; + case WLANSNR: + ret = wlan_get_snr(priv, wrq); + break; + case WLAN_GET_RXINFO: + ret = wlan_get_rxinfo(priv, wrq); + } + break; + + default: + ret = -EINVAL; + break; + } + LEAVE(); + return ret; +} + + diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c new file mode 100644 index 0000000..11682cb --- /dev/null +++ b/drivers/net/wireless/libertas/join.c @@ -0,0 +1,1055 @@ +/** + * Functions implementing wlan infrastructure and adhoc join routines, + * IOCTL handlers as well as command preperation and response routines + * for sending adhoc start, adhoc join, and association commands + * to the firmware. + */ +#include <linux/netdevice.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "join.h" +#include "dev.h" + +/** + * @brief This function finds out the common rates between rate1 and rate2. + * + * It will fill common rates in rate1 as output if found. + * + * NOTE: Setting the MSB of the basic rates need to be taken + * care, either before or after calling this function + * + * @param adapter A pointer to wlan_adapter structure + * @param rate1 the buffer which keeps input and output + * @param rate1_size the size of rate1 buffer + * @param rate2 the buffer which keeps rate2 + * @param rate2_size the size of rate2 buffer. + * + * @return 0 or -1 + */ +static int get_common_rates(wlan_adapter * adapter, u8 * rate1, + int rate1_size, u8 * rate2, int rate2_size) +{ + u8 *ptr = rate1; + int ret = 0; + u8 tmp[30]; + int i; + + memset(&tmp, 0, sizeof(tmp)); + memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp))); + memset(rate1, 0, rate1_size); + + /* Mask the top bit of the original values */ + for (i = 0; tmp[i] && i < sizeof(tmp); i++) + tmp[i] &= 0x7F; + + for (i = 0; rate2[i] && i < rate2_size; i++) { + /* Check for Card Rate in tmp, excluding the top bit */ + if (strchr(tmp, rate2[i] & 0x7F)) { + /* values match, so copy the Card Rate to rate1 */ + *rate1++ = rate2[i]; + } + } + + lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp)); + lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size); + lbs_dbg_hex("Common rates:", ptr, rate1_size); + lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate); + + if (!adapter->is_datarate_auto) { + while (*ptr) { + if ((*ptr & 0x7f) == adapter->datarate) { + ret = 0; + goto done; + } + ptr++; + } + lbs_pr_alert( "Previously set fixed data rate %#x isn't " + "compatible with the network.\n", adapter->datarate); + + ret = -1; + goto done; + } + + ret = 0; +done: + return ret; +} + +int libertas_send_deauth(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11infrastructure && + adapter->connect_status == libertas_connected) + ret = libertas_send_deauthentication(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +int libertas_do_adhocstop_ioctl(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->inframode == wlan802_11ibss && + adapter->connect_status == libertas_connected) + ret = libertas_stop_adhoc_network(priv); + else + ret = -ENOTSUPP; + + return ret; +} + +/** + * @brief Associate to a specific BSS discovered in a scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to the BSS descriptor to associate with. + * + * @return 0-success, otherwise fail + */ +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate, + 0, cmd_option_waitforrsp, + 0, pbssdesc->macaddress); + + if (ret) { + LEAVE(); + return ret; + } + + /* set preamble to firmware */ + if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble) + adapter->preamble = cmd_type_short_preamble; + else + adapter->preamble = cmd_type_long_preamble; + + libertas_set_radio_control(priv); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate, + 0, cmd_option_waitforrsp, 0, pbssdesc); + + LEAVE(); + return ret; +} + +/** + * @brief Start an Adhoc Network + * + * @param priv A pointer to wlan_private structure + * @param adhocssid The ssid of the Adhoc Network + * @return 0--success, -1--fail + */ +int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + adapter->adhoccreate = 1; + + if (!adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocStart: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocStart: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start, + 0, cmd_option_waitforrsp, 0, adhocssid); + + return ret; +} + +/** + * @brief Join an adhoc network found in a previous scan + * + * @param priv A pointer to wlan_private structure + * @param pbssdesc Pointer to a BSS descriptor found in a previous scan + * to attempt to join + * + * @return 0--success, -1--fail + */ +int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n", + adapter->curbssparams.ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n", + adapter->curbssparams.ssid.ssidlength); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid); + lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n", + pbssdesc->ssid.ssidlength); + + /* check if the requested SSID is already joined */ + if (adapter->curbssparams.ssid.ssidlength + && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid) + && (adapter->curbssparams.bssdescriptor.inframode == + wlan802_11ibss)) { + + lbs_pr_debug(1, + "ADHOC_J_CMD: New ad-hoc SSID is the same as current, " + "not attempting to re-join"); + + return -1; + } + + /*Use shortpreamble only when both creator and card supports + short preamble */ + if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) { + lbs_pr_debug(1, "AdhocJoin: Long preamble\n"); + adapter->preamble = cmd_type_long_preamble; + } else { + lbs_pr_debug(1, "AdhocJoin: Short preamble\n"); + adapter->preamble = cmd_type_short_preamble; + } + + libertas_set_radio_control(priv); + + lbs_pr_debug(1, "curbssparams.channel = %d\n", + adapter->curbssparams.channel); + lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band); + + adapter->adhoccreate = 0; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join, + 0, cmd_option_waitforrsp, + OID_802_11_SSID, pbssdesc); + + return ret; +} + +int libertas_stop_adhoc_network(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Send Deauthentication Request + * + * @param priv A pointer to wlan_private structure + * @return 0--success, -1--fail + */ +int libertas_send_deauthentication(wlan_private * priv) +{ + return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate, + 0, cmd_option_waitforrsp, 0, NULL); +} + +/** + * @brief Set Idle Off + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_off(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 }; + int i; + + ENTER(); + + if (adapter->connect_status == libertas_disconnected) { + if (adapter->inframode == wlan802_11infrastructure) { + if (memcmp(adapter->previousbssid, zeromac, + sizeof(zeromac)) != 0) { + + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + lbs_pr_debug(1, "Previous BSSID = " + "%02x:%02x:%02x:%02x:%02x:%02x:\n", + adapter->previousbssid[0], + adapter->previousbssid[1], + adapter->previousbssid[2], + adapter->previousbssid[3], + adapter->previousbssid[4], + adapter->previousbssid[5]); + + i = libertas_find_SSID_in_list(adapter, + &adapter->previousssid, + adapter->previousbssid, + adapter->inframode); + + if (i < 0) { + libertas_send_specific_BSSID_scan(priv, + adapter-> + previousbssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, + adapter-> + previousbssid, + adapter-> + inframode); + } + + if (i < 0) { + /* If the BSSID could not be found, try just the SSID */ + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i < 0) { + libertas_send_specific_SSID_scan(priv, + &adapter-> + previousssid, + 1); + i = libertas_find_SSID_in_list(adapter, + &adapter-> + previousssid, NULL, + adapter-> + inframode); + } + + if (i >= 0) { + ret = + wlan_associate(priv, + &adapter-> + scantable[i]); + } + } + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ad_hoc_start, + 0, + cmd_option_waitforrsp, + 0, &adapter->previousssid); + } + } + /* else it is connected */ + + lbs_pr_debug(1, "\nwlanidle is off"); + LEAVE(); + return ret; +} + +/** + * @brief Set Idle On + * + * @param priv A pointer to wlan_private structure + * @return 0 --success, otherwise fail + */ +int libertas_idle_on(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + if (adapter->connect_status == libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + lbs_pr_debug(1, "Previous SSID = %s\n", + adapter->previousssid.ssid); + memmove(&adapter->previousssid, + &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + libertas_send_deauth(priv); + + } else if (adapter->inframode == wlan802_11ibss) { + ret = libertas_stop_adhoc_network(priv); + } + + } + + lbs_pr_debug(1, "\nwlanidle is on"); + + return ret; +} + +/** + * @brief This function prepares command of authenticate. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure + * @param pdata_buf Void cast of pointer to a BSSID to authenticate with + * + * @return 0 or -1 + */ +int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_authenticate *pauthenticate = + &cmd->params.auth; + u8 *bssid = pdata_buf; + + cmd->command = cpu_to_le16(cmd_802_11_authenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate) + + S_DS_GEN); + + pauthenticate->authtype = adapter->secinfo.authmode; + memcpy(pauthenticate->macaddr, bssid, ETH_ALEN); + + lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n", + bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); + + return 0; +} + +int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth; + + ENTER(); + + cmd->command = cpu_to_le16(cmd_802_11_deauthenticate); + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) + + S_DS_GEN); + + /* set AP MAC address */ + memmove(dauth->macaddr, adapter->curbssparams.bssid, + ETH_ALEN); + + /* Reason code 3 = Station is leaving */ +#define REASON_CODE_STA_LEAVING 3 + dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING); + + LEAVE(); + return 0; +} + +int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_associate *passo = &cmd->params.associate; + int ret = 0; + struct bss_descriptor *pbssdesc; + u8 *card_rates; + u8 *pos; + int card_rates_size; + u16 tmpcap; + struct mrvlietypes_ssidparamset *ssid; + struct mrvlietypes_phyparamset *phy; + struct mrvlietypes_ssparamset *ss; + struct mrvlietypes_ratesparamset *rates; + struct mrvlietypes_rsnparamset *rsn; + + ENTER(); + + pbssdesc = pdata_buf; + pos = (u8 *) passo; + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_associate); + + /* Save so we know which BSS Desc to use in the response handler */ + adapter->pattemptedbssdesc = pbssdesc; + + memcpy(passo->peerstaaddr, + pbssdesc->macaddress, sizeof(passo->peerstaaddr)); + pos += sizeof(passo->peerstaaddr); + + /* set the listen interval */ + passo->listeninterval = adapter->listeninterval; + + pos += sizeof(passo->capinfo); + pos += sizeof(passo->listeninterval); + pos += sizeof(passo->bcnperiod); + pos += sizeof(passo->dtimperiod); + + ssid = (struct mrvlietypes_ssidparamset *) pos; + ssid->header.type = cpu_to_le16(TLV_TYPE_SSID); + ssid->header.len = pbssdesc->ssid.ssidlength; + memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len); + pos += sizeof(ssid->header) + ssid->header.len; + ssid->header.len = cpu_to_le16(ssid->header.len); + + phy = (struct mrvlietypes_phyparamset *) pos; + phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS); + phy->header.len = sizeof(phy->fh_ds.dsparamset); + memcpy(&phy->fh_ds.dsparamset, + &pbssdesc->phyparamset.dsparamset.currentchan, + sizeof(phy->fh_ds.dsparamset)); + pos += sizeof(phy->header) + phy->header.len; + phy->header.len = cpu_to_le16(phy->header.len); + + ss = (struct mrvlietypes_ssparamset *) pos; + ss->header.type = cpu_to_le16(TLV_TYPE_CF); + ss->header.len = sizeof(ss->cf_ibss.cfparamset); + pos += sizeof(ss->header) + ss->header.len; + ss->header.len = cpu_to_le16(ss->header.len); + + rates = (struct mrvlietypes_ratesparamset *) pos; + rates->header.type = cpu_to_le16(TLV_TYPE_RATES); + + memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES, + card_rates, card_rates_size)) { + ret = -1; + goto done; + } + + rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES); + adapter->curbssparams.numofrates = rates->header.len; + + pos += sizeof(rates->header) + rates->header.len; + rates->header.len = cpu_to_le16(rates->header.len); + + if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + rsn = (struct mrvlietypes_rsnparamset *) pos; + rsn->header.type = (u16) adapter->wpa_ie[0]; /* WPA_IE or WPA2_IE */ + rsn->header.type = cpu_to_le16(rsn->header.type); + rsn->header.len = (u16) adapter->wpa_ie[1]; + memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len); + lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn, + sizeof(rsn->header) + rsn->header.len); + pos += sizeof(rsn->header) + rsn->header.len; + rsn->header.len = cpu_to_le16(rsn->header.len); + } + + /* update curbssparams */ + adapter->curbssparams.channel = + (pbssdesc->phyparamset.dsparamset.currentchan); + + /* Copy the infra. association rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, &rates->rates, + min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len)); + + lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len); + + /* set IBSS field */ + if (pbssdesc->inframode == wlan802_11infrastructure) { +#define CAPINFO_ESS_MODE 1 + passo->capinfo.ess = CAPINFO_ESS_MODE; + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN); + + /* set the capability info at last */ + memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo)); + tmpcap &= CAPINFO_MASK; + lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, void *pssid) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads; + int ret = 0; + int cmdappendsize = 0; + int i; + u16 tmpcap; + struct bss_descriptor *pbssdesc; + struct WLAN_802_11_SSID *ssid = pssid; + + ENTER(); + + if (!adapter) { + ret = -1; + goto done; + } + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start); + + pbssdesc = &adapter->curbssparams.bssdescriptor; + adapter->pattemptedbssdesc = pbssdesc; + + /* + * Fill in the parameters for 2 data structures: + * 1. cmd_ds_802_11_ad_hoc_start command + * 2. adapter->scantable[i] + * + * Driver will fill up SSID, bsstype,IBSS param, Physical Param, + * probe delay, and cap info. + * + * Firmware will fill up beacon period, DTIM, Basic rates + * and operational rates. + */ + + memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE); + + memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength); + + lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID); + + memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE); + memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength); + + pbssdesc->ssid.ssidlength = ssid->ssidlength; + + /* set the BSS type */ + adhs->bsstype = cmd_bss_type_ibss; + pbssdesc->inframode = wlan802_11ibss; + adhs->beaconperiod = adapter->beaconperiod; + + /* set Physical param set */ +#define DS_PARA_IE_ID 3 +#define DS_PARA_IE_LEN 1 + + adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID; + adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN; + + WARN_ON(!adapter->adhocchannel); + + lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n", + adapter->adhocchannel); + + adapter->curbssparams.channel = adapter->adhocchannel; + + pbssdesc->channel = adapter->adhocchannel; + adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel; + + memcpy(&pbssdesc->phyparamset, + &adhs->phyparamset, sizeof(union ieeetypes_phyparamset)); + + /* set IBSS param set */ +#define IBSS_PARA_IE_ID 6 +#define IBSS_PARA_IE_LEN 2 + + adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID; + adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN; + adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow; + memcpy(&pbssdesc->ssparamset, + &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + /* set capability info */ + adhs->cap.ess = 0; + adhs->cap.ibss = 1; + pbssdesc->cap.ibss = 1; + + /* probedelay */ + adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time); + + /* set up privacy in adapter->scantable[i] */ + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + +#define AD_HOC_CAP_PRIVACY_ON 1 + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n"); + pbssdesc->privacy = wlan802_11privfilter8021xWEP; + adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } else { + lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting " + "privacy to ACCEPT ALL\n"); + pbssdesc->privacy = wlan802_11privfilteracceptall; + } + + memset(adhs->datarate, 0, sizeof(adhs->datarate)); + + if (adapter->adhoc_grate_enabled) { + memcpy(adhs->datarate, libertas_adhoc_rates_g, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g))); + } else { + memcpy(adhs->datarate, libertas_adhoc_rates_b, + min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b))); + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* Copy the ad-hoc creating rates into Current BSS state structure */ + memcpy(&adapter->curbssparams.datarates, + &adhs->datarate, adapter->curbssparams.numofrates); + + lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n", + adhs->datarate[0], adhs->datarate[1], + adhs->datarate[2], adhs->datarate[3]); + + lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n"); + + if (libertas_create_dnld_countryinfo_11d(priv)) { + lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n"); + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &adhs->cap, sizeof(u16)); + tmpcap = cpu_to_le16(tmpcap); + memcpy(&adhs->cap, &tmpcap, sizeof(u16)); + + ret = 0; +done: + LEAVE(); + return ret; +} + +int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd) +{ + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop); + cmd->size = cpu_to_le16(S_DS_GEN); + + return 0; +} + +int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj; + struct bss_descriptor *pbssdesc = pdata_buf; + int cmdappendsize = 0; + int ret = 0; + u8 *card_rates; + int card_rates_size; + u16 tmpcap; + int i; + + ENTER(); + + adapter->pattemptedbssdesc = pbssdesc; + + cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join); + + padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss; + + padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod; + + memcpy(&padhocjoin->bssdescriptor.BSSID, + &pbssdesc->macaddress, ETH_ALEN); + + memcpy(&padhocjoin->bssdescriptor.SSID, + &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength); + + memcpy(&padhocjoin->bssdescriptor.phyparamset, + &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset)); + + memcpy(&padhocjoin->bssdescriptor.ssparamset, + &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset)); + + memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo)); + tmpcap &= CAPINFO_MASK; + + lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n", + tmpcap, CAPINFO_MASK); + memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap, + sizeof(struct ieeetypes_capinfo)); + + /* information on BSSID descriptor passed to FW */ + lbs_pr_debug(1, + "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n", + padhocjoin->bssdescriptor.BSSID[0], + padhocjoin->bssdescriptor.BSSID[1], + padhocjoin->bssdescriptor.BSSID[2], + padhocjoin->bssdescriptor.BSSID[3], + padhocjoin->bssdescriptor.BSSID[4], + padhocjoin->bssdescriptor.BSSID[5], + padhocjoin->bssdescriptor.SSID); + + lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n", + (u32) padhocjoin->bssdescriptor.datarates); + + /* failtimeout */ + padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT); + + /* probedelay */ + padhocjoin->probedelay = + cpu_to_le16(cmd_scan_probe_delay_time); + + /* Copy Data rates from the rates recorded in scan response */ + memset(padhocjoin->bssdescriptor.datarates, 0, + sizeof(padhocjoin->bssdescriptor.datarates)); + memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates, + min(sizeof(padhocjoin->bssdescriptor.datarates), + sizeof(pbssdesc->datarates))); + + card_rates = libertas_supported_rates; + card_rates_size = sizeof(libertas_supported_rates); + + adapter->curbssparams.channel = pbssdesc->channel; + + if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates, + sizeof(padhocjoin->bssdescriptor.datarates), + card_rates, card_rates_size)) { + lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n"); + ret = -1; + goto done; + } + + /* Find the last non zero */ + for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates) + && padhocjoin->bssdescriptor.datarates[i]; i++) ; + + adapter->curbssparams.numofrates = i; + + /* + * Copy the adhoc joining rates to Current BSS State structure + */ + memcpy(adapter->curbssparams.datarates, + padhocjoin->bssdescriptor.datarates, + adapter->curbssparams.numofrates); + + padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow = + cpu_to_le16(pbssdesc->atimwindow); + + if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) { + padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON; + } + + if (adapter->psmode == wlan802_11powermodemax_psp) { + /* wake up first */ + enum WLAN_802_11_POWER_MODE Localpsmode; + + Localpsmode = wlan802_11powermodecam; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_ps_mode, + cmd_act_set, + 0, 0, &Localpsmode); + + if (ret) { + ret = -1; + goto done; + } + } + + if (libertas_parse_dnld_countryinfo_11d(priv)) { + ret = -1; + goto done; + } + + cmd->size = + cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) + + S_DS_GEN + cmdappendsize); + + memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap, + sizeof(struct ieeetypes_capinfo)); + tmpcap = cpu_to_le16(tmpcap); + + memcpy(&padhocjoin->bssdescriptor.cap, + &tmpcap, sizeof(struct ieeetypes_capinfo)); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + union iwreq_data wrqu; + struct ieeetypes_assocrsp *passocrsp; + struct bss_descriptor *pbssdesc; + + ENTER(); + + passocrsp = (struct ieeetypes_assocrsp *) & resp->params; + + if (passocrsp->statuscode) { + + libertas_mac_event_disconnected(priv); + + lbs_pr_debug(1, + "ASSOC_RESP: Association failed, status code = %d\n", + passocrsp->statuscode); + + ret = -1; + goto done; + } + + lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params, + le16_to_cpu(resp->size) - S_DS_GEN); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + /* Set the attempted BSSID Index to current */ + pbssdesc = adapter->pattemptedbssdesc; + + lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + /* Set the new BSSID (AP's MAC address) to current BSSID */ + memcpy(adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + + lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n", + adapter->currentpacketfilter); + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0; + adapter->NF[TYPE_RXPD][TYPE_AVG] = 0; + + memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR)); + memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF)); + adapter->nextSNRNF = 0; + adapter->numSNRNF = 0; + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + lbs_pr_debug(1, "ASSOC_RESP: Associated \n"); + + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + done: + LEAVE(); + return ret; +} + +int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} + +int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + u16 command = le16_to_cpu(resp->command); + u16 result = le16_to_cpu(resp->result); + struct cmd_ds_802_11_ad_hoc_result *padhocresult; + union iwreq_data wrqu; + struct bss_descriptor *pbssdesc; + + ENTER(); + + padhocresult = &resp->params.result; + + lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size)); + lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command); + lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result); + + pbssdesc = adapter->pattemptedbssdesc; + + /* + * Join result code 0 --> SUCCESS + */ + if (result) { + lbs_pr_debug(1, "ADHOC_RESP failed\n"); + if (adapter->connect_status == libertas_connected) { + libertas_mac_event_disconnected(priv); + } + + memset(&adapter->curbssparams.bssdescriptor, + 0x00, sizeof(adapter->curbssparams.bssdescriptor)); + + LEAVE(); + return -1; + } + + /* + * Now the join cmd should be successful + * If BSSID has changed use SSID to compare instead of BSSID + */ + lbs_pr_debug(1, "ADHOC_J_RESP %s\n", pbssdesc->ssid.ssid); + + /* Send a Media Connected event, according to the Spec */ + adapter->connect_status = libertas_connected; + + if (command == cmd_ret_802_11_ad_hoc_start) { + /* Update the created network descriptor with the new BSSID */ + memcpy(pbssdesc->macaddress, + padhocresult->BSSID, ETH_ALEN); + } else { + + /* Make a copy of current BSSID descriptor, only needed for join since + * the current descriptor is already being used for adhoc start + */ + memmove(&adapter->curbssparams.bssdescriptor, + pbssdesc, sizeof(struct bss_descriptor)); + } + + /* Set the BSSID from the joined/started descriptor */ + memcpy(&adapter->curbssparams.bssid, + pbssdesc->macaddress, ETH_ALEN); + + /* Set the new SSID to current SSID */ + memcpy(&adapter->curbssparams.ssid, + &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID)); + + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + + memset(&wrqu, 0, sizeof(wrqu)); + memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + + lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n"); + lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel); + lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + padhocresult->BSSID[0], padhocresult->BSSID[1], + padhocresult->BSSID[2], padhocresult->BSSID[3], + padhocresult->BSSID[4], padhocresult->BSSID[5]); + + LEAVE(); + return ret; +} + +int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp) +{ + ENTER(); + + libertas_mac_event_disconnected(priv); + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h new file mode 100644 index 0000000..8efa245 --- /dev/null +++ b/drivers/net/wireless/libertas/join.h @@ -0,0 +1,64 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan infrastructure and adhoc join routines + * + * Driver interface functions and type declarations for the join module + * implemented in wlan_join.c. Process all start/join requests for + * both adhoc and infrastructure networks + */ +#ifndef _WLAN_JOIN_H +#define _WLAN_JOIN_H + +#include "defs.h" + +struct cmd_ds_command; +extern int libertas_cmd_80211_authenticate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); +extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pssid); +extern int libertas_cmd_80211_deauthenticate(wlan_private * priv, + struct cmd_ds_command *cmd); +extern int libertas_cmd_80211_associate(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_disassociate(wlan_private * priv, + struct cmd_ds_command *resp); +extern int libertas_ret_80211_associate(wlan_private * priv, + struct cmd_ds_command *resp); + +extern int libertas_idle_on(wlan_private * priv); +extern int libertas_idle_off(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); +extern int libertas_reassociation_thread(void *data); + +struct WLAN_802_11_SSID; +struct bss_descriptor; + +extern int libertas_start_adhoc_network(wlan_private * priv, + struct WLAN_802_11_SSID *adhocssid); +extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc); +extern int libertas_stop_adhoc_network(wlan_private * priv); + +extern int libertas_send_deauthentication(wlan_private * priv); +extern int libertas_send_deauth(wlan_private * priv); + +extern int libertas_do_adhocstop_ioctl(wlan_private * priv); + +int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc); + +#endif diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c new file mode 100644 index 0000000..dcbf102 --- /dev/null +++ b/drivers/net/wireless/libertas/main.c @@ -0,0 +1,1258 @@ +/** + * This file contains the major functions in WLAN + * driver. It includes init, exit, open, close and main + * thread etc.. + */ + +#include <linux/delay.h> +#include <linux/freezer.h> +#include <linux/etherdevice.h> +#include <linux/netdevice.h> +#include <linux/if_arp.h> + +#include <net/iw_handler.h> + +#include "host.h" +#include "sbi.h" +#include "decl.h" +#include "dev.h" +#include "fw.h" +#include "wext.h" +#include "debugfs.h" +#include "assoc.h" + +#ifdef ENABLE_PM +static struct pm_dev *wlan_pm_dev = NULL; +#endif + +#define WLAN_TX_PWR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_US_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_JP_DEFAULT 16 /*50mW */ +#define WLAN_TX_PWR_FR_DEFAULT 20 /*100mW */ +#define WLAN_TX_PWR_EMEA_DEFAULT 20 /*100mW */ + +/* Format { channel, frequency (MHz), maxtxpower } */ +/* band: 'B/G', region: USA FCC/Canada IC */ +static struct chan_freq_power channel_freq_power_US_BG[] = { + {1, 2412, WLAN_TX_PWR_US_DEFAULT}, + {2, 2417, WLAN_TX_PWR_US_DEFAULT}, + {3, 2422, WLAN_TX_PWR_US_DEFAULT}, + {4, 2427, WLAN_TX_PWR_US_DEFAULT}, + {5, 2432, WLAN_TX_PWR_US_DEFAULT}, + {6, 2437, WLAN_TX_PWR_US_DEFAULT}, + {7, 2442, WLAN_TX_PWR_US_DEFAULT}, + {8, 2447, WLAN_TX_PWR_US_DEFAULT}, + {9, 2452, WLAN_TX_PWR_US_DEFAULT}, + {10, 2457, WLAN_TX_PWR_US_DEFAULT}, + {11, 2462, WLAN_TX_PWR_US_DEFAULT} +}; + +/* band: 'B/G', region: Europe ETSI */ +static struct chan_freq_power channel_freq_power_EU_BG[] = { + {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT}, + {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT}, + {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT}, + {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT}, + {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT}, + {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT}, + {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT}, + {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT}, + {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT}, + {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT}, + {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT}, + {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT}, + {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT} +}; + +/* band: 'B/G', region: Spain */ +static struct chan_freq_power channel_freq_power_SPN_BG[] = { + {10, 2457, WLAN_TX_PWR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_DEFAULT} +}; + +/* band: 'B/G', region: France */ +static struct chan_freq_power channel_freq_power_FR_BG[] = { + {10, 2457, WLAN_TX_PWR_FR_DEFAULT}, + {11, 2462, WLAN_TX_PWR_FR_DEFAULT}, + {12, 2467, WLAN_TX_PWR_FR_DEFAULT}, + {13, 2472, WLAN_TX_PWR_FR_DEFAULT} +}; + +/* band: 'B/G', region: Japan */ +static struct chan_freq_power channel_freq_power_JPN_BG[] = { + {1, 2412, WLAN_TX_PWR_JP_DEFAULT}, + {2, 2417, WLAN_TX_PWR_JP_DEFAULT}, + {3, 2422, WLAN_TX_PWR_JP_DEFAULT}, + {4, 2427, WLAN_TX_PWR_JP_DEFAULT}, + {5, 2432, WLAN_TX_PWR_JP_DEFAULT}, + {6, 2437, WLAN_TX_PWR_JP_DEFAULT}, + {7, 2442, WLAN_TX_PWR_JP_DEFAULT}, + {8, 2447, WLAN_TX_PWR_JP_DEFAULT}, + {9, 2452, WLAN_TX_PWR_JP_DEFAULT}, + {10, 2457, WLAN_TX_PWR_JP_DEFAULT}, + {11, 2462, WLAN_TX_PWR_JP_DEFAULT}, + {12, 2467, WLAN_TX_PWR_JP_DEFAULT}, + {13, 2472, WLAN_TX_PWR_JP_DEFAULT}, + {14, 2484, WLAN_TX_PWR_JP_DEFAULT} +}; + +/** + * the structure for channel, frequency and power + */ +struct region_cfp_table { + u8 region; + struct chan_freq_power *cfp_BG; + int cfp_no_BG; +}; + +/** + * the structure for the mapping between region and CFP + */ +static struct region_cfp_table region_cfp_table[] = { + {0x10, /*US FCC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x20, /*CANADA IC */ + channel_freq_power_US_BG, + sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power), + } + , + {0x30, /*EU*/ channel_freq_power_EU_BG, + sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power), + } + , + {0x31, /*SPAIN*/ channel_freq_power_SPN_BG, + sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power), + } + , + {0x32, /*FRANCE*/ channel_freq_power_FR_BG, + sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power), + } + , + {0x40, /*JAPAN*/ channel_freq_power_JPN_BG, + sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power), + } + , +/*Add new region here */ +}; + +/** + * the rates supported by the card + */ +u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] = + { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12, + 0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00 +}; + +/** + * the rates supported + */ +u8 libertas_supported_rates[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc G mode + */ +u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] = + { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c, +0 }; + +/** + * the rates supported for ad-hoc B mode + */ +u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 }; + +/** + * the global variable of a pointer to wlan_private + * structure variable + */ +static wlan_private *wlanpriv = NULL; + +#define MAX_DEVS 5 +static struct net_device *libertas_devs[MAX_DEVS]; +static int libertas_found = 0; + +/** + * the table to keep region code + */ +u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] = + { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 }; + +static u8 *default_fw_name = "usb8388.bin"; + +/** + * Attributes exported through sysfs + */ + +/** + * @brief Get function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_get(struct device * dev, + struct device_attribute *attr, char * buf) { + struct cmd_ds_mesh_access mesh_access; + + memset(&mesh_access, 0, sizeof(mesh_access)); + libertas_prepare_and_send_command(to_net_dev(dev)->priv, + cmd_mesh_access, + cmd_act_mesh_get_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + + return snprintf(buf, 3, "%d\n", mesh_access.data[0]); +} + +/** + * @brief Set function for sysfs attribute libertas_mpp + */ +static ssize_t libertas_mpp_set(struct device * dev, + struct device_attribute *attr, const char * buf, size_t count) { + struct cmd_ds_mesh_access mesh_access; + + + memset(&mesh_access, 0, sizeof(mesh_access)); + sscanf(buf, "%d", &(mesh_access.data[0])); + libertas_prepare_and_send_command((to_net_dev(dev))->priv, + cmd_mesh_access, + cmd_act_mesh_set_mpp, + cmd_option_waitforrsp, 0, (void *)&mesh_access); + return strlen(buf); +} + +/** + * libertas_mpp attribute to be exported per mshX interface + * through sysfs (/sys/class/net/mshX/libertas-mpp) + */ +static DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get, + libertas_mpp_set ); + +/** + * @brief Check if the device can be open and wait if necessary. + * + * @param dev A pointer to net_device structure + * @return 0 + * + * For USB adapter, on some systems the device open handler will be + * called before FW ready. Use the following flag check and wait + * function to work around the issue. + * + */ +static int pre_open_check(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + int i = 0; + + while (!adapter->fw_ready && i < 20) { + i++; + msleep_interruptible(100); + } + if (!adapter->fw_ready) { + lbs_pr_info("FW not ready, pre_open_check() return failure\n"); + LEAVE(); + return -1; + } + + return 0; +} + +/** + * @brief This function opens the device + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_dev_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + + priv->open = 1; + + if (adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + } else + netif_carrier_off(priv->wlan_dev.netdev); + + LEAVE(); + return 0; +} +/** + * @brief This function opens the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->mesh_open = 1 ; + netif_start_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +/** + * @brief This function opens the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_open(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv ; + + if(pre_open_check(dev) == -1) + return -1; + priv->infra_open = 1 ; + netif_wake_queue(priv->wlan_dev.netdev); + if (priv->open == 0) + return wlan_dev_open(priv->wlan_dev.netdev) ; + return 0; +} + +static int wlan_dev_close(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + netif_carrier_off(priv->wlan_dev.netdev); + priv->open = 0; + + LEAVE(); + return 0; +} + +/** + * @brief This function closes the mshX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int mesh_close(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) (dev->priv); + + priv->mesh_open = 0; + netif_stop_queue(priv->mesh_dev); + if (priv->infra_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + +/** + * @brief This function closes the ethX interface + * + * @param dev A pointer to net_device structure + * @return 0 + */ +static int wlan_close(struct net_device *dev) { + wlan_private *priv = (wlan_private *) dev->priv; + + netif_stop_queue(priv->wlan_dev.netdev); + priv->infra_open = 0; + if (priv->mesh_open == 0) + return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ; + else + return 0; +} + + +#ifdef ENABLE_PM + +/** + * @brief This function is a callback function. it is called by + * kernel to enter or exit power saving mode. + * + * @param pmdev A pointer to pm_dev + * @param pmreq pm_request_t + * @param pmdata A pointer to pmdata + * @return 0 or -1 + */ +static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq, + void *pmdata) +{ + wlan_private *priv = wlanpriv; + wlan_adapter *adapter = priv->adapter; + struct net_device *dev = priv->wlan_dev.netdev; + + lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq); + + switch (pmreq) { + case PM_SUSPEND: + lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n"); + + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + if ((adapter->psstate != PS_STATE_SLEEP) + ) { + lbs_pr_debug(1, + "wlan_pm_callback: can't enter sleep mode\n"); + return -1; + } else { + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_detach(dev); + lbs_pr_debug(1, + "netif_device_detach().\n"); + } + libertas_sbi_suspend(priv); + } + break; + } + + /* in non associated mode */ + + /* + * Detach the network interface + * if the network is running + */ + if (netif_running(dev)) + netif_device_detach(dev); + + /* + * Storing and restoring of the regs be taken care + * at the driver rest will be done at wlan driver + * this makes driver independent of the card + */ + + libertas_sbi_suspend(priv); + + break; + + case PM_RESUME: + /* in associated mode */ + if (adapter->connect_status == libertas_connected) { + { + /* + * Bring the inteface up first + * This case should not happen still ... + */ + libertas_sbi_resume(priv); + + /* + * Attach the network interface + * if the network is running + */ + if (netif_running(dev)) { + netif_device_attach(dev); + lbs_pr_debug(1, + "after netif_device_attach().\n"); + } + lbs_pr_debug(1, + "After netif attach, in associated mode.\n"); + } + break; + } + + /* in non associated mode */ + + /* + * Bring the inteface up first + * This case should not happen still ... + */ + + libertas_sbi_resume(priv); + + if (netif_running(dev)) + netif_device_attach(dev); + + lbs_pr_debug(1, "after netif attach, in NON associated mode.\n"); + break; + } + + return 0; +} +#endif /* ENABLE_PM */ + +static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + int ret = 0; + wlan_private *priv = dev->priv; + + ENTER(); + + if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) { + priv->stats.tx_dropped++; + goto done; + } + + netif_stop_queue(priv->wlan_dev.netdev); + + if (libertas_process_tx(priv, skb) == 0) + dev->trans_start = jiffies; +done: + LEAVE(); + return ret; +} + +/** + * @brief Mark mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) +{ + wlan_private *priv = dev->priv; + ENTER(); + SET_MESH_FRAME(skb); + LEAVE(); + + return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev); +} + +/** + * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit + * + */ +static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) { + ENTER(); + UNSET_MESH_FRAME(skb); + LEAVE(); + return wlan_hard_start_xmit(skb, dev); +} + +static void wlan_tx_timeout(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + ENTER(); + + lbs_pr_err("tx watch dog timeout!\n"); + + priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED; + dev->trans_start = jiffies; + + if (priv->adapter->currenttxskb) { + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* If we are here, we have not received feedback from + the previous packet. Assume TX_FAIL and move on. */ + priv->adapter->eventcause = 0x01000000; + libertas_send_tx_feedback(priv); + } else + wake_up_interruptible(&priv->mainthread.waitq); + } else if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); + + LEAVE(); +} + +/** + * @brief This function returns the network statistics + * + * @param dev A pointer to wlan_private structure + * @return A pointer to net_device_stats structure + */ +static struct net_device_stats *wlan_get_stats(struct net_device *dev) +{ + wlan_private *priv = (wlan_private *) dev->priv; + + return &priv->stats; +} + +static int wlan_set_mac_address(struct net_device *dev, void *addr) +{ + int ret = 0; + wlan_private *priv = (wlan_private *) dev->priv; + wlan_adapter *adapter = priv->adapter; + struct sockaddr *phwaddr = addr; + + ENTER(); + + memset(adapter->current_addr, 0, ETH_ALEN); + + /* dev->dev_addr is 8 bytes */ + lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN); + + lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN); + memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN); + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + lbs_pr_debug(1, "set mac address failed.\n"); + ret = -1; + goto done; + } + + lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN); + memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN); + memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN); + +done: + LEAVE(); + return ret; +} + +static int wlan_copy_multicast_address(wlan_adapter * adapter, + struct net_device *dev) +{ + int i = 0; + struct dev_mc_list *mcptr = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++) { + memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN); + mcptr = mcptr->next; + } + + return i; + +} + +static void wlan_set_multicast_list(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int oldpacketfilter; + + ENTER(); + + oldpacketfilter = adapter->currentpacketfilter; + + if (dev->flags & IFF_PROMISC) { + lbs_pr_debug(1, "enable Promiscuous mode\n"); + adapter->currentpacketfilter |= + cmd_act_mac_promiscuous_enable; + adapter->currentpacketfilter &= + ~(cmd_act_mac_all_multicast_enable | + cmd_act_mac_multicast_enable); + } else { + /* Multicast */ + adapter->currentpacketfilter &= + ~cmd_act_mac_promiscuous_enable; + + if (dev->flags & IFF_ALLMULTI || dev->mc_count > + MRVDRV_MAX_MULTICAST_LIST_SIZE) { + lbs_pr_debug(1, "Enabling All Multicast!\n"); + adapter->currentpacketfilter |= + cmd_act_mac_all_multicast_enable; + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_all_multicast_enable; + + if (!dev->mc_count) { + lbs_pr_debug(1, "No multicast addresses - " + "disabling multicast!\n"); + adapter->currentpacketfilter &= + ~cmd_act_mac_multicast_enable; + } else { + int i; + + adapter->currentpacketfilter |= + cmd_act_mac_multicast_enable; + + adapter->nr_of_multicastmacaddr = + wlan_copy_multicast_address(adapter, dev); + + lbs_pr_debug(1, "Multicast addresses: %d\n", + dev->mc_count); + + for (i = 0; i < dev->mc_count; i++) { + lbs_pr_debug(1, "Multicast address %d:" + "%x %x %x %x %x %x\n", i, + adapter->multicastlist[i][0], + adapter->multicastlist[i][1], + adapter->multicastlist[i][2], + adapter->multicastlist[i][3], + adapter->multicastlist[i][4], + adapter->multicastlist[i][5]); + } + /* set multicast addresses to firmware */ + libertas_prepare_and_send_command(priv, + cmd_mac_multicast_adr, + cmd_act_set, 0, 0, + NULL); + } + } + } + + if (adapter->currentpacketfilter != oldpacketfilter) { + libertas_set_mac_packet_filter(priv); + } + + LEAVE(); +} + +/** + * @brief This function hanldes the major job in WLAN driver. + * it handles the event generated by firmware, rx data received + * from firmware and tx data sent from kernel. + * + * @param data A pointer to wlan_thread structure + * @return 0 + */ +static int wlan_service_main_thread(void *data) +{ + struct wlan_thread *thread = data; + wlan_private *priv = thread->priv; + wlan_adapter *adapter = priv->adapter; + wait_queue_t wait; + u8 ireg = 0; + + ENTER(); + + wlan_activate_thread(thread); + + init_waitqueue_entry(&wait, current); + + for (;;) { + lbs_pr_debug(1, "main-thread 111: intcounter=%d " + "currenttxskb=%p dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + add_wait_queue(&thread->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irq(&adapter->driver_lock); + if ((adapter->psstate == PS_STATE_SLEEP) || + (!adapter->intcounter + && (priv->wlan_dev.dnld_sent || adapter->cur_cmd || + list_empty(&adapter->cmdpendingq)))) { + lbs_pr_debug(1, + "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n", + adapter->connect_status, adapter->intcounter, + adapter->psmode, adapter->psstate); + spin_unlock_irq(&adapter->driver_lock); + schedule(); + } else + spin_unlock_irq(&adapter->driver_lock); + + + lbs_pr_debug(1, + "main-thread 222 (waking up): intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&thread->waitq, &wait); + try_to_freeze(); + + lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + if (kthread_should_stop() + || adapter->surpriseremoved) { + lbs_pr_debug(1, + "main-thread: break from main thread: surpriseremoved=0x%x\n", + adapter->surpriseremoved); + break; + } + + + spin_lock_irq(&adapter->driver_lock); + if (adapter->intcounter) { + u8 int_status; + adapter->intcounter = 0; + int_status = libertas_sbi_get_int_status(priv, &ireg); + + if (int_status) { + lbs_pr_debug(1, + "main-thread: reading HOST_INT_STATUS_REG failed\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + adapter->hisregcpy |= ireg; + } + + lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p " + "dnld_sent=%d\n", + adapter->intcounter, + adapter->currenttxskb, priv->wlan_dev.dnld_sent); + + /* command response? */ + if (adapter->hisregcpy & his_cmdupldrdy) { + lbs_pr_debug(1, "main-thread: cmd response ready.\n"); + + adapter->hisregcpy &= ~his_cmdupldrdy; + spin_unlock_irq(&adapter->driver_lock); + libertas_process_rx_command(priv); + spin_lock_irq(&adapter->driver_lock); + } + + /* Any Card Event */ + if (adapter->hisregcpy & his_cardevent) { + lbs_pr_debug(1, "main-thread: Card Event Activity.\n"); + + adapter->hisregcpy &= ~his_cardevent; + + if (libertas_sbi_read_event_cause(priv)) { + lbs_pr_alert( + "main-thread: libertas_sbi_read_event_cause failed.\n"); + spin_unlock_irq(&adapter->driver_lock); + continue; + } + spin_unlock_irq(&adapter->driver_lock); + libertas_process_event(priv); + } else + spin_unlock_irq(&adapter->driver_lock); + + /* Check if we need to confirm Sleep Request received previously */ + if (adapter->psstate == PS_STATE_PRE_SLEEP) { + if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) { + if (adapter->connect_status == + libertas_connected) { + lbs_pr_debug(1, + "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p " + "dnld_sent=%d cur_cmd=%p, confirm now\n", + adapter->intcounter, + adapter->currenttxskb, + priv->wlan_dev.dnld_sent, + adapter->cur_cmd); + + libertas_ps_confirm_sleep(priv, + (u16) adapter->psmode); + } else { + /* workaround for firmware sending + * deauth/linkloss event immediately + * after sleep request, remove this + * after firmware fixes it + */ + adapter->psstate = PS_STATE_AWAKE; + lbs_pr_alert( + "main-thread: ignore PS_SleepConfirm in non-connected state\n"); + } + } + } + + /* The PS state is changed during processing of Sleep Request + * event above + */ + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) + continue; + + /* Execute the next command */ + if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd) + libertas_execute_next_command(priv); + + /* Wake-up command waiters which can't sleep in + * libertas_prepare_and_send_command + */ + if (!adapter->nr_cmd_pending) + wake_up_all(&adapter->cmd_pending); + + libertas_tx_runqueue(priv); + } + + del_timer(&adapter->command_timer); + adapter->nr_cmd_pending = 0; + wake_up_all(&adapter->cmd_pending); + wlan_deactivate_thread(thread); + + LEAVE(); + return 0; +} + +/** + * @brief This function adds the card. it will probe the + * card, allocate the wlan_priv and initialize the device. + * + * @param card A pointer to card + * @return A pointer to wlan_private structure + */ +wlan_private *wlan_add_card(void *card) +{ + struct net_device *dev = NULL; + struct net_device *mesh_dev = NULL; + wlan_private *priv = NULL; + + ENTER(); + + /* Allocate an Ethernet device and register it */ + if (!(dev = alloc_etherdev(sizeof(wlan_private)))) { + lbs_pr_alert( "Init ethernet device failed!\n"); + return NULL; + } + + priv = dev->priv; + + /* allocate buffer for wlan_adapter */ + if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) { + lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n"); + goto err_kmalloc; + } + + /* Allocate a virtual mesh device */ + if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) { + lbs_pr_debug(1, "Init ethernet device failed!\n"); + return NULL; + } + + /* Both intervaces share the priv structure */ + mesh_dev->priv = priv; + + /* init wlan_adapter */ + memset(priv->adapter, 0, sizeof(wlan_adapter)); + + priv->wlan_dev.netdev = dev; + priv->wlan_dev.card = card; + priv->mesh_open = 0; + priv->infra_open = 0; + priv->mesh_dev = mesh_dev; + wlanpriv = priv; + + SET_MODULE_OWNER(dev); + SET_MODULE_OWNER(mesh_dev); + + /* Setup the OS Interface to our functions */ + dev->open = wlan_open; + dev->hard_start_xmit = wlan_pre_start_xmit; + dev->stop = wlan_close; + dev->do_ioctl = libertas_do_ioctl; + dev->set_mac_address = wlan_set_mac_address; + mesh_dev->open = mesh_open; + mesh_dev->hard_start_xmit = mesh_pre_start_xmit; + mesh_dev->stop = mesh_close; + mesh_dev->do_ioctl = libertas_do_ioctl; + memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr, + sizeof(wlanpriv->wlan_dev.netdev->dev_addr)); + +#define WLAN_WATCHDOG_TIMEOUT (5 * HZ) + + dev->tx_timeout = wlan_tx_timeout; + dev->get_stats = wlan_get_stats; + dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT; + dev->ethtool_ops = &libertas_ethtool_ops; + mesh_dev->get_stats = wlan_get_stats; + mesh_dev->ethtool_ops = &libertas_ethtool_ops; + +#ifdef WIRELESS_EXT + dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; + mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def; +#endif +#define NETIF_F_DYNALLOC 16 + dev->features |= NETIF_F_DYNALLOC; + dev->flags |= IFF_BROADCAST | IFF_MULTICAST; + dev->set_multicast_list = wlan_set_multicast_list; + + INIT_LIST_HEAD(&priv->adapter->cmdfreeq); + INIT_LIST_HEAD(&priv->adapter->cmdpendingq); + + spin_lock_init(&priv->adapter->driver_lock); + init_waitqueue_head(&priv->adapter->cmd_pending); + priv->adapter->nr_cmd_pending = 0; + + lbs_pr_debug(1, "Starting kthread...\n"); + priv->mainthread.priv = priv; + wlan_create_thread(wlan_service_main_thread, + &priv->mainthread, "wlan_main_service"); + + priv->assoc_thread = + create_singlethread_workqueue("libertas_assoc"); + INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker); + + /* + * Register the device. Fillup the private data structure with + * relevant information from the card and request for the required + * IRQ. + */ + if (libertas_sbi_register_dev(priv) < 0) { + lbs_pr_info("failed to register wlan device!\n"); + goto err_registerdev; + } + + /* init FW and HW */ + if (libertas_init_fw(priv)) { + lbs_pr_debug(1, "Firmware Init failed\n"); + goto err_registerdev; + } + + if (register_netdev(dev)) { + lbs_pr_err("Cannot register network device!\n"); + goto err_init_fw; + } + + /* Register virtual mesh interface */ + if (register_netdev(mesh_dev)) { + lbs_pr_info("Cannot register mesh virtual interface!\n"); + goto err_init_fw; + } + + lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name); + + libertas_debugfs_init_one(priv, dev); + + if (libertas_found == MAX_DEVS) + goto err_init_fw; + libertas_devs[libertas_found] = dev; + libertas_found++; +#ifdef ENABLE_PM + if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback))) + lbs_pr_alert( "failed to register PM callback\n"); +#endif + if (device_create_file(&(mesh_dev->dev), &dev_attr_libertas_mpp)) + goto err_create_file; + + LEAVE(); + return priv; + +err_create_file: + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); +err_init_fw: + libertas_sbi_unregister_dev(priv); +err_registerdev: + destroy_workqueue(priv->assoc_thread); + /* Stop the thread servicing the interrupts */ + wake_up_interruptible(&priv->mainthread.waitq); + wlan_terminate_thread(&priv->mainthread); + kfree(priv->adapter); +err_kmalloc: + free_netdev(dev); + free_netdev(mesh_dev); + wlanpriv = NULL; + + LEAVE(); + return NULL; +} + +static void wake_pending_cmdnodes(wlan_private *priv) +{ + struct cmd_ctrl_node *cmdnode; + unsigned long flags; + + spin_lock_irqsave(&priv->adapter->driver_lock, flags); + list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) { + cmdnode->cmdwaitqwoken = 1; + wake_up_interruptible(&cmdnode->cmdwait_q); + } + spin_unlock_irqrestore(&priv->adapter->driver_lock, flags); +} + + +int wlan_remove_card(void *card) +{ + wlan_private *priv = libertas_sbi_get_priv(card); + wlan_adapter *adapter; + struct net_device *dev; + struct net_device *mesh_dev; + union iwreq_data wrqu; + int i; + + ENTER(); + + if (!priv) { + LEAVE(); + return 0; + } + + adapter = priv->adapter; + + if (!adapter) { + LEAVE(); + return 0; + } + + dev = priv->wlan_dev.netdev; + mesh_dev = priv->mesh_dev; + + netif_stop_queue(mesh_dev); + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + + wake_pending_cmdnodes(priv); + + device_remove_file(&(mesh_dev->dev), &dev_attr_libertas_mpp); + unregister_netdev(mesh_dev); + unregister_netdev(dev); + + cancel_delayed_work(&priv->assoc_work); + destroy_workqueue(priv->assoc_thread); + + if (adapter->psmode == wlan802_11powermodemax_psp) { + adapter->psmode = wlan802_11powermodecam; + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN); + wrqu.ap_addr.sa_family = ARPHRD_ETHER; + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL); + +#ifdef ENABLE_PM + pm_unregister(wlan_pm_dev); +#endif + + adapter->surpriseremoved = 1; + + /* Stop the thread servicing the interrupts */ + wlan_terminate_thread(&priv->mainthread); + + libertas_debugfs_remove_one(priv); + + lbs_pr_debug(1, "Free adapter\n"); + libertas_free_adapter(priv); + + for (i = 0; i<libertas_found; i++) { + if (libertas_devs[i]==priv->wlan_dev.netdev) { + libertas_devs[i] = libertas_devs[--libertas_found]; + libertas_devs[libertas_found] = NULL ; + break ; + } + } + + lbs_pr_debug(1, "Unregister finish\n"); + + priv->wlan_dev.netdev = NULL; + priv->mesh_dev = NULL ; + free_netdev(mesh_dev); + free_netdev(dev); + wlanpriv = NULL; + + LEAVE(); + return 0; +} + +/** + * @brief This function finds the CFP in + * region_cfp_table based on region and band parameter. + * + * @param region The region code + * @param band The band + * @param cfp_no A pointer to CFP number + * @return A pointer to CFP + */ +struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no) +{ + int i, end; + + ENTER(); + + end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table); + + for (i = 0; i < end ; i++) { + lbs_pr_debug(1, "region_cfp_table[i].region=%d\n", + region_cfp_table[i].region); + if (region_cfp_table[i].region == region) { + *cfp_no = region_cfp_table[i].cfp_no_BG; + LEAVE(); + return region_cfp_table[i].cfp_BG; + } + } + + LEAVE(); + return NULL; +} + +int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band) +{ + wlan_adapter *adapter = priv->adapter; + int i = 0; + + struct chan_freq_power *cfp; + int cfp_no; + + ENTER(); + + memset(adapter->region_channel, 0, sizeof(adapter->region_channel)); + + { + cfp = libertas_get_region_cfp_table(region, band, &cfp_no); + if (cfp != NULL) { + adapter->region_channel[i].nrcfp = cfp_no; + adapter->region_channel[i].CFP = cfp; + } else { + lbs_pr_debug(1, "wrong region code %#x in band B-G\n", + region); + return -1; + } + adapter->region_channel[i].valid = 1; + adapter->region_channel[i].region = region; + adapter->region_channel[i].band = band; + i++; + } + LEAVE(); + return 0; +} + +/** + * @brief This function handles the interrupt. it will change PS + * state if applicable. it will wake up main_thread to handle + * the interrupt event as well. + * + * @param dev A pointer to net_device structure + * @return n/a + */ +void libertas_interrupt(struct net_device *dev) +{ + wlan_private *priv = dev->priv; + + ENTER(); + + lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n", + priv->adapter->intcounter); + + priv->adapter->intcounter++; + + if (priv->adapter->psstate == PS_STATE_SLEEP) { + priv->adapter->psstate = PS_STATE_AWAKE; + netif_wake_queue(dev); + } + + wake_up_interruptible(&priv->mainthread.waitq); + + LEAVE(); +} + +static int wlan_init_module(void) +{ + int ret = 0; + + ENTER(); + + if (libertas_fw_name == NULL) { + libertas_fw_name = default_fw_name; + } + + libertas_debugfs_init(); + + if (libertas_sbi_register()) { + ret = -1; + libertas_debugfs_remove(); + goto done; + } + +done: + LEAVE(); + return ret; +} + +static void wlan_cleanup_module(void) +{ + int i; + + ENTER(); + + for (i = 0; i<libertas_found; i++) { + wlan_private *priv = libertas_devs[i]->priv; + reset_device(priv); + } + + libertas_sbi_unregister(); + libertas_debugfs_remove(); + + LEAVE(); +} + +module_init(wlan_init_module); +module_exit(wlan_cleanup_module); + +MODULE_DESCRIPTION("M-WLAN Driver"); +MODULE_AUTHOR("Marvell International Ltd."); +MODULE_LICENSE("GPL"); diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h new file mode 100644 index 0000000..5d118f4 --- /dev/null +++ b/drivers/net/wireless/libertas/radiotap.h @@ -0,0 +1,57 @@ +#include <net/ieee80211_radiotap.h> + +struct tx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 rate; + u8 txpower; + u8 rts_retries; + u8 data_retries; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12]; +#endif +} __attribute__ ((packed)); + +#define TX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \ + (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \ + (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \ + 0) + +#define IEEE80211_FC_VERSION_MASK 0x0003 +#define IEEE80211_FC_TYPE_MASK 0x000c +#define IEEE80211_FC_TYPE_MGT 0x0000 +#define IEEE80211_FC_TYPE_CTL 0x0004 +#define IEEE80211_FC_TYPE_DATA 0x0008 +#define IEEE80211_FC_SUBTYPE_MASK 0x00f0 +#define IEEE80211_FC_TOFROMDS_MASK 0x0300 +#define IEEE80211_FC_TODS_MASK 0x0100 +#define IEEE80211_FC_FROMDS_MASK 0x0200 +#define IEEE80211_FC_NODS 0x0000 +#define IEEE80211_FC_TODS 0x0100 +#define IEEE80211_FC_FROMDS 0x0200 +#define IEEE80211_FC_DSTODS 0x0300 + +struct rx_radiotap_hdr { + struct ieee80211_radiotap_header hdr; + u8 flags; + u8 rate; + u16 chan_freq; + u16 chan_flags; + u8 antenna; + u8 antsignal; + u16 rx_flags; +#if 0 + u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18]; +#endif +} __attribute__ ((packed)); + +#define RX_RADIOTAP_PRESENT ( \ + (1 << IEEE80211_RADIOTAP_FLAGS) | \ + (1 << IEEE80211_RADIOTAP_RATE) | \ + (1 << IEEE80211_RADIOTAP_CHANNEL) | \ + (1 << IEEE80211_RADIOTAP_ANTENNA) | \ + (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\ + (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \ + 0) + diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c new file mode 100644 index 0000000..7e3f78f --- /dev/null +++ b/drivers/net/wireless/libertas/rx.c @@ -0,0 +1,459 @@ +/** + * This file contains the handling of RX in wlan driver. + */ +#include <linux/etherdevice.h> +#include <linux/types.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "decl.h" +#include "dev.h" +#include "wext.h" + +struct eth803hdr { + u8 dest_addr[6]; + u8 src_addr[6]; + u16 h803_len; +} __attribute__ ((packed)); + +struct rfc1042hdr { + u8 llc_dsap; + u8 llc_ssap; + u8 llc_ctrl; + u8 snap_oui[3]; + u16 snap_type; +} __attribute__ ((packed)); + +struct rxpackethdr { + struct rxpd rx_pd; + struct eth803hdr eth803_hdr; + struct rfc1042hdr rfc1042_hdr; +} __attribute__ ((packed)); + +struct rx80211packethdr { + struct rxpd rx_pd; + void *eth80211_hdr; +} __attribute__ ((packed)); + +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb); + +/** + * @brief This function computes the avgSNR . + * + * @param priv A pointer to wlan_private structure + * @return avgSNR + */ +static u8 wlan_getavgsnr(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawSNR[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function computes the AvgNF + * + * @param priv A pointer to wlan_private structure + * @return AvgNF + */ +static u8 wlan_getavgnf(wlan_private * priv) +{ + u8 i; + u16 temp = 0; + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF == 0) + return 0; + for (i = 0; i < adapter->numSNRNF; i++) + temp += adapter->rawNF[i]; + return (u8) (temp / adapter->numSNRNF); + +} + +/** + * @brief This function save the raw SNR/NF to our internel buffer + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + if (adapter->numSNRNF < adapter->data_avg_factor) + adapter->numSNRNF++; + adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr; + adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf; + adapter->nextSNRNF++; + if (adapter->nextSNRNF >= adapter->data_avg_factor) + adapter->nextSNRNF = 0; + return; +} + +/** + * @brief This function computes the RSSI in received packet. + * + * @param priv A pointer to wlan_private structure + * @param prxpd A pointer to rxpd structure of received packet + * @return n/a + */ +static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd) +{ + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf); + lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr; + adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf; + wlan_save_rawSNRNF(priv, p_rx_pd); + + adapter->rxpd_rate = p_rx_pd->rx_rate; + + adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE; + adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE; + lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n", + adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG], + adapter->NF[TYPE_RXPD][TYPE_NOAVG]); + + adapter->RSSI[TYPE_RXPD][TYPE_AVG] = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE); + + LEAVE(); +} + +int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb) +{ + lbs_pr_debug(1, "skb->data=%p\n", skb->data); + + if(IS_MESH_FRAME(skb)) + skb->dev = priv->mesh_dev; + else + skb->dev = priv->wlan_dev.netdev; + skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + + netif_rx(skb); + + return 0; +} + +/** + * @brief This function processes received packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rxpackethdr *p_rx_pkt; + struct rxpd *p_rx_pd; + + int hdrchop; + struct ethhdr *p_ethhdr; + + const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + ENTER(); + + if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH) + lbs_dbg_hex("RX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + return process_rxed_802_11_packet(priv, skb); + + p_rx_pkt = (struct rxpackethdr *) skb->data; + p_rx_pd = &p_rx_pkt->rx_pd; + if (p_rx_pd->rx_control & RxPD_MESH_FRAME) + SET_MESH_FRAME(skb); + else + UNSET_MESH_FRAME(skb); + + lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) { + lbs_pr_debug(1, "RX error: frame received with bad status\n"); + lbs_pr_alert("rxpd Not OK\n"); + priv->stats.rx_errors++; + ret = 0; + goto done; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_rx_pkt->eth803_hdr.dest_addr)); + lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_rx_pkt->eth803_hdr.src_addr)); + + if (memcmp(&p_rx_pkt->rfc1042_hdr, + rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) { + /* + * Replace the 803 header and rfc1042 header (llc/snap) with an + * EthernetII header, keep the src/dst and snap_type (ethertype) + * + * The firmware only passes up SNAP frames converting + * all RX Data from 802.11 to 802.2/LLC/SNAP frames. + * + * To create the Ethernet II, just move the src, dst address right + * before the snap_type. + */ + p_ethhdr = (struct ethhdr *) + ((u8 *) & p_rx_pkt->eth803_hdr + + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr) + - sizeof(p_rx_pkt->eth803_hdr.dest_addr) + - sizeof(p_rx_pkt->eth803_hdr.src_addr) + - sizeof(p_rx_pkt->rfc1042_hdr.snap_type)); + + memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr, + sizeof(p_ethhdr->h_source)); + memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr, + sizeof(p_ethhdr->h_dest)); + + /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header + * that was removed + */ + hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt; + } else { + lbs_dbg_hex("RX Data: LLC/SNAP", + (u8 *) & p_rx_pkt->rfc1042_hdr, + sizeof(p_rx_pkt->rfc1042_hdr)); + + /* Chop off the rxpd */ + hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt; + } + + /* Chop off the leading header bytes so the skb points to the start of + * either the reconstructed EthII frame or the 802.2/llc/snap frame + */ + skb_pull(skb, hdrchop); + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) + adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate); + + wlan_compute_rssi(priv, p_rx_pd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet" + " returns failure\n"); + ret = -1; + goto done; + } + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + return ret; +} + +/** + * @brief This function converts Tx/Rx rates from the Marvell WLAN format + * (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u8 convert_mv_rate_to_radiotap(u8 rate) +{ + switch (rate) { + case 0: /* 1 Mbps */ + return 2; + case 1: /* 2 Mbps */ + return 4; + case 2: /* 5.5 Mbps */ + return 11; + case 3: /* 11 Mbps */ + return 22; + case 4: /* 6 Mbps */ + return 12; + case 5: /* 9 Mbps */ + return 18; + case 6: /* 12 Mbps */ + return 24; + case 7: /* 18 Mbps */ + return 36; + case 8: /* 24 Mbps */ + return 48; + case 9: /* 36 Mbps */ + return 72; + case 10: /* 48 Mbps */ + return 96; + case 11: /* 54 Mbps */ + return 108; + } + lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate); + return 0; +} + +/** + * @brief This function processes a received 802.11 packet and forwards it + * to kernel/upper layer + * + * @param priv A pointer to wlan_private + * @param skb A pointer to skb which includes the received packet + * @return 0 or -1 + */ +static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + struct rx80211packethdr *p_rx_pkt; + struct rxpd *prxpd; + struct rx_radiotap_hdr radiotap_hdr; + struct rx_radiotap_hdr *pradiotap_hdr; + + ENTER(); + + p_rx_pkt = (struct rx80211packethdr *) skb->data; + prxpd = &p_rx_pkt->rx_pd; + + // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100)); + + if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) { + lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n"); + priv->stats.rx_length_errors++; + ret = 0; + goto done; + } + + /* + * Check rxpd status and update 802.3 stat, + */ + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) { + //lbs_pr_debug(1, "RX error: frame received with bad status\n"); + priv->stats.rx_errors++; + } + + lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n", + skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd)); + + /* create the exported radio header */ + switch (priv->adapter->radiomode) { + case WLAN_RADIOMODE_NONE: + /* no radio header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + + case WLAN_RADIOMODE_RADIOTAP: + /* radiotap header */ + radiotap_hdr.hdr.it_version = 0; + /* XXX must check this value for pad */ + radiotap_hdr.hdr.it_pad = 0; + radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr); + radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT; + /* unknown values */ + radiotap_hdr.flags = 0; + radiotap_hdr.chan_freq = 0; + radiotap_hdr.chan_flags = 0; + radiotap_hdr.antenna = 0; + /* known values */ + radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate); + /* XXX must check no carryout */ + radiotap_hdr.antsignal = prxpd->snr + prxpd->nf; + radiotap_hdr.rx_flags = 0; + if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) + radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS; + //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18); + + // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100)); + + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + + /* add space for the new radio header */ + if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) && + pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, + GFP_ATOMIC)) { + lbs_pr_alert( "%s: couldn't pskb_expand_head\n", + __func__); + } + + pradiotap_hdr = + (struct rx_radiotap_hdr *)skb_push(skb, + sizeof(struct + rx_radiotap_hdr)); + memcpy(pradiotap_hdr, &radiotap_hdr, + sizeof(struct rx_radiotap_hdr)); + //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100)); + break; + + default: + /* unknown header */ + lbs_pr_alert( "Unknown radiomode (%i)\n", + priv->adapter->radiomode); + /* don't export any header */ + /* chop the rxpd */ + skb_pull(skb, sizeof(struct rxpd)); + break; + } + + /* Take the data rate from the rxpd structure + * only if the rate is auto + */ + if (adapter->is_datarate_auto) { + adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate); + } + + wlan_compute_rssi(priv, prxpd); + + lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len); + + if (libertas_upload_rx_packet(priv, skb)) { + lbs_pr_debug(1, "RX error: libertas_upload_rx_packet " + "returns failure\n"); + ret = -1; + goto done; + } + + priv->stats.rx_bytes += skb->len; + priv->stats.rx_packets++; + + ret = 0; +done: + LEAVE(); + + skb->protocol = __constant_htons(0x0019); /* ETH_P_80211_RAW */ + + return (ret); +} diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h new file mode 100644 index 0000000..59d3a59c --- /dev/null +++ b/drivers/net/wireless/libertas/sbi.h @@ -0,0 +1,40 @@ +/** + * This file contains IF layer definitions. + */ + +#ifndef _SBI_H_ +#define _SBI_H_ + +#include <linux/interrupt.h> + +#include "defs.h" + +/** INT status Bit Definition*/ +#define his_cmddnldrdy 0x01 +#define his_cardevent 0x02 +#define his_cmdupldrdy 0x04 + +#ifndef DEV_NAME_LEN +#define DEV_NAME_LEN 32 +#endif + +#define SBI_EVENT_CAUSE_SHIFT 3 + +/* Probe and Check if the card is present*/ +int libertas_sbi_register_dev(wlan_private * priv); +int libertas_sbi_unregister_dev(wlan_private *); +int libertas_sbi_get_int_status(wlan_private * priv, u8 *); +int libertas_sbi_register(void); +void libertas_sbi_unregister(void); +int libertas_sbi_prog_firmware(wlan_private *); + +int libertas_sbi_read_event_cause(wlan_private *); +int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb); +wlan_private *libertas_sbi_get_priv(void *card); + +#ifdef ENABLE_PM +int libertas_sbi_suspend(wlan_private *); +int libertas_sbi_resume(wlan_private *); +#endif + +#endif /* _SBI_H */ diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c new file mode 100644 index 0000000..e187062 --- /dev/null +++ b/drivers/net/wireless/libertas/scan.c @@ -0,0 +1,2044 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Functions implementing wlan scan IOCTL and firmware command APIs + * + * IOCTL handlers as well as command preperation and response routines + * for sending scan commands to the firmware. + */ +#include <linux/ctype.h> +#include <linux/if.h> +#include <linux/netdevice.h> +#include <linux/wireless.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "decl.h" +#include "dev.h" +#include "scan.h" + +//! Approximate amount of data needed to pass a scan result back to iwlist +#define MAX_SCAN_CELL_SIZE (IW_EV_ADDR_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_UINT_LEN \ + + IW_EV_FREQ_LEN \ + + IW_EV_QUAL_LEN \ + + IW_ESSID_MAX_SIZE \ + + IW_EV_PARAM_LEN \ + + 40) /* 40 for WPAIE */ + +//! Memory needed to store a max sized channel List TLV for a firmware scan +#define CHAN_TLV_MAX_SIZE (sizeof(struct mrvlietypesheader) \ + + (MRVDRV_MAX_CHANNELS_PER_SCAN \ + * sizeof(struct chanscanparamset))) + +//! Memory needed to store a max number/size SSID TLV for a firmware scan +#define SSID_TLV_MAX_SIZE (1 * sizeof(struct mrvlietypes_ssidparamset)) + +//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max +#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config) \ + + sizeof(struct mrvlietypes_numprobes) \ + + CHAN_TLV_MAX_SIZE \ + + SSID_TLV_MAX_SIZE) + +//! The maximum number of channels the firmware can scan per command +#define MRVDRV_MAX_CHANNELS_PER_SCAN 14 + +/** + * @brief Number of channels to scan per firmware scan command issuance. + * + * Number restricted to prevent hitting the limit on the amount of scan data + * returned in a single firmware scan command. + */ +#define MRVDRV_CHANNELS_PER_SCAN_CMD 4 + +//! Scan time specified in the channel TLV for each channel for passive scans +#define MRVDRV_PASSIVE_SCAN_CHAN_TIME 100 + +//! Scan time specified in the channel TLV for each channel for active scans +#define MRVDRV_ACTIVE_SCAN_CHAN_TIME 100 + +//! Macro to enable/disable SSID checking before storing a scan table +#ifdef DISCARD_BAD_SSID +#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid) +#else +#define CHECK_SSID_IS_VALID(x) 1 +#endif + +/** + * @brief Check if a scanned network compatible with the driver settings + * + * WEP WPA WPA2 ad-hoc encrypt Network + * enabled enabled enabled AES mode privacy WPA WPA2 Compatible + * 0 0 0 0 NONE 0 0 0 yes No security + * 1 0 0 0 NONE 1 0 0 yes Static WEP + * 0 1 0 0 x 1x 1 x yes WPA + * 0 0 1 0 x 1x x 1 yes WPA2 + * 0 0 0 1 NONE 1 0 0 yes Ad-hoc AES + * 0 0 0 0 !=NONE 1 0 0 yes Dynamic WEP + * + * + * @param adapter A pointer to wlan_adapter + * @param index Index in scantable to check against current driver settings + * @param mode Network mode: Infrastructure or IBSS + * + * @return Index in scantable, or error code if negative + */ +static int is_network_compatible(wlan_adapter * adapter, int index, int mode) +{ + ENTER(); + + if (adapter->scantable[index].inframode == mode) { + if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].wpa_supplicant.wpa_ie[0] != + WPA_IE + && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] != + WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE + && !adapter->scantable[index].privacy) { + /* no security */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && adapter->scantable[index].privacy) { + /* static WEP enabled */ + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + == WPA_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + == WPA2_IE) + /* privacy bit may NOT be set in some APs like LinkSys WRT54G + && adapter->scantable[index].privacy */ + ) { + /* WPA2 enabled */ + lbs_pr_debug(1, + "is_network_compatible() WPA2: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x " + "privacy=%#x\n", index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled + && (adapter->scantable[index].wpa_supplicant. + wpa_ie[0] + != WPA_IE) + && (adapter->scantable[index].wpa2_supplicant. + wpa_ie[0] + != WPA2_IE) + && adapter->secinfo.Encryptionmode != CIPHER_NONE + && adapter->scantable[index].privacy) { + /* dynamic WEP enabled */ + lbs_pr_debug(1, + "is_network_compatible() dynamic WEP: index=%d " + "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant. + wpa_ie[0], + adapter->scantable[index].wpa2_supplicant. + wpa_ie[0], adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return index; + } + + /* security doesn't match */ + lbs_pr_debug(1, + "is_network_compatible() FAILED: index=%d wpa_ie=%#x " + "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n", + index, + adapter->scantable[index].wpa_supplicant.wpa_ie[0], + adapter->scantable[index].wpa2_supplicant.wpa_ie[0], + (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) ? "e" : "d", + (adapter->secinfo.WPAenabled) ? "e" : "d", + (adapter->secinfo.WPA2enabled) ? "e" : "d", + adapter->secinfo.Encryptionmode, + adapter->scantable[index].privacy); + LEAVE(); + return -ECONNREFUSED; + } + + /* mode doesn't match */ + LEAVE(); + return -ENETUNREACH; +} + +/** + * @brief This function validates a SSID as being able to be printed + * + * @param pssid SSID structure to validate + * + * @return TRUE or FALSE + */ +static u8 ssid_valid(struct WLAN_802_11_SSID *pssid) +{ + int ssididx; + + for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) { + if (!isprint(pssid->ssid[ssididx])) { + return 0; + } + } + + return 1; +} + +/** + * @brief Post process the scan table after a new scan command has completed + * + * Inspect each entry of the scan table and try to find an entry that + * matches our current associated/joined network from the scan. If + * one is found, update the stored copy of the bssdescriptor for our + * current network. + * + * Debug dump the current scan table contents if compiled accordingly. + * + * @param priv A pointer to wlan_private structure + * + * @return void + */ +static void wlan_scan_process_results(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + int foundcurrent; + int i; + + foundcurrent = 0; + + if (adapter->connect_status == libertas_connected) { + /* try to find the current BSSID in the new scan list */ + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, + &adapter->curbssparams.ssid) && + !memcmp(adapter->curbssparams.bssid, + adapter->scantable[i].macaddress, + ETH_ALEN)) { + foundcurrent = 1; + } + } + + if (foundcurrent) { + /* Make a copy of current BSSID descriptor */ + memcpy(&adapter->curbssparams.bssdescriptor, + &adapter->scantable[i], + sizeof(adapter->curbssparams.bssdescriptor)); + } + } + + for (i = 0; i < adapter->numinscantable; i++) { + lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, " + "RSSI[%03d], SSID[%s]\n", + i, + adapter->scantable[i].macaddress[0], + adapter->scantable[i].macaddress[1], + adapter->scantable[i].macaddress[2], + adapter->scantable[i].macaddress[3], + adapter->scantable[i].macaddress[4], + adapter->scantable[i].macaddress[5], + (s32) adapter->scantable[i].rssi, + adapter->scantable[i].ssid.ssid); + } +} + +/** + * @brief Create a channel list for the driver to scan based on region info + * + * Use the driver region/band information to construct a comprehensive list + * of channels to scan. This routine is used for any scan that is not + * provided a specific channel list to scan. + * + * @param priv A pointer to wlan_private structure + * @param scanchanlist Output parameter: resulting channel list to scan + * @param filteredscan Flag indicating whether or not a BSSID or SSID filter + * is being sent in the command to firmware. Used to + * increase the number of channels sent in a scan + * command and to disable the firmware channel scan + * filter. + * + * @return void + */ +static void wlan_scan_create_channel_list(wlan_private * priv, + struct chanscanparamset * scanchanlist, + u8 filteredscan) +{ + + wlan_adapter *adapter = priv->adapter; + struct region_channel *scanregion; + struct chan_freq_power *cfp; + int rgnidx; + int chanidx; + int nextchan; + u8 scantype; + + chanidx = 0; + + /* Set the default scan type to the user specified type, will later + * be changed to passive on a per channel basis if restricted by + * regulatory requirements (11d or 11h) + */ + scantype = adapter->scantype; + + for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) { + if (priv->adapter->enable11d && + adapter->connect_status != libertas_connected) { + /* Scan all the supported chan for the first scan */ + if (!adapter->universal_channel[rgnidx].valid) + continue; + scanregion = &adapter->universal_channel[rgnidx]; + + /* clear the parsed_region_chan for the first scan */ + memset(&adapter->parsed_region_chan, 0x00, + sizeof(adapter->parsed_region_chan)); + } else { + if (!adapter->region_channel[rgnidx].valid) + continue; + scanregion = &adapter->region_channel[rgnidx]; + } + + for (nextchan = 0; + nextchan < scanregion->nrcfp; nextchan++, chanidx++) { + + cfp = scanregion->CFP + nextchan; + + if (priv->adapter->enable11d) { + scantype = + libertas_get_scan_type_11d(cfp->channel, + &adapter-> + parsed_region_chan); + } + + switch (scanregion->band) { + case BAND_B: + case BAND_G: + default: + scanchanlist[chanidx].radiotype = + cmd_scan_radio_type_bg; + break; + } + + if (scantype == cmd_scan_type_passive) { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_PASSIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 1; + } else { + scanchanlist[chanidx].maxscantime = + cpu_to_le16 + (MRVDRV_ACTIVE_SCAN_CHAN_TIME); + scanchanlist[chanidx].chanscanmode.passivescan = + 0; + } + + scanchanlist[chanidx].channumber = cfp->channel; + + if (filteredscan) { + scanchanlist[chanidx].chanscanmode. + disablechanfilt = 1; + } + } + } +} + +/** + * @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds + * + * Application layer or other functions can invoke wlan_scan_networks + * with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct. + * This structure is used as the basis of one or many wlan_scan_cmd_config + * commands that are sent to the command processing module and sent to + * firmware. + * + * Create a wlan_scan_cmd_config based on the following user supplied + * parameters (if present): + * - SSID filter + * - BSSID filter + * - Number of Probes to be sent + * - channel list + * + * If the SSID or BSSID filter is not present, disable/clear the filter. + * If the number of probes is not set, use the adapter default setting + * Qualify the channel + * + * @param priv A pointer to wlan_private structure + * @param puserscanin NULL or pointer to scan configuration parameters + * @param ppchantlvout Output parameter: Pointer to the start of the + * channel TLV portion of the output scan config + * @param pscanchanlist Output parameter: Pointer to the resulting channel + * list to scan + * @param pmaxchanperscan Output parameter: Number of channels to scan for + * each issuance of the firmware scan command + * @param pfilteredscan Output parameter: Flag indicating whether or not + * a BSSID or SSID filter is being sent in the + * command to firmware. Used to increase the number + * of channels sent in a scan command and to + * disable the firmware channel scan filter. + * @param pscancurrentonly Output parameter: Flag indicating whether or not + * we are only scanning our current active channel + * + * @return resulting scan configuration + */ +static struct wlan_scan_cmd_config * +wlan_scan_setup_scan_config(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin, + struct mrvlietypes_chanlistparamset ** ppchantlvout, + struct chanscanparamset * pscanchanlist, + int *pmaxchanperscan, + u8 * pfilteredscan, + u8 * pscancurrentonly) +{ + wlan_adapter *adapter = priv->adapter; + const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + struct mrvlietypes_numprobes *pnumprobestlv; + struct mrvlietypes_ssidparamset *pssidtlv; + struct wlan_scan_cmd_config * pscancfgout = NULL; + u8 *ptlvpos; + u16 numprobes; + u16 ssidlen; + int chanidx; + int scantype; + int scandur; + int channel; + int radiotype; + + pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL); + if (pscancfgout == NULL) + goto out; + + /* The tlvbufferlen is calculated for each scan command. The TLVs added + * in this routine will be preserved since the routine that sends + * the command will append channelTLVs at *ppchantlvout. The difference + * between the *ppchantlvout and the tlvbuffer start will be used + * to calculate the size of anything we add in this routine. + */ + pscancfgout->tlvbufferlen = 0; + + /* Running tlv pointer. Assigned to ppchantlvout at end of function + * so later routines know where channels can be added to the command buf + */ + ptlvpos = pscancfgout->tlvbuffer; + + /* + * Set the initial scan paramters for progressive scanning. If a specific + * BSSID or SSID is used, the number of channels in the scan command + * will be increased to the absolute maximum + */ + *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD; + + /* Initialize the scan as un-filtered by firmware, set to TRUE below if + * a SSID or BSSID filter is sent in the command + */ + *pfilteredscan = 0; + + /* Initialize the scan as not being only on the current channel. If + * the channel list is customized, only contains one channel, and + * is the active channel, this is set true and data flow is not halted. + */ + *pscancurrentonly = 0; + + if (puserscanin) { + + /* Set the bss type scan filter, use adapter setting if unset */ + pscancfgout->bsstype = + (puserscanin->bsstype ? puserscanin->bsstype : adapter-> + scanmode); + + /* Set the number of probes to send, use adapter setting if unset */ + numprobes = (puserscanin->numprobes ? puserscanin->numprobes : + adapter->scanprobes); + + /* + * Set the BSSID filter to the incoming configuration, + * if non-zero. If not set, it will remain disabled (all zeros). + */ + memcpy(pscancfgout->specificBSSID, + puserscanin->specificBSSID, + sizeof(pscancfgout->specificBSSID)); + + ssidlen = strlen(puserscanin->specificSSID); + + if (ssidlen) { + pssidtlv = + (struct mrvlietypes_ssidparamset *) pscancfgout-> + tlvbuffer; + pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID); + pssidtlv->header.len = cpu_to_le16(ssidlen); + memcpy(pssidtlv->ssid, puserscanin->specificSSID, + ssidlen); + ptlvpos += sizeof(pssidtlv->header) + ssidlen; + } + + /* + * The default number of channels sent in the command is low to + * ensure the response buffer from the firmware does not truncate + * scan results. That is not an issue with an SSID or BSSID + * filter applied to the scan results in the firmware. + */ + if (ssidlen || (memcmp(pscancfgout->specificBSSID, + &zeromac, sizeof(zeromac)) != 0)) { + *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN; + *pfilteredscan = 1; + } + } else { + pscancfgout->bsstype = adapter->scanmode; + numprobes = adapter->scanprobes; + } + + /* If the input config or adapter has the number of Probes set, add tlv */ + if (numprobes) { + pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos; + pnumprobestlv->header.type = + cpu_to_le16(TLV_TYPE_NUMPROBES); + pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes); + pnumprobestlv->numprobes = cpu_to_le16(numprobes); + + ptlvpos += + sizeof(pnumprobestlv->header) + pnumprobestlv->header.len; + + pnumprobestlv->header.len = + cpu_to_le16(pnumprobestlv->header.len); + } + + /* + * Set the output for the channel TLV to the address in the tlv buffer + * past any TLVs that were added in this fuction (SSID, numprobes). + * channel TLVs will be added past this for each scan command, preserving + * the TLVs that were previously added. + */ + *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos; + + if (puserscanin && puserscanin->chanlist[0].channumber) { + + lbs_pr_debug(1, "Scan: Using supplied channel list\n"); + + for (chanidx = 0; + chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX + && puserscanin->chanlist[chanidx].channumber; chanidx++) { + + channel = puserscanin->chanlist[chanidx].channumber; + (pscanchanlist + chanidx)->channumber = channel; + + radiotype = puserscanin->chanlist[chanidx].radiotype; + (pscanchanlist + chanidx)->radiotype = radiotype; + + scantype = puserscanin->chanlist[chanidx].scantype; + + if (scantype == cmd_scan_type_passive) { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 1; + } else { + (pscanchanlist + + chanidx)->chanscanmode.passivescan = 0; + } + + if (puserscanin->chanlist[chanidx].scantime) { + scandur = + puserscanin->chanlist[chanidx].scantime; + } else { + if (scantype == cmd_scan_type_passive) { + scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME; + } else { + scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME; + } + } + + (pscanchanlist + chanidx)->minscantime = + cpu_to_le16(scandur); + (pscanchanlist + chanidx)->maxscantime = + cpu_to_le16(scandur); + } + + /* Check if we are only scanning the current channel */ + if ((chanidx == 1) && (puserscanin->chanlist[0].channumber + == + priv->adapter->curbssparams.channel)) { + *pscancurrentonly = 1; + lbs_pr_debug(1, "Scan: Scanning current channel only"); + } + + } else { + lbs_pr_debug(1, "Scan: Creating full region channel list\n"); + wlan_scan_create_channel_list(priv, pscanchanlist, + *pfilteredscan); + } + +out: + return pscancfgout; +} + +/** + * @brief Construct and send multiple scan config commands to the firmware + * + * Previous routines have created a wlan_scan_cmd_config with any requested + * TLVs. This function splits the channel TLV into maxchanperscan lists + * and sends the portion of the channel TLV along with the other TLVs + * to the wlan_cmd routines for execution in the firmware. + * + * @param priv A pointer to wlan_private structure + * @param maxchanperscan Maximum number channels to be included in each + * scan command sent to firmware + * @param filteredscan Flag indicating whether or not a BSSID or SSID + * filter is being used for the firmware command + * scan command sent to firmware + * @param pscancfgout Scan configuration used for this scan. + * @param pchantlvout Pointer in the pscancfgout where the channel TLV + * should start. This is past any other TLVs that + * must be sent down in each firmware command. + * @param pscanchanlist List of channels to scan in maxchanperscan segments + * + * @return 0 or error return otherwise + */ +static int wlan_scan_channel_list(wlan_private * priv, + int maxchanperscan, + u8 filteredscan, + struct wlan_scan_cmd_config * pscancfgout, + struct mrvlietypes_chanlistparamset * pchantlvout, + struct chanscanparamset * pscanchanlist) +{ + struct chanscanparamset *ptmpchan; + struct chanscanparamset *pstartchan; + u8 scanband; + int doneearly; + int tlvidx; + int ret = 0; + + ENTER(); + + if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) { + lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n", + pscancfgout, pchantlvout, pscanchanlist); + return -1; + } + + pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST); + + /* Set the temp channel struct pointer to the start of the desired list */ + ptmpchan = pscanchanlist; + + /* Loop through the desired channel list, sending a new firmware scan + * commands for each maxchanperscan channels (or for 1,6,11 individually + * if configured accordingly) + */ + while (ptmpchan->channumber) { + + tlvidx = 0; + pchantlvout->header.len = 0; + scanband = ptmpchan->radiotype; + pstartchan = ptmpchan; + doneearly = 0; + + /* Construct the channel TLV for the scan command. Continue to + * insert channel TLVs until: + * - the tlvidx hits the maximum configured per scan command + * - the next channel to insert is 0 (end of desired channel list) + * - doneearly is set (controlling individual scanning of 1,6,11) + */ + while (tlvidx < maxchanperscan && ptmpchan->channumber + && !doneearly) { + + lbs_pr_debug(1, + "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n", + ptmpchan->channumber, ptmpchan->radiotype, + ptmpchan->chanscanmode.passivescan, + ptmpchan->chanscanmode.disablechanfilt, + ptmpchan->maxscantime); + + /* Copy the current channel TLV to the command being prepared */ + memcpy(pchantlvout->chanscanparam + tlvidx, + ptmpchan, sizeof(pchantlvout->chanscanparam)); + + /* Increment the TLV header length by the size appended */ + pchantlvout->header.len += + sizeof(pchantlvout->chanscanparam); + + /* + * The tlv buffer length is set to the number of bytes of the + * between the channel tlv pointer and the start of the + * tlv buffer. This compensates for any TLVs that were appended + * before the channel list. + */ + pscancfgout->tlvbufferlen = ((u8 *) pchantlvout + - pscancfgout->tlvbuffer); + + /* Add the size of the channel tlv header and the data length */ + pscancfgout->tlvbufferlen += + (sizeof(pchantlvout->header) + + pchantlvout->header.len); + + /* Increment the index to the channel tlv we are constructing */ + tlvidx++; + + doneearly = 0; + + /* Stop the loop if the *current* channel is in the 1,6,11 set + * and we are not filtering on a BSSID or SSID. + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + + /* Increment the tmp pointer to the next channel to be scanned */ + ptmpchan++; + + /* Stop the loop if the *next* channel is in the 1,6,11 set. + * This will cause it to be the only channel scanned on the next + * interation + */ + if (!filteredscan && (ptmpchan->channumber == 1 + || ptmpchan->channumber == 6 + || ptmpchan->channumber == 11)) { + doneearly = 1; + } + } + + /* Send the scan command to the firmware with the specified cfg */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0, + 0, 0, pscancfgout); + } + + LEAVE(); + return ret; +} + +/** + * @brief Internal function used to start a scan based on an input config + * + * Use the input user scan configuration information when provided in + * order to send the appropriate scan commands to firmware to populate or + * update the internal driver scan table + * + * @param priv A pointer to wlan_private structure + * @param puserscanin Pointer to the input configuration for the requested + * scan. + * + * @return 0 or < 0 if error + */ +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin) +{ + wlan_adapter *adapter = priv->adapter; + struct mrvlietypes_chanlistparamset *pchantlvout; + struct chanscanparamset * scan_chan_list = NULL; + struct wlan_scan_cmd_config * scan_cfg = NULL; + u8 keeppreviousscan; + u8 filteredscan; + u8 scancurrentchanonly; + int maxchanperscan; + int ret; + + ENTER(); + + scan_chan_list = kzalloc(sizeof(struct chanscanparamset) * + WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL); + if (scan_chan_list == NULL) { + ret = -ENOMEM; + goto out; + } + + scan_cfg = wlan_scan_setup_scan_config(priv, + puserscanin, + &pchantlvout, + scan_chan_list, + &maxchanperscan, + &filteredscan, + &scancurrentchanonly); + if (scan_cfg == NULL) { + ret = -ENOMEM; + goto out; + } + + keeppreviousscan = 0; + + if (puserscanin) { + keeppreviousscan = puserscanin->keeppreviousscan; + } + + if (!keeppreviousscan) { + memset(adapter->scantable, 0x00, + sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST); + adapter->numinscantable = 0; + } + + /* Keep the data path active if we are only scanning our current channel */ + if (!scancurrentchanonly) { + netif_stop_queue(priv->wlan_dev.netdev); + netif_carrier_off(priv->wlan_dev.netdev); + } + + ret = wlan_scan_channel_list(priv, + maxchanperscan, + filteredscan, + scan_cfg, + pchantlvout, + scan_chan_list); + + /* Process the resulting scan table: + * - Remove any bad ssids + * - Update our current BSS information from scan data + */ + wlan_scan_process_results(priv); + + if (priv->adapter->connect_status == libertas_connected) { + netif_carrier_on(priv->wlan_dev.netdev); + netif_wake_queue(priv->wlan_dev.netdev); + } + +out: + if (scan_cfg) + kfree(scan_cfg); + + if (scan_chan_list) + kfree(scan_chan_list); + + LEAVE(); + return ret; +} + +/** + * @brief Inspect the scan response buffer for pointers to expected TLVs + * + * TLVs can be included at the end of the scan response BSS information. + * Parse the data in the buffer for pointers to TLVs that can potentially + * be passed back in the response + * + * @param ptlv Pointer to the start of the TLV buffer to parse + * @param tlvbufsize size of the TLV buffer + * @param ptsftlv Output parameter: Pointer to the TSF TLV if found + * + * @return void + */ +static +void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv, + int tlvbufsize, + struct mrvlietypes_tsftimestamp ** ptsftlv) +{ + struct mrvlietypes_data *pcurrenttlv; + int tlvbufleft; + u16 tlvtype; + u16 tlvlen; + + pcurrenttlv = ptlv; + tlvbufleft = tlvbufsize; + *ptsftlv = NULL; + + lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize); + lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize); + + while (tlvbufleft >= sizeof(struct mrvlietypesheader)) { + tlvtype = le16_to_cpu(pcurrenttlv->header.type); + tlvlen = le16_to_cpu(pcurrenttlv->header.len); + + switch (tlvtype) { + case TLV_TYPE_TSFTIMESTAMP: + *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv; + break; + + default: + lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n", + tlvtype); + /* Give up, this seems corrupted */ + return; + } /* switch */ + + tlvbufleft -= (sizeof(ptlv->header) + tlvlen); + pcurrenttlv = + (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen); + } /* while */ +} + +/** + * @brief Interpret a BSS scan response returned from the firmware + * + * Parse the various fixed fields and IEs passed back for a a BSS probe + * response or beacon from the scan command. Record information as needed + * in the scan table struct bss_descriptor for that entry. + * + * @param pBSSIDEntry Output parameter: Pointer to the BSS Entry + * + * @return 0 or -1 + */ +static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry, + u8 ** pbeaconinfo, int *bytesleft) +{ + enum ieeetypes_elementid elemID; + struct ieeetypes_fhparamset *pFH; + struct ieeetypes_dsparamset *pDS; + struct ieeetypes_cfparamset *pCF; + struct ieeetypes_ibssparamset *pibss; + struct ieeetypes_capinfo *pcap; + struct WLAN_802_11_FIXED_IEs fixedie; + u8 *pcurrentptr; + u8 *pRate; + u8 elemlen; + u8 bytestocopy; + u8 ratesize; + u16 beaconsize; + u8 founddatarateie; + int bytesleftforcurrentbeacon; + + struct WPA_SUPPLICANT *pwpa_supplicant; + struct WPA_SUPPLICANT *pwpa2_supplicant; + struct IE_WPA *pIe; + const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 }; + + struct ieeetypes_countryinfoset *pcountryinfo; + + ENTER(); + + founddatarateie = 0; + ratesize = 0; + beaconsize = 0; + + if (*bytesleft >= sizeof(beaconsize)) { + /* Extract & convert beacon size from the command buffer */ + memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize)); + beaconsize = le16_to_cpu(beaconsize); + *bytesleft -= sizeof(beaconsize); + *pbeaconinfo += sizeof(beaconsize); + } + + if (beaconsize == 0 || beaconsize > *bytesleft) { + + *pbeaconinfo += *bytesleft; + *bytesleft = 0; + + return -1; + } + + /* Initialize the current working beacon pointer for this BSS iteration */ + pcurrentptr = *pbeaconinfo; + + /* Advance the return beacon pointer past the current beacon */ + *pbeaconinfo += beaconsize; + *bytesleft -= beaconsize; + + bytesleftforcurrentbeacon = beaconsize; + + pwpa_supplicant = &pBSSEntry->wpa_supplicant; + pwpa2_supplicant = &pBSSEntry->wpa2_supplicant; + + memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN); + lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n", + pBSSEntry->macaddress[0], pBSSEntry->macaddress[1], + pBSSEntry->macaddress[2], pBSSEntry->macaddress[3], + pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]); + + pcurrentptr += ETH_ALEN; + bytesleftforcurrentbeacon -= ETH_ALEN; + + if (bytesleftforcurrentbeacon < 12) { + lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n"); + return -1; + } + + /* + * next 4 fields are RSSI, time stamp, beacon interval, + * and capability information + */ + + /* RSSI is 1 byte long */ + pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr)); + lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr); + pcurrentptr += 1; + bytesleftforcurrentbeacon -= 1; + + /* time stamp is 8 bytes long */ + memcpy(fixedie.timestamp, pcurrentptr, 8); + memcpy(pBSSEntry->timestamp, pcurrentptr, 8); + pcurrentptr += 8; + bytesleftforcurrentbeacon -= 8; + + /* beacon interval is 2 bytes long */ + memcpy(&fixedie.beaconinterval, pcurrentptr, 2); + pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* capability information is 2 bytes long */ + memcpy(&fixedie.capabilities, pcurrentptr, 2); + lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n", + fixedie.capabilities); + fixedie.capabilities = le16_to_cpu(fixedie.capabilities); + pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities; + memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo)); + pcurrentptr += 2; + bytesleftforcurrentbeacon -= 2; + + /* rest of the current buffer are IE's */ + lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n", + bytesleftforcurrentbeacon); + + lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr, + bytesleftforcurrentbeacon); + + if (pcap->privacy) { + lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n"); + pBSSEntry->privacy = wlan802_11privfilter8021xWEP; + } else { + pBSSEntry->privacy = wlan802_11privfilteracceptall; + } + + if (pcap->ibss == 1) { + pBSSEntry->inframode = wlan802_11ibss; + } else { + pBSSEntry->inframode = wlan802_11infrastructure; + } + + /* process variable IE */ + while (bytesleftforcurrentbeacon >= 2) { + elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr)); + elemlen = *((u8 *) pcurrentptr + 1); + + if (bytesleftforcurrentbeacon < elemlen) { + lbs_pr_debug(1, "InterpretIE: error in processing IE, " + "bytes left < IE length\n"); + bytesleftforcurrentbeacon = 0; + continue; + } + + switch (elemID) { + + case SSID: + pBSSEntry->ssid.ssidlength = elemlen; + memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2), + elemlen); + lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid); + break; + + case SUPPORTED_RATES: + memcpy(pBSSEntry->datarates, (pcurrentptr + 2), + elemlen); + memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2), + elemlen); + ratesize = elemlen; + founddatarateie = 1; + break; + + case EXTRA_IE: + lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n"); + pBSSEntry->extra_ie = 1; + break; + + case FH_PARAM_SET: + pFH = (struct ieeetypes_fhparamset *) pcurrentptr; + memmove(&pBSSEntry->phyparamset.fhparamset, pFH, + sizeof(struct ieeetypes_fhparamset)); + pBSSEntry->phyparamset.fhparamset.dwelltime + = + le16_to_cpu(pBSSEntry->phyparamset.fhparamset. + dwelltime); + break; + + case DS_PARAM_SET: + pDS = (struct ieeetypes_dsparamset *) pcurrentptr; + + pBSSEntry->channel = pDS->currentchan; + + memcpy(&pBSSEntry->phyparamset.dsparamset, pDS, + sizeof(struct ieeetypes_dsparamset)); + break; + + case CF_PARAM_SET: + pCF = (struct ieeetypes_cfparamset *) pcurrentptr; + + memcpy(&pBSSEntry->ssparamset.cfparamset, pCF, + sizeof(struct ieeetypes_cfparamset)); + break; + + case IBSS_PARAM_SET: + pibss = (struct ieeetypes_ibssparamset *) pcurrentptr; + pBSSEntry->atimwindow = + le32_to_cpu(pibss->atimwindow); + + memmove(&pBSSEntry->ssparamset.ibssparamset, pibss, + sizeof(struct ieeetypes_ibssparamset)); + + pBSSEntry->ssparamset.ibssparamset.atimwindow + = + le16_to_cpu(pBSSEntry->ssparamset.ibssparamset. + atimwindow); + break; + + /* Handle Country Info IE */ + case COUNTRY_INFO: + pcountryinfo = + (struct ieeetypes_countryinfoset *) pcurrentptr; + + if (pcountryinfo->len < + sizeof(pcountryinfo->countrycode) + || pcountryinfo->len > 254) { + lbs_pr_debug(1, "InterpretIE: 11D- Err " + "CountryInfo len =%d min=%d max=254\n", + pcountryinfo->len, + sizeof(pcountryinfo->countrycode)); + LEAVE(); + return -1; + } + + memcpy(&pBSSEntry->countryinfo, + pcountryinfo, pcountryinfo->len + 2); + lbs_dbg_hex("InterpretIE: 11D- CountryInfo:", + (u8 *) pcountryinfo, + (u32) (pcountryinfo->len + 2)); + break; + + case EXTENDED_SUPPORTED_RATES: + /* + * only process extended supported rate + * if data rate is already found. + * data rate IE should come before + * extended supported rate IE + */ + if (founddatarateie) { + if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) { + bytestocopy = + (WLAN_SUPPORTED_RATES - ratesize); + } else { + bytestocopy = elemlen; + } + + pRate = (u8 *) pBSSEntry->datarates; + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + + pRate = (u8 *) pBSSEntry->libertas_supported_rates; + + pRate += ratesize; + memmove(pRate, (pcurrentptr + 2), bytestocopy); + } + break; + + case VENDOR_SPECIFIC_221: +#define IE_ID_LEN_FIELDS_BYTES 2 + pIe = (struct IE_WPA *)pcurrentptr; + + if (!memcmp(pIe->oui, oui01, sizeof(oui01))) { + pwpa_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa_supplicant->wpa_ie)); + memcpy(pwpa_supplicant->wpa_ie, + pcurrentptr, + pwpa_supplicant->wpa_ie_len); + lbs_dbg_hex("InterpretIE: Resp WPA_IE", + pwpa_supplicant->wpa_ie, elemlen); + } + break; + case WPA2_IE: + pIe = (struct IE_WPA *)pcurrentptr; + pwpa2_supplicant->wpa_ie_len + = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES, + sizeof(pwpa2_supplicant->wpa_ie)); + memcpy(pwpa2_supplicant->wpa_ie, + pcurrentptr, pwpa2_supplicant->wpa_ie_len); + + lbs_dbg_hex("InterpretIE: Resp WPA2_IE", + pwpa2_supplicant->wpa_ie, elemlen); + break; + case TIM: + break; + + case CHALLENGE_TEXT: + break; + } + + pcurrentptr += elemlen + 2; + + /* need to account for IE ID and IE len */ + bytesleftforcurrentbeacon -= (elemlen + 2); + + } /* while (bytesleftforcurrentbeacon > 2) */ + + return 0; +} + +/** + * @brief Compare two SSIDs + * + * @param ssid1 A pointer to ssid to compare + * @param ssid2 A pointer to ssid to compare + * + * @return 0--ssid is same, otherwise is different + */ +int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2) +{ + if (!ssid1 || !ssid2) + return -1; + + if (ssid1->ssidlength != ssid2->ssidlength) + return -1; + + return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength); +} + +/** + * @brief This function finds a specific compatible BSSID in the scan list + * + * @param adapter A pointer to wlan_adapter + * @param bssid BSSID to find in the scan list + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list, or error return code (< 0) + */ +int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode) +{ + int ret = -ENETUNREACH; + int i; + + if (!bssid) + return -EFAULT; + + lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n", + adapter->numinscantable); + + /* Look through the scan table for a compatible match. The ret return + * variable will be equal to the index in the scan table (greater + * than zero) if the network is compatible. The loop will continue + * past a matched bssid that is not compatible in case there is an + * AP with multiple SSIDs assigned to the same BSSID + */ + for (i = 0; ret < 0 && i < adapter->numinscantable; i++) { + if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + ret = is_network_compatible(adapter, i, mode); + break; + default: + ret = i; + break; + } + } + } + + return ret; +} + +/** + * @brief This function finds ssid in ssid list. + * + * @param adapter A pointer to wlan_adapter + * @param ssid SSID to find in the list + * @param bssid BSSID to qualify the SSID selection (if provided) + * @param mode Network mode: Infrastructure or IBSS + * + * @return index in BSSID list + */ +int libertas_find_SSID_in_list(wlan_adapter * adapter, + struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode) +{ + int net = -ENETUNREACH; + u8 bestrssi = 0; + int i; + int j; + + lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) && + (!bssid || + !memcmp(adapter->scantable[i]. + macaddress, bssid, ETH_ALEN))) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + j = is_network_compatible(adapter, i, mode); + + if (j >= 0) { + if (bssid) { + return i; + } + + if (SCAN_RSSI + (adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter-> + scantable[i]. + rssi); + net = i; + } + } else { + if (net == -ENETUNREACH) { + net = j; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) + > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + net = i; + } + break; + } + } + } + + return net; +} + +/** + * @brief This function finds the best SSID in the Scan List + * + * Search the scan table for the best SSID that also matches the current + * adapter network preference (infrastructure or adhoc) + * + * @param adapter A pointer to wlan_adapter + * + * @return index in BSSID list + */ +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode) +{ + int bestnet = -ENETUNREACH; + u8 bestrssi = 0; + int i; + + ENTER(); + + lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable); + + for (i = 0; i < adapter->numinscantable; i++) { + switch (mode) { + case wlan802_11infrastructure: + case wlan802_11ibss: + if (is_network_compatible(adapter, i, mode) >= 0) { + if (SCAN_RSSI(adapter->scantable[i].rssi) > + bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i]. + rssi); + bestnet = i; + } + } + break; + case wlan802_11autounknown: + default: + if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) { + bestrssi = + SCAN_RSSI(adapter->scantable[i].rssi); + bestnet = i; + } + break; + } + } + + LEAVE(); + return bestnet; +} + +/** + * @brief Find the AP with specific ssid in the scan list + * + * @param priv A pointer to wlan_private structure + * @param pSSID A pointer to AP's ssid + * + * @return 0--success, otherwise--fail + */ +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct bss_descriptor *preqbssid; + int i; + + ENTER(); + + memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID)); + + wlan_scan_networks(priv, NULL); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + i = libertas_find_best_SSID_in_list(adapter, preferred_mode); + if (i < 0) { + ret = -1; + goto out; + } + + preqbssid = &adapter->scantable[i]; + memcpy(pSSID, &preqbssid->ssid, + sizeof(struct WLAN_802_11_SSID)); + *out_mode = preqbssid->inframode; + + if (!pSSID->ssidlength) { + ret = -1; + } + +out: + LEAVE(); + return ret; +} + +/** + * @brief Scan Network + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + union iwreq_data wrqu; + + ENTER(); + + if (!wlan_scan_networks(priv, NULL)) { + memset(&wrqu, 0, sizeof(union iwreq_data)); + wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, + NULL); + } + + if (adapter->surpriseremoved) + return -1; + + LEAVE(); + return 0; +} + +/** + * @brief Send a scan command for all available channels filtered on a spec + * + * @param priv A pointer to wlan_private structure + * @param prequestedssid A pointer to AP's ssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan) +{ + wlan_adapter *adapter = priv->adapter; + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (prequestedssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + + memcpy(scancfg.specificSSID, prequestedssid->ssid, + prequestedssid->ssidlength); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (adapter->surpriseremoved) + return -1; + wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief scan an AP with specific BSSID + * + * @param priv A pointer to wlan_private structure + * @param bssid A pointer to AP's bssid + * @param keeppreviousscan Flag used to save/clear scan table before scan + * + * @return 0-success, otherwise fail + */ +int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan) +{ + struct wlan_ioctl_user_scan_cfg scancfg; + + ENTER(); + + if (bssid == NULL) { + return -1; + } + + memset(&scancfg, 0x00, sizeof(scancfg)); + memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID)); + scancfg.keeppreviousscan = keeppreviousscan; + + wlan_scan_networks(priv, &scancfg); + if (priv->adapter->surpriseremoved) + return -1; + wait_event_interruptible(priv->adapter->cmd_pending, + !priv->adapter->nr_cmd_pending); + + LEAVE(); + return 0; +} + +/** + * @brief Retrieve the scan table entries via wireless tools IOCTL call + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param dwrq A pointer to iw_point structure + * @param extra A pointer to extra data buf + * + * @return 0 --success, otherwise fail + */ +int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + char *current_ev = extra; + char *end_buf = extra + IW_SCAN_MAX_DATA; + struct chan_freq_power *cfp; + struct bss_descriptor *pscantable; + char *current_val; /* For rates */ + struct iw_event iwe; /* Temporary buffer */ + int i; + int j; + int rate; +#define PERFECT_RSSI ((u8)50) +#define WORST_RSSI ((u8)0) +#define RSSI_DIFF ((u8)(PERFECT_RSSI - WORST_RSSI)) + u8 rssi; + + u8 buf[16 + 256 * 2]; + u8 *ptr; + + ENTER(); + + /* + * if there's either commands in the queue or one being + * processed return -EAGAIN for iwlist to retry later. + */ + if (adapter->nr_cmd_pending) + return -EAGAIN; + + if (adapter->connect_status == libertas_connected) + lbs_pr_debug(1, "Current ssid: %32s\n", + adapter->curbssparams.ssid.ssid); + + lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n", + adapter->numinscantable); + + /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP. + * The new API using SIOCGIWSCAN is only limited by buffer size + * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes + * which is 4096. + */ + for (i = 0; i < adapter->numinscantable; i++) { + if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) { + lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p " + "MAX_SCAN_CELL_SIZE=%d\n", + i, current_ev, end_buf, MAX_SCAN_CELL_SIZE); + break; + } + + pscantable = &adapter->scantable[i]; + + lbs_pr_debug(1, "i=%d ssid: %32s\n", i, pscantable->ssid.ssid); + + cfp = + libertas_find_cfp_by_band_and_channel(adapter, 0, + pscantable->channel); + if (!cfp) { + lbs_pr_debug(1, "Invalid channel number %d\n", + pscantable->channel); + continue; + } + + if (!ssid_valid(&adapter->scantable[i].ssid)) { + continue; + } + + /* First entry *MUST* be the AP MAC address */ + iwe.cmd = SIOCGIWAP; + iwe.u.ap_addr.sa_family = ARPHRD_ETHER; + memcpy(iwe.u.ap_addr.sa_data, + &adapter->scantable[i].macaddress, ETH_ALEN); + + iwe.len = IW_EV_ADDR_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //Add the ESSID + iwe.u.data.length = adapter->scantable[i].ssid.ssidlength; + + if (iwe.u.data.length > 32) { + iwe.u.data.length = 32; + } + + iwe.cmd = SIOCGIWESSID; + iwe.u.data.flags = 1; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable[i].ssid. + ssid); + + //Add mode + iwe.cmd = SIOCGIWMODE; + iwe.u.mode = adapter->scantable[i].inframode + 1; + iwe.len = IW_EV_UINT_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + //frequency + iwe.cmd = SIOCGIWFREQ; + iwe.u.freq.m = (long)cfp->freq * 100000; + iwe.u.freq.e = 1; + iwe.len = IW_EV_FREQ_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add quality statistics */ + iwe.cmd = IWEVQUAL; + iwe.u.qual.updated = IW_QUAL_ALL_UPDATED; + iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi); + + rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE; + iwe.u.qual.qual = + (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) * + (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) / + (RSSI_DIFF * RSSI_DIFF); + if (iwe.u.qual.qual > 100) + iwe.u.qual.qual = 100; + else if (iwe.u.qual.qual < 1) + iwe.u.qual.qual = 0; + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + iwe.u.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + if ((adapter->inframode == wlan802_11ibss) && + !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rssi, + 0, + cmd_option_waitforrsp, + 0, NULL); + + if (!ret) { + iwe.u.qual.level = + CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE, + adapter->NF[TYPE_RXPD][TYPE_AVG] / + AVG_SCALE); + } + } + iwe.len = IW_EV_QUAL_LEN; + current_ev = + iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len); + + /* Add encryption capability */ + iwe.cmd = SIOCGIWENCODE; + if (adapter->scantable[i].privacy) { + iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; + } else { + iwe.u.data.flags = IW_ENCODE_DISABLED; + } + iwe.u.data.length = 0; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, + adapter->scantable->ssid. + ssid); + + current_val = current_ev + IW_EV_LCP_LEN; + + iwe.cmd = SIOCGIWRATE; + + iwe.u.bitrate.fixed = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.value = 0; + + /* Bit rate given in 500 kb/s units (+ 0x80) */ + for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates); + j++) { + if (adapter->scantable[i].libertas_supported_rates[j] == 0) { + break; + } + rate = + (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) * + 500000; + if (rate > iwe.u.bitrate.value) { + iwe.u.bitrate.value = rate; + } + + iwe.u.bitrate.value = + (adapter->scantable[i].libertas_supported_rates[j] + & 0x7f) * 500000; + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, + end_buf, &iwe, iwe.len); + + } + if ((adapter->scantable[i].inframode == wlan802_11ibss) + && !libertas_SSID_cmp(&adapter->curbssparams.ssid, + &adapter->scantable[i].ssid) + && adapter->adhoccreate) { + iwe.u.bitrate.value = 22 * 500000; + } + iwe.len = IW_EV_PARAM_LEN; + current_ev = + iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, + iwe.len); + + /* Add new value to event */ + current_val = current_ev + IW_EV_LCP_LEN; + + if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa2_supplicant.wpa_ie, + adapter->scantable[i].wpa2_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa2_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + memcpy(buf, adapter->scantable[i]. + wpa_supplicant.wpa_ie, + adapter->scantable[i].wpa_supplicant. + wpa_ie_len); + iwe.cmd = IWEVGENIE; + iwe.u.data.length = adapter->scantable[i]. + wpa_supplicant.wpa_ie_len; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = iwe_stream_add_point(current_ev, end_buf, + &iwe, buf); + } + + + if (adapter->scantable[i].extra_ie != 0) { + memset(&iwe, 0, sizeof(iwe)); + memset(buf, 0, sizeof(buf)); + ptr = buf; + ptr += sprintf(ptr, "extra_ie"); + iwe.u.data.length = strlen(buf); + + lbs_pr_debug(1, "iwe.u.data.length %d\n", + iwe.u.data.length); + lbs_pr_debug(1, "BUF: %s \n", buf); + + iwe.cmd = IWEVCUSTOM; + iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; + current_ev = + iwe_stream_add_point(current_ev, end_buf, &iwe, + buf); + } + + current_val = current_ev + IW_EV_LCP_LEN; + + /* + * Check if we added any event + */ + if ((current_val - current_ev) > IW_EV_LCP_LEN) + current_ev = current_val; + } + + dwrq->length = (current_ev - extra); + dwrq->flags = 0; + + LEAVE(); + return 0; +} + +/** + * @brief Prepare a scan command to be sent to the firmware + * + * Use the wlan_scan_cmd_config sent to the command processing module in + * the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command + * struct to send to firmware. + * + * The fixed fields specifying the BSS type and BSSID filters as well as a + * variable number/length of TLVs are sent in the command to firmware. + * + * @param priv A pointer to wlan_private structure + * @param cmd A pointer to cmd_ds_command structure to be sent to + * firmware with the cmd_DS_801_11_SCAN structure + * @param pdata_buf Void pointer cast of a wlan_scan_cmd_config struct used + * to set the fields/TLVs for the command sent to firmware + * + * @return 0 or -1 + * + * @sa wlan_scan_create_channel_list + */ +int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, void *pdata_buf) +{ + struct cmd_ds_802_11_scan *pscan = &cmd->params.scan; + struct wlan_scan_cmd_config *pscancfg; + + ENTER(); + + pscancfg = pdata_buf; + + /* Set fixed field variables in scan command */ + pscan->bsstype = pscancfg->bsstype; + memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID)); + memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen); + + cmd->command = cpu_to_le16(cmd_802_11_scan); + + /* size is equal to the sizeof(fixed portions) + the TLV len + header */ + cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + + sizeof(pscan->BSSID) + + pscancfg->tlvbufferlen + S_DS_GEN); + + lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n", + cmd->command, cmd->size, cmd->seqnum); + LEAVE(); + return 0; +} + +/** + * @brief This function handles the command response of scan + * + * The response buffer for the scan command has the following + * memory layout: + * + * .-----------------------------------------------------------. + * | header (4 * sizeof(u16)): Standard command response hdr | + * .-----------------------------------------------------------. + * | bufsize (u16) : sizeof the BSS Description data | + * .-----------------------------------------------------------. + * | NumOfSet (u8) : Number of BSS Descs returned | + * .-----------------------------------------------------------. + * | BSSDescription data (variable, size given in bufsize) | + * .-----------------------------------------------------------. + * | TLV data (variable, size calculated using header->size, | + * | bufsize and sizeof the fixed fields above) | + * .-----------------------------------------------------------. + * + * @param priv A pointer to wlan_private structure + * @param resp A pointer to cmd_ds_command + * + * @return 0 or -1 + */ +int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp) +{ + wlan_adapter *adapter = priv->adapter; + struct cmd_ds_802_11_scan_rsp *pscan; + struct bss_descriptor newbssentry; + struct mrvlietypes_data *ptlv; + struct mrvlietypes_tsftimestamp *ptsftlv; + u8 *pbssinfo; + u16 scanrespsize; + int bytesleft; + int numintable; + int bssIdx; + int idx; + int tlvbufsize; + u64 tsfval; + + ENTER(); + + pscan = &resp->params.scanresp; + + if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) { + lbs_pr_debug(1, + "SCAN_RESP: Invalid number of AP returned (%d)!!\n", + pscan->nr_sets); + LEAVE(); + return -1; + } + + bytesleft = le16_to_cpu(pscan->bssdescriptsize); + lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft); + + scanrespsize = le16_to_cpu(resp->size); + lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n", + pscan->nr_sets); + + numintable = adapter->numinscantable; + pbssinfo = pscan->bssdesc_and_tlvbuffer; + + /* The size of the TLV buffer is equal to the entire command response + * size (scanrespsize) minus the fixed fields (sizeof()'s), the + * BSS Descriptions (bssdescriptsize as bytesLef) and the command + * response header (S_DS_GEN) + */ + tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize) + + sizeof(pscan->nr_sets) + + S_DS_GEN); + + ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft); + + /* Search the TLV buffer space in the scan response for any valid TLVs */ + wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv); + + /* + * Process each scan response returned (pscan->nr_sets). Save + * the information in the newbssentry and then insert into the + * driver scan table either as an update to an existing entry + * or as an addition at the end of the table + */ + for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) { + /* Zero out the newbssentry we are about to store info in */ + memset(&newbssentry, 0x00, sizeof(newbssentry)); + + /* Process the data fields and IEs returned for this BSS */ + if ((InterpretBSSDescriptionWithIE(&newbssentry, + &pbssinfo, + &bytesleft) == + 0) + && CHECK_SSID_IS_VALID(&newbssentry.ssid)) { + + lbs_pr_debug(1, + "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n", + newbssentry.macaddress[0], + newbssentry.macaddress[1], + newbssentry.macaddress[2], + newbssentry.macaddress[3], + newbssentry.macaddress[4], + newbssentry.macaddress[5]); + + /* + * Search the scan table for the same bssid + */ + for (bssIdx = 0; bssIdx < numintable; bssIdx++) { + if (memcmp(newbssentry.macaddress, + adapter->scantable[bssIdx]. + macaddress, + sizeof(newbssentry.macaddress)) == + 0) { + /* + * If the SSID matches as well, it is a duplicate of + * this entry. Keep the bssIdx set to this + * entry so we replace the old contents in the table + */ + if ((newbssentry.ssid.ssidlength == + adapter->scantable[bssIdx].ssid. + ssidlength) + && + (memcmp + (newbssentry.ssid.ssid, + adapter->scantable[bssIdx].ssid. + ssid, + newbssentry.ssid.ssidlength) == + 0)) { + lbs_pr_debug(1, + "SCAN_RESP: Duplicate of index: %d\n", + bssIdx); + break; + } + } + } + /* + * If the bssIdx is equal to the number of entries in the table, + * the new entry was not a duplicate; append it to the scan + * table + */ + if (bssIdx == numintable) { + /* Range check the bssIdx, keep it limited to the last entry */ + if (bssIdx == MRVDRV_MAX_BSSID_LIST) { + bssIdx--; + } else { + numintable++; + } + } + + /* + * If the TSF TLV was appended to the scan results, save the + * this entries TSF value in the networktsf field. The + * networktsf is the firmware's TSF value at the time the + * beacon or probe response was received. + */ + if (ptsftlv) { + memcpy(&tsfval, &ptsftlv->tsftable[idx], + sizeof(tsfval)); + tsfval = le64_to_cpu(tsfval); + + memcpy(&newbssentry.networktsf, + &tsfval, sizeof(newbssentry.networktsf)); + } + + /* Copy the locally created newbssentry to the scan table */ + memcpy(&adapter->scantable[bssIdx], + &newbssentry, + sizeof(adapter->scantable[bssIdx])); + + } else { + + /* error parsing/interpreting the scan response, skipped */ + lbs_pr_debug(1, "SCAN_RESP: " + "InterpretBSSDescriptionWithIE returned ERROR\n"); + } + } + + lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n", + pscan->nr_sets, numintable - adapter->numinscantable, + numintable); + + /* Update the total number of BSSIDs in the scan table */ + adapter->numinscantable = numintable; + + LEAVE(); + return 0; +} diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h new file mode 100644 index 0000000..d93aa7f --- /dev/null +++ b/drivers/net/wireless/libertas/scan.h @@ -0,0 +1,216 @@ +/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */ +/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */ + +/** + * Interface for the wlan network scan routines + * + * Driver interface functions and type declarations for the scan module + * implemented in wlan_scan.c. + */ +#ifndef _WLAN_SCAN_H +#define _WLAN_SCAN_H + +#include "hostcmd.h" + +/** + * @brief Maximum number of channels that can be sent in a setuserscan ioctl + * + * @sa wlan_ioctl_user_scan_cfg + */ +#define WLAN_IOCTL_USER_SCAN_CHAN_MAX 50 + +//! Infrastructure BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_BSS 1 + +//! Adhoc BSS scan type in wlan_scan_cmd_config +#define WLAN_SCAN_BSS_TYPE_IBSS 2 + +//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter +#define WLAN_SCAN_BSS_TYPE_ANY 3 + +/** + * @brief Structure used internally in the wlan driver to configure a scan. + * + * Sent to the command processing module to configure the firmware + * scan command prepared by libertas_cmd_80211_scan. + * + * @sa wlan_scan_networks + * + */ +struct wlan_scan_cmd_config { + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Specific BSSID used to filter scan results in the firmware + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief length of TLVs sent in command starting at tlvBuffer + */ + int tlvbufferlen; + + /** + * @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command + * + * @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t + * @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t + */ + u8 tlvbuffer[1]; //!< SSID TLV(s) and ChanList TLVs are stored here +}; + +/** + * @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg + * + * Multiple instances of this structure are included in the IOCTL command + * to configure a instance of a scan on the specific channel. + */ +struct wlan_ioctl_user_scan_chan { + u8 channumber; //!< channel Number to scan + u8 radiotype; //!< Radio type: 'B/G' band = 0, 'A' band = 1 + u8 scantype; //!< Scan type: Active = 0, Passive = 1 + u16 scantime; //!< Scan duration in milliseconds; if 0 default used +}; + +/** + * @brief IOCTL input structure to configure an immediate scan cmd to firmware + * + * Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl. Specifies + * a number of parameters to be used in general for the scan as well + * as a channel list (wlan_ioctl_user_scan_chan) for each scan period + * desired. + * + * @sa libertas_set_user_scan_ioctl + */ +struct wlan_ioctl_user_scan_cfg { + + /** + * @brief Flag set to keep the previous scan table intact + * + * If set, the scan results will accumulate, replacing any previous + * matched entries for a BSS with the new scan data + */ + u8 keeppreviousscan; //!< Do not erase the existing scan results + + /** + * @brief BSS type to be sent in the firmware command + * + * Field can be used to restrict the types of networks returned in the + * scan. valid settings are: + * + * - WLAN_SCAN_BSS_TYPE_BSS (infrastructure) + * - WLAN_SCAN_BSS_TYPE_IBSS (adhoc) + * - WLAN_SCAN_BSS_TYPE_ANY (unrestricted, adhoc and infrastructure) + */ + u8 bsstype; + + /** + * @brief Configure the number of probe requests for active chan scans + */ + u8 numprobes; + + /** + * @brief BSSID filter sent in the firmware command to limit the results + */ + u8 specificBSSID[ETH_ALEN]; + + /** + * @brief SSID filter sent in the firmware command to limit the results + */ + char specificSSID[IW_ESSID_MAX_SIZE + 1]; + + /** + * @brief Variable number (fixed maximum) of channels to scan up + */ + struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX]; +}; + +/** + * @brief Structure used to store information for each beacon/probe response + */ +struct bss_descriptor { + u8 macaddress[ETH_ALEN]; + + struct WLAN_802_11_SSID ssid; + + /* WEP encryption requirement */ + u32 privacy; + + /* receive signal strength in dBm */ + long rssi; + + u32 channel; + + u16 beaconperiod; + + u32 atimwindow; + + enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode; + u8 libertas_supported_rates[WLAN_SUPPORTED_RATES]; + + int extra_ie; + + u8 timestamp[8]; //!< TSF value included in the beacon/probe response + union ieeetypes_phyparamset phyparamset; + union IEEEtypes_ssparamset ssparamset; + struct ieeetypes_capinfo cap; + u8 datarates[WLAN_SUPPORTED_RATES]; + + __le64 networktsf; //!< TSF timestamp from the current firmware TSF + + struct ieeetypes_countryinfofullset countryinfo; + + struct WPA_SUPPLICANT wpa_supplicant; + struct WPA_SUPPLICANT wpa2_supplicant; + +}; + +extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, + struct WLAN_802_11_SSID *ssid2); +extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid, + u8 * bssid, int mode); +int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode); +extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode); + +int libertas_find_best_network_SSID(wlan_private * priv, + struct WLAN_802_11_SSID *pSSID, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode, + enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode); + +extern int libertas_send_specific_SSID_scan(wlan_private * priv, + struct WLAN_802_11_SSID *prequestedssid, + u8 keeppreviousscan); +extern int libertas_send_specific_BSSID_scan(wlan_private * priv, + u8 * bssid, u8 keeppreviousscan); + +extern int libertas_cmd_80211_scan(wlan_private * priv, + struct cmd_ds_command *cmd, + void *pdata_buf); + +extern int libertas_ret_80211_scan(wlan_private * priv, + struct cmd_ds_command *resp); + +int wlan_scan_networks(wlan_private * priv, + const struct wlan_ioctl_user_scan_cfg * puserscanin); + +struct ifreq; + +struct iw_point; +struct iw_param; +struct iw_request_info; +extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra); +extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra); + +#endif /* _WLAN_SCAN_H */ diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h new file mode 100644 index 0000000..207b8a6 --- /dev/null +++ b/drivers/net/wireless/libertas/thread.h @@ -0,0 +1,52 @@ +#ifndef __WLAN_THREAD_H_ +#define __WLAN_THREAD_H_ + +#include <linux/kthread.h> + +struct wlan_thread { + struct task_struct *task; + wait_queue_head_t waitq; + pid_t pid; + void *priv; +}; + +static inline void wlan_activate_thread(struct wlan_thread * thr) +{ + /** Record the thread pid */ + thr->pid = current->pid; + + /** Initialize the wait queue */ + init_waitqueue_head(&thr->waitq); +} + +static inline void wlan_deactivate_thread(struct wlan_thread * thr) +{ + ENTER(); + + thr->pid = 0; + + LEAVE(); +} + +static inline void wlan_create_thread(int (*wlanfunc) (void *), + struct wlan_thread * thr, char *name) +{ + thr->task = kthread_run(wlanfunc, thr, "%s", name); +} + +static inline int wlan_terminate_thread(struct wlan_thread * thr) +{ + ENTER(); + + /* Check if the thread is active or not */ + if (!thr->pid) { + printk(KERN_ERR "Thread does not exist\n"); + return -1; + } + kthread_stop(thr->task); + + LEAVE(); + return 0; +} + +#endif diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c new file mode 100644 index 0000000..82d0622 --- /dev/null +++ b/drivers/net/wireless/libertas/tx.c @@ -0,0 +1,285 @@ +/** + * This file contains the handling of TX in wlan driver. + */ +#include <linux/netdevice.h> + +#include "hostcmd.h" +#include "radiotap.h" +#include "sbi.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "wext.h" + +/** + * @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE + * units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1) + * + * @param rate Input rate + * @return Output Rate (0 if invalid) + */ +static u32 convert_radiotap_rate_to_mv(u8 rate) +{ + switch (rate) { + case 2: /* 1 Mbps */ + return 0 | (1 << 4); + case 4: /* 2 Mbps */ + return 1 | (1 << 4); + case 11: /* 5.5 Mbps */ + return 2 | (1 << 4); + case 22: /* 11 Mbps */ + return 3 | (1 << 4); + case 12: /* 6 Mbps */ + return 4 | (1 << 4); + case 18: /* 9 Mbps */ + return 5 | (1 << 4); + case 24: /* 12 Mbps */ + return 6 | (1 << 4); + case 36: /* 18 Mbps */ + return 7 | (1 << 4); + case 48: /* 24 Mbps */ + return 8 | (1 << 4); + case 72: /* 36 Mbps */ + return 9 | (1 << 4); + case 96: /* 48 Mbps */ + return 10 | (1 << 4); + case 108: /* 54 Mbps */ + return 11 | (1 << 4); + } + return 0; +} + +/** + * @brief This function processes a single packet and sends + * to IF layer + * + * @param priv A pointer to wlan_private structure + * @param skb A pointer to skb which includes TX packet + * @return 0 or -1 + */ +static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct txpd localtxpd; + struct txpd *plocaltxpd = &localtxpd; + u8 *p802x_hdr; + struct tx_radiotap_hdr *pradiotap_hdr; + u32 new_rate; + u8 *ptr = priv->adapter->tmptxbuf; + + ENTER(); + + if (priv->adapter->surpriseremoved) + return -1; + + if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX packet: ", skb->data, + min_t(unsigned int, skb->len, 100)); + + if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) { + lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n", + skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE); + ret = -1; + goto done; + } + + memset(plocaltxpd, 0, sizeof(struct txpd)); + + plocaltxpd->tx_packet_length = skb->len; + + /* offset of actual data */ + plocaltxpd->tx_packet_location = sizeof(struct txpd); + + /* TxCtrl set by user or default */ + plocaltxpd->tx_control = adapter->pkttxctrl; + + p802x_hdr = skb->data; + if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + + /* locate radiotap header */ + pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data; + + /* set txpd fields from the radiotap header */ + new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate); + if (new_rate != 0) { + /* erase tx_control[4:0] */ + plocaltxpd->tx_control &= ~0x1f; + /* write new tx_control[4:0] */ + plocaltxpd->tx_control |= new_rate; + } + + /* skip the radiotap header */ + p802x_hdr += sizeof(struct tx_radiotap_hdr); + plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr); + + } + /* copy destination address from 802.3 or 802.11 header */ + if (priv->adapter->linkmode == WLAN_LINKMODE_802_11) + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN); + else + memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN); + + lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd)); + + if (IS_MESH_FRAME(skb)) { + plocaltxpd->tx_control |= TxPD_MESH_FRAME; + } + + memcpy(ptr, plocaltxpd, sizeof(struct txpd)); + + ptr += sizeof(struct txpd); + + lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length); + memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length); + ret = libertas_sbi_host_to_card(priv, MVMS_DAT, + priv->adapter->tmptxbuf, + plocaltxpd->tx_packet_length + + sizeof(struct txpd)); + + if (ret) { + lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret); + goto done; + } + + lbs_pr_debug(1, "SendSinglePacket succeeds\n"); + + done: + if (!ret) { + priv->stats.tx_packets++; + priv->stats.tx_bytes += skb->len; + } else { + priv->stats.tx_dropped++; + priv->stats.tx_errors++; + } + + if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) { + /* Keep the skb to echo it back once Tx feedback is + received from FW */ + skb_orphan(skb); + /* stop processing outgoing pkts */ + netif_stop_queue(priv->wlan_dev.netdev); + /* freeze any packets already in our queues */ + priv->adapter->TxLockFlag = 1; + } else { + dev_kfree_skb_any(skb); + priv->adapter->currenttxskb = NULL; + } + + LEAVE(); + return ret; +} + + +void libertas_tx_runqueue(wlan_private *priv) +{ + wlan_adapter *adapter = priv->adapter; + int i; + + spin_lock(&adapter->txqueue_lock); + for (i = 0; i < adapter->tx_queue_idx; i++) { + struct sk_buff *skb = adapter->tx_queue_ps[i]; + spin_unlock(&adapter->txqueue_lock); + SendSinglePacket(priv, skb); + spin_lock(&adapter->txqueue_lock); + } + adapter->tx_queue_idx = 0; + spin_unlock(&adapter->txqueue_lock); +} + +static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb) +{ + wlan_adapter *adapter = priv->adapter; + + spin_lock(&adapter->txqueue_lock); + + WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE); + adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb; + if (adapter->tx_queue_idx == NR_TX_QUEUE) + netif_stop_queue(priv->wlan_dev.netdev); + else + netif_start_queue(priv->wlan_dev.netdev); + + spin_unlock(&adapter->txqueue_lock); +} + +/** + * @brief This function checks the conditions and sends packet to IF + * layer if everything is ok. + * + * @param priv A pointer to wlan_private structure + * @return n/a + */ +int libertas_process_tx(wlan_private * priv, struct sk_buff *skb) +{ + int ret = -1; + + ENTER(); + + lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100)); + + if (priv->wlan_dev.dnld_sent) { + lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n", + priv->wlan_dev.dnld_sent); + goto done; + } + + if ((priv->adapter->psstate == PS_STATE_SLEEP) || + (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) { + wlan_tx_queue(priv, skb); + return ret; + } + + priv->adapter->currenttxskb = skb; + + ret = SendSinglePacket(priv, skb); +done: + LEAVE(); + return ret; +} + +/** + * @brief This function sends to the host the last transmitted packet, + * filling the radiotap headers with transmission information. + * + * @param priv A pointer to wlan_private structure + * @param status A 32 bit value containing transmission status. + * + * @returns void + */ +void libertas_send_tx_feedback(wlan_private * priv) +{ + wlan_adapter *adapter = priv->adapter; + struct tx_radiotap_hdr *radiotap_hdr; + u32 status = adapter->eventcause; + int txfail; + int try_count; + + if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP || + adapter->currenttxskb == NULL) + return; + + radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data; + + if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0) + lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr, + min_t(unsigned int, adapter->currenttxskb->len, 100)); + + txfail = (status >> 24); + +#if 0 + /* The version of roofnet that we've tested does not use this yet + * But it may be used in the future. + */ + if (txfail) + radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL; +#endif + try_count = (status >> 16) & 0xff; + radiotap_hdr->data_retries = (try_count) ? + (1 + adapter->txretrycount - try_count) : 0; + libertas_upload_rx_packet(priv, adapter->currenttxskb); + adapter->currenttxskb = NULL; + priv->adapter->TxLockFlag = 0; + if (priv->adapter->connect_status == libertas_connected) + netif_wake_queue(priv->wlan_dev.netdev); +} diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h new file mode 100644 index 0000000..09d62f8 --- /dev/null +++ b/drivers/net/wireless/libertas/types.h @@ -0,0 +1,289 @@ +/** + * This header file contains definition for global types + */ +#ifndef _WLAN_TYPES_ +#define _WLAN_TYPES_ + +#include <linux/if_ether.h> + +/** IEEE type definitions */ +enum ieeetypes_elementid { + SSID = 0, + SUPPORTED_RATES, + FH_PARAM_SET, + DS_PARAM_SET, + CF_PARAM_SET, + TIM, + IBSS_PARAM_SET, + COUNTRY_INFO = 7, + + CHALLENGE_TEXT = 16, + + EXTENDED_SUPPORTED_RATES = 50, + + VENDOR_SPECIFIC_221 = 221, + + WPA_IE = 221, + WPA2_IE = 48, + + EXTRA_IE = 133, +} __attribute__ ((packed)); + +#define CAPINFO_MASK (~(0xda00)) + +struct ieeetypes_capinfo { + u8 ess:1; + u8 ibss:1; + u8 cfpollable:1; + u8 cfpollrqst:1; + u8 privacy:1; + u8 shortpreamble:1; + u8 pbcc:1; + u8 chanagility:1; + u8 spectrummgmt:1; + u8 rsrvd3:1; + u8 shortslottime:1; + u8 apsd:1; + u8 rsvrd2:1; + u8 dsssofdm:1; + u8 rsrvd1:2; +} __attribute__ ((packed)); + +struct ieeetypes_cfparamset { + u8 elementid; + u8 len; + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + + +struct ieeetypes_ibssparamset { + u8 elementid; + u8 len; + u16 atimwindow; +} __attribute__ ((packed)); + +union IEEEtypes_ssparamset { + struct ieeetypes_cfparamset cfparamset; + struct ieeetypes_ibssparamset ibssparamset; +} __attribute__ ((packed)); + +struct ieeetypes_fhparamset { + u8 elementid; + u8 len; + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct ieeetypes_dsparamset { + u8 elementid; + u8 len; + u8 currentchan; +} __attribute__ ((packed)); + +union ieeetypes_phyparamset { + struct ieeetypes_fhparamset fhparamset; + struct ieeetypes_dsparamset dsparamset; +} __attribute__ ((packed)); + +struct ieeetypes_assocrsp { + struct ieeetypes_capinfo capability; + u16 statuscode; + u16 aid; + u8 iebuffer[1]; +} __attribute__ ((packed)); + +/** TLV type ID definition */ +#define PROPRIETARY_TLV_BASE_ID 0x0100 + +/* Terminating TLV type */ +#define MRVL_TERMINATE_TLV_ID 0xffff + +#define TLV_TYPE_SSID 0x0000 +#define TLV_TYPE_RATES 0x0001 +#define TLV_TYPE_PHY_FH 0x0002 +#define TLV_TYPE_PHY_DS 0x0003 +#define TLV_TYPE_CF 0x0004 +#define TLV_TYPE_IBSS 0x0006 + +#define TLV_TYPE_DOMAIN 0x0007 + +#define TLV_TYPE_POWER_CAPABILITY 0x0021 + +#define TLV_TYPE_KEY_MATERIAL (PROPRIETARY_TLV_BASE_ID + 0) +#define TLV_TYPE_CHANLIST (PROPRIETARY_TLV_BASE_ID + 1) +#define TLV_TYPE_NUMPROBES (PROPRIETARY_TLV_BASE_ID + 2) +#define TLV_TYPE_RSSI_LOW (PROPRIETARY_TLV_BASE_ID + 4) +#define TLV_TYPE_SNR_LOW (PROPRIETARY_TLV_BASE_ID + 5) +#define TLV_TYPE_FAILCOUNT (PROPRIETARY_TLV_BASE_ID + 6) +#define TLV_TYPE_BCNMISS (PROPRIETARY_TLV_BASE_ID + 7) +#define TLV_TYPE_LED_GPIO (PROPRIETARY_TLV_BASE_ID + 8) +#define TLV_TYPE_LEDBEHAVIOR (PROPRIETARY_TLV_BASE_ID + 9) +#define TLV_TYPE_PASSTHROUGH (PROPRIETARY_TLV_BASE_ID + 10) +#define TLV_TYPE_REASSOCAP (PROPRIETARY_TLV_BASE_ID + 11) +#define TLV_TYPE_POWER_TBL_2_4GHZ (PROPRIETARY_TLV_BASE_ID + 12) +#define TLV_TYPE_POWER_TBL_5GHZ (PROPRIETARY_TLV_BASE_ID + 13) +#define TLV_TYPE_BCASTPROBE (PROPRIETARY_TLV_BASE_ID + 14) +#define TLV_TYPE_NUMSSID_PROBE (PROPRIETARY_TLV_BASE_ID + 15) +#define TLV_TYPE_WMMQSTATUS (PROPRIETARY_TLV_BASE_ID + 16) +#define TLV_TYPE_CRYPTO_DATA (PROPRIETARY_TLV_BASE_ID + 17) +#define TLV_TYPE_WILDCARDSSID (PROPRIETARY_TLV_BASE_ID + 18) +#define TLV_TYPE_TSFTIMESTAMP (PROPRIETARY_TLV_BASE_ID + 19) +#define TLV_TYPE_RSSI_HIGH (PROPRIETARY_TLV_BASE_ID + 22) +#define TLV_TYPE_SNR_HIGH (PROPRIETARY_TLV_BASE_ID + 23) + +/** TLV related data structures*/ +struct mrvlietypesheader { + u16 type; + u16 len; +} __attribute__ ((packed)); + +struct mrvlietypes_data { + struct mrvlietypesheader header; + u8 Data[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ratesparamset { + struct mrvlietypesheader header; + u8 rates[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_ssidparamset { + struct mrvlietypesheader header; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_wildcardssidparamset { + struct mrvlietypesheader header; + u8 MaxSsidlength; + u8 ssid[1]; +} __attribute__ ((packed)); + +struct chanscanmode { + u8 passivescan:1; + u8 disablechanfilt:1; + u8 reserved_2_7:6; +} __attribute__ ((packed)); + +struct chanscanparamset { + u8 radiotype; + u8 channumber; + struct chanscanmode chanscanmode; + u16 minscantime; + u16 maxscantime; +} __attribute__ ((packed)); + +struct mrvlietypes_chanlistparamset { + struct mrvlietypesheader header; + struct chanscanparamset chanscanparam[1]; +} __attribute__ ((packed)); + +struct cfparamset { + u8 cfpcnt; + u8 cfpperiod; + u16 cfpmaxduration; + u16 cfpdurationremaining; +} __attribute__ ((packed)); + +struct ibssparamset { + u16 atimwindow; +} __attribute__ ((packed)); + +struct mrvlietypes_ssparamset { + struct mrvlietypesheader header; + union { + struct cfparamset cfparamset[1]; + struct ibssparamset ibssparamset[1]; + } cf_ibss; +} __attribute__ ((packed)); + +struct fhparamset { + u16 dwelltime; + u8 hopset; + u8 hoppattern; + u8 hopindex; +} __attribute__ ((packed)); + +struct dsparamset { + u8 currentchan; +} __attribute__ ((packed)); + +struct mrvlietypes_phyparamset { + struct mrvlietypesheader header; + union { + struct fhparamset fhparamset[1]; + struct dsparamset dsparamset[1]; + } fh_ds; +} __attribute__ ((packed)); + +struct mrvlietypes_rsnparamset { + struct mrvlietypesheader header; + u8 rsnie[1]; +} __attribute__ ((packed)); + +struct mrvlietypes_tsftimestamp { + struct mrvlietypesheader header; + __le64 tsftable[1]; +} __attribute__ ((packed)); + +/** Local Power capability */ +struct mrvlietypes_powercapability { + struct mrvlietypesheader header; + s8 minpower; + s8 maxpower; +} __attribute__ ((packed)); + +struct mrvlietypes_rssithreshold { + struct mrvlietypesheader header; + u8 rssivalue; + u8 rssifreq; +} __attribute__ ((packed)); + +struct mrvlietypes_snrthreshold { + struct mrvlietypesheader header; + u8 snrvalue; + u8 snrfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_failurecount { + struct mrvlietypesheader header; + u8 failvalue; + u8 Failfreq; +} __attribute__ ((packed)); + +struct mrvlietypes_beaconsmissed { + struct mrvlietypesheader header; + u8 beaconmissed; + u8 reserved; +} __attribute__ ((packed)); + +struct mrvlietypes_numprobes { + struct mrvlietypesheader header; + u16 numprobes; +} __attribute__ ((packed)); + +struct mrvlietypes_bcastprobe { + struct mrvlietypesheader header; + u16 bcastprobe; +} __attribute__ ((packed)); + +struct mrvlietypes_numssidprobe { + struct mrvlietypesheader header; + u16 numssidprobe; +} __attribute__ ((packed)); + +struct led_pin { + u8 led; + u8 pin; +} __attribute__ ((packed)); + +struct mrvlietypes_ledgpio { + struct mrvlietypesheader header; + struct led_pin ledpin[1]; +} __attribute__ ((packed)); + +#endif /* _WLAN_TYPES_ */ diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h new file mode 100644 index 0000000..e86f65ae --- /dev/null +++ b/drivers/net/wireless/libertas/version.h @@ -0,0 +1,8 @@ +#define DRIVER_RELEASE_VERSION "320.p0" +const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION +#ifdef DEBUG + "-dbg" +#endif + ""; + + diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c new file mode 100644 index 0000000..4a52336 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.c @@ -0,0 +1,2769 @@ +/** + * This file contains ioctl functions + */ +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/if.h> +#include <linux/if_arp.h> +#include <linux/wireless.h> +#include <linux/bitops.h> + +#include <net/ieee80211.h> +#include <net/iw_handler.h> + +#include "host.h" +#include "radiotap.h" +#include "decl.h" +#include "defs.h" +#include "dev.h" +#include "join.h" +#include "version.h" +#include "wext.h" +#include "assoc.h" + + +/** + * @brief Convert mw value to dbm value + * + * @param mw the value of mw + * @return the value of dbm + */ +static int mw_to_dbm(int mw) +{ + if (mw < 2) + return 0; + else if (mw < 3) + return 3; + else if (mw < 4) + return 5; + else if (mw < 6) + return 7; + else if (mw < 7) + return 8; + else if (mw < 8) + return 9; + else if (mw < 10) + return 10; + else if (mw < 13) + return 11; + else if (mw < 16) + return 12; + else if (mw < 20) + return 13; + else if (mw < 25) + return 14; + else if (mw < 32) + return 15; + else if (mw < 40) + return 16; + else if (mw < 50) + return 17; + else if (mw < 63) + return 18; + else if (mw < 79) + return 19; + else if (mw < 100) + return 20; + else + return 21; +} + +/** + * @brief Find the channel frequency power info with specific channel + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param channel the channel for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter, + u8 band, u16 channel) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].channel == channel) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && channel) + lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find " + "cfp by band %d & channel %d\n", band, channel); + + return cfp; +} + +/** + * @brief Find the channel frequency power info with specific frequency + * + * @param adapter A pointer to wlan_adapter structure + * @param band it can be BAND_A, BAND_G or BAND_B + * @param freq the frequency for looking + * @return A pointer to struct chan_freq_power structure or NULL if not find. + */ +static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter, + u8 band, u32 freq) +{ + struct chan_freq_power *cfp = NULL; + struct region_channel *rc; + int count = sizeof(adapter->region_channel) / + sizeof(adapter->region_channel[0]); + int i, j; + + for (j = 0; !cfp && (j < count); j++) { + rc = &adapter->region_channel[j]; + + if (adapter->enable11d) + rc = &adapter->universal_channel[j]; + if (!rc->valid || !rc->CFP) + continue; + if (rc->band != band) + continue; + for (i = 0; i < rc->nrcfp; i++) { + if (rc->CFP[i].freq == freq) { + cfp = &rc->CFP[i]; + break; + } + } + } + + if (!cfp && freq) + lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by " + "band %d & freq %d\n", band, freq); + + return cfp; +} + +static int updatecurrentchannel(wlan_private * priv) +{ + int ret; + + /* + ** the channel in f/w could be out of sync, get the current channel + */ + ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_get, + cmd_option_waitforrsp, 0, NULL); + + lbs_pr_debug(1, "Current channel = %d\n", + priv->adapter->curbssparams.channel); + + return ret; +} + +static int setcurrentchannel(wlan_private * priv, int channel) +{ + lbs_pr_debug(1, "Set channel = %d\n", channel); + + /* + ** Current channel is not set to adhocchannel requested, set channel + */ + return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel, + cmd_opt_802_11_rf_channel_set, + cmd_option_waitforrsp, 0, &channel)); +} + +static int changeadhocchannel(wlan_private * priv, int channel) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + adapter->adhocchannel = channel; + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel == adapter->adhocchannel) { + /* adhocchannel is set to the current channel already */ + LEAVE(); + return 0; + } + + lbs_pr_debug(1, "Updating channel from %d to %d\n", + adapter->curbssparams.channel, adapter->adhocchannel); + + setcurrentchannel(priv, adapter->adhocchannel); + + updatecurrentchannel(priv); + + if (adapter->curbssparams.channel != adapter->adhocchannel) { + lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n", + adapter->adhocchannel, adapter->curbssparams.channel); + LEAVE(); + return -1; + } + + if (adapter->connect_status == libertas_connected) { + int i; + struct WLAN_802_11_SSID curadhocssid; + + lbs_pr_debug(1, "channel Changed while in an IBSS\n"); + + /* Copy the current ssid */ + memcpy(&curadhocssid, &adapter->curbssparams.ssid, + sizeof(struct WLAN_802_11_SSID)); + + /* Exit Adhoc mode */ + lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n"); + ret = libertas_stop_adhoc_network(priv); + + if (ret) { + LEAVE(); + return ret; + } + /* Scan for the network, do not save previous results. Stale + * scan data will cause us to join a non-existant adhoc network + */ + libertas_send_specific_SSID_scan(priv, &curadhocssid, 0); + + // find out the BSSID that matches the current SSID + i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL, + wlan802_11ibss); + + if (i >= 0) { + lbs_pr_debug(1, "SSID found at %d in List," + "so join\n", i); + libertas_join_adhoc_network(priv, &adapter->scantable[i]); + } else { + // else send START command + lbs_pr_debug(1, "SSID not found in list, " + "so creating adhoc with ssid = %s\n", + curadhocssid.ssid); + libertas_start_adhoc_network(priv, &curadhocssid); + } // end of else (START command) + } + + LEAVE(); + return 0; +} + +/** + * @brief Set Radio On/OFF + * + * @param priv A pointer to wlan_private structure + * @option Radio Option + * @return 0 --success, otherwise fail + */ +int wlan_radio_ioctl(wlan_private * priv, u8 option) +{ + int ret = 0; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->radioon != option) { + lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off"); + adapter->radioon = option; + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_radio_control, + cmd_act_set, + cmd_option_waitforrsp, 0, NULL); + } + + LEAVE(); + return ret; +} + +/** + * @brief Copy rates + * + * @param dest A pointer to Dest Buf + * @param src A pointer to Src Buf + * @param len The len of Src Buf + * @return Number of rates copyed + */ +static inline int copyrates(u8 * dest, int pos, u8 * src, int len) +{ + int i; + + for (i = 0; i < len && src[i]; i++, pos++) { + if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES) + break; + dest[pos] = src[i]; + } + + return pos; +} + +/** + * @brief Get active data rates + * + * @param adapter A pointer to wlan_adapter structure + * @param rate The buf to return the active rates + * @return The number of rates + */ +static int get_active_data_rates(wlan_adapter * adapter, + u8* rates) +{ + int k = 0; + + ENTER(); + + if (adapter->connect_status != libertas_connected) { + if (adapter->inframode == wlan802_11infrastructure) { + //Infra. mode + lbs_pr_debug(1, "Infra\n"); + k = copyrates(rates, k, libertas_supported_rates, + sizeof(libertas_supported_rates)); + } else { + //ad-hoc mode + lbs_pr_debug(1, "Adhoc G\n"); + k = copyrates(rates, k, libertas_adhoc_rates_g, + sizeof(libertas_adhoc_rates_g)); + } + } else { + k = copyrates(rates, 0, adapter->curbssparams.datarates, + adapter->curbssparams.numofrates); + } + + LEAVE(); + + return k; +} + +static int wlan_get_name(struct net_device *dev, struct iw_request_info *info, + char *cwrq, char *extra) +{ + const char *cp; + char comm[6] = { "COMM-" }; + char mrvl[6] = { "MRVL-" }; + int cnt; + + ENTER(); + + strcpy(cwrq, mrvl); + + cp = strstr(libertas_driver_version, comm); + if (cp == libertas_driver_version) //skip leading "COMM-" + cp = libertas_driver_version + strlen(comm); + else + cp = libertas_driver_version; + + cnt = strlen(mrvl); + cwrq += cnt; + while (cnt < 16 && (*cp != '-')) { + *cwrq++ = toupper(*cp++); + cnt++; + } + *cwrq = '\0'; + + LEAVE(); + + return 0; +} + +static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct chan_freq_power *cfp; + + ENTER(); + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, + adapter->curbssparams.channel); + + if (!cfp) { + if (adapter->curbssparams.channel) + lbs_pr_debug(1, "Invalid channel=%d\n", + adapter->curbssparams.channel); + return -EINVAL; + } + + fwrq->m = (long)cfp->freq * 100000; + fwrq->e = 1; + + lbs_pr_debug(1, "freq=%u\n", fwrq->m); + + LEAVE(); + return 0; +} + +static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->connect_status == libertas_connected) { + memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN); + } else { + memset(awrq->sa_data, 0, ETH_ALEN); + } + awrq->sa_family = ARPHRD_ETHER; + + LEAVE(); + return 0; +} + +static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Check the size of the string + */ + + if (dwrq->length > 16) { + return -E2BIG; + } + + mutex_lock(&adapter->lock); + memset(adapter->nodename, 0, sizeof(adapter->nodename)); + memcpy(adapter->nodename, extra, dwrq->length); + mutex_unlock(&adapter->lock); + + LEAVE(); + return 0; +} + +static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* + * Get the Nick Name saved + */ + + mutex_lock(&adapter->lock); + strncpy(extra, adapter->nodename, 16); + mutex_unlock(&adapter->lock); + + extra[16] = '\0'; + + /* + * If none, we may want to get the one that was set + */ + + /* + * Push it out ! + */ + dwrq->length = strlen(extra) + 1; + + LEAVE(); + return 0; +} + +static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rthr = vwrq->value; + + ENTER(); + + if (vwrq->disabled) { + adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE; + } else { + if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE) + return -EINVAL; + adapter->rtsthsd = rthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, &rthr); + + LEAVE(); + return ret; +} + +static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->rtsthsd = 0; + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_RTS_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->rtsthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE) + || (vwrq->value > MRVDRV_RTS_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return 0; +} + +static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + int fthr = vwrq->value; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->disabled) { + adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE; + } else { + if (fthr < MRVDRV_FRAG_MIN_VALUE + || fthr > MRVDRV_FRAG_MAX_VALUE) + return -EINVAL; + adapter->fragthsd = fthr; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, &fthr); + LEAVE(); + return ret; +} + +static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + adapter->fragthsd = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_FRAGMENTATION_THRESHOLD, NULL); + if (ret) { + LEAVE(); + return ret; + } + + vwrq->value = adapter->fragthsd; + vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE) + || (vwrq->value > MRVDRV_FRAG_MAX_VALUE)); + vwrq->fixed = 1; + + LEAVE(); + return ret; +} + +static int wlan_get_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (adapter->inframode) { + case wlan802_11ibss: + *uwrq = IW_MODE_ADHOC; + break; + + case wlan802_11infrastructure: + *uwrq = IW_MODE_INFRA; + break; + + default: + case wlan802_11autounknown: + *uwrq = IW_MODE_AUTO; + break; + } + + LEAVE(); + return 0; +} + +static int wlan_get_txpow(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_get, + cmd_option_waitforrsp, 0, NULL); + + if (ret) { + LEAVE(); + return ret; + } + + lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel); + vwrq->value = adapter->txpowerlevel; + vwrq->fixed = 1; + if (adapter->radioon) { + vwrq->disabled = 0; + vwrq->flags = IW_TXPOW_DBM; + } else { + vwrq->disabled = 1; + } + + LEAVE(); + return 0; +} + +static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (vwrq->flags == IW_RETRY_LIMIT) { + /* The MAC has a 4-bit Total_Tx_Count register + Total_Tx_Count = 1 + Tx_Retry_Count */ +#define TX_RETRY_MIN 0 +#define TX_RETRY_MAX 14 + if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX) + return -EINVAL; + + /* Adding 1 to convert retry count to try count */ + adapter->txretrycount = vwrq->value + 1; + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib, + cmd_act_set, + cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + + if (ret) { + LEAVE(); + return ret; + } + } else { + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + +static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + + ENTER(); + adapter->txretrycount = 0; + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_snmp_mib, + cmd_act_get, cmd_option_waitforrsp, + OID_802_11_TX_RETRYCOUNT, NULL); + if (ret) { + LEAVE(); + return ret; + } + vwrq->disabled = 0; + if (!vwrq->flags) { + vwrq->flags = IW_RETRY_LIMIT; + /* Subtract 1 to convert try count to retry count */ + vwrq->value = adapter->txretrycount - 1; + } + + LEAVE(); + return 0; +} + +static inline void sort_channels(struct iw_freq *freq, int num) +{ + int i, j; + struct iw_freq temp; + + for (i = 0; i < num; i++) + for (j = i + 1; j < num; j++) + if (freq[i].i > freq[j].i) { + temp.i = freq[i].i; + temp.m = freq[i].m; + + freq[i].i = freq[j].i; + freq[i].m = freq[j].m; + + freq[j].i = temp.i; + freq[j].m = temp.m; + } +} + +/* data rate listing + MULTI_BANDS: + abg a b b/g + Infra G(12) A(8) B(4) G(12) + Adhoc A+B(12) A(8) B(4) B(4) + + non-MULTI_BANDS: + b b/g + Infra B(4) G(12) + Adhoc B(4) B(4) + */ +/** + * @brief Get Range Info + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_range(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int i, j; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_range *range = (struct iw_range *)extra; + struct chan_freq_power *cfp; + u8 rates[WLAN_SUPPORTED_RATES]; + + u8 flag = 0; + + ENTER(); + + dwrq->length = sizeof(struct iw_range); + memset(range, 0, sizeof(struct iw_range)); + + range->min_nwid = 0; + range->max_nwid = 0; + + memset(rates, 0, sizeof(rates)); + range->num_bitrates = get_active_data_rates(adapter, rates); + + for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i]; + i++) { + range->bitrate[i] = (rates[i] & 0x7f) * 500000; + } + range->num_bitrates = i; + lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES, + range->num_bitrates); + + range->num_frequency = 0; + if (priv->adapter->enable11d && + adapter->connect_status == libertas_connected) { + u8 chan_no; + u8 band; + + struct parsed_region_chan_11d *parsed_region_chan = + &adapter->parsed_region_chan; + + if (parsed_region_chan == NULL) { + lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n"); + LEAVE(); + return 0; + } + band = parsed_region_chan->band; + lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band, + parsed_region_chan->nr_chan); + + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (i < parsed_region_chan->nr_chan); i++) { + chan_no = parsed_region_chan->chanpwr[i].chan; + lbs_pr_debug(1, "chan_no=%d\n", chan_no); + range->freq[range->num_frequency].i = (long)chan_no; + range->freq[range->num_frequency].m = + (long)libertas_chan_2_freq(chan_no, band) * 100000; + range->freq[range->num_frequency].e = 1; + range->num_frequency++; + } + flag = 1; + } + if (!flag) { + for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && (j < sizeof(adapter->region_channel) + / sizeof(adapter->region_channel[0])); j++) { + cfp = adapter->region_channel[j].CFP; + for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES) + && adapter->region_channel[j].valid + && cfp + && (i < adapter->region_channel[j].nrcfp); i++) { + range->freq[range->num_frequency].i = + (long)cfp->channel; + range->freq[range->num_frequency].m = + (long)cfp->freq * 100000; + range->freq[range->num_frequency].e = 1; + cfp++; + range->num_frequency++; + } + } + } + + lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n", + IW_MAX_FREQUENCIES, range->num_frequency); + + range->num_channels = range->num_frequency; + + sort_channels(&range->freq[0], range->num_frequency); + + /* + * Set an indication of the max TCP throughput in bit/s that we can + * expect using this interface + */ + if (i > 2) + range->throughput = 5000 * 1000; + else + range->throughput = 1500 * 1000; + + range->min_rts = MRVDRV_RTS_MIN_VALUE; + range->max_rts = MRVDRV_RTS_MAX_VALUE; + range->min_frag = MRVDRV_FRAG_MIN_VALUE; + range->max_frag = MRVDRV_FRAG_MAX_VALUE; + + range->encoding_size[0] = 5; + range->encoding_size[1] = 13; + range->num_encoding_sizes = 2; + range->max_encoding_tokens = 4; + + range->min_pmp = 1000000; + range->max_pmp = 120000000; + range->min_pmt = 1000; + range->max_pmt = 1000000; + range->pmp_flags = IW_POWER_PERIOD; + range->pmt_flags = IW_POWER_TIMEOUT; + range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; + + /* + * Minimum version we recommend + */ + range->we_version_source = 15; + + /* + * Version we are compiled with + */ + range->we_version_compiled = WIRELESS_EXT; + + range->retry_capa = IW_RETRY_LIMIT; + range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX; + + range->min_retry = TX_RETRY_MIN; + range->max_retry = TX_RETRY_MAX; + + /* + * Set the qual, level and noise range values + */ + range->max_qual.qual = 100; + range->max_qual.level = 0; + range->max_qual.noise = 0; + range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->avg_qual.qual = 70; + /* TODO: Find real 'good' to 'bad' threshold value for RSSI */ + range->avg_qual.level = 0; + range->avg_qual.noise = 0; + range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + + range->sensitivity = 0; + + /* + * Setup the supported power level ranges + */ + memset(range->txpower, 0, sizeof(range->txpower)); + range->txpower[0] = 5; + range->txpower[1] = 7; + range->txpower[2] = 9; + range->txpower[3] = 11; + range->txpower[4] = 13; + range->txpower[5] = 15; + range->txpower[6] = 17; + range->txpower[7] = 19; + + range->num_txpower = 8; + range->txpower_capa = IW_TXPOW_DBM; + range->txpower_capa |= IW_TXPOW_RANGE; + + range->event_capa[0] = (IW_EVENT_CAPA_K_0 | + IW_EVENT_CAPA_MASK(SIOCGIWAP) | + IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); + range->event_capa[1] = IW_EVENT_CAPA_K_1; + + if (adapter->fwcapinfo & FW_CAPINFO_WPA) { + range->enc_capa = IW_ENC_CAPA_WPA + | IW_ENC_CAPA_WPA2 + | IW_ENC_CAPA_CIPHER_TKIP + | IW_ENC_CAPA_CIPHER_CCMP; + } + + LEAVE(); + return 0; +} + +static int wlan_set_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + /* PS is currently supported only in Infrastructure mode + * Remove this check if it is to be supported in IBSS mode also + */ + + if (vwrq->disabled) { + adapter->psmode = wlan802_11powermodecam; + if (adapter->psstate != PS_STATE_FULL_POWER) { + libertas_ps_wakeup(priv, cmd_option_waitforrsp); + } + + return 0; + } + + if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { + lbs_pr_debug(1, + "Setting power timeout command is not supported\n"); + return -EINVAL; + } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { + lbs_pr_debug(1, "Setting power period command is not supported\n"); + return -EINVAL; + } + + if (adapter->psmode != wlan802_11powermodecam) { + return 0; + } + + adapter->psmode = wlan802_11powermodemax_psp; + + if (adapter->connect_status == libertas_connected) { + libertas_ps_sleep(priv, cmd_option_waitforrsp); + } + + LEAVE(); + return 0; +} + +static int wlan_get_power(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int mode; + + ENTER(); + + mode = adapter->psmode; + + if ((vwrq->disabled = (mode == wlan802_11powermodecam)) + || adapter->connect_status == libertas_disconnected) { + LEAVE(); + return 0; + } + + vwrq->value = 0; + + LEAVE(); + return 0; +} + +/* + * iwpriv settable callbacks + */ + +static const iw_handler wlan_private_handler[] = { + NULL, /* SIOCIWFIRSTPRIV */ +}; + +static const struct iw_priv_args wlan_private_args[] = { + /* + * { cmd, set_args, get_args, name } + */ + { + WLANSCAN_TYPE, + IW_PRIV_TYPE_CHAR | 8, + IW_PRIV_TYPE_CHAR | 8, + "scantype"}, + + { + WLAN_SETINT_GETINT, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANNF, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getNF"}, + { + WLANRSSI, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getRSSI"}, + { + WLANENABLE11D, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "enable11d"}, + { + WLANADHOCGRATE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "adhocgrate"}, + + { + WLAN_SUBCMD_SET_PRESCAN, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "prescan"}, + { + WLAN_SETONEINT_GETONEINT, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + ""}, + { + WLAN_BEACON_INTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "bcninterval"}, + { + WLAN_LISTENINTRVL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "lolisteninter"}, + { + WLAN_TXCONTROL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "txcontrol"}, + { + WLAN_NULLPKTINTERVAL, + IW_PRIV_TYPE_INT | 1, + IW_PRIV_TYPE_INT | 1, + "psnullinterval"}, + /* Using iwpriv sub-command feature */ + { + WLAN_SETONEINT_GETNONE, /* IOCTL: 24 */ + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + ""}, + + { + WLAN_SUBCMD_SETRXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setrxant"}, + { + WLAN_SUBCMD_SETTXANTENNA, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "settxant"}, + { + WLANSETAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "authalgs", + }, + { + WLANSET8021XAUTHALG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "8021xauthalgs", + }, + { + WLANSETENCRYPTIONMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "encryptionmode", + }, + { + WLANSETREGION, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setregioncode"}, + { + WLAN_SET_LISTEN_INTERVAL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setlisteninter"}, + { + WLAN_SET_MULTIPLE_DTIM, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setmultipledtim"}, + { + WLAN_SET_ATIM_WINDOW, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "atimwindow"}, + { + WLANSETBCNAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setbcnavg"}, + { + WLANSETDATAAVG, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "setdataavg"}, + { + WLAN_SET_LINKMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "linkmode"}, + { + WLAN_SET_RADIOMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "radiomode"}, + { + WLAN_SET_DEBUGMODE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "debugmode"}, + { + WLAN_SUBCMD_MESH_SET_TTL, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + IW_PRIV_TYPE_NONE, + "mesh_set_ttl"}, + { + WLAN_SETNONE_GETONEINT, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + ""}, + { + WLANGETREGION, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getregioncode"}, + { + WLAN_GET_LISTEN_INTERVAL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getlisteninter"}, + { + WLAN_GET_MULTIPLE_DTIM, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getmultipledtim"}, + { + WLAN_GET_TX_RATE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "gettxrate"}, + { + WLANGETBCNAVG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "getbcnavg"}, + { + WLAN_GET_LINKMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_linkmode"}, + { + WLAN_GET_RADIOMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_radiomode"}, + { + WLAN_GET_DEBUGMODE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "get_debugmode"}, + { + WLAN_SUBCMD_FWT_CLEANUP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_cleanup"}, + { + WLAN_SUBCMD_FWT_TIME, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "fwt_time"}, + { + WLAN_SUBCMD_MESH_GET_TTL, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, + "mesh_get_ttl"}, + { + WLAN_SETNONE_GETTWELVE_CHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + ""}, + { + WLAN_SUBCMD_GETRXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "getrxant"}, + { + WLAN_SUBCMD_GETTXANTENNA, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettxant"}, + { + WLAN_GET_TSF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 12, + "gettsf"}, + { + WLAN_SETNONE_GETNONE, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + ""}, + { + WLANDEAUTH, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "deauth"}, + { + WLANADHOCSTOP, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "adhocstop"}, + { + WLANRADIOON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radioon"}, + { + WLANRADIOOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "radiooff"}, + { + WLANWLANIDLEON, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-on"}, + { + WLANWLANIDLEOFF, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "wlanidle-off"}, + { + WLAN_SUBCMD_FWT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "fwt_reset"}, + { + WLAN_SUBCMD_BT_RESET, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_NONE, + "bt_reset"}, + { + WLAN_SET128CHAR_GET128CHAR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + ""}, + /* BT Management */ + { + WLAN_SUBCMD_BT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_add"}, + { + WLAN_SUBCMD_BT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_del"}, + { + WLAN_SUBCMD_BT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "bt_list"}, + /* FWT Management */ + { + WLAN_SUBCMD_FWT_ADD, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_add"}, + { + WLAN_SUBCMD_FWT_DEL, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_del"}, + { + WLAN_SUBCMD_FWT_LOOKUP, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_lookup"}, + { + WLAN_SUBCMD_FWT_LIST_NEIGHBOR, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_neigh"}, + { + WLAN_SUBCMD_FWT_LIST, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list"}, + { + WLAN_SUBCMD_FWT_LIST_ROUTE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "fwt_list_route"}, + { + WLANSCAN_MODE, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "scanmode"}, + { + WLAN_GET_ADHOC_STATUS, + IW_PRIV_TYPE_CHAR | 128, + IW_PRIV_TYPE_CHAR | 128, + "getadhocstatus"}, + { + WLAN_SETNONE_GETWORDCHAR, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | 128, + ""}, + { + WLANSETWPAIE, + IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24, + IW_PRIV_TYPE_NONE, + "setwpaie"}, + { + WLANGETLOG, + IW_PRIV_TYPE_NONE, + IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE, + "getlog"}, + { + WLAN_SET_GET_SIXTEEN_INT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + ""}, + { + WLAN_TPCCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "tpccfg"}, + { + WLAN_POWERCFG, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "powercfg"}, + { + WLAN_AUTO_FREQ_SET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "setafc"}, + { + WLAN_AUTO_FREQ_GET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getafc"}, + { + WLAN_SCANPROBES, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "scanprobes"}, + { + WLAN_LED_GPIO_CTRL, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "ledgpio"}, + { + WLAN_ADAPT_RATESET, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "rateadapt"}, + { + WLAN_INACTIVITY_TIMEOUT, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "inactivityto"}, + { + WLANSNR, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getSNR"}, + { + WLAN_GET_RATE, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrate"}, + { + WLAN_GET_RXINFO, + IW_PRIV_TYPE_INT | 16, + IW_PRIV_TYPE_INT | 16, + "getrxinfo"}, +}; + +static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev) +{ + enum { + POOR = 30, + FAIR = 60, + GOOD = 80, + VERY_GOOD = 90, + EXCELLENT = 95, + PERFECT = 100 + }; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 rssi_qual; + u32 tx_qual; + u32 quality = 0; + int stats_valid = 0; + u8 rssi; + u32 tx_retries; + + ENTER(); + + priv->wstats.status = adapter->inframode; + + /* If we're not associated, all quality values are meaningless */ + if (adapter->connect_status != libertas_connected) + goto out; + + /* Quality by RSSI */ + priv->wstats.qual.level = + CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG], + adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + + if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) { + priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE; + } else { + priv->wstats.qual.noise = + CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]); + } + + lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level); + lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise); + + rssi = priv->wstats.qual.level - priv->wstats.qual.noise; + if (rssi < 15) + rssi_qual = rssi * POOR / 10; + else if (rssi < 20) + rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR; + else if (rssi < 30) + rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR; + else if (rssi < 40) + rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) / + 10 + GOOD; + else + rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) / + 10 + VERY_GOOD; + quality = rssi_qual; + + /* Quality by TX errors */ + priv->wstats.discard.retries = priv->stats.tx_errors; + + tx_retries = adapter->logmsg.retry; + + if (tx_retries > 75) + tx_qual = (90 - tx_retries) * POOR / 15; + else if (tx_retries > 70) + tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR; + else if (tx_retries > 65) + tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR; + else if (tx_retries > 50) + tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) / + 15 + GOOD; + else + tx_qual = (50 - tx_retries) * + (PERFECT - VERY_GOOD) / 50 + VERY_GOOD; + quality = min(quality, tx_qual); + + priv->wstats.discard.code = adapter->logmsg.wepundecryptable; + priv->wstats.discard.fragment = adapter->logmsg.fcserror; + priv->wstats.discard.retries = tx_retries; + priv->wstats.discard.misc = adapter->logmsg.ackfailure; + + /* Calculate quality */ + priv->wstats.qual.qual = max(quality, (u32)100); + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM; + stats_valid = 1; + + /* update stats asynchronously for future calls */ + libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0, + 0, 0, NULL); + libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0, + 0, 0, NULL); +out: + if (!stats_valid) { + priv->wstats.miss.beacon = 0; + priv->wstats.discard.retries = 0; + priv->wstats.qual.qual = 0; + priv->wstats.qual.level = 0; + priv->wstats.qual.noise = 0; + priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED; + priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID | + IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID; + } + + LEAVE (); + return &priv->wstats; + + +} + +static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info, + struct iw_freq *fwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int rc = -EINPROGRESS; /* Call commit handler */ + struct chan_freq_power *cfp; + + ENTER(); + + /* + * If setting by frequency, convert to a channel + */ + if (fwrq->e == 1) { + + long f = fwrq->m / 100000; + int c = 0; + + cfp = find_cfp_by_band_and_freq(adapter, 0, f); + if (!cfp) { + lbs_pr_debug(1, "Invalid freq=%ld\n", f); + return -EINVAL; + } + + c = (int)cfp->channel; + + if (c < 0) + return -EINVAL; + + fwrq->e = 0; + fwrq->m = c; + } + + /* + * Setting by channel number + */ + if (fwrq->m > 1000 || fwrq->e > 0) { + rc = -EOPNOTSUPP; + } else { + int channel = fwrq->m; + + cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel); + if (!cfp) { + rc = -EINVAL; + } else { + if (adapter->inframode == wlan802_11ibss) { + rc = changeadhocchannel(priv, channel); + /* If station is WEP enabled, send the + * command to set WEP in firmware + */ + if (adapter->secinfo.WEPstatus == + wlan802_11WEPenabled) { + lbs_pr_debug(1, "set_freq: WEP enabled\n"); + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_set_wep, + cmd_act_add, + cmd_option_waitforrsp, + 0, + NULL); + + if (ret) { + LEAVE(); + return ret; + } + + adapter->currentpacketfilter |= + cmd_act_mac_wep_enable; + + libertas_set_mac_packet_filter(priv); + } + } else { + rc = -EOPNOTSUPP; + } + } + } + + LEAVE(); + return rc; +} + +/** + * @brief use index to get the data rate + * + * @param index The index of data rate + * @return data rate or 0 + */ +u32 libertas_index_to_data_rate(u8 index) +{ + if (index >= sizeof(libertas_wlan_data_rates)) + index = 0; + + return libertas_wlan_data_rates[index]; +} + +/** + * @brief use rate to get the index + * + * @param rate data rate + * @return index or 0 + */ +u8 libertas_data_rate_to_index(u32 rate) +{ + u8 *ptr; + + if (rate) + if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate, + sizeof(libertas_wlan_data_rates)))) + return (ptr - libertas_wlan_data_rates); + + return 0; +} + +static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + u32 data_rate; + u16 action; + int ret = 0; + u8 rates[WLAN_SUPPORTED_RATES]; + u8 *rate; + + ENTER(); + + lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value); + + if (vwrq->value == -1) { + action = cmd_act_set_tx_auto; // Auto + adapter->is_datarate_auto = 1; + adapter->datarate = 0; + } else { + if (vwrq->value % 100000) { + return -EINVAL; + } + + data_rate = vwrq->value / 500000; + + memset(rates, 0, sizeof(rates)); + get_active_data_rates(adapter, rates); + rate = rates; + while (*rate) { + lbs_pr_debug(1, "Rate=0x%X Wanted=0x%X\n", *rate, + data_rate); + if ((*rate & 0x7f) == (data_rate & 0x7f)) + break; + rate++; + } + if (!*rate) { + lbs_pr_alert( "The fixed data rate 0x%X is out " + "of range.\n", data_rate); + return -EINVAL; + } + + adapter->datarate = data_rate; + action = cmd_act_set_tx_fix_rate; + adapter->is_datarate_auto = 0; + } + + ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate, + action, cmd_option_waitforrsp, 0, NULL); + + LEAVE(); + return ret; +} + +static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->is_datarate_auto) { + vwrq->fixed = 0; + } else { + vwrq->fixed = 1; + } + + vwrq->value = adapter->datarate * 500000; + + LEAVE(); + return 0; +} + +static int wlan_set_mode(struct net_device *dev, + struct iw_request_info *info, u32 * uwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode; + + ENTER(); + + switch (*uwrq) { + case IW_MODE_ADHOC: + lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n", + adapter->datarate); + new_mode = wlan802_11ibss; + adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL; + break; + + case IW_MODE_INFRA: + lbs_pr_debug(1, "Wanted mode is Infrastructure\n"); + new_mode = wlan802_11infrastructure; + break; + + case IW_MODE_AUTO: + lbs_pr_debug(1, "Wanted mode is Auto\n"); + new_mode = wlan802_11autounknown; + break; + + default: + lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq); + return -EINVAL; + } + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + assoc_req->mode = new_mode; + } + + if (ret == 0) { + set_bit(ASSOC_FLAG_MODE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +/** + * @brief Get Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_get_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, u8 * extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; + + ENTER(); + + lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n", + dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx); + + dwrq->flags = 0; + + /* Authentication method */ + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeopen: + dwrq->flags = IW_ENCODE_OPEN; + break; + + case wlan802_11authmodeshared: + case wlan802_11authmodenetworkEAP: + dwrq->flags = IW_ENCODE_RESTRICTED; + break; + default: + dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN; + break; + } + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) { + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + memset(extra, 0, 16); + + mutex_lock(&adapter->lock); + + /* Default to returning current transmit key */ + if (index < 0) + index = adapter->wep_tx_keyidx; + + if ((adapter->wep_keys[index].len) && + (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) { + memcpy(extra, adapter->wep_keys[index].key, + adapter->wep_keys[index].len); + dwrq->length = adapter->wep_keys[index].len; + + dwrq->flags |= (index + 1); + /* Return WEP enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else if ((adapter->secinfo.WPAenabled) + || (adapter->secinfo.WPA2enabled)) { + /* return WPA enabled */ + dwrq->flags &= ~IW_ENCODE_DISABLED; + } else { + dwrq->flags |= IW_ENCODE_DISABLED; + } + + mutex_unlock(&adapter->lock); + + dwrq->flags |= IW_ENCODE_NOKEY; + + lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n", + extra[0], extra[1], extra[2], + extra[3], extra[4], extra[5], dwrq->length); + + lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags); + + LEAVE(); + return 0; +} + +/** + * @brief Set Encryption key (internal) + * + * @param priv A pointer to private card structure + * @param key_material A pointer to key material + * @param key_length length of key material + * @param index key index to set + * @param set_tx_key Force set TX key (1 = yes, 0 = no) + * @return 0 --success, otherwise fail + */ +static int wlan_set_wep_key(struct assoc_request *assoc_req, + const char *key_material, + u16 key_length, + u16 index, + int set_tx_key) +{ + struct WLAN_802_11_KEY *pkey; + + ENTER(); + + /* Paranoid validation of key index */ + if (index > 3) { + LEAVE(); + return -EINVAL; + } + + /* validate max key length */ + if (key_length > KEY_LEN_WEP_104) { + LEAVE(); + return -EINVAL; + } + + pkey = &assoc_req->wep_keys[index]; + + if (key_length > 0) { + memset(pkey, 0, sizeof(struct WLAN_802_11_KEY)); + pkey->type = KEY_TYPE_ID_WEP; + + /* Standardize the key length */ + pkey->len = (key_length > KEY_LEN_WEP_40) ? + KEY_LEN_WEP_104 : KEY_LEN_WEP_40; + memcpy(pkey->key, key_material, key_length); + } + + if (set_tx_key) { + /* Ensure the chosen key is valid */ + if (!pkey->len) { + lbs_pr_debug(1, "key not set, so cannot enable it\n"); + LEAVE(); + return -EINVAL; + } + assoc_req->wep_tx_keyidx = index; + } + + assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled; + + LEAVE(); + return 0; +} + +static int validate_key_index(u16 def_index, u16 raw_index, + u16 *out_index, u16 *is_default) +{ + if (!out_index || !is_default) + return -EINVAL; + + /* Verify index if present, otherwise use default TX key index */ + if (raw_index > 0) { + if (raw_index > 4) + return -EINVAL; + *out_index = raw_index - 1; + } else { + *out_index = def_index; + *is_default = 1; + } + return 0; +} + +static void disable_wep(struct assoc_request *assoc_req) +{ + int i; + + /* Set Open System auth mode */ + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + + /* Clear WEP keys and mark WEP as disabled */ + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + for (i = 0; i < 4; i++) + assoc_req->wep_keys[i].len = 0; + + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); +} + +/** + * @brief Set Encryption key + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encode(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + u16 is_default = 0, index = 0, set_tx_key = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->flags & IW_ENCODE_DISABLED) { + disable_wep (assoc_req); + goto out; + } + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) { + ret = -EINVAL; + goto out; + } + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default)) + set_tx_key = 1; + + ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key); + if (ret) + goto out; + + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = wlan802_11authmodeopen; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Get Extended Encryption key (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 on success, otherwise failure + */ +static int wlan_get_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = -EINVAL; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int index, max_key_len; + + ENTER(); + + max_key_len = dwrq->length - sizeof(*ext); + if (max_key_len < 0) + goto out; + + index = dwrq->flags & IW_ENCODE_INDEX; + if (index) { + if (index < 1 || index > 4) + goto out; + index--; + } else { + index = adapter->wep_tx_keyidx; + } + + if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY && + ext->alg != IW_ENCODE_ALG_WEP) { + if (index != 0 || adapter->inframode != wlan802_11infrastructure) + goto out; + } + + dwrq->flags = index + 1; + memset(ext, 0, sizeof(*ext)); + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) + && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_NONE; + ext->key_len = 0; + dwrq->flags |= IW_ENCODE_DISABLED; + } else { + u8 *key = NULL; + + if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled) + && !adapter->secinfo.WPAenabled + && !adapter->secinfo.WPA2enabled) { + ext->alg = IW_ENCODE_ALG_WEP; + ext->key_len = adapter->wep_keys[index].len; + key = &adapter->wep_keys[index].key[0]; + } else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) && + (adapter->secinfo.WPAenabled || + adapter->secinfo.WPA2enabled)) { + /* WPA */ + ext->alg = IW_ENCODE_ALG_TKIP; + ext->key_len = 0; + } else { + goto out; + } + + if (ext->key_len > max_key_len) { + ret = -E2BIG; + goto out; + } + + if (ext->key_len) + memcpy(ext->key, key, ext->key_len); + else + dwrq->flags |= IW_ENCODE_NOKEY; + dwrq->flags |= IW_ENCODE_ENABLED; + } + ret = 0; + +out: + LEAVE(); + return ret; +} + +/** + * @brief Set Encryption key Extended (WPA/802.1x and WEP) + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param vwrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_encodeext(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; + int alg = ext->alg; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) { + disable_wep (assoc_req); + } else if (alg == IW_ENCODE_ALG_WEP) { + u16 is_default = 0, index, set_tx_key = 0; + + ret = validate_key_index(assoc_req->wep_tx_keyidx, + (dwrq->flags & IW_ENCODE_INDEX), + &index, &is_default); + if (ret) + goto out; + + /* If WEP isn't enabled, or if there is no key data but a valid + * index, or if the set-TX-key flag was passed, set the TX key. + */ + if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled) + || (dwrq->length == 0 && !is_default) + || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY)) + set_tx_key = 1; + + /* Copy key to driver */ + ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index, + set_tx_key); + if (ret) + goto out; + + if (dwrq->flags & IW_ENCODE_RESTRICTED) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->flags & IW_ENCODE_OPEN) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + + /* Mark the various WEP bits as modified */ + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + if (dwrq->length) + set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags); + if (set_tx_key) + set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags); + + } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) { + struct WLAN_802_11_KEY * pkey; + + /* validate key length */ + if (((alg == IW_ENCODE_ALG_TKIP) + && (ext->key_len != KEY_LEN_WPA_TKIP)) + || ((alg == IW_ENCODE_ALG_CCMP) + && (ext->key_len != KEY_LEN_WPA_AES))) { + lbs_pr_debug(1, "Invalid size %d for key of alg" + "type %d.\n", + ext->key_len, + alg); + ret = -EINVAL; + goto out; + } + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) + pkey = &assoc_req->wpa_mcast_key; + else + pkey = &assoc_req->wpa_unicast_key; + + memset(pkey, 0, sizeof (struct WLAN_802_11_KEY)); + memcpy(pkey->key, ext->key, ext->key_len); + pkey->len = ext->key_len; + pkey->flags = KEY_INFO_WPA_ENABLED; + + if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { + pkey->flags |= KEY_INFO_WPA_MCAST; + set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags); + } else { + pkey->flags |= KEY_INFO_WPA_UNICAST; + set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags); + } + + if (alg == IW_ENCODE_ALG_TKIP) + pkey->type = KEY_TYPE_ID_TKIP; + else if (alg == IW_ENCODE_ALG_CCMP) + pkey->type = KEY_TYPE_ID_AES; + + /* If WPA isn't enabled yet, do that now */ + if ( assoc_req->secinfo.WPAenabled == 0 + && assoc_req->secinfo.WPA2enabled == 0) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + } + + disable_wep (assoc_req); + } + +out: + if (ret == 0) { + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + + +static int wlan_set_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct assoc_request * assoc_req; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + if (dwrq->length > MAX_WPA_IE_LEN || + (dwrq->length && extra == NULL)) { + ret = -EINVAL; + goto out; + } + + if (dwrq->length) { + memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length); + assoc_req->wpa_ie_len = dwrq->length; + } else { + memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie)); + assoc_req->wpa_ie_len = 0; + } + +out: + if (ret == 0) { + set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_genie(struct net_device *dev, + struct iw_request_info *info, + struct iw_point *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + if (adapter->wpa_ie_len == 0) { + dwrq->length = 0; + LEAVE(); + return 0; + } + + if (dwrq->length < adapter->wpa_ie_len) { + LEAVE(); + return -E2BIG; + } + + dwrq->length = adapter->wpa_ie_len; + memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len); + + LEAVE(); + return 0; +} + + +static int wlan_set_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + int updated = 0; + + ENTER(); + + mutex_lock(&adapter->lock); + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + goto out; + } + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_TKIP_COUNTERMEASURES: + case IW_AUTH_CIPHER_PAIRWISE: + case IW_AUTH_CIPHER_GROUP: + case IW_AUTH_KEY_MGMT: + /* + * libertas does not use these parameters + */ + break; + + case IW_AUTH_WPA_VERSION: + if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) { + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + updated = 1; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + if (dwrq->value) { + adapter->currentpacketfilter |= + cmd_act_mac_strict_protection_enable; + } else { + adapter->currentpacketfilter &= + ~cmd_act_mac_strict_protection_enable; + } + updated = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) { + assoc_req->secinfo.authmode = + wlan802_11authmodeshared; + } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) { + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } else if (dwrq->value & IW_AUTH_ALG_LEAP) { + assoc_req->secinfo.authmode = + wlan802_11authmodenetworkEAP; + } else { + ret = -EINVAL; + } + updated = 1; + break; + + case IW_AUTH_WPA_ENABLED: + if (dwrq->value) { + if (!assoc_req->secinfo.WPAenabled && + !assoc_req->secinfo.WPA2enabled) { + assoc_req->secinfo.WPAenabled = 1; + assoc_req->secinfo.WPA2enabled = 1; + assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled; + assoc_req->secinfo.authmode = + wlan802_11authmodeopen; + } + } else { + assoc_req->secinfo.WPAenabled = 0; + assoc_req->secinfo.WPA2enabled = 0; + } + updated = 1; + break; + + default: + ret = -EOPNOTSUPP; + break; + } + +out: + if (ret == 0) { + if (updated) + set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags); + wlan_postpone_association_work(priv); + } else if (ret != -EOPNOTSUPP) { + wlan_cancel_association_work(priv); + } + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +static int wlan_get_auth(struct net_device *dev, + struct iw_request_info *info, + struct iw_param *dwrq, + char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + + switch (dwrq->flags & IW_AUTH_INDEX) { + case IW_AUTH_WPA_VERSION: + dwrq->value = 0; + if (adapter->secinfo.WPAenabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA; + if (adapter->secinfo.WPA2enabled) + dwrq->value |= IW_AUTH_WPA_VERSION_WPA2; + if (!dwrq->value) + dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED; + break; + + case IW_AUTH_DROP_UNENCRYPTED: + dwrq->value = 0; + if (adapter->currentpacketfilter & + cmd_act_mac_strict_protection_enable) + dwrq->value = 1; + break; + + case IW_AUTH_80211_AUTH_ALG: + switch (adapter->secinfo.authmode) { + case wlan802_11authmodeshared: + dwrq->value = IW_AUTH_ALG_SHARED_KEY; + break; + case wlan802_11authmodeopen: + dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM; + break; + case wlan802_11authmodenetworkEAP: + dwrq->value = IW_AUTH_ALG_LEAP; + break; + default: + break; + } + break; + + case IW_AUTH_WPA_ENABLED: + if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled) + dwrq->value = 1; + break; + + default: + LEAVE(); + return -EOPNOTSUPP; + } + + LEAVE(); + return 0; +} + + +static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info, + struct iw_param *vwrq, char *extra) +{ + int ret = 0; + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + u16 dbm; + + ENTER(); + + if (vwrq->disabled) { + wlan_radio_ioctl(priv, RADIO_OFF); + return 0; + } + + adapter->preamble = cmd_type_auto_preamble; + + wlan_radio_ioctl(priv, RADIO_ON); + + if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) { + dbm = (u16) mw_to_dbm(vwrq->value); + } else + dbm = (u16) vwrq->value; + + /* auto tx power control */ + + if (vwrq->fixed == 0) + dbm = 0xffff; + + lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm); + + ret = libertas_prepare_and_send_command(priv, + cmd_802_11_rf_tx_power, + cmd_act_tx_power_opt_set_low, + cmd_option_waitforrsp, 0, (void *)&dbm); + + LEAVE(); + return ret; +} + +static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + + ENTER(); + /* + * Note : if dwrq->flags != 0, we should get the relevant SSID from + * the SSID list... + */ + + /* + * Get the current SSID + */ + if (adapter->connect_status == libertas_connected) { + memcpy(extra, adapter->curbssparams.ssid.ssid, + adapter->curbssparams.ssid.ssidlength); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } else { + memset(extra, 0, 32); + extra[adapter->curbssparams.ssid.ssidlength] = '\0'; + } + /* + * If none, we may want to get the one that was set + */ + + /* To make the driver backward compatible with WPA supplicant v0.2.4 */ + if (dwrq->length == 32) /* check with WPA supplicant buffer size */ + dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength, + IW_ESSID_MAX_SIZE); + else + dwrq->length = adapter->curbssparams.ssid.ssidlength + 1; + + dwrq->flags = 1; /* active */ + + LEAVE(); + return 0; +} + +static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info, + struct iw_point *dwrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + int ret = 0; + struct WLAN_802_11_SSID ssid; + struct assoc_request * assoc_req; + int ssid_len = dwrq->length; + + ENTER(); + + /* + * WE-20 and earlier NULL pad the end of the SSID and increment + * SSID length so it can be used like a string. WE-21 and later don't, + * but some userspace tools aren't able to cope with the change. + */ + if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0')) + ssid_len--; + + /* Check the size of the string */ + if (ssid_len > IW_ESSID_MAX_SIZE) { + ret = -E2BIG; + goto out; + } + + memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID)); + + if (!dwrq->flags || !ssid_len) { + /* "any" SSID requested; leave SSID blank */ + } else { + /* Specific SSID requested */ + memcpy(&ssid.ssid, extra, ssid_len); + ssid.ssidlength = ssid_len; + } + + lbs_pr_debug(1, "Requested new SSID = %s\n", + (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any"); + +out: + mutex_lock(&adapter->lock); + if (ret == 0) { + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + ret = -ENOMEM; + } else { + /* Copy the SSID to the association request */ + memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID)); + set_bit(ASSOC_FLAG_SSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + } + + /* Cancel the association request if there was an error */ + if (ret != 0) { + wlan_cancel_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + LEAVE(); + return ret; +} + +/** + * @brief Connect to the AP or Ad-hoc Network with specific bssid + * + * @param dev A pointer to net_device structure + * @param info A pointer to iw_request_info structure + * @param awrq A pointer to iw_param structure + * @param extra A pointer to extra data buf + * @return 0 --success, otherwise fail + */ +static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info, + struct sockaddr *awrq, char *extra) +{ + wlan_private *priv = dev->priv; + wlan_adapter *adapter = priv->adapter; + struct assoc_request * assoc_req; + int ret = 0; + + ENTER(); + + if (awrq->sa_family != ARPHRD_ETHER) + return -EINVAL; + + lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data)); + + mutex_lock(&adapter->lock); + + /* Get or create the current association request */ + assoc_req = wlan_get_association_request(adapter); + if (!assoc_req) { + wlan_cancel_association_work(priv); + ret = -ENOMEM; + } else { + /* Copy the BSSID to the association request */ + memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN); + set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags); + wlan_postpone_association_work(priv); + } + + mutex_unlock(&adapter->lock); + + return ret; +} + +void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen) +{ + union { + u32 l; + u8 c[4]; + } ver; + char fwver[32]; + + mutex_lock(&adapter->lock); + ver.l = adapter->fwreleasenumber; + mutex_unlock(&adapter->lock); + + if (ver.c[3] == 0) + sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]); + else + sprintf(fwver, "%u.%u.%u.p%u", + ver.c[2], ver.c[1], ver.c[0], ver.c[3]); + + snprintf(fwversion, maxlen, fwver); +} + + +/* + * iwconfig settable callbacks + */ +static const iw_handler wlan_handler[] = { + (iw_handler) NULL, /* SIOCSIWCOMMIT */ + (iw_handler) wlan_get_name, /* SIOCGIWNAME */ + (iw_handler) NULL, /* SIOCSIWNWID */ + (iw_handler) NULL, /* SIOCGIWNWID */ + (iw_handler) wlan_set_freq, /* SIOCSIWFREQ */ + (iw_handler) wlan_get_freq, /* SIOCGIWFREQ */ + (iw_handler) wlan_set_mode, /* SIOCSIWMODE */ + (iw_handler) wlan_get_mode, /* SIOCGIWMODE */ + (iw_handler) NULL, /* SIOCSIWSENS */ + (iw_handler) NULL, /* SIOCGIWSENS */ + (iw_handler) NULL, /* SIOCSIWRANGE */ + (iw_handler) wlan_get_range, /* SIOCGIWRANGE */ + (iw_handler) NULL, /* SIOCSIWPRIV */ + (iw_handler) NULL, /* SIOCGIWPRIV */ + (iw_handler) NULL, /* SIOCSIWSTATS */ + (iw_handler) NULL, /* SIOCGIWSTATS */ + iw_handler_set_spy, /* SIOCSIWSPY */ + iw_handler_get_spy, /* SIOCGIWSPY */ + iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ + iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ + (iw_handler) wlan_set_wap, /* SIOCSIWAP */ + (iw_handler) wlan_get_wap, /* SIOCGIWAP */ + (iw_handler) NULL, /* SIOCSIWMLME */ + (iw_handler) NULL, /* SIOCGIWAPLIST - deprecated */ + (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */ + (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */ + (iw_handler) wlan_set_essid, /* SIOCSIWESSID */ + (iw_handler) wlan_get_essid, /* SIOCGIWESSID */ + (iw_handler) wlan_set_nick, /* SIOCSIWNICKN */ + (iw_handler) wlan_get_nick, /* SIOCGIWNICKN */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_rate, /* SIOCSIWRATE */ + (iw_handler) wlan_get_rate, /* SIOCGIWRATE */ + (iw_handler) wlan_set_rts, /* SIOCSIWRTS */ + (iw_handler) wlan_get_rts, /* SIOCGIWRTS */ + (iw_handler) wlan_set_frag, /* SIOCSIWFRAG */ + (iw_handler) wlan_get_frag, /* SIOCGIWFRAG */ + (iw_handler) wlan_set_txpow, /* SIOCSIWTXPOW */ + (iw_handler) wlan_get_txpow, /* SIOCGIWTXPOW */ + (iw_handler) wlan_set_retry, /* SIOCSIWRETRY */ + (iw_handler) wlan_get_retry, /* SIOCGIWRETRY */ + (iw_handler) wlan_set_encode, /* SIOCSIWENCODE */ + (iw_handler) wlan_get_encode, /* SIOCGIWENCODE */ + (iw_handler) wlan_set_power, /* SIOCSIWPOWER */ + (iw_handler) wlan_get_power, /* SIOCGIWPOWER */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) NULL, /* -- hole -- */ + (iw_handler) wlan_set_genie, /* SIOCSIWGENIE */ + (iw_handler) wlan_get_genie, /* SIOCGIWGENIE */ + (iw_handler) wlan_set_auth, /* SIOCSIWAUTH */ + (iw_handler) wlan_get_auth, /* SIOCGIWAUTH */ + (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */ + (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */ + (iw_handler) NULL, /* SIOCSIWPMKSA */ +}; + +struct iw_handler_def libertas_handler_def = { + .num_standard = sizeof(wlan_handler) / sizeof(iw_handler), + .num_private = sizeof(wlan_private_handler) / sizeof(iw_handler), + .num_private_args = sizeof(wlan_private_args) / + sizeof(struct iw_priv_args), + .standard = (iw_handler *) wlan_handler, + .private = (iw_handler *) wlan_private_handler, + .private_args = (struct iw_priv_args *)wlan_private_args, + .get_wireless_stats = wlan_get_wireless_stats, +}; diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h new file mode 100644 index 0000000..39f367c3 --- /dev/null +++ b/drivers/net/wireless/libertas/wext.h @@ -0,0 +1,147 @@ +/** + * This file contains definition for IOCTL call. + */ +#ifndef _WLAN_WEXT_H_ +#define _WLAN_WEXT_H_ + +#define SUBCMD_OFFSET 4 +#define SUBCMD_DATA(x) *((int *)(x->u.name + SUBCMD_OFFSET)) + +/** PRIVATE CMD ID */ +#define WLANIOCTL SIOCIWFIRSTPRIV + +#define WLANSETWPAIE (WLANIOCTL + 0) + +#define WLAN_SETINT_GETINT (WLANIOCTL + 7) +#define WLANNF 1 +#define WLANRSSI 2 +#define WLANENABLE11D 5 +#define WLANADHOCGRATE 6 +#define WLAN_SUBCMD_SET_PRESCAN 11 + +#define WLAN_SETNONE_GETNONE (WLANIOCTL + 8) +#define WLANDEAUTH 1 +#define WLANRADIOON 2 +#define WLANRADIOOFF 3 +#define WLANREMOVEADHOCAES 4 +#define WLANADHOCSTOP 5 +#define WLANCIPHERTEST 6 +#define WLANCRYPTOTEST 7 + +#define WLANWLANIDLEON 10 +#define WLANWLANIDLEOFF 11 +#define WLAN_SUBCMD_BT_RESET 13 +#define WLAN_SUBCMD_FWT_RESET 14 + +#define WLANGETLOG (WLANIOCTL + 9) +#define GETLOG_BUFSIZE 300 + +#define WLANSCAN_TYPE (WLANIOCTL + 11) + +#define WLAN_SETNONE_GETONEINT (WLANIOCTL + 15) +#define WLANGETREGION 1 +#define WLAN_GET_LISTEN_INTERVAL 2 +#define WLAN_GET_MULTIPLE_DTIM 3 +#define WLAN_GET_TX_RATE 4 +#define WLANGETBCNAVG 5 + +#define WLAN_GET_LINKMODE 6 +#define WLAN_GET_RADIOMODE 7 +#define WLAN_GET_DEBUGMODE 8 +#define WLAN_SUBCMD_FWT_CLEANUP 15 +#define WLAN_SUBCMD_FWT_TIME 16 +#define WLAN_SUBCMD_MESH_GET_TTL 17 + +#define WLANREGCFRDWR (WLANIOCTL + 18) + +#define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19) +#define WLAN_SUBCMD_GETRXANTENNA 1 +#define WLAN_SUBCMD_GETTXANTENNA 2 +#define WLAN_GET_TSF 3 + +#define WLAN_SETNONE_GETWORDCHAR (WLANIOCTL + 21) +#define WLANGETADHOCAES 1 + +#define WLAN_SETONEINT_GETONEINT (WLANIOCTL + 23) +#define WLAN_BEACON_INTERVAL 1 +#define WLAN_LISTENINTRVL 4 + +#define WLAN_TXCONTROL 6 +#define WLAN_NULLPKTINTERVAL 7 + +#define WLAN_SETONEINT_GETNONE (WLANIOCTL + 24) +#define WLAN_SUBCMD_SETRXANTENNA 1 +#define WLAN_SUBCMD_SETTXANTENNA 2 +#define WLANSETAUTHALG 5 +#define WLANSET8021XAUTHALG 6 +#define WLANSETENCRYPTIONMODE 7 +#define WLANSETREGION 8 +#define WLAN_SET_LISTEN_INTERVAL 9 + +#define WLAN_SET_MULTIPLE_DTIM 10 +#define WLAN_SET_ATIM_WINDOW 11 +#define WLANSETBCNAVG 13 +#define WLANSETDATAAVG 14 +#define WLAN_SET_LINKMODE 15 +#define WLAN_SET_RADIOMODE 16 +#define WLAN_SET_DEBUGMODE 17 +#define WLAN_SUBCMD_MESH_SET_TTL 18 + +#define WLAN_SET128CHAR_GET128CHAR (WLANIOCTL + 25) +#define WLANSCAN_MODE 6 + +#define WLAN_GET_ADHOC_STATUS 9 + +#define WLAN_SUBCMD_BT_ADD 18 +#define WLAN_SUBCMD_BT_DEL 19 +#define WLAN_SUBCMD_BT_LIST 20 +#define WLAN_SUBCMD_FWT_ADD 21 +#define WLAN_SUBCMD_FWT_DEL 22 +#define WLAN_SUBCMD_FWT_LOOKUP 23 +#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR 24 +#define WLAN_SUBCMD_FWT_LIST 25 +#define WLAN_SUBCMD_FWT_LIST_ROUTE 26 + +#define WLAN_SET_GET_SIXTEEN_INT (WLANIOCTL + 29) +#define WLAN_TPCCFG 1 +#define WLAN_POWERCFG 2 + +#define WLAN_AUTO_FREQ_SET 3 +#define WLAN_AUTO_FREQ_GET 4 +#define WLAN_LED_GPIO_CTRL 5 +#define WLAN_SCANPROBES 6 +#define WLAN_ADAPT_RATESET 8 +#define WLAN_INACTIVITY_TIMEOUT 9 +#define WLANSNR 10 +#define WLAN_GET_RATE 11 +#define WLAN_GET_RXINFO 12 + +#define WLANCMD52RDWR (WLANIOCTL + 30) +#define WLANCMD53RDWR (WLANIOCTL + 31) +#define CMD53BUFLEN 32 + +#define REG_MAC 0x19 +#define REG_BBP 0x1a +#define REG_RF 0x1b +#define REG_EEPROM 0x59 +#define WLAN_LINKMODE_802_3 0 +#define WLAN_LINKMODE_802_11 2 +#define WLAN_RADIOMODE_NONE 0 +#define WLAN_RADIOMODE_RADIOTAP 2 + +/** wlan_ioctl_regrdwr */ +struct wlan_ioctl_regrdwr { + /** Which register to access */ + u16 whichreg; + /** Read or Write */ + u16 action; + u32 offset; + u16 NOB; + u32 value; +}; + +extern struct iw_handler_def libertas_handler_def; +int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i); +int wlan_radio_ioctl(wlan_private * priv, u8 option); + +#endif /* _WLAN_WEXT_H_ */ diff --git a/drivers/net/wireless/netwave_cs.c b/drivers/net/wireless/netwave_cs.c index a009ab5..45b00e1 100644 --- a/drivers/net/wireless/netwave_cs.c +++ b/drivers/net/wireless/netwave_cs.c @@ -1283,7 +1283,6 @@ static int netwave_rx(struct net_device *dev) skb_reserve( skb, 2); /* Align IP on 16 byte */ skb_put( skb, rcvLen); - skb->dev = dev; /* Copy packet fragments to the skb data area */ ptr = (u_char*) skb->data; diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c index 4e7f6cf..062286d 100644 --- a/drivers/net/wireless/orinoco.c +++ b/drivers/net/wireless/orinoco.c @@ -689,7 +689,7 @@ static void orinoco_stat_gather(struct net_device *dev, /* Note : gcc will optimise the whole section away if * WIRELESS_SPY is not defined... - Jean II */ if (SPY_NUMBER(priv)) { - orinoco_spy_gather(dev, skb->mac.raw + ETH_ALEN, + orinoco_spy_gather(dev, skb_mac_header(skb) + ETH_ALEN, desc->signal, desc->silence); } } @@ -770,7 +770,7 @@ static void orinoco_rx_monitor(struct net_device *dev, u16 rxfid, /* Copy the 802.11 header to the skb */ memcpy(skb_put(skb, hdrlen), &(desc->frame_ctl), hdrlen); - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); /* If any, copy the data from the card to the skb */ if (datalen > 0) { @@ -915,7 +915,6 @@ static void __orinoco_ev_rx(struct net_device *dev, hermes_t *hw) memcpy(hdr->h_source, desc.addr2, ETH_ALEN); dev->last_rx = jiffies; - skb->dev = dev; skb->protocol = eth_type_trans(skb, dev); skb->ip_summed = CHECKSUM_NONE; if (fc & IEEE80211_FCTL_TODS) diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c index 838d510..841b3c1 100644 --- a/drivers/net/wireless/prism54/isl_ioctl.c +++ b/drivers/net/wireless/prism54/isl_ioctl.c @@ -1395,11 +1395,16 @@ static int prism54_set_auth(struct net_device *ndev, break; case IW_AUTH_RX_UNENCRYPTED_EAPOL: - dot1x = param->value ? 1 : 0; + /* dot1x should be the opposite of RX_UNENCRYPTED_EAPOL; + * turn off dot1x when allowing receipt of unencrypted EAPOL + * frames, turn on dot1x when receipt should be disallowed + */ + dot1x = param->value ? 0 : 0x01; break; case IW_AUTH_PRIVACY_INVOKED: privinvoked = param->value ? 1 : 0; + break; case IW_AUTH_DROP_UNENCRYPTED: exunencrypt = param->value ? 1 : 0; @@ -1589,6 +1594,7 @@ static int prism54_set_encodeext(struct net_device *ndev, } key.type = DOT11_PRIV_TKIP; key.length = KEY_SIZE_TKIP; + break; default: return -EINVAL; } diff --git a/drivers/net/wireless/prism54/islpci_eth.c b/drivers/net/wireless/prism54/islpci_eth.c index b112291..dd070cc 100644 --- a/drivers/net/wireless/prism54/islpci_eth.c +++ b/drivers/net/wireless/prism54/islpci_eth.c @@ -136,7 +136,7 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) printk("islpci_eth_transmit:wds_mac\n"); #endif memmove(skb->data + 6, src, skb->len); - memcpy(skb->data, wds_mac, 6); + skb_copy_to_linear_data(skb, wds_mac, 6); } else { memmove(skb->data, src, skb->len); } @@ -162,13 +162,16 @@ islpci_eth_transmit(struct sk_buff *skb, struct net_device *ndev) skb_put(newskb, init_wds ? skb->len + 6 : skb->len); if (init_wds) { - memcpy(newskb->data + 6, skb->data, skb->len); - memcpy(newskb->data, wds_mac, 6); + skb_copy_from_linear_data(skb, + newskb->data + 6, + skb->len); + skb_copy_to_linear_data(newskb, wds_mac, 6); #ifdef ISLPCI_ETH_DEBUG printk("islpci_eth_transmit:wds_mac\n"); #endif } else - memcpy(newskb->data, skb->data, skb->len); + skb_copy_from_linear_data(skb, newskb->data, + skb->len); #if VERBOSE > SHOW_ERROR_MESSAGES DEBUG(SHOW_TRACING, "memcpy %p %p %i wds %i\n", @@ -303,7 +306,7 @@ islpci_monitor_rx(islpci_private *priv, struct sk_buff **skb) skb_pull(*skb, sizeof (struct rfmon_header)); (*skb)->protocol = htons(ETH_P_802_2); - (*skb)->mac.raw = (*skb)->data; + skb_reset_mac_header(*skb); (*skb)->pkt_type = PACKET_OTHERHOST; return 0; @@ -374,10 +377,6 @@ islpci_eth_receive(islpci_private *priv) DEBUG(SHOW_BUFFER_CONTENTS, "\nrx %p ", skb->data); display_buffer((char *) skb->data, skb->len); #endif - - /* do some additional sk_buff and network layer parameters */ - skb->dev = ndev; - /* take care of monitor mode and spy monitoring. */ if (unlikely(priv->iw_mode == IW_MODE_MONITOR)) discard = islpci_monitor_rx(priv, &skb); @@ -398,8 +397,10 @@ islpci_eth_receive(islpci_private *priv) /* Update spy records */ wireless_spy_update(ndev, annex->addr2, &wstats); - memcpy(skb->data + sizeof (struct rfmon_header), - skb->data, 2 * ETH_ALEN); + skb_copy_from_linear_data(skb, + (skb->data + + sizeof(struct rfmon_header)), + 2 * ETH_ALEN); skb_pull(skb, sizeof (struct rfmon_header)); } skb->protocol = eth_type_trans(skb, ndev); diff --git a/drivers/net/wireless/prism54/oid_mgt.c b/drivers/net/wireless/prism54/oid_mgt.c index e6cf9df..4278032 100644 --- a/drivers/net/wireless/prism54/oid_mgt.c +++ b/drivers/net/wireless/prism54/oid_mgt.c @@ -16,6 +16,8 @@ * */ +#include <linux/kernel.h> + #include "prismcompat.h" #include "islpci_dev.h" #include "islpci_mgt.h" @@ -692,7 +694,7 @@ mgt_update_addr(islpci_private *priv) return ret; } -#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0])) +#define VEC_SIZE(a) ARRAY_SIZE(a) int mgt_commit(islpci_private *priv) diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c index 47b2ccb..3be6242 100644 --- a/drivers/net/wireless/ray_cs.c +++ b/drivers/net/wireless/ray_cs.c @@ -2232,7 +2232,6 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, unsigned i return; } skb_reserve( skb, 2); /* Align IP on 16 byte (TBD check this)*/ - skb->dev = dev; DEBUG(4,"ray_cs rx_data total_len = %x, rx_len = %x\n",total_len,rx_len); @@ -2243,7 +2242,8 @@ static void rx_data(struct net_device *dev, struct rcs __iomem *prcs, unsigned i rx_ptr += copy_from_rx_buff(local, rx_ptr, pkt_addr & RX_BUFF_END, rx_len); /* Get source address */ #ifdef WIRELESS_SPY - memcpy(linksrcaddr, ((struct mac_header *)skb->data)->addr_2, ETH_ALEN); + skb_copy_from_linear_data_offset(skb, offsetof(struct mac_header, addr_2), + linksrcaddr, ETH_ALEN); #endif /* Now, deal with encapsulation/translation/sniffer */ if (!sniffer) { diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c index f5ce1c6..2a299a0 100644 --- a/drivers/net/wireless/strip.c +++ b/drivers/net/wireless/strip.c @@ -2009,7 +2009,7 @@ static void deliver_packet(struct strip *strip_info, STRIP_Header * header, packetlen); skb->dev = get_strip_dev(strip_info); skb->protocol = header->protocol; - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); /* Having put a fake header on the front of the sk_buff for the */ /* benefit of tools like tcpdump, skb_pull now 'consumes' that */ diff --git a/drivers/net/wireless/todo.txt b/drivers/net/wireless/todo.txt deleted file mode 100644 index 3223401..0000000 --- a/drivers/net/wireless/todo.txt +++ /dev/null @@ -1,15 +0,0 @@ - Wireless Todo - ------------- - -1) Bring other kernel Wireless LAN drivers here - Completed - -2) Bring new Wireless LAN driver not yet in the kernel there - See my web page for details - In particular : HostAP - -3) Misc - o Mark wavelan, wavelan_cs, netwave_cs drivers as obsolete - o Maybe arlan.c, ray_cs.c and strip.c also deserve to be obsolete - - Jean II diff --git a/drivers/net/wireless/wavelan.c b/drivers/net/wireless/wavelan.c index 24221e4..1cf090d 100644 --- a/drivers/net/wireless/wavelan.c +++ b/drivers/net/wireless/wavelan.c @@ -28,7 +28,7 @@ */ static u8 wv_irq_to_psa(int irq) { - if (irq < 0 || irq >= NELS(irqvals)) + if (irq < 0 || irq >= ARRAY_SIZE(irqvals)) return 0; return irqvals[irq]; @@ -42,7 +42,7 @@ static int __init wv_psa_to_irq(u8 irqval) { int irq; - for (irq = 0; irq < NELS(irqvals); irq++) + for (irq = 0; irq < ARRAY_SIZE(irqvals); irq++) if (irqvals[irq] == irqval) return irq; @@ -1695,7 +1695,7 @@ static int wv_frequency_list(unsigned long ioaddr, /* I/O port of the card */ /* Look in the table if the frequency is allowed */ if (table[9 - (freq / 16)] & (1 << (freq % 16))) { /* Compute approximate channel number */ - while ((c < NELS(channel_bands)) && + while ((c < ARRAY_SIZE(channel_bands)) && (((channel_bands[c] >> 1) - 24) < freq)) c++; list[i].i = c; /* Set the list index */ @@ -2512,14 +2512,13 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize) return; } - skb->dev = dev; - /* Copy the packet to the buffer. */ obram_read(ioaddr, buf_off, skb_put(skb, sksize), sksize); skb->protocol = eth_type_trans(skb, dev); #ifdef DEBUG_RX_INFO - wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); + wv_packet_info(skb_mac_header(skb), sksize, dev->name, + "wv_packet_read"); #endif /* DEBUG_RX_INFO */ /* Statistics-gathering and associated stuff. @@ -2555,7 +2554,7 @@ wv_packet_read(struct net_device * dev, u16 buf_off, int sksize) /* Spying stuff */ #ifdef IW_WIRELESS_SPY - wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); #endif /* IW_WIRELESS_SPY */ #ifdef HISTOGRAM @@ -2939,7 +2938,7 @@ static int wavelan_packet_xmit(struct sk_buff *skb, struct net_device * dev) * need to pad. Jean II */ if (skb->len < ETH_ZLEN) { memset(data, 0, ETH_ZLEN); - memcpy(data, skb->data, skb->len); + skb_copy_from_linear_data(skb, data, skb->len); /* Write packet on the card */ if(wv_packet_write(dev, data, ETH_ZLEN)) return 1; /* We failed */ @@ -4269,7 +4268,7 @@ struct net_device * __init wavelan_probe(int unit) printk(KERN_DEBUG "%s: <-wavelan_probe()\n", dev->name); #endif } else { /* Scan all possible addresses of the WaveLAN hardware. */ - for (i = 0; i < NELS(iobase); i++) { + for (i = 0; i < ARRAY_SIZE(iobase); i++) { dev->irq = def_irq; if (wavelan_config(dev, iobase[i]) == 0) { #ifdef DEBUG_CALLBACK_TRACE @@ -4280,7 +4279,7 @@ struct net_device * __init wavelan_probe(int unit) break; } } - if (i == NELS(iobase)) + if (i == ARRAY_SIZE(iobase)) r = -ENODEV; } if (r) @@ -4327,14 +4326,14 @@ int __init init_module(void) #endif /* Copy the basic set of address to be probed. */ - for (i = 0; i < NELS(iobase); i++) + for (i = 0; i < ARRAY_SIZE(iobase); i++) io[i] = iobase[i]; } /* Loop on all possible base addresses. */ i = -1; - while ((io[++i] != 0) && (i < NELS(io))) { + while ((io[++i] != 0) && (i < ARRAY_SIZE(io))) { struct net_device *dev = alloc_etherdev(sizeof(net_local)); if (!dev) break; diff --git a/drivers/net/wireless/wavelan.p.h b/drivers/net/wireless/wavelan.p.h index 72b646c..fe24281 100644 --- a/drivers/net/wireless/wavelan.p.h +++ b/drivers/net/wireless/wavelan.p.h @@ -449,9 +449,6 @@ static const char *version = "wavelan.c : v24 (SMP + wireless extensions) 11/12/ /* Watchdog temporisation */ #define WATCHDOG_JIFFIES (512*HZ/100) -/* Macro to get the number of elements in an array */ -#define NELS(a) (sizeof(a) / sizeof(a[0])) - /* ------------------------ PRIVATE IOCTL ------------------------ */ #define SIOCSIPQTHR SIOCIWFIRSTPRIV /* Set quality threshold */ diff --git a/drivers/net/wireless/wavelan_cs.c b/drivers/net/wireless/wavelan_cs.c index 5eb8163..67b867f8 100644 --- a/drivers/net/wireless/wavelan_cs.c +++ b/drivers/net/wireless/wavelan_cs.c @@ -1168,7 +1168,7 @@ wv_mmc_show(struct net_device * dev) m.mmr_unused0[6], m.mmr_unused0[7]); #endif /* DEBUG_SHOW_UNUSED */ - printk(KERN_DEBUG "Encryption algorythm: %02X - Status: %02X\n", + printk(KERN_DEBUG "Encryption algorithm: %02X - Status: %02X\n", m.mmr_des_avail, m.mmr_des_status); #ifdef DEBUG_SHOW_UNUSED printk(KERN_DEBUG "mmc_unused1[]: %02X:%02X:%02X:%02X:%02X\n", @@ -2884,14 +2884,12 @@ wv_packet_read(struct net_device * dev, return; } - skb->dev = dev; - skb_reserve(skb, 2); fd_p = read_ringbuf(dev, fd_p, (char *) skb_put(skb, sksize), sksize); skb->protocol = eth_type_trans(skb, dev); #ifdef DEBUG_RX_INFO - wv_packet_info(skb->mac.raw, sksize, dev->name, "wv_packet_read"); + wv_packet_info(skb_mac_header(skb), sksize, dev->name, "wv_packet_read"); #endif /* DEBUG_RX_INFO */ /* Statistics gathering & stuff associated. @@ -2925,7 +2923,7 @@ wv_packet_read(struct net_device * dev, #endif /* WAVELAN_ROAMING */ #ifdef WIRELESS_SPY - wl_spy_gather(dev, skb->mac.raw + WAVELAN_ADDR_SIZE, stats); + wl_spy_gather(dev, skb_mac_header(skb) + WAVELAN_ADDR_SIZE, stats); #endif /* WIRELESS_SPY */ #ifdef HISTOGRAM wl_his_gather(dev, stats); @@ -3590,9 +3588,9 @@ wv_82593_config(struct net_device * dev) cfblk.acloc = TRUE; /* Disable source addr insertion by i82593 */ cfblk.preamb_len = 0; /* 2 bytes preamble (SFD) */ cfblk.loopback = FALSE; - cfblk.lin_prio = 0; /* conform to 802.3 backoff algoritm */ - cfblk.exp_prio = 5; /* conform to 802.3 backoff algoritm */ - cfblk.bof_met = 1; /* conform to 802.3 backoff algoritm */ + cfblk.lin_prio = 0; /* conform to 802.3 backoff algorithm */ + cfblk.exp_prio = 5; /* conform to 802.3 backoff algorithm */ + cfblk.bof_met = 1; /* conform to 802.3 backoff algorithm */ cfblk.ifrm_spc = 0x20 >> 4; /* 32 bit times interframe spacing */ cfblk.slottim_low = 0x20 >> 5; /* 32 bit times slot time */ cfblk.slottim_hi = 0x0; diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c index c250f08..ce9230b 100644 --- a/drivers/net/wireless/wl3501_cs.c +++ b/drivers/net/wireless/wl3501_cs.c @@ -26,7 +26,6 @@ * Tested with Planet AP in 2.5.73-bk, 216 Kbytes/s in Infrastructure mode * with a SMP machine (dual pentium 100), using pktgen, 432 pps (pkt_size = 60) */ -#undef REALLY_SLOW_IO /* most systems can safely undef this */ #include <linux/delay.h> #include <linux/types.h> diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c index 6cb66a3..935b144 100644 --- a/drivers/net/wireless/zd1201.c +++ b/drivers/net/wireless/zd1201.c @@ -327,7 +327,6 @@ static void zd1201_usbrx(struct urb *urb) memcpy(skb_put(skb, 6), &data[datalen-8], 6); memcpy(skb_put(skb, 2), &data[datalen-24], 2); memcpy(skb_put(skb, len), data, len); - skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; @@ -385,7 +384,6 @@ static void zd1201_usbrx(struct urb *urb) memcpy(skb_put(skb, 2), &data[6], 2); memcpy(skb_put(skb, len), data+8, len); } - skb->dev = zd->dev; skb->dev->last_rx = jiffies; skb->protocol = eth_type_trans(skb, zd->dev); zd->stats.rx_packets++; @@ -809,10 +807,10 @@ static int zd1201_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) txbuf[4] = 0x00; txbuf[5] = 0x00; - memcpy(txbuf+6, skb->data+12, skb->len-12); + skb_copy_from_linear_data_offset(skb, 12, txbuf + 6, skb->len - 12); if (pad) txbuf[skb->len-12+6]=0; - memcpy(txbuf+skb->len-12+6+pad, skb->data, 12); + skb_copy_from_linear_data(skb, txbuf + skb->len - 12 + 6 + pad, 12); *(__be16*)&txbuf[skb->len+6+pad] = htons(skb->len-12+6); txbuf[txbuflen-1] = 0; diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig index 66ed55b..d1ab24a 100644 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -1,6 +1,7 @@ config ZD1211RW tristate "ZyDAS ZD1211/ZD1211B USB-wireless support" - depends on USB && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL + depends on USB && IEEE80211_SOFTMAC && WLAN_80211 && EXPERIMENTAL + select WIRELESS_EXT select FW_LOADER ---help--- This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c index 12dfc0b..95b4a2a 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.c +++ b/drivers/net/wireless/zd1211rw/zd_chip.c @@ -67,11 +67,12 @@ static int scnprint_id(struct zd_chip *chip, char *buffer, size_t size) i += scnprint_mac_oui(chip->e2p_mac, buffer+i, size-i); i += scnprintf(buffer+i, size-i, " "); i += zd_rf_scnprint_id(&chip->rf, buffer+i, size-i); - i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c", chip->pa_type, + i += scnprintf(buffer+i, size-i, " pa%1x %c%c%c%c%c", chip->pa_type, chip->patch_cck_gain ? 'g' : '-', chip->patch_cr157 ? '7' : '-', chip->patch_6m_band_edge ? '6' : '-', - chip->new_phy_layout ? 'N' : '-'); + chip->new_phy_layout ? 'N' : '-', + chip->al2230s_bit ? 'S' : '-'); return i; } @@ -113,8 +114,8 @@ int zd_ioread32v_locked(struct zd_chip *chip, u32 *values, const zd_addr_t *addr /* Allocate a single memory block for values and addresses. */ count16 = 2*count; - a16 = kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), - GFP_NOFS); + a16 = (zd_addr_t *) kmalloc(count16 * (sizeof(zd_addr_t) + sizeof(u16)), + GFP_KERNEL); if (!a16) { dev_dbg_f(zd_chip_dev(chip), "error ENOMEM in allocation of a16\n"); @@ -163,7 +164,7 @@ int _zd_iowrite32v_locked(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs, /* Allocate a single memory block for values and addresses. */ count16 = 2*count; - ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_NOFS); + ioreqs16 = kmalloc(count16 * sizeof(struct zd_ioreq16), GFP_KERNEL); if (!ioreqs16) { r = -ENOMEM; dev_dbg_f(zd_chip_dev(chip), @@ -337,6 +338,7 @@ static int read_pod(struct zd_chip *chip, u8 *rf_type) chip->patch_cr157 = (value >> 13) & 0x1; chip->patch_6m_band_edge = (value >> 21) & 0x1; chip->new_phy_layout = (value >> 31) & 0x1; + chip->al2230s_bit = (value >> 7) & 0x1; chip->link_led = ((value >> 4) & 1) ? LED1 : LED2; chip->supports_tx_led = 1; if (value & (1 << 24)) { /* LED scenario */ @@ -591,16 +593,16 @@ int zd_chip_unlock_phy_regs(struct zd_chip *chip) return r; } -/* CR157 can be optionally patched by the EEPROM */ +/* CR157 can be optionally patched by the EEPROM for original ZD1211 */ static int patch_cr157(struct zd_chip *chip) { int r; - u32 value; + u16 value; if (!chip->patch_cr157) return 0; - r = zd_ioread32_locked(chip, &value, E2P_PHY_REG); + r = zd_ioread16_locked(chip, &value, E2P_PHY_REG); if (r) return r; @@ -613,16 +615,24 @@ static int patch_cr157(struct zd_chip *chip) * Vendor driver says: for FCC regulation, enabled per HWFeature 6M band edge * bit (for AL2230, AL2230S) */ -static int patch_6m_band_edge(struct zd_chip *chip, int channel) +static int patch_6m_band_edge(struct zd_chip *chip, u8 channel) +{ + ZD_ASSERT(mutex_is_locked(&chip->mutex)); + if (!chip->patch_6m_band_edge) + return 0; + + return zd_rf_patch_6m_band_edge(&chip->rf, channel); +} + +/* Generic implementation of 6M band edge patching, used by most RFs via + * zd_rf_generic_patch_6m() */ +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel) { struct zd_ioreq16 ioreqs[] = { { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, { CR47, 0x1e }, }; - if (!chip->patch_6m_band_edge || !chip->rf.patch_6m_band_edge) - return 0; - /* FIXME: Channel 11 is not the edge for all regulatory domains. */ if (channel == 1 || channel == 11) ioreqs[0].value = 0x12; @@ -682,17 +692,17 @@ static int zd1211_hw_reset_phy(struct zd_chip *chip) { CR111, 0x27 }, { CR112, 0x27 }, { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x26 }, { CR116, 0x24 }, { CR117, 0xfc }, { CR118, 0xfa }, { CR120, 0x4f }, - { CR123, 0x27 }, { CR125, 0xaa }, { CR127, 0x03 }, - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR131, 0x0C }, { CR136, 0xdf }, { CR137, 0x40 }, - { CR138, 0xa0 }, { CR139, 0xb0 }, { CR140, 0x99 }, - { CR141, 0x82 }, { CR142, 0x54 }, { CR143, 0x1c }, - { CR144, 0x6c }, { CR147, 0x07 }, { CR148, 0x4c }, - { CR149, 0x50 }, { CR150, 0x0e }, { CR151, 0x18 }, - { CR160, 0xfe }, { CR161, 0xee }, { CR162, 0xaa }, - { CR163, 0xfa }, { CR164, 0xfa }, { CR165, 0xea }, - { CR166, 0xbe }, { CR167, 0xbe }, { CR168, 0x6a }, - { CR169, 0xba }, { CR170, 0xba }, { CR171, 0xba }, + { CR125, 0xaa }, { CR127, 0x03 }, { CR128, 0x14 }, + { CR129, 0x12 }, { CR130, 0x10 }, { CR131, 0x0C }, + { CR136, 0xdf }, { CR137, 0x40 }, { CR138, 0xa0 }, + { CR139, 0xb0 }, { CR140, 0x99 }, { CR141, 0x82 }, + { CR142, 0x54 }, { CR143, 0x1c }, { CR144, 0x6c }, + { CR147, 0x07 }, { CR148, 0x4c }, { CR149, 0x50 }, + { CR150, 0x0e }, { CR151, 0x18 }, { CR160, 0xfe }, + { CR161, 0xee }, { CR162, 0xaa }, { CR163, 0xfa }, + { CR164, 0xfa }, { CR165, 0xea }, { CR166, 0xbe }, + { CR167, 0xbe }, { CR168, 0x6a }, { CR169, 0xba }, + { CR170, 0xba }, { CR171, 0xba }, /* Note: CR204 must lead the CR203 */ { CR204, 0x7d }, { }, @@ -790,11 +800,6 @@ static int zd1211b_hw_reset_phy(struct zd_chip *chip) goto out; r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); - if (r) - goto unlock; - - r = patch_cr157(chip); -unlock: t = zd_chip_unlock_phy_regs(chip); if (t && !r) r = t; diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h index b07569e..ce0a5f6 100644 --- a/drivers/net/wireless/zd1211rw/zd_chip.h +++ b/drivers/net/wireless/zd1211rw/zd_chip.h @@ -641,8 +641,8 @@ enum { * also only 11 channels. */ #define E2P_ALLOWED_CHANNEL E2P_DATA(0x18) -#define E2P_PHY_REG E2P_DATA(0x1a) #define E2P_DEVICE_VER E2P_DATA(0x20) +#define E2P_PHY_REG E2P_DATA(0x25) #define E2P_36M_CAL_VALUE1 E2P_DATA(0x28) #define E2P_36M_CAL_VALUE2 E2P_DATA(0x2a) #define E2P_36M_CAL_VALUE3 E2P_DATA(0x2c) @@ -711,7 +711,7 @@ struct zd_chip { u16 link_led; unsigned int pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1, - new_phy_layout:1, + new_phy_layout:1, al2230s_bit:1, is_zd1211b:1, supports_tx_led:1; }; @@ -833,6 +833,7 @@ int zd_chip_enable_rx(struct zd_chip *chip); void zd_chip_disable_rx(struct zd_chip *chip); int zd_chip_enable_hwint(struct zd_chip *chip); int zd_chip_disable_hwint(struct zd_chip *chip); +int zd_chip_generic_patch_6m_band(struct zd_chip *chip, int channel); int zd_chip_set_rts_cts_rate_locked(struct zd_chip *chip, u8 rts_rate, int preamble); diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c index a085241..6753d24 100644 --- a/drivers/net/wireless/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zd1211rw/zd_mac.c @@ -156,18 +156,8 @@ void zd_mac_clear(struct zd_mac *mac) static int reset_mode(struct zd_mac *mac) { struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); - struct zd_ioreq32 ioreqs[3] = { - { CR_RX_FILTER, STA_RX_FILTER }, - { CR_SNIFFER_ON, 0U }, - }; - - if (ieee->iw_mode == IW_MODE_MONITOR) { - ioreqs[0].value = 0xffffffff; - ioreqs[1].value = 0x1; - ioreqs[2].value = ENC_SNIFFER; - } - - return zd_iowrite32a(&mac->chip, ioreqs, 3); + u32 filter = (ieee->iw_mode == IW_MODE_MONITOR) ? ~0 : STA_RX_FILTER; + return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter); } int zd_mac_open(struct net_device *netdev) @@ -904,16 +894,21 @@ static int fill_ctrlset(struct zd_mac *mac, static int zd_mac_tx(struct zd_mac *mac, struct ieee80211_txb *txb, int pri) { int i, r; + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); for (i = 0; i < txb->nr_frags; i++) { struct sk_buff *skb = txb->fragments[i]; r = fill_ctrlset(mac, txb, i); - if (r) + if (r) { + ieee->stats.tx_dropped++; return r; + } r = zd_usb_tx(&mac->chip.usb, skb->data, skb->len); - if (r) + if (r) { + ieee->stats.tx_dropped++; return r; + } } /* FIXME: shouldn't this be handled by the upper layers? */ @@ -970,14 +965,14 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, switch (ieee->iw_mode) { case IW_MODE_ADHOC: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != 0 || - memcmp(hdr->addr3, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr3, ieee->bssid) != 0) return 0; break; case IW_MODE_AUTO: case IW_MODE_INFRA: if ((fc & (IEEE80211_FCTL_TODS|IEEE80211_FCTL_FROMDS)) != IEEE80211_FCTL_FROMDS || - memcmp(hdr->addr2, ieee->bssid, ETH_ALEN) != 0) + compare_ether_addr(hdr->addr2, ieee->bssid) != 0) return 0; break; default: @@ -985,9 +980,9 @@ static int is_data_packet_for_us(struct ieee80211_device *ieee, return 0; } - return memcmp(hdr->addr1, netdev->dev_addr, ETH_ALEN) == 0 || + return compare_ether_addr(hdr->addr1, netdev->dev_addr) == 0 || (is_multicast_ether_addr(hdr->addr1) && - memcmp(hdr->addr3, netdev->dev_addr, ETH_ALEN) != 0) || + compare_ether_addr(hdr->addr3, netdev->dev_addr) != 0) || (netdev->flags & IFF_PROMISC); } @@ -1043,7 +1038,7 @@ static void update_qual_rssi(struct zd_mac *mac, hdr = (struct ieee80211_hdr_3addr *)buffer; if (length < offsetof(struct ieee80211_hdr_3addr, addr3)) return; - if (memcmp(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid, ETH_ALEN) != 0) + if (compare_ether_addr(hdr->addr2, zd_mac_to_ieee80211(mac)->bssid) != 0) return; spin_lock_irqsave(&mac->lock, flags); @@ -1063,9 +1058,23 @@ static int fill_rx_stats(struct ieee80211_rx_stats *stats, *pstatus = status = zd_tail(buffer, length, sizeof(struct rx_status)); if (status->frame_status & ZD_RX_ERROR) { - /* FIXME: update? */ + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + ieee->stats.rx_errors++; + if (status->frame_status & ZD_RX_TIMEOUT_ERROR) + ieee->stats.rx_missed_errors++; + else if (status->frame_status & ZD_RX_FIFO_OVERRUN_ERROR) + ieee->stats.rx_fifo_errors++; + else if (status->frame_status & ZD_RX_DECRYPTION_ERROR) + ieee->ieee_stats.rx_discards_undecryptable++; + else if (status->frame_status & ZD_RX_CRC32_ERROR) { + ieee->stats.rx_crc_errors++; + ieee->ieee_stats.rx_fcs_errors++; + } + else if (status->frame_status & ZD_RX_CRC16_ERROR) + ieee->stats.rx_crc_errors++; return -EINVAL; } + memset(stats, 0, sizeof(struct ieee80211_rx_stats)); stats->len = length - (ZD_PLCP_HEADER_SIZE + IEEE80211_FCS_LEN + + sizeof(struct rx_status)); @@ -1094,14 +1103,16 @@ static void zd_mac_rx(struct zd_mac *mac, struct sk_buff *skb) if (skb->len < ZD_PLCP_HEADER_SIZE + IEEE80211_1ADDR_LEN + IEEE80211_FCS_LEN + sizeof(struct rx_status)) { - dev_dbg_f(zd_mac_dev(mac), "Packet with length %u to small.\n", - skb->len); + ieee->stats.rx_errors++; + ieee->stats.rx_length_errors++; goto free_skb; } r = fill_rx_stats(&stats, &status, mac, skb->data, skb->len); if (r) { - /* Only packets with rx errors are included here. */ + /* Only packets with rx errors are included here. + * The error stats have already been set in fill_rx_stats. + */ goto free_skb; } @@ -1114,8 +1125,10 @@ static void zd_mac_rx(struct zd_mac *mac, struct sk_buff *skb) r = filter_rx(ieee, skb->data, skb->len, &stats); if (r <= 0) { - if (r < 0) + if (r < 0) { + ieee->stats.rx_errors++; dev_dbg_f(zd_mac_dev(mac), "Error in packet.\n"); + } goto free_skb; } @@ -1146,7 +1159,9 @@ int zd_mac_rx_irq(struct zd_mac *mac, const u8 *buffer, unsigned int length) skb = dev_alloc_skb(sizeof(struct zd_rt_hdr) + length); if (!skb) { + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); dev_warn(zd_mac_dev(mac), "Could not allocate skb.\n"); + ieee->stats.rx_dropped++; return -ENOMEM; } skb_reserve(skb, sizeof(struct zd_rt_hdr)); diff --git a/drivers/net/wireless/zd1211rw/zd_rf.c b/drivers/net/wireless/zd1211rw/zd_rf.c index f50cff3..549c23b 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.c +++ b/drivers/net/wireless/zd1211rw/zd_rf.c @@ -23,7 +23,7 @@ #include "zd_ieee80211.h" #include "zd_chip.h" -static const char *rfs[] = { +static const char * const rfs[] = { [0] = "unknown RF0", [1] = "unknown RF1", [UW2451_RF] = "UW2451_RF", @@ -34,7 +34,7 @@ static const char *rfs[] = { [AL2210_RF] = "AL2210_RF", [MAXIM_NEW_RF] = "MAXIM_NEW_RF", [UW2453_RF] = "UW2453_RF", - [AL2230S_RF] = "AL2230S_RF", + [UNKNOWN_A_RF] = "UNKNOWN_A_RF", [RALINK_RF] = "RALINK_RF", [INTERSIL_RF] = "INTERSIL_RF", [RF2959_RF] = "RF2959_RF", @@ -154,3 +154,17 @@ int zd_switch_radio_off(struct zd_rf *rf) r = t; return r; } + +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel) +{ + if (!rf->patch_6m_band_edge) + return 0; + + return rf->patch_6m_band_edge(rf, channel); +} + +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel) +{ + return zd_chip_generic_patch_6m_band(zd_rf_to_chip(rf), channel); +} + diff --git a/drivers/net/wireless/zd1211rw/zd_rf.h b/drivers/net/wireless/zd1211rw/zd_rf.h index a57732e..aa9cc10 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf.h +++ b/drivers/net/wireless/zd1211rw/zd_rf.h @@ -26,7 +26,7 @@ #define AL2210_RF 0x7 #define MAXIM_NEW_RF 0x8 #define UW2453_RF 0x9 -#define AL2230S_RF 0xa +#define UNKNOWN_A_RF 0xa #define RALINK_RF 0xb #define INTERSIL_RF 0xc #define RF2959_RF 0xd @@ -47,17 +47,13 @@ struct zd_rf { u8 type; u8 channel; - /* - * Whether this RF should patch the 6M band edge - * (assuming E2P_POD agrees) - */ - u8 patch_6m_band_edge:1; /* RF-specific functions */ int (*init_hw)(struct zd_rf *rf); int (*set_channel)(struct zd_rf *rf, u8 channel); int (*switch_radio_on)(struct zd_rf *rf); int (*switch_radio_off)(struct zd_rf *rf); + int (*patch_6m_band_edge)(struct zd_rf *rf, u8 channel); }; const char *zd_rf_name(u8 type); @@ -72,6 +68,9 @@ int zd_rf_set_channel(struct zd_rf *rf, u8 channel); int zd_switch_radio_on(struct zd_rf *rf); int zd_switch_radio_off(struct zd_rf *rf); +int zd_rf_patch_6m_band_edge(struct zd_rf *rf, u8 channel); +int zd_rf_generic_patch_6m(struct zd_rf *rf, u8 channel); + /* Functions for individual RF chips */ int zd_rf_init_rf2959(struct zd_rf *rf); diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c index 25323a1..511392a 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al2230.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al2230.c @@ -59,6 +59,18 @@ static const struct zd_ioreq16 zd1211b_ioreqs_shared_1[] = { { CR240, 0x57 }, { CR9, 0xe0 }, }; +static const struct zd_ioreq16 ioreqs_init_al2230s[] = { + { CR47, 0x1e }, /* MARK_002 */ + { CR106, 0x22 }, + { CR107, 0x2a }, /* MARK_002 */ + { CR109, 0x13 }, /* MARK_002 */ + { CR118, 0xf8 }, /* MARK_002 */ + { CR119, 0x12 }, { CR122, 0xe0 }, + { CR128, 0x10 }, /* MARK_001 from 0xe->0x10 */ + { CR129, 0x0e }, /* MARK_001 from 0xd->0x0e */ + { CR130, 0x10 }, /* MARK_001 from 0xb->0x0d */ +}; + static int zd1211b_al2230_finalize_rf(struct zd_chip *chip) { int r; @@ -90,7 +102,7 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) int r; struct zd_chip *chip = zd_rf_to_chip(rf); - static const struct zd_ioreq16 ioreqs[] = { + static const struct zd_ioreq16 ioreqs_init[] = { { CR15, 0x20 }, { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x11 }, { CR28, 0x3e }, { CR29, 0x00 }, { CR44, 0x33 }, { CR106, 0x2a }, { CR107, 0x1a }, @@ -117,10 +129,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) { CR119, 0x10 }, { CR120, 0x4f }, { CR121, 0x77 }, { CR122, 0xe0 }, { CR137, 0x88 }, { CR252, 0xff }, { CR253, 0xff }, + }; - /* These following happen separately in the vendor driver */ - { }, - + static const struct zd_ioreq16 ioreqs_pll[] = { /* shdnb(PLL_ON)=0 */ { CR251, 0x2f }, /* shdnb(PLL_ON)=1 */ @@ -128,7 +139,7 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) { CR138, 0x28 }, { CR203, 0x06 }, }; - static const u32 rv[] = { + static const u32 rv1[] = { /* Channel 1 */ 0x03f790, 0x033331, @@ -137,6 +148,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x0b3331, 0x03b812, 0x00fff3, + }; + + static const u32 rv2[] = { 0x000da4, 0x0f4dc5, /* fix freq shift, 0x04edc5 */ 0x0805b6, @@ -148,8 +162,9 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x0bdffc, 0x00000d, 0x00500f, + }; - /* These writes happen separately in the vendor driver */ + static const u32 rv3[] = { 0x00d00f, 0x004c0f, 0x00540f, @@ -157,11 +172,38 @@ static int zd1211_al2230_init_hw(struct zd_rf *rf) 0x00500f, }; - r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + r = zd_iowrite16a_locked(chip, ioreqs_init, ARRAY_SIZE(ioreqs_init)); + if (r) + return r; + + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + + r = zd_rfwritev_locked(chip, rv1, ARRAY_SIZE(rv1), RF_RV_BITS); + if (r) + return r; + + /* improve band edge for AL2230S */ + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x000824, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x0005a4, RF_RV_BITS); if (r) return r; - r = zd_rfwritev_locked(chip, rv, ARRAY_SIZE(rv), RF_RV_BITS); + r = zd_rfwritev_locked(chip, rv2, ARRAY_SIZE(rv2), RF_RV_BITS); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_pll, ARRAY_SIZE(ioreqs_pll)); + if (r) + return r; + + r = zd_rfwritev_locked(chip, rv3, ARRAY_SIZE(rv3), RF_RV_BITS); if (r) return r; @@ -227,7 +269,9 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) 0x481dc0, 0xcfff00, 0x25a000, + }; + static const u32 rv2[] = { /* To improve AL2230 yield, improve phase noise, 4713 */ 0x25a000, 0xa3b2f0, @@ -250,7 +294,7 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) { CR251, 0x7f }, /* shdnb(PLL_ON)=1 */ }; - static const u32 rv2[] = { + static const u32 rv3[] = { /* To improve AL2230 yield, 4713 */ 0xf01b00, 0xf01e00, @@ -269,18 +313,37 @@ static int zd1211b_al2230_init_hw(struct zd_rf *rf) r = zd_iowrite16a_locked(chip, ioreqs1, ARRAY_SIZE(ioreqs1)); if (r) return r; + + if (chip->al2230s_bit) { + r = zd_iowrite16a_locked(chip, ioreqs_init_al2230s, + ARRAY_SIZE(ioreqs_init_al2230s)); + if (r) + return r; + } + r = zd_rfwritev_cr_locked(chip, zd1211b_al2230_table[0], 3); if (r) return r; r = zd_rfwritev_cr_locked(chip, rv1, ARRAY_SIZE(rv1)); if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + + if (chip->al2230s_bit) + r = zd_rfwrite_locked(chip, 0x241000, RF_RV_BITS); + else + r = zd_rfwrite_locked(chip, 0x25a000, RF_RV_BITS); if (r) return r; + r = zd_rfwritev_cr_locked(chip, rv2, ARRAY_SIZE(rv2)); if (r) return r; + r = zd_iowrite16a_locked(chip, ioreqs2, ARRAY_SIZE(ioreqs2)); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, rv3, ARRAY_SIZE(rv3)); + if (r) + return r; r = zd_iowrite16a_locked(chip, ioreqs3, ARRAY_SIZE(ioreqs3)); if (r) return r; @@ -368,6 +431,6 @@ int zd_rf_init_al2230(struct zd_rf *rf) rf->set_channel = zd1211_al2230_set_channel; rf->switch_radio_on = zd1211_al2230_switch_radio_on; } - rf->patch_6m_band_edge = 1; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c index a289f95..5e5e9dd 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_al7230b.c @@ -51,9 +51,52 @@ static const u32 std_rv[] = { 0xd8c010, }; -static int al7230b_init_hw(struct zd_rf *rf) +static const u32 rv_init1[] = { + 0x3c9000, + 0xbfffff, + 0x700000, + 0xf15d58, +}; + +static const u32 rv_init2[] = { + 0xf15d59, + 0xf15d5c, + 0xf15d58, +}; + +static const struct zd_ioreq16 ioreqs_sw[] = { + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, +}; + +static int zd1211b_al7230b_finalize(struct zd_chip *chip) { - int i, r; + int r; + static const struct zd_ioreq16 ioreqs[] = { + { CR80, 0x30 }, { CR81, 0x30 }, { CR79, 0x58 }, + { CR12, 0xf0 }, { CR77, 0x1b }, { CR78, 0x58 }, + { CR203, 0x04 }, + { }, + { CR240, 0x80 }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); + if (r) + return r; + + if (chip->new_phy_layout) { + /* antenna selection? */ + r = zd_iowrite16_locked(chip, 0xe5, CR9); + if (r) + return r; + } + + return zd_iowrite16_locked(chip, 0x04, CR203); +} + +static int zd1211_al7230b_init_hw(struct zd_rf *rf) +{ + int r; struct zd_chip *chip = zd_rf_to_chip(rf); /* All of these writes are identical to AL2230 unless otherwise @@ -117,39 +160,136 @@ static int al7230b_init_hw(struct zd_rf *rf) }; static const struct zd_ioreq16 ioreqs_2[] = { - /* PLL_ON */ - { CR251, 0x3f }, + { CR251, 0x3f }, /* PLL_ON */ { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, + { CR38, 0x38 }, { CR136, 0xdf }, }; r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x09ec04); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x8cccc8); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); + if (r) + return r; - r = zd_rfwrite_cr_locked(chip, 0x3c9000); + r = zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xbfffff); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0x700000); + + r = zd_iowrite16_locked(chip, 0x06, CR203); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + r = zd_iowrite16_locked(chip, 0x80, CR240); + if (r) + return r; + + return 0; +} + +static int zd1211b_al7230b_init_hw(struct zd_rf *rf) +{ + int r; + struct zd_chip *chip = zd_rf_to_chip(rf); + + static const struct zd_ioreq16 ioreqs_1[] = { + { CR240, 0x57 }, { CR9, 0x9 }, + { }, + { CR10, 0x8b }, { CR15, 0x20 }, + { CR17, 0x2B }, /* for newest (3rd cut) AL2230 */ + { CR20, 0x10 }, /* 4N25->Stone Request */ + { CR23, 0x40 }, { CR24, 0x20 }, { CR26, 0x93 }, + { CR28, 0x3e }, { CR29, 0x00 }, + { CR33, 0x28 }, /* 5613 */ + { CR34, 0x30 }, + { CR35, 0x3e }, /* for newest (3rd cut) AL2230 */ + { CR41, 0x24 }, { CR44, 0x32 }, + { CR46, 0x99 }, /* for newest (3rd cut) AL2230 */ + { CR47, 0x1e }, + + /* ZD1215 5610 */ + { CR48, 0x00 }, { CR49, 0x00 }, { CR51, 0x01 }, + { CR52, 0x80 }, { CR53, 0x7e }, { CR65, 0x00 }, + { CR66, 0x00 }, { CR67, 0x00 }, { CR68, 0x00 }, + { CR69, 0x28 }, + + { CR79, 0x58 }, { CR80, 0x30 }, { CR81, 0x30 }, + { CR87, 0x0A }, { CR89, 0x04 }, + { CR90, 0x58 }, /* 5112 */ + { CR91, 0x00 }, /* 5613 */ + { CR92, 0x0a }, + { CR98, 0x8d }, /* 4804, for 1212 new algorithm */ + { CR99, 0x00 }, { CR100, 0x02 }, { CR101, 0x13 }, + { CR102, 0x27 }, + { CR106, 0x20 }, /* change to 0x24 for AL7230B */ + { CR109, 0x13 }, /* 4804, for 1212 new algorithm */ + { CR112, 0x1f }, + }; + + static const struct zd_ioreq16 ioreqs_new_phy[] = { + { CR107, 0x28 }, + { CR110, 0x1f }, /* 5127, 0x13->0x1f */ + { CR111, 0x1f }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x2a }, { CR118, 0xfa }, { CR119, 0x12 }, + { CR121, 0x6c }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_old_phy[] = { + { CR107, 0x24 }, + { CR110, 0x13 }, /* 5127, 0x13->0x1f */ + { CR111, 0x13 }, /* 0x13 to 0x1f for AL7230B */ + { CR116, 0x24 }, { CR118, 0xfc }, { CR119, 0x11 }, + { CR121, 0x6a }, /* 5613 */ + }; + + static const struct zd_ioreq16 ioreqs_2[] = { + { CR113, 0x27 }, { CR114, 0x27 }, { CR115, 0x24 }, + { CR117, 0xfa }, { CR120, 0x4f }, + { CR122, 0xfc }, /* E0->FCh at 4901 */ + { CR123, 0x57 }, /* 5613 */ + { CR125, 0xad }, /* 4804, for 1212 new algorithm */ + { CR126, 0x6c }, /* 5613 */ + { CR127, 0x03 }, /* 4804, for 1212 new algorithm */ + { CR130, 0x10 }, + { CR131, 0x00 }, /* 5112 */ + { CR137, 0x50 }, /* 5613 */ + { CR138, 0xa8 }, /* 5112 */ + { CR144, 0xac }, /* 5613 */ + { CR148, 0x40 }, /* 5112 */ + { CR149, 0x40 }, /* 4O07, 50->40 */ + { CR150, 0x1a }, /* 5112, 0C->1A */ + { CR252, 0x34 }, { CR253, 0x34 }, + { CR251, 0x2f }, /* PLL_OFF */ + }; + + static const struct zd_ioreq16 ioreqs_3[] = { + { CR251, 0x7f }, /* PLL_ON */ + { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, + { CR38, 0x38 }, { CR136, 0xdf }, + }; + + r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + if (r) + return r; + + if (chip->new_phy_layout) + r = zd_iowrite16a_locked(chip, ioreqs_new_phy, + ARRAY_SIZE(ioreqs_new_phy)); + else + r = zd_iowrite16a_locked(chip, ioreqs_old_phy, + ARRAY_SIZE(ioreqs_old_phy)); if (r) return r; @@ -157,38 +297,36 @@ static int al7230b_init_hw(struct zd_rf *rf) if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d59); + r = zd_rfwritev_cr_locked(chip, chan_rv[0], ARRAY_SIZE(chan_rv[0])); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d5c); + + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); if (r) return r; - r = zd_rfwrite_cr_locked(chip, 0xf15d58); + + r = zd_rfwritev_cr_locked(chip, rv_init1, ARRAY_SIZE(rv_init1)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x06, CR203); + r = zd_iowrite16a_locked(chip, ioreqs_3, ARRAY_SIZE(ioreqs_3)); if (r) return r; - r = zd_iowrite16_locked(chip, 0x80, CR240); + + r = zd_rfwritev_cr_locked(chip, rv_init2, ARRAY_SIZE(rv_init2)); if (r) return r; - return 0; + return zd1211b_al7230b_finalize(chip); } -static int al7230b_set_channel(struct zd_rf *rf, u8 channel) +static int zd1211_al7230b_set_channel(struct zd_rf *rf, u8 channel) { - int i, r; + int r; const u32 *rv = chan_rv[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); - struct zd_ioreq16 ioreqs_1[] = { - { CR128, 0x14 }, { CR129, 0x12 }, { CR130, 0x10 }, - { CR38, 0x38 }, { CR136, 0xdf }, - }; - - struct zd_ioreq16 ioreqs_2[] = { + static const struct zd_ioreq16 ioreqs[] = { /* PLL_ON */ { CR251, 0x3f }, { CR203, 0x06 }, { CR240, 0x08 }, @@ -203,11 +341,9 @@ static int al7230b_set_channel(struct zd_rf *rf, u8 channel) if (r) return r; - for (i = 0; i < ARRAY_SIZE(std_rv); i++) { - r = zd_rfwrite_cr_locked(chip, std_rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) @@ -216,24 +352,69 @@ static int al7230b_set_channel(struct zd_rf *rf, u8 channel) if (r) return r; - r = zd_iowrite16a_locked(chip, ioreqs_1, ARRAY_SIZE(ioreqs_1)); + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); if (r) return r; - for (i = 0; i < 2; i++) { - r = zd_rfwrite_cr_locked(chip, rv[i]); - if (r) - return r; - } + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; r = zd_rfwrite_cr_locked(chip, 0x3c9000); if (r) return r; - return zd_iowrite16a_locked(chip, ioreqs_2, ARRAY_SIZE(ioreqs_2)); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } -static int al7230b_switch_radio_on(struct zd_rf *rf) +static int zd1211b_al7230b_set_channel(struct zd_rf *rf, u8 channel) +{ + int r; + const u32 *rv = chan_rv[channel-1]; + struct zd_chip *chip = zd_rf_to_chip(rf); + + r = zd_iowrite16_locked(chip, 0x57, CR240); + if (r) + return r; + r = zd_iowrite16_locked(chip, 0xe4, CR9); + if (r) + return r; + + /* PLL_OFF */ + r = zd_iowrite16_locked(chip, 0x2f, CR251); + if (r) + return r; + r = zd_rfwritev_cr_locked(chip, std_rv, ARRAY_SIZE(std_rv)); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + r = zd_rfwrite_cr_locked(chip, 0xf15d58); + if (r) + return r; + + r = zd_iowrite16a_locked(chip, ioreqs_sw, ARRAY_SIZE(ioreqs_sw)); + if (r) + return r; + + r = zd_rfwritev_cr_locked(chip, rv, 2); + if (r) + return r; + + r = zd_rfwrite_cr_locked(chip, 0x3c9000); + if (r) + return r; + + r = zd_iowrite16_locked(chip, 0x7f, CR251); + if (r) + return r; + + return zd1211b_al7230b_finalize(chip); +} + +static int zd1211_al7230b_switch_radio_on(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); static const struct zd_ioreq16 ioreqs[] = { @@ -244,6 +425,17 @@ static int al7230b_switch_radio_on(struct zd_rf *rf) return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +static int zd1211b_al7230b_switch_radio_on(struct zd_rf *rf) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + static const struct zd_ioreq16 ioreqs[] = { + { CR11, 0x00 }, + { CR251, 0x7f }, + }; + + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + static int al7230b_switch_radio_off(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); @@ -255,20 +447,45 @@ static int al7230b_switch_radio_off(struct zd_rf *rf) return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); } +/* ZD1211B+AL7230B 6m band edge patching differs slightly from other + * configurations */ +static int zd1211b_al7230b_patch_6m(struct zd_rf *rf, u8 channel) +{ + struct zd_chip *chip = zd_rf_to_chip(rf); + struct zd_ioreq16 ioreqs[] = { + { CR128, 0x14 }, { CR129, 0x12 }, + }; + + /* FIXME: Channel 11 is not the edge for all regulatory domains. */ + if (channel == 1) { + ioreqs[0].value = 0x0e; + ioreqs[1].value = 0x10; + } else if (channel == 11) { + ioreqs[0].value = 0x10; + ioreqs[1].value = 0x10; + } + + dev_dbg_f(zd_chip_dev(chip), "patching for channel %d\n", channel); + return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs)); +} + int zd_rf_init_al7230b(struct zd_rf *rf) { struct zd_chip *chip = zd_rf_to_chip(rf); if (chip->is_zd1211b) { - dev_err(zd_chip_dev(chip), "AL7230B is currently not " - "supported for ZD1211B devices\n"); - return -ENODEV; + rf->init_hw = zd1211b_al7230b_init_hw; + rf->switch_radio_on = zd1211b_al7230b_switch_radio_on; + rf->set_channel = zd1211b_al7230b_set_channel; + rf->patch_6m_band_edge = zd1211b_al7230b_patch_6m; + } else { + rf->init_hw = zd1211_al7230b_init_hw; + rf->switch_radio_on = zd1211_al7230b_switch_radio_on; + rf->set_channel = zd1211_al7230b_set_channel; + rf->patch_6m_band_edge = zd_rf_generic_patch_6m; } - rf->init_hw = al7230b_init_hw; - rf->set_channel = al7230b_set_channel; - rf->switch_radio_on = al7230b_switch_radio_on; rf->switch_radio_off = al7230b_switch_radio_off; - rf->patch_6m_band_edge = 1; + return 0; } diff --git a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c index 5824727..2d736bd 100644 --- a/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c +++ b/drivers/net/wireless/zd1211rw/zd_rf_rf2959.c @@ -21,7 +21,7 @@ #include "zd_usb.h" #include "zd_chip.h" -static u32 rf2959_table[][2] = { +static const u32 rf2959_table[][2] = { RF_CHANNEL( 1) = { 0x181979, 0x1e6666 }, RF_CHANNEL( 2) = { 0x181989, 0x1e6666 }, RF_CHANNEL( 3) = { 0x181999, 0x1e6666 }, @@ -228,7 +228,7 @@ static int rf2959_init_hw(struct zd_rf *rf) static int rf2959_set_channel(struct zd_rf *rf, u8 channel) { int i, r; - u32 *rv = rf2959_table[channel-1]; + const u32 *rv = rf2959_table[channel-1]; struct zd_chip *chip = zd_rf_to_chip(rf); for (i = 0; i < 2; i++) { diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index 75ef556..e04cffc 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -52,6 +52,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0b3b, 0x1630), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x0586, 0x3401), .driver_info = DEVICE_ZD1211 }, { USB_DEVICE(0x14ea, 0xab13), .driver_info = DEVICE_ZD1211 }, + { USB_DEVICE(0x13b1, 0x001e), .driver_info = DEVICE_ZD1211 }, /* ZD1211B */ { USB_DEVICE(0x0ace, 0x1215), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x157e, 0x300d), .driver_info = DEVICE_ZD1211B }, @@ -62,6 +63,10 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x13b1, 0x0024), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0586, 0x340f), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0b05, 0x171b), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3410), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0baf, 0x0121), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x0586, 0x3412), .driver_info = DEVICE_ZD1211B }, /* "Driverless" devices that need ejecting */ { USB_DEVICE(0x0ace, 0x2011), .driver_info = DEVICE_INSTALLER }, {} @@ -313,6 +318,12 @@ out: static inline void handle_retry_failed_int(struct urb *urb) { + struct zd_usb *usb = urb->context; + struct zd_mac *mac = zd_usb_to_mac(usb); + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + + ieee->stats.tx_errors++; + ieee->ieee_stats.tx_retry_limit_exceeded++; dev_dbg_f(urb_dev(urb), "retry failed interrupt\n"); } @@ -406,7 +417,7 @@ int zd_usb_enable_int(struct zd_usb *usb) dev_dbg_f(zd_usb_dev(usb), "\n"); - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { r = -ENOMEM; goto out; @@ -424,7 +435,7 @@ int zd_usb_enable_int(struct zd_usb *usb) /* TODO: make it a DMA buffer */ r = -ENOMEM; - transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_NOFS); + transfer_buffer = kmalloc(USB_MAX_EP_INT_BUFFER, GFP_KERNEL); if (!transfer_buffer) { dev_dbg_f(zd_usb_dev(usb), "couldn't allocate transfer_buffer\n"); @@ -438,7 +449,7 @@ int zd_usb_enable_int(struct zd_usb *usb) intr->interval); dev_dbg_f(zd_usb_dev(usb), "submit urb %p\n", intr->urb); - r = usb_submit_urb(urb, GFP_NOFS); + r = usb_submit_urb(urb, GFP_KERNEL); if (r) { dev_dbg_f(zd_usb_dev(usb), "Couldn't submit urb. Error number %d\n", r); @@ -487,6 +498,9 @@ static void handle_rx_packet(struct zd_usb *usb, const u8 *buffer, if (length < sizeof(struct rx_length_info)) { /* It's not a complete packet anyhow. */ + struct ieee80211_device *ieee = zd_mac_to_ieee80211(mac); + ieee->stats.rx_errors++; + ieee->stats.rx_length_errors++; return; } length_info = (struct rx_length_info *) @@ -584,10 +598,10 @@ static struct urb *alloc_urb(struct zd_usb *usb) struct urb *urb; void *buffer; - urb = usb_alloc_urb(0, GFP_NOFS); + urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) return NULL; - buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_NOFS, + buffer = usb_buffer_alloc(udev, USB_MAX_RX_SIZE, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { usb_free_urb(urb); @@ -620,7 +634,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) dev_dbg_f(zd_usb_dev(usb), "\n"); r = -ENOMEM; - urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_NOFS); + urbs = kcalloc(URBS_COUNT, sizeof(struct urb *), GFP_KERNEL); if (!urbs) goto error; for (i = 0; i < URBS_COUNT; i++) { @@ -641,7 +655,7 @@ int zd_usb_enable_rx(struct zd_usb *usb) spin_unlock_irq(&rx->lock); for (i = 0; i < URBS_COUNT; i++) { - r = usb_submit_urb(urbs[i], GFP_NOFS); + r = usb_submit_urb(urbs[i], GFP_KERNEL); if (r) goto error_submit; } @@ -923,6 +937,8 @@ static int probe(struct usb_interface *intf, const struct usb_device_id *id) goto error; } + usb_reset_device(interface_to_usbdev(intf)); + netdev = zd_netdev_alloc(intf); if (netdev == NULL) { r = -ENOMEM; @@ -1024,6 +1040,7 @@ static int __init usb_init(void) r = usb_register(&driver); if (r) { + destroy_workqueue(zd_workqueue); printk(KERN_ERR "%s usb_register() failed. Error number %d\n", driver.name, r); return r; @@ -1144,7 +1161,7 @@ int zd_usb_ioread16v(struct zd_usb *usb, u16 *values, } req_len = sizeof(struct usb_req_read_regs) + count * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; req->id = cpu_to_le16(USB_REQ_READ_REGS); @@ -1207,7 +1224,7 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs, req_len = sizeof(struct usb_req_write_regs) + count * sizeof(struct reg_data); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; @@ -1287,7 +1304,7 @@ int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits) bit_value_template &= ~(RF_IF_LE|RF_CLK|RF_DATA); req_len = sizeof(struct usb_req_rfwrite) + bits * sizeof(__le16); - req = kmalloc(req_len, GFP_NOFS); + req = kmalloc(req_len, GFP_KERNEL); if (!req) return -ENOMEM; diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c index 2412ce4..3f4a7cf 100644 --- a/drivers/net/yellowfin.c +++ b/drivers/net/yellowfin.c @@ -1137,7 +1137,6 @@ static int yellowfin_rx(struct net_device *dev) skb = dev_alloc_skb(pkt_len + 2); if (skb == NULL) break; - skb->dev = dev; skb_reserve(skb, 2); /* 16 byte align the IP header */ eth_copy_and_sum(skb, rx_skb->data, pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/net/znet.c b/drivers/net/znet.c index b24b072..4032e9f 100644 --- a/drivers/net/znet.c +++ b/drivers/net/znet.c @@ -774,7 +774,6 @@ static void znet_rx(struct net_device *dev) znet->stats.rx_dropped++; break; } - skb->dev = dev; if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) { int semi_cnt = (znet->rx_end - znet->rx_cur)<<1; diff --git a/drivers/oprofile/event_buffer.c b/drivers/oprofile/event_buffer.c index 00e937e..e7fbac5 100644 --- a/drivers/oprofile/event_buffer.c +++ b/drivers/oprofile/event_buffer.c @@ -70,11 +70,12 @@ void wake_up_buffer_waiter(void) int alloc_event_buffer(void) { int err = -ENOMEM; + unsigned long flags; - spin_lock(&oprofilefs_lock); + spin_lock_irqsave(&oprofilefs_lock, flags); buffer_size = fs_buffer_size; buffer_watershed = fs_buffer_watershed; - spin_unlock(&oprofilefs_lock); + spin_unlock_irqrestore(&oprofilefs_lock, flags); if (buffer_watershed >= buffer_size) return -EINVAL; diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c index 6e67b42..8543cb2 100644 --- a/drivers/oprofile/oprofilefs.c +++ b/drivers/oprofile/oprofilefs.c @@ -65,6 +65,7 @@ ssize_t oprofilefs_ulong_to_user(unsigned long val, char __user * buf, size_t co int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, size_t count) { char tmpbuf[TMPBUFSIZE]; + unsigned long flags; if (!count) return 0; @@ -77,9 +78,9 @@ int oprofilefs_ulong_from_user(unsigned long * val, char const __user * buf, siz if (copy_from_user(tmpbuf, buf, count)) return -EFAULT; - spin_lock(&oprofilefs_lock); + spin_lock_irqsave(&oprofilefs_lock, flags); *val = simple_strtoul(tmpbuf, NULL, 0); - spin_unlock(&oprofilefs_lock); + spin_unlock_irqrestore(&oprofilefs_lock, flags); return 0; } diff --git a/drivers/parisc/hppb.c b/drivers/parisc/hppb.c index 07dc2b6..9bb4db5 100644 --- a/drivers/parisc/hppb.c +++ b/drivers/parisc/hppb.c @@ -10,10 +10,6 @@ ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** -** This Driver currently only supports the console (port 0) on the MUX. -** Additional work will be needed on this driver to enable the full -** functionality of the MUX. -** */ #include <linux/types.h> @@ -67,7 +63,7 @@ static int hppb_probe(struct parisc_device *dev) } card = card->next; } - printk(KERN_INFO "Found GeckoBoa at 0x%lx\n", dev->hpa.start); + printk(KERN_INFO "Found GeckoBoa at 0x%x\n", dev->hpa.start); card->hpa = dev->hpa.start; card->mmio_region.name = "HP-PB Bus"; @@ -78,16 +74,18 @@ static int hppb_probe(struct parisc_device *dev) status = ccio_request_resource(dev, &card->mmio_region); if(status < 0) { - printk(KERN_ERR "%s: failed to claim HP-PB bus space (%08lx, %08lx)\n", + printk(KERN_ERR "%s: failed to claim HP-PB bus space (%08x, %08x)\n", __FILE__, card->mmio_region.start, card->mmio_region.end); } return 0; } - static struct parisc_device_id hppb_tbl[] = { - { HPHW_BCPORT, HVERSION_REV_ANY_ID, 0x500, 0xc }, + { HPHW_BCPORT, HVERSION_REV_ANY_ID, 0x500, 0xc }, /* E25 and K */ + { HPHW_BCPORT, 0x0, 0x501, 0xc }, /* E35 */ + { HPHW_BCPORT, 0x0, 0x502, 0xc }, /* E45 */ + { HPHW_BCPORT, 0x0, 0x503, 0xc }, /* E55 */ { 0, } }; diff --git a/drivers/parisc/iosapic_private.h b/drivers/parisc/iosapic_private.h index 41e7ec2..6e05e30 100644 --- a/drivers/parisc/iosapic_private.h +++ b/drivers/parisc/iosapic_private.h @@ -132,7 +132,7 @@ struct iosapic_irt { struct vector_info { struct iosapic_info *iosapic; /* I/O SAPIC this vector is on */ struct irt_entry *irte; /* IRT entry */ - u32 *eoi_addr; /* precalculate EOI reg address */ + u32 __iomem *eoi_addr; /* precalculate EOI reg address */ u32 eoi_data; /* IA64: ? PA: swapped txn_data */ int txn_irq; /* virtual IRQ number for processor */ ulong txn_addr; /* IA64: id_eid PA: partial HPA */ diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c index ba67699..21c4c29 100644 --- a/drivers/parisc/lba_pci.c +++ b/drivers/parisc/lba_pci.c @@ -168,7 +168,8 @@ lba_dump_res(struct resource *r, int d) printk(KERN_DEBUG "(%p)", r->parent); for (i = d; i ; --i) printk(" "); - printk(KERN_DEBUG "%p [%lx,%lx]/%lx\n", r, r->start, r->end, r->flags); + printk(KERN_DEBUG "%p [%lx,%lx]/%lx\n", r, + (long)r->start, (long)r->end, r->flags); lba_dump_res(r->child, d+2); lba_dump_res(r->sibling, d); } @@ -647,7 +648,7 @@ truncate_pat_collision(struct resource *root, struct resource *new) printk(KERN_WARNING "LBA: Truncating lmmio_space [%lx/%lx] " "to [%lx,%lx]\n", start, end, - new->start, new->end ); + (long)new->start, (long)new->end ); return 0; /* truncation successful */ } @@ -715,8 +716,8 @@ lba_fixup_bus(struct pci_bus *bus) printk("FAILED: lba_fixup_bus() request for " "elmmio_space [%lx/%lx]\n", - ldev->hba.elmmio_space.start, - ldev->hba.elmmio_space.end); + (long)ldev->hba.elmmio_space.start, + (long)ldev->hba.elmmio_space.end); /* lba_dump_res(&iomem_resource, 2); */ /* BUG(); */ @@ -738,15 +739,15 @@ lba_fixup_bus(struct pci_bus *bus) &(ldev->hba.lmmio_space))) { printk(KERN_WARNING "LBA: lmmio_space [%lx/%lx] duplicate!\n", - ldev->hba.lmmio_space.start, - ldev->hba.lmmio_space.end); + (long)ldev->hba.lmmio_space.start, + (long)ldev->hba.lmmio_space.end); } else { err = request_resource(&iomem_resource, &(ldev->hba.lmmio_space)); if (err < 0) { printk(KERN_ERR "FAILED: lba_fixup_bus() request for " "lmmio_space [%lx/%lx]\n", - ldev->hba.lmmio_space.start, - ldev->hba.lmmio_space.end); + (long)ldev->hba.lmmio_space.start, + (long)ldev->hba.lmmio_space.end); } else bus->resource[i++] = &(ldev->hba.lmmio_space); } @@ -758,8 +759,8 @@ lba_fixup_bus(struct pci_bus *bus) if (err < 0) { printk("FAILED: lba_fixup_bus() request for " "gmmio_space [%lx/%lx]\n", - ldev->hba.gmmio_space.start, - ldev->hba.gmmio_space.end); + (long)ldev->hba.gmmio_space.start, + (long)ldev->hba.gmmio_space.end); lba_dump_res(&iomem_resource, 2); BUG(); } @@ -980,7 +981,7 @@ LBA_PORT_IN(32, 0) #define LBA_PORT_OUT(size, mask) \ static void lba_pat_out##size (struct pci_hba_data *l, u16 addr, u##size val) \ { \ - void *where = (void *) PIOP_TO_GMMIO(LBA_DEV(l), addr); \ + void __iomem *where = PIOP_TO_GMMIO(LBA_DEV(l), addr); \ DBG_PORT("%s(0x%p, 0x%x, 0x%x)\n", __FUNCTION__, l, addr, val); \ WRITE_REG##size(val, where); \ /* flush the I/O down to the elroy at least */ \ @@ -1063,16 +1064,16 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) /* used to fix up pre-initialized MEM BARs */ if (!lba_dev->hba.lmmio_space.start) { sprintf(lba_dev->hba.lmmio_name, - "PCI%02lx LMMIO", - lba_dev->hba.bus_num.start); + "PCI%02x LMMIO", + (int)lba_dev->hba.bus_num.start); lba_dev->hba.lmmio_space_offset = p->start - io->start; r = &lba_dev->hba.lmmio_space; r->name = lba_dev->hba.lmmio_name; } else if (!lba_dev->hba.elmmio_space.start) { sprintf(lba_dev->hba.elmmio_name, - "PCI%02lx ELMMIO", - lba_dev->hba.bus_num.start); + "PCI%02x ELMMIO", + (int)lba_dev->hba.bus_num.start); r = &lba_dev->hba.elmmio_space; r->name = lba_dev->hba.elmmio_name; } else { @@ -1089,8 +1090,8 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) case PAT_GMMIO: /* MMIO space > 4GB phys addr; for 64-bit BAR */ - sprintf(lba_dev->hba.gmmio_name, "PCI%02lx GMMIO", - lba_dev->hba.bus_num.start); + sprintf(lba_dev->hba.gmmio_name, "PCI%02x GMMIO", + (int)lba_dev->hba.bus_num.start); r = &lba_dev->hba.gmmio_space; r->name = lba_dev->hba.gmmio_name; r->start = p->start; @@ -1112,8 +1113,8 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) */ lba_dev->iop_base = ioremap_nocache(p->start, 64 * 1024 * 1024); - sprintf(lba_dev->hba.io_name, "PCI%02lx Ports", - lba_dev->hba.bus_num.start); + sprintf(lba_dev->hba.io_name, "PCI%02x Ports", + (int)lba_dev->hba.bus_num.start); r = &lba_dev->hba.io_space; r->name = lba_dev->hba.io_name; r->start = HBA_PORT_BASE(lba_dev->hba.hba_num); @@ -1166,8 +1167,8 @@ lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) ** Legacy boxes but it's nice to see in /proc/iomem. */ r = &(lba_dev->hba.lmmio_space); - sprintf(lba_dev->hba.lmmio_name, "PCI%02lx LMMIO", - lba_dev->hba.bus_num.start); + sprintf(lba_dev->hba.lmmio_name, "PCI%02x LMMIO", + (int)lba_dev->hba.bus_num.start); r->name = lba_dev->hba.lmmio_name; #if 1 @@ -1275,8 +1276,8 @@ lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) ** an existing (but unused portion of) distributed range. */ r = &(lba_dev->hba.elmmio_space); - sprintf(lba_dev->hba.elmmio_name, "PCI%02lx ELMMIO", - lba_dev->hba.bus_num.start); + sprintf(lba_dev->hba.elmmio_name, "PCI%02x ELMMIO", + (int)lba_dev->hba.bus_num.start); r->name = lba_dev->hba.elmmio_name; #if 1 @@ -1297,8 +1298,8 @@ lba_legacy_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev) #endif r = &(lba_dev->hba.io_space); - sprintf(lba_dev->hba.io_name, "PCI%02lx Ports", - lba_dev->hba.bus_num.start); + sprintf(lba_dev->hba.io_name, "PCI%02x Ports", + (int)lba_dev->hba.bus_num.start); r->name = lba_dev->hba.io_name; r->flags = IORESOURCE_IO; r->start = READ_REG32(lba_dev->hba.base_addr + LBA_IOS_BASE) & ~1L; @@ -1406,13 +1407,20 @@ lba_hw_init(struct lba_device *d) return 0; } - +/* + * Unfortunately, when firmware numbers busses, it doesn't take into account + * Cardbus bridges. So we have to renumber the busses to suit ourselves. + * Elroy/Mercury don't actually know what bus number they're attached to; + * we use bus 0 to indicate the directly attached bus and any other bus + * number will be taken care of by the PCI-PCI bridge. + */ +static unsigned int lba_next_bus = 0; /* -** Determine if lba should claim this chip (return 0) or not (return 1). -** If so, initialize the chip and tell other partners in crime they -** have work to do. -*/ + * Determine if lba should claim this chip (return 0) or not (return 1). + * If so, initialize the chip and tell other partners in crime they + * have work to do. + */ static int __init lba_driver_probe(struct parisc_device *dev) { @@ -1440,7 +1448,7 @@ lba_driver_probe(struct parisc_device *dev) } printk(KERN_INFO "Elroy version %s (0x%x) found at 0x%lx\n", - version, func_class & 0xf, dev->hpa.start); + version, func_class & 0xf, (long)dev->hpa.start); if (func_class < 2) { printk(KERN_WARNING "Can't support LBA older than " @@ -1470,17 +1478,16 @@ lba_driver_probe(struct parisc_device *dev) */ printk(KERN_INFO "%s version TR%d.%d (0x%x) found at 0x%lx\n", IS_MERCURY(dev) ? "Mercury" : "Quicksilver", major, - minor, func_class, dev->hpa.start); + minor, func_class, (long)dev->hpa.start); cfg_ops = &mercury_cfg_ops; } else { - printk(KERN_ERR "Unknown LBA found at 0x%lx\n", dev->hpa.start); + printk(KERN_ERR "Unknown LBA found at 0x%lx\n", + (long)dev->hpa.start); return -ENODEV; } - /* - ** Tell I/O SAPIC driver we have a IRQ handler/region. - */ + /* Tell I/O SAPIC driver we have a IRQ handler/region. */ tmp_obj = iosapic_register(dev->hpa.start + LBA_IOSAPIC_BASE); /* NOTE: PCI devices (e.g. 103c:1005 graphics card) which don't @@ -1529,16 +1536,17 @@ lba_driver_probe(struct parisc_device *dev) lba_legacy_resources(dev, lba_dev); } - /* - ** Tell PCI support another PCI bus was found. - ** Walks PCI bus for us too. - */ + if (lba_dev->hba.bus_num.start < lba_next_bus) + lba_dev->hba.bus_num.start = lba_next_bus; + dev->dev.platform_data = lba_dev; lba_bus = lba_dev->hba.hba_bus = pci_scan_bus_parented(&dev->dev, lba_dev->hba.bus_num.start, cfg_ops, NULL); - if (lba_bus) + if (lba_bus) { + lba_next_bus = lba_bus->subordinate + 1; pci_bus_add_devices(lba_bus); + } /* This is in lieu of calling pci_assign_unassigned_resources() */ if (is_pdc_pat()) { diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c index 9a731c1..3df82fe 100644 --- a/drivers/parisc/led.c +++ b/drivers/parisc/led.c @@ -66,8 +66,8 @@ static char lcd_text_default[32] __read_mostly; static struct workqueue_struct *led_wq; -static void led_work_func(void *); -static DECLARE_WORK(led_task, led_work_func, NULL); +static void led_work_func(struct work_struct *); +static DECLARE_DELAYED_WORK(led_task, led_work_func); #if 0 #define DPRINTK(x) printk x @@ -136,7 +136,7 @@ static int start_task(void) /* Create the work queue and queue the LED task */ led_wq = create_singlethread_workqueue("led_wq"); - queue_work(led_wq, &led_task); + queue_delayed_work(led_wq, &led_task, 0); return 0; } @@ -372,8 +372,6 @@ static __inline__ int led_get_net_activity(void) continue; if (LOOPBACK(in_dev->ifa_list->ifa_local)) continue; - if (!dev->get_stats) - continue; stats = dev->get_stats(dev); rx_total += stats->rx_packets; tx_total += stats->tx_packets; @@ -441,7 +439,7 @@ static __inline__ int led_get_diskio_activity(void) #define LED_UPDATE_INTERVAL (1 + (HZ*19/1000)) -static void led_work_func (void *unused) +static void led_work_func (struct work_struct *unused) { static unsigned long last_jiffies; static unsigned long count_HZ; /* counter in range 0..HZ */ @@ -588,7 +586,7 @@ int __init register_led_driver(int model, unsigned long cmd_reg, unsigned long d /* Ensure the work is queued */ if (led_wq) { - queue_work(led_wq, &led_task); + queue_delayed_work(led_wq, &led_task, 0); } return 0; @@ -629,7 +627,7 @@ void __init register_led_regions(void) ** avoid a race condition while writing the CMD/DATA register pair. ** */ -int lcd_print( char *str ) +int lcd_print( const char *str ) { int i; @@ -658,7 +656,7 @@ int lcd_print( char *str ) /* re-queue the work */ if (led_wq) { - queue_work(led_wq, &led_task); + queue_delayed_work(led_wq, &led_task, 0); } return lcd_info.lcd_width; diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c index 97e9dc0..6dedbde 100644 --- a/drivers/parisc/power.c +++ b/drivers/parisc/power.c @@ -2,7 +2,7 @@ * linux/drivers/parisc/power.c * HP PARISC soft power switch support driver * - * Copyright (c) 2001-2005 Helge Deller <deller@gmx.de> + * Copyright (c) 2001-2007 Helge Deller <deller@gmx.de> * All rights reserved. * * @@ -29,7 +29,6 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * * - * * HINT: * Support of the soft power switch button may be enabled or disabled at * runtime through the "/proc/sys/kernel/power" procfs entry. @@ -38,34 +37,28 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/kernel.h> -#include <linux/string.h> #include <linux/notifier.h> #include <linux/reboot.h> #include <linux/sched.h> -#include <linux/interrupt.h> -#include <linux/workqueue.h> +#include <linux/kthread.h> #include <asm/pdc.h> #include <asm/io.h> #include <asm/led.h> -#include <asm/uaccess.h> +#define DRIVER_NAME "powersw" +#define KTHREAD_NAME "kpowerswd" -#ifdef DEBUG -# define DPRINTK(x...) printk(x) -#else -# define DPRINTK(x...) -#endif - - -/* filename in /proc which can be used to enable/disable the power switch */ -#define SYSCTL_FILENAME "sys/kernel/power" +/* how often should the power button be polled ? */ +#define POWERSWITCH_POLL_PER_SEC 2 +/* how long does the power button needs to be down until we react ? */ +#define POWERSWITCH_DOWN_SEC 2 +/* assembly code to access special registers */ +/* taken from PCXL ERS page 82 */ #define DIAG_CODE(code) (0x14000000 + ((code)<<5)) -/* this will go to processor.h or any other place... */ -/* taken from PCXL ERS page 82 */ #define MFCPU_X(rDiagReg, t_ch, t_th, code) \ (DIAG_CODE(code) + ((rDiagReg)<<21) + ((t_ch)<<16) + ((t_th)<<0) ) @@ -76,111 +69,95 @@ #define __getDIAG(dr) ( { \ register unsigned long __res asm("r28");\ __asm__ __volatile__ ( \ - ".word %1\n nop\n" : "=&r" (__res) : "i" (MFCPU_T(dr,28)) \ + ".word %1" : "=&r" (__res) : "i" (MFCPU_T(dr,28) ) \ ); \ __res; \ } ) - -static void deferred_poweroff(void *dummy) -{ - if (kill_cad_pid(SIGINT, 1)) { - /* just in case killing init process failed */ - machine_power_off(); - } -} - -/* - * This function gets called from interrupt context. - * As it's called within an interrupt, it wouldn't sync if we don't - * use schedule_work(). - */ - -static DECLARE_WORK(poweroff_work, deferred_poweroff, NULL); - -static void poweroff(void) -{ - static int powering_off __read_mostly; - - if (powering_off) - return; - - powering_off++; - schedule_work(&poweroff_work); -} - - -/* local time-counter for shutdown */ +/* local shutdown counter */ static int shutdown_timer __read_mostly; /* check, give feedback and start shutdown after one second */ static void process_shutdown(void) { if (shutdown_timer == 0) - DPRINTK(KERN_INFO "Shutdown requested...\n"); + printk(KERN_ALERT KTHREAD_NAME ": Shutdown requested...\n"); shutdown_timer++; /* wait until the button was pressed for 1 second */ - if (shutdown_timer == HZ) { -#if defined (DEBUG) || defined(CONFIG_CHASSIS_LCD_LED) - static char msg[] = "Shutting down..."; -#endif - DPRINTK(KERN_INFO "%s\n", msg); + if (shutdown_timer == (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC)) { + static const char msg[] = "Shutting down..."; + printk(KERN_INFO KTHREAD_NAME ": %s\n", msg); lcd_print(msg); - poweroff(); + + /* send kill signal */ + if (kill_cad_pid(SIGINT, 1)) { + /* just in case killing init process failed */ + if (pm_power_off) + pm_power_off(); + } } } -/* main power switch tasklet struct (scheduled from time.c) */ -DECLARE_TASKLET_DISABLED(power_tasklet, NULL, 0); +/* main power switch task struct */ +static struct task_struct *power_task; + +/* filename in /proc which can be used to enable/disable the power switch */ +#define SYSCTL_FILENAME "sys/kernel/power" /* soft power switch enabled/disabled */ int pwrsw_enabled __read_mostly = 1; -/* - * On gecko style machines (e.g. 712/xx and 715/xx) - * the power switch status is stored in Bit 0 ("the highest bit") - * of CPU diagnose register 25. - * - */ -static void gecko_tasklet_func(unsigned long unused) +/* main kernel thread worker. It polls the button state */ +static int kpowerswd(void *param) { - if (unlikely(!pwrsw_enabled)) - return; - - if (__getDIAG(25) & 0x80000000) { - /* power switch button not pressed or released again */ - /* Warning: Some machines do never reset this DIAG flag! */ - shutdown_timer = 0; - } else { - process_shutdown(); - } -} - + __set_current_state(TASK_RUNNING); + + do { + int button_not_pressed; + unsigned long soft_power_reg = (unsigned long) param; + + schedule_timeout_interruptible(pwrsw_enabled ? HZ : HZ/POWERSWITCH_POLL_PER_SEC); + __set_current_state(TASK_RUNNING); + + if (unlikely(!pwrsw_enabled)) + continue; + + if (soft_power_reg) { + /* + * Non-Gecko-style machines: + * Check the power switch status which is read from the + * real I/O location at soft_power_reg. + * Bit 31 ("the lowest bit) is the status of the power switch. + * This bit is "1" if the button is NOT pressed. + */ + button_not_pressed = (gsc_readl(soft_power_reg) & 0x1); + } else { + /* + * On gecko style machines (e.g. 712/xx and 715/xx) + * the power switch status is stored in Bit 0 ("the highest bit") + * of CPU diagnose register 25. + * Warning: Some machines never reset the DIAG flag, even if + * the button has been released again. + */ + button_not_pressed = (__getDIAG(25) & 0x80000000); + } + + if (likely(button_not_pressed)) { + if (unlikely(shutdown_timer && /* avoid writing if not necessary */ + shutdown_timer < (POWERSWITCH_DOWN_SEC*POWERSWITCH_POLL_PER_SEC))) { + shutdown_timer = 0; + printk(KERN_INFO KTHREAD_NAME ": Shutdown request aborted.\n"); + } + } else + process_shutdown(); + + + } while (!kthread_should_stop()); - -/* - * Check the power switch status which is read from the - * real I/O location at soft_power_reg. - * Bit 31 ("the lowest bit) is the status of the power switch. - */ - -static void polling_tasklet_func(unsigned long soft_power_reg) -{ - unsigned long current_status; - - if (unlikely(!pwrsw_enabled)) - return; - - current_status = gsc_readl(soft_power_reg); - if (current_status & 0x1) { - /* power switch button not pressed */ - shutdown_timer = 0; - } else { - process_shutdown(); - } + return 0; } @@ -220,7 +197,7 @@ static struct notifier_block parisc_panic_block = { static int __init power_init(void) { unsigned long ret; - unsigned long soft_power_reg = 0; + unsigned long soft_power_reg; #if 0 request_irq( IRQ_FROM_REGION(CPU_IRQ_REGION)+2, &powerfail_interrupt, @@ -235,44 +212,44 @@ static int __init power_init(void) soft_power_reg = -1UL; switch (soft_power_reg) { - case 0: printk(KERN_INFO "Gecko-style soft power switch enabled.\n"); - power_tasklet.func = gecko_tasklet_func; + case 0: printk(KERN_INFO DRIVER_NAME ": Gecko-style soft power switch enabled.\n"); break; - case -1UL: printk(KERN_INFO "Soft power switch support not available.\n"); + case -1UL: printk(KERN_INFO DRIVER_NAME ": Soft power switch support not available.\n"); return -ENODEV; - default: printk(KERN_INFO "Soft power switch enabled, polling @ 0x%08lx.\n", + default: printk(KERN_INFO DRIVER_NAME ": Soft power switch at 0x%08lx enabled.\n", soft_power_reg); - power_tasklet.data = soft_power_reg; - power_tasklet.func = polling_tasklet_func; + } + + power_task = kthread_run(kpowerswd, (void*)soft_power_reg, KTHREAD_NAME); + if (IS_ERR(power_task)) { + printk(KERN_ERR DRIVER_NAME ": thread creation failed. Driver not loaded.\n"); + pdc_soft_power_button(0); + return -EIO; } /* Register a call for panic conditions. */ atomic_notifier_chain_register(&panic_notifier_list, &parisc_panic_block); - tasklet_enable(&power_tasklet); - return 0; } static void __exit power_exit(void) { - if (!power_tasklet.func) - return; + kthread_stop(power_task); - tasklet_disable(&power_tasklet); atomic_notifier_chain_unregister(&panic_notifier_list, &parisc_panic_block); - power_tasklet.func = NULL; + pdc_soft_power_button(0); } -module_init(power_init); +arch_initcall(power_init); module_exit(power_exit); -MODULE_AUTHOR("Helge Deller"); +MODULE_AUTHOR("Helge Deller <deller@gmx.de>"); MODULE_DESCRIPTION("Soft power switch driver"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index 76a29da..322957a 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -109,7 +109,7 @@ static unsigned long piranha_bad_128k = 0; #ifdef SBA_AGP_SUPPORT static int sba_reserve_agpgart = 1; -module_param(sba_reserve_agpgart, int, 1); +module_param(sba_reserve_agpgart, int, 0444); MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); #endif @@ -846,7 +846,7 @@ static void *sba_alloc_consistent(struct device *hwdev, size_t size, if (!hwdev) { /* only support PCI */ *dma_handle = 0; - return 0; + return NULL; } ret = (void *) __get_free_pages(gfp, get_order(size)); diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index b61c17b..3de2623 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -1546,7 +1546,7 @@ static void __devinit detect_and_report_smsc (void) } #endif /* CONFIG_PARPORT_PC_SUPERIO */ -static int __devinit get_superio_dma (struct parport *p) +static int get_superio_dma (struct parport *p) { int i=0; while( (superios[i].io != p->base) && (i<NR_SUPERIOS)) @@ -2106,7 +2106,7 @@ static int parport_irq_probe(struct parport *pb) /* --- DMA detection -------------------------------------- */ /* Only if chipset conforms to ECP ISA Interface Standard */ -static int __devinit programmable_dma_support (struct parport *p) +static int programmable_dma_support (struct parport *p) { unsigned char oecr = inb (ECONTROL (p)); int dma; @@ -2123,7 +2123,7 @@ static int __devinit programmable_dma_support (struct parport *p) return dma; } -static int __devinit parport_dma_probe (struct parport *p) +static int parport_dma_probe (struct parport *p) { const struct parport_pc_private *priv = p->private_data; if (priv->ecr) diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c index 9793533..400bb90 100644 --- a/drivers/parport/parport_sunbpp.c +++ b/drivers/parport/parport_sunbpp.c @@ -126,7 +126,7 @@ static unsigned char status_sunbpp_to_pc(struct parport *p) if (!(value_tcr & P_TCR_BUSY)) bits |= PARPORT_STATUS_BUSY; - dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", regs->p_tcr, regs->p_ir)); + dprintk((KERN_DEBUG "tcr 0x%x ir 0x%x\n", value_tcr, value_ir)); dprintk((KERN_DEBUG "read status 0x%x\n", bits)); return bits; } @@ -147,7 +147,7 @@ static unsigned char control_sunbpp_to_pc(struct parport *p) if (value_or & P_OR_SLCT_IN) bits |= PARPORT_CONTROL_SELECT; - dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or)); + dprintk((KERN_DEBUG "tcr 0x%x or 0x%x\n", value_tcr, value_or)); dprintk((KERN_DEBUG "read control 0x%x\n", bits)); return bits; } @@ -165,7 +165,8 @@ static unsigned char parport_sunbpp_frob_control(struct parport *p, unsigned char value_tcr = sbus_readb(®s->p_tcr); unsigned char value_or = sbus_readb(®s->p_or); - dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or)); + dprintk((KERN_DEBUG "frob1: tcr 0x%x or 0x%x\n", + value_tcr, value_or)); if (mask & PARPORT_CONTROL_STROBE) { if (val & PARPORT_CONTROL_STROBE) { value_tcr &= ~P_TCR_DS; @@ -197,7 +198,8 @@ static unsigned char parport_sunbpp_frob_control(struct parport *p, sbus_writeb(value_or, ®s->p_or); sbus_writeb(value_tcr, ®s->p_tcr); - dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n", regs->p_tcr, regs->p_or)); + dprintk((KERN_DEBUG "frob2: tcr 0x%x or 0x%x\n", + value_tcr, value_or)); return parport_sunbpp_read_control(p); } diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 68555c1..435c195 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -38,6 +38,36 @@ static int msi_cache_init(void) return 0; } +static void msi_set_enable(struct pci_dev *dev, int enable) +{ + int pos; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + control &= ~PCI_MSI_FLAGS_ENABLE; + if (enable) + control |= PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + } +} + +static void msix_set_enable(struct pci_dev *dev, int enable) +{ + int pos; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + control &= ~PCI_MSIX_FLAGS_ENABLE; + if (enable) + control |= PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + } +} + static void msi_set_mask_bit(unsigned int irq, int flag) { struct msi_desc *entry; @@ -55,6 +85,8 @@ static void msi_set_mask_bit(unsigned int irq, int flag) mask_bits &= ~(1); mask_bits |= flag; pci_write_config_dword(entry->dev, pos, mask_bits); + } else { + msi_set_enable(entry->dev, !flag); } break; case PCI_CAP_ID_MSIX: @@ -62,12 +94,14 @@ static void msi_set_mask_bit(unsigned int irq, int flag) int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET; writel(flag, entry->mask_base + offset); + readl(entry->mask_base + offset); break; } default: BUG(); break; } + entry->msi_attrib.masked = !!flag; } void read_msi_msg(unsigned int irq, struct msi_msg *msg) @@ -147,6 +181,7 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) default: BUG(); } + entry->msg = *msg; } void mask_msi_irq(unsigned int irq) @@ -192,201 +227,61 @@ static struct msi_desc* alloc_msi_entry(void) return entry; } -static void enable_msi_mode(struct pci_dev *dev, int pos, int type) -{ - u16 control; - - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (type == PCI_CAP_ID_MSI) { - /* Set enabled bits to single MSI & enable MSI_enable bit */ - msi_enable(control, 1); - pci_write_config_word(dev, msi_control_reg(pos), control); - dev->msi_enabled = 1; - } else { - msix_enable(control); - pci_write_config_word(dev, msi_control_reg(pos), control); - dev->msix_enabled = 1; - } - - pci_intx(dev, 0); /* disable intx */ -} - -void disable_msi_mode(struct pci_dev *dev, int pos, int type) -{ - u16 control; - - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (type == PCI_CAP_ID_MSI) { - /* Set enabled bits to single MSI & enable MSI_enable bit */ - msi_disable(control); - pci_write_config_word(dev, msi_control_reg(pos), control); - dev->msi_enabled = 0; - } else { - msix_disable(control); - pci_write_config_word(dev, msi_control_reg(pos), control); - dev->msix_enabled = 0; - } - - pci_intx(dev, 1); /* enable intx */ -} - #ifdef CONFIG_PM -static int __pci_save_msi_state(struct pci_dev *dev) -{ - int pos, i = 0; - u16 control; - struct pci_cap_saved_state *save_state; - u32 *cap; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (pos <= 0 || dev->no_msi) - return 0; - - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSI_FLAGS_ENABLE)) - return 0; - - save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u32) * 5, - GFP_KERNEL); - if (!save_state) { - printk(KERN_ERR "Out of memory in pci_save_msi_state\n"); - return -ENOMEM; - } - cap = &save_state->data[0]; - - pci_read_config_dword(dev, pos, &cap[i++]); - control = cap[0] >> 16; - pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, &cap[i++]); - if (control & PCI_MSI_FLAGS_64BIT) { - pci_read_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, &cap[i++]); - pci_read_config_dword(dev, pos + PCI_MSI_DATA_64, &cap[i++]); - } else - pci_read_config_dword(dev, pos + PCI_MSI_DATA_32, &cap[i++]); - if (control & PCI_MSI_FLAGS_MASKBIT) - pci_read_config_dword(dev, pos + PCI_MSI_MASK_BIT, &cap[i++]); - save_state->cap_nr = PCI_CAP_ID_MSI; - pci_add_saved_cap(dev, save_state); - return 0; -} - static void __pci_restore_msi_state(struct pci_dev *dev) { - int i = 0, pos; - u16 control; - struct pci_cap_saved_state *save_state; - u32 *cap; - - save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSI); - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (!save_state || pos <= 0) - return; - cap = &save_state->data[0]; - - control = cap[i++] >> 16; - pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_LO, cap[i++]); - if (control & PCI_MSI_FLAGS_64BIT) { - pci_write_config_dword(dev, pos + PCI_MSI_ADDRESS_HI, cap[i++]); - pci_write_config_dword(dev, pos + PCI_MSI_DATA_64, cap[i++]); - } else - pci_write_config_dword(dev, pos + PCI_MSI_DATA_32, cap[i++]); - if (control & PCI_MSI_FLAGS_MASKBIT) - pci_write_config_dword(dev, pos + PCI_MSI_MASK_BIT, cap[i++]); - pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); - enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); - pci_remove_saved_cap(save_state); - kfree(save_state); -} - -static int __pci_save_msix_state(struct pci_dev *dev) -{ int pos; - int irq, head, tail = 0; u16 control; - struct pci_cap_saved_state *save_state; - - if (!dev->msix_enabled) - return 0; - - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos <= 0 || dev->no_msi) - return 0; - - /* save the capability */ - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSIX_FLAGS_ENABLE)) - return 0; - save_state = kzalloc(sizeof(struct pci_cap_saved_state) + sizeof(u16), - GFP_KERNEL); - if (!save_state) { - printk(KERN_ERR "Out of memory in pci_save_msix_state\n"); - return -ENOMEM; - } - *((u16 *)&save_state->data[0]) = control; - - /* save the table */ - irq = head = dev->first_msi_irq; - while (head != tail) { - struct msi_desc *entry; - - entry = get_irq_msi(irq); - read_msi_msg(irq, &entry->msg_save); - - tail = entry->link.tail; - irq = tail; - } - - save_state->cap_nr = PCI_CAP_ID_MSIX; - pci_add_saved_cap(dev, save_state); - return 0; -} + struct msi_desc *entry; -int pci_save_msi_state(struct pci_dev *dev) -{ - int rc; + if (!dev->msi_enabled) + return; - rc = __pci_save_msi_state(dev); - if (rc) - return rc; + entry = get_irq_msi(dev->irq); + pos = entry->msi_attrib.pos; - rc = __pci_save_msix_state(dev); + pci_intx(dev, 0); /* disable intx */ + msi_set_enable(dev, 0); + write_msi_msg(dev->irq, &entry->msg); + if (entry->msi_attrib.maskbit) + msi_set_mask_bit(dev->irq, entry->msi_attrib.masked); - return rc; + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + control &= ~(PCI_MSI_FLAGS_QSIZE | PCI_MSI_FLAGS_ENABLE); + if (entry->msi_attrib.maskbit || !entry->msi_attrib.masked) + control |= PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); } static void __pci_restore_msix_state(struct pci_dev *dev) { - u16 save; int pos; int irq, head, tail = 0; struct msi_desc *entry; - struct pci_cap_saved_state *save_state; + u16 control; if (!dev->msix_enabled) return; - save_state = pci_find_saved_cap(dev, PCI_CAP_ID_MSIX); - if (!save_state) - return; - save = *((u16 *)&save_state->data[0]); - pci_remove_saved_cap(save_state); - kfree(save_state); - - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos <= 0) - return; - /* route the table */ + pci_intx(dev, 0); /* disable intx */ + msix_set_enable(dev, 0); irq = head = dev->first_msi_irq; + entry = get_irq_msi(irq); + pos = entry->msi_attrib.pos; while (head != tail) { entry = get_irq_msi(irq); - write_msi_msg(irq, &entry->msg_save); + write_msi_msg(irq, &entry->msg); + msi_set_mask_bit(irq, entry->msi_attrib.masked); tail = entry->link.tail; irq = tail; } - pci_write_config_word(dev, msi_control_reg(pos), save); - enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + control &= ~PCI_MSIX_FLAGS_MASKALL; + control |= PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); } void pci_restore_msi_state(struct pci_dev *dev) @@ -411,6 +306,8 @@ static int msi_capability_init(struct pci_dev *dev) int pos, irq; u16 control; + msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */ + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ @@ -422,6 +319,7 @@ static int msi_capability_init(struct pci_dev *dev) entry->msi_attrib.is_64 = is_64bit_address(control); entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); + entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; if (is_mask_bit_support(control)) { @@ -454,7 +352,9 @@ static int msi_capability_init(struct pci_dev *dev) set_irq_msi(irq, entry); /* Set MSI enabled bits */ - enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + pci_intx(dev, 0); /* disable intx */ + msi_set_enable(dev, 1); + dev->msi_enabled = 1; dev->irq = irq; return 0; @@ -481,6 +381,8 @@ static int msix_capability_init(struct pci_dev *dev, u8 bir; void __iomem *base; + msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */ + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); /* Request & Map MSI-X table region */ pci_read_config_word(dev, msi_control_reg(pos), &control); @@ -505,6 +407,7 @@ static int msix_capability_init(struct pci_dev *dev, entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = j; entry->msi_attrib.maskbit = 1; + entry->msi_attrib.masked = 1; entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; entry->dev = dev; @@ -549,7 +452,9 @@ static int msix_capability_init(struct pci_dev *dev, } dev->first_msi_irq = entries[0].vector; /* Set MSI-X enabled bits */ - enable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + pci_intx(dev, 0); /* disable intx */ + msix_set_enable(dev, 1); + dev->msix_enabled = 1; return 0; } @@ -611,12 +516,11 @@ int pci_enable_msi(struct pci_dev* dev) WARN_ON(!!dev->msi_enabled); /* Check whether driver already requested for MSI-X irqs */ - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (pos > 0 && dev->msix_enabled) { - printk(KERN_INFO "PCI: %s: Can't enable MSI. " - "Device already has MSI-X enabled\n", - pci_name(dev)); - return -EINVAL; + if (dev->msix_enabled) { + printk(KERN_INFO "PCI: %s: Can't enable MSI. " + "Device already has MSI-X enabled\n", + pci_name(dev)); + return -EINVAL; } status = msi_capability_init(dev); return status; @@ -625,8 +529,7 @@ int pci_enable_msi(struct pci_dev* dev) void pci_disable_msi(struct pci_dev* dev) { struct msi_desc *entry; - int pos, default_irq; - u16 control; + int default_irq; if (!pci_msi_enable) return; @@ -636,16 +539,9 @@ void pci_disable_msi(struct pci_dev* dev) if (!dev->msi_enabled) return; - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); - if (!pos) - return; - - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSI_FLAGS_ENABLE)) - return; - - - disable_msi_mode(dev, pos, PCI_CAP_ID_MSI); + msi_set_enable(dev, 0); + pci_intx(dev, 1); /* enable intx */ + dev->msi_enabled = 0; entry = get_irq_msi(dev->first_msi_irq); if (!entry || !entry->dev || entry->msi_attrib.type != PCI_CAP_ID_MSI) { @@ -746,8 +642,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) WARN_ON(!!dev->msix_enabled); /* Check whether driver already requested for MSI irq */ - if (pci_find_capability(dev, PCI_CAP_ID_MSI) > 0 && - dev->msi_enabled) { + if (dev->msi_enabled) { printk(KERN_INFO "PCI: %s: Can't enable MSI-X. " "Device already has an MSI irq assigned\n", pci_name(dev)); @@ -760,8 +655,6 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) void pci_disable_msix(struct pci_dev* dev) { int irq, head, tail = 0, warning = 0; - int pos; - u16 control; if (!pci_msi_enable) return; @@ -771,15 +664,9 @@ void pci_disable_msix(struct pci_dev* dev) if (!dev->msix_enabled) return; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - if (!pos) - return; - - pci_read_config_word(dev, msi_control_reg(pos), &control); - if (!(control & PCI_MSIX_FLAGS_ENABLE)) - return; - - disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); + msix_set_enable(dev, 0); + pci_intx(dev, 1); /* enable intx */ + dev->msix_enabled = 0; irq = head = dev->first_msi_irq; while (head != tail) { diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index a064f36..b5ac810 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -317,6 +317,10 @@ static int __init acpi_pci_init(void) { int ret; + if (acpi_gbl_FADT.boot_flags & BAF_MSI_NOT_SUPPORTED) { + printk(KERN_INFO"ACPI FADT declares the system doesn't support MSI, so disable it\n"); + pci_no_msi(); + } ret = register_acpi_bus_type(&acpi_pci_bus); if (ret) return 0; diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 4438ae1..39e80fc 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -415,6 +415,7 @@ static struct kobj_type pci_driver_kobj_type = { * __pci_register_driver - register a new pci driver * @drv: the driver structure to register * @owner: owner module of drv + * @mod_name: module name string * * Adds the driver structure to the list of registered drivers. * Returns a negative value on error, otherwise 0. @@ -433,11 +434,6 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner, drv->driver.mod_name = mod_name; drv->driver.kobj.ktype = &pci_driver_kobj_type; - if (pci_multithread_probe) - drv->driver.multithread_probe = pci_multithread_probe; - else - drv->driver.multithread_probe = drv->multithread_probe; - spin_lock_init(&drv->dynids.lock); INIT_LIST_HEAD(&drv->dynids.list); @@ -573,6 +569,7 @@ struct bus_type pci_bus_type = { static int __init pci_driver_init(void) { + pci_bus_type.multithread_probe = pci_multithread_probe; return bus_register(&pci_bus_type); } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 7a94076..cd913a2 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -143,6 +143,14 @@ static ssize_t is_enabled_show(struct device *dev, return sprintf (buf, "%u\n", atomic_read(&pdev->enable_cnt)); } +#ifdef CONFIG_NUMA +static ssize_t +numa_node_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf (buf, "%d\n", dev->numa_node); +} +#endif + static ssize_t msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -194,6 +202,9 @@ struct device_attribute pci_dev_attrs[] = { __ATTR_RO(irq), __ATTR_RO(local_cpus), __ATTR_RO(modalias), +#ifdef CONFIG_NUMA + __ATTR_RO(numa_node), +#endif __ATTR(enable, 0600, is_enabled_show, is_enabled_store), __ATTR(broken_parity_status,(S_IRUGO|S_IWUSR), broken_parity_status_show,broken_parity_status_store), diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8b44cff..2a45827 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -13,6 +13,7 @@ #include <linux/delay.h> #include <linux/init.h> #include <linux/pci.h> +#include <linux/pm.h> #include <linux/module.h> #include <linux/spinlock.h> #include <linux/string.h> @@ -21,6 +22,12 @@ unsigned int pci_pm_d3_delay = 10; +#define DEFAULT_CARDBUS_IO_SIZE (256) +#define DEFAULT_CARDBUS_MEM_SIZE (64*1024*1024) +/* pci=cbmemsize=nnM,cbiosize=nn can override this */ +unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; +unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -545,7 +552,9 @@ static int pci_save_pcie_state(struct pci_dev *dev) if (pos <= 0) return 0; - save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); + if (!save_state) + save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); if (!save_state) { dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n"); return -ENOMEM; @@ -576,8 +585,6 @@ static void pci_restore_pcie_state(struct pci_dev *dev) pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]); pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]); pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]); - pci_remove_saved_cap(save_state); - kfree(save_state); } @@ -591,7 +598,9 @@ static int pci_save_pcix_state(struct pci_dev *dev) if (pos <= 0) return 0; - save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); + save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); + if (!save_state) + save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); if (!save_state) { dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n"); return -ENOMEM; @@ -616,8 +625,6 @@ static void pci_restore_pcix_state(struct pci_dev *dev) cap = (u16 *)&save_state->data[0]; pci_write_config_word(dev, pos + PCI_X_CMD, cap[i++]); - pci_remove_saved_cap(save_state); - kfree(save_state); } @@ -632,8 +639,6 @@ pci_save_state(struct pci_dev *dev) /* XXX: 100% dword access ok here? */ for (i = 0; i < 16; i++) pci_read_config_dword(dev, i * 4,&dev->saved_config_space[i]); - if ((i = pci_save_msi_state(dev)) != 0) - return i; if ((i = pci_save_pcie_state(dev)) != 0) return i; if ((i = pci_save_pcix_state(dev)) != 0) @@ -751,7 +756,8 @@ int pci_enable_device(struct pci_dev *dev) * when a device is enabled using managed PCI device enable interface. */ struct pci_devres { - unsigned int disable:1; + unsigned int enabled:1; + unsigned int pinned:1; unsigned int orig_intx:1; unsigned int restore_intx:1; u32 region_mask; @@ -775,7 +781,7 @@ static void pcim_release(struct device *gendev, void *res) if (this->restore_intx) pci_intx(dev, this->orig_intx); - if (this->disable) + if (this->enabled && !this->pinned) pci_disable_device(dev); } @@ -814,12 +820,12 @@ int pcim_enable_device(struct pci_dev *pdev) dr = get_pci_dr(pdev); if (unlikely(!dr)) return -ENOMEM; - WARN_ON(!!dr->disable); + WARN_ON(!!dr->enabled); rc = pci_enable_device(pdev); if (!rc) { pdev->is_managed = 1; - dr->disable = 1; + dr->enabled = 1; } return rc; } @@ -837,9 +843,9 @@ void pcim_pin_device(struct pci_dev *pdev) struct pci_devres *dr; dr = find_pci_dr(pdev); - WARN_ON(!dr || !dr->disable); + WARN_ON(!dr || !dr->enabled); if (dr) - dr->disable = 0; + dr->pinned = 1; } /** @@ -870,18 +876,11 @@ pci_disable_device(struct pci_dev *dev) dr = find_pci_dr(dev); if (dr) - dr->disable = 0; + dr->enabled = 0; if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; - if (dev->msi_enabled) - disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), - PCI_CAP_ID_MSI); - if (dev->msix_enabled) - disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), - PCI_CAP_ID_MSIX); - pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; @@ -893,31 +892,48 @@ pci_disable_device(struct pci_dev *dev) } /** - * pci_enable_wake - enable device to generate PME# when suspended - * @dev: - PCI device to operate on - * @state: - Current state of device. - * @enable: - Flag to enable or disable generation - * - * Set the bits in the device's PM Capabilities to generate PME# when - * the system is suspended. + * pci_enable_wake - enable PCI device as wakeup event source + * @dev: PCI device affected + * @state: PCI state from which device will issue wakeup events + * @enable: True to enable event generation; false to disable * - * -EIO is returned if device doesn't have PM Capabilities. - * -EINVAL is returned if device supports it, but can't generate wake events. - * 0 if operation is successful. - * + * This enables the device as a wakeup event source, or disables it. + * When such events involves platform-specific hooks, those hooks are + * 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. + * + * -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. */ 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. + */ + + 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 doesn't support PM Capabilities, but request is to disable - * wake events, it's a nop; otherwise fail */ - if (!pm) - return enable ? -EIO : 0; + /* 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. + */ + if (!pm) + return enable ? status : 0; /* Check device's ability to generate PME# */ pci_read_config_word(dev,pm+PCI_PM_PMC,&value); @@ -926,8 +942,14 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) 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 (!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; + } pci_read_config_word(dev, pm + PCI_PM_CTRL, &value); @@ -938,7 +960,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) value &= ~PCI_PM_CTRL_PME_ENABLE; pci_write_config_word(dev, pm + PCI_PM_CTRL, value); - + return 0; } @@ -1271,6 +1293,33 @@ pci_intx(struct pci_dev *pdev, int enable) } } +/** + * pci_msi_off - disables any msi or msix capabilities + * @pdev: the PCI device to operate on + * + * If you want to use msi see pci_enable_msi and friends. + * This is a lower level primitive that allows us to disable + * msi operation at the device level. + */ +void pci_msi_off(struct pci_dev *dev) +{ + int pos; + u16 control; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos) { + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + control &= ~PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); + } + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + if (pos) { + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + control &= ~PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + } +} + #ifndef HAVE_ARCH_PCI_SET_DMA_MASK /* * These can be overridden by arch-specific implementations @@ -1300,7 +1349,7 @@ pci_set_consistent_dma_mask(struct pci_dev *dev, u64 mask) /** * pci_select_bars - Make BAR mask from the type of resource - * @pdev: the PCI device for which BAR mask is made + * @dev: the PCI device for which BAR mask is made * @flags: resource type mask to be selected * * This helper routine makes bar mask from the type of resource. @@ -1333,6 +1382,10 @@ static int __devinit pci_setup(char *str) if (*str && (str = pcibios_setup(str)) && *str) { if (!strcmp(str, "nomsi")) { pci_no_msi(); + } else if (!strncmp(str, "cbiosize=", 9)) { + pci_cardbus_io_size = memparse(str + 9, &str); + } else if (!strncmp(str, "cbmemsize=", 10)) { + pci_cardbus_mem_size = memparse(str + 10, &str); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index a4f2d58..62ea04c 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -46,18 +46,14 @@ extern struct rw_semaphore pci_bus_sem; extern unsigned int pci_pm_d3_delay; #ifdef CONFIG_PCI_MSI -void disable_msi_mode(struct pci_dev *dev, int pos, int type); void pci_no_msi(void); #else -static inline void disable_msi_mode(struct pci_dev *dev, int pos, int type) { } static inline void pci_no_msi(void) { } #endif #if defined(CONFIG_PCI_MSI) && defined(CONFIG_PM) -int pci_save_msi_state(struct pci_dev *dev); void pci_restore_msi_state(struct pci_dev *dev); #else -static inline int pci_save_msi_state(struct pci_dev *dev) { return 0; } static inline void pci_restore_msi_state(struct pci_dev *dev) {} #endif diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index b164de0..db6ad8e 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -66,7 +66,7 @@ static struct pci_error_handlers aer_error_handlers = { .resume = aer_error_resume, }; -static struct pcie_port_service_driver aerdrv = { +static struct pcie_port_service_driver aerdriver = { .name = "aer", .id_table = &aer_id[0], @@ -328,7 +328,7 @@ static void aer_error_resume(struct pci_dev *dev) **/ static int __init aer_service_init(void) { - return pcie_port_service_register(&aerdrv); + return pcie_port_service_register(&aerdriver); } /** @@ -338,7 +338,7 @@ static int __init aer_service_init(void) **/ static void __exit aer_service_exit(void) { - pcie_port_service_unregister(&aerdrv); + pcie_port_service_unregister(&aerdriver); } module_init(aer_service_init); diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 3c0a58f..bf655db 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -85,7 +85,7 @@ struct aer_rpc { struct mutex rpc_mutex; /* * only one thread could do * recovery on the same - * root port hierachy + * root port hierarchy */ wait_queue_head_t wait_release; }; diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index f17e7ed..df38364 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -93,7 +93,7 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, if (!dev->irq && dev->pin) { printk(KERN_WARNING "%s->Dev[%04x:%04x] has invalid IRQ. Check vendor BIOS\n", - __FUNCTION__, dev->device, dev->vendor); + __FUNCTION__, dev->vendor, dev->device); } if (pcie_port_device_register(dev)) { pci_disable_device(dev); @@ -276,7 +276,7 @@ static struct pci_error_handlers pcie_portdrv_err_handler = { .resume = pcie_portdrv_err_resume, }; -static struct pci_driver pcie_portdrv = { +static struct pci_driver pcie_portdriver = { .name = (char *)device_name, .id_table = &port_pci_ids[0], @@ -298,7 +298,7 @@ static int __init pcie_portdrv_init(void) printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); goto out; } - retval = pci_register_driver(&pcie_portdrv); + retval = pci_register_driver(&pcie_portdriver); if (retval) pcie_port_bus_unregister(); out: @@ -307,7 +307,7 @@ static int __init pcie_portdrv_init(void) static void __exit pcie_portdrv_exit(void) { - pci_unregister_driver(&pcie_portdrv); + pci_unregister_driver(&pcie_portdriver); pcie_port_bus_unregister(); } diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 1e6eda2..3411483 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -963,6 +963,13 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, k8t_sound_ho * bridge. Unfortunately, this device has no subvendor/subdevice ID. So it * becomes necessary to do this tweak in two steps -- I've chosen the Host * bridge as trigger. + * + * Note that we used to unhide the SMBus that way on Toshiba laptops + * (Satellite A40 and Tecra M2) but then found that the thermal management + * was done by SMM code, which could cause unsynchronized concurrent + * accesses to the SMBus registers, with potentially bad effects. Thus you + * should be very careful when adding new entries: if SMM is accessing the + * Intel SMBus, this is a very good reason to leave it hidden. */ static int asus_hides_smbus; @@ -1040,17 +1047,6 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) case 0x099c: /* HP Compaq nx6110 */ asus_hides_smbus = 1; } - } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_TOSHIBA)) { - if (dev->device == PCI_DEVICE_ID_INTEL_82855GM_HB) - switch(dev->subsystem_device) { - case 0x0001: /* Toshiba Satellite A40 */ - asus_hides_smbus = 1; - } - else if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB) - switch(dev->subsystem_device) { - case 0x0001: /* Toshiba Tecra M2 */ - asus_hides_smbus = 1; - } } else if (unlikely(dev->subsystem_vendor == PCI_VENDOR_ID_SAMSUNG)) { if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB) switch(dev->subsystem_device) { @@ -1218,45 +1214,68 @@ DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, asus_hides_a * do this early on to make the additional device appear during * the PCI scanning. */ - -static void quirk_jmicron_dualfn(struct pci_dev *pdev) +static void quirk_jmicron_ata(struct pci_dev *pdev) { - u32 conf; + u32 conf1, conf5, class; u8 hdr; /* Only poke fn 0 */ if (PCI_FUNC(pdev->devfn)) return; - switch(pdev->device) { - case PCI_DEVICE_ID_JMICRON_JMB365: - case PCI_DEVICE_ID_JMICRON_JMB366: - /* Redirect IDE second PATA port to the right spot */ - pci_read_config_dword(pdev, 0x80, &conf); - conf |= (1 << 24); - /* Fall through */ - pci_write_config_dword(pdev, 0x80, conf); - case PCI_DEVICE_ID_JMICRON_JMB361: - case PCI_DEVICE_ID_JMICRON_JMB363: - pci_read_config_dword(pdev, 0x40, &conf); - /* Enable dual function mode, AHCI on fn 0, IDE fn1 */ - /* Set the class codes correctly and then direct IDE 0 */ - conf &= ~0x000FF200; /* Clear bit 9 and 12-19 */ - conf |= 0x00C2A102; /* Set 1, 8, 13, 15, 17, 22, 23 */ - pci_write_config_dword(pdev, 0x40, conf); - - /* Reconfigure so that the PCI scanner discovers the - device is now multifunction */ - - pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr); - pdev->hdr_type = hdr & 0x7f; - pdev->multifunction = !!(hdr & 0x80); + pci_read_config_dword(pdev, 0x40, &conf1); + pci_read_config_dword(pdev, 0x80, &conf5); - break; + conf1 &= ~0x00CFF302; /* Clear bit 1, 8, 9, 12-19, 22, 23 */ + conf5 &= ~(1 << 24); /* Clear bit 24 */ + + switch (pdev->device) { + case PCI_DEVICE_ID_JMICRON_JMB360: + /* The controller should be in single function ahci mode */ + conf1 |= 0x0002A100; /* Set 8, 13, 15, 17 */ + break; + + case PCI_DEVICE_ID_JMICRON_JMB365: + case PCI_DEVICE_ID_JMICRON_JMB366: + /* Redirect IDE second PATA port to the right spot */ + conf5 |= (1 << 24); + /* Fall through */ + case PCI_DEVICE_ID_JMICRON_JMB361: + case PCI_DEVICE_ID_JMICRON_JMB363: + /* Enable dual function mode, AHCI on fn 0, IDE fn1 */ + /* Set the class codes correctly and then direct IDE 0 */ + conf1 |= 0x00C2A102; /* Set 1, 8, 13, 15, 17, 22, 23 */ + break; + + case PCI_DEVICE_ID_JMICRON_JMB368: + /* The controller should be in single function IDE mode */ + conf1 |= 0x00C00000; /* Set 22, 23 */ + break; } -} -DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, quirk_jmicron_dualfn); -DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_JMICRON, PCI_ANY_ID, quirk_jmicron_dualfn); + + pci_write_config_dword(pdev, 0x40, conf1); + pci_write_config_dword(pdev, 0x80, conf5); + + /* Update pdev accordingly */ + pci_read_config_byte(pdev, PCI_HEADER_TYPE, &hdr); + pdev->hdr_type = hdr & 0x7f; + pdev->multifunction = !!(hdr & 0x80); + + pci_read_config_dword(pdev, PCI_CLASS_REVISION, &class); + pdev->class = class >> 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB360, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361, quirk_jmicron_ata); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363, quirk_jmicron_ata); +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); #endif @@ -1284,119 +1303,6 @@ static void __init quirk_alder_ioapic(struct pci_dev *pdev) DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_alder_ioapic ); #endif -enum ide_combined_type { COMBINED = 0, IDE = 1, LIBATA = 2 }; -/* Defaults to combined */ -static enum ide_combined_type combined_mode; - -static int __init combined_setup(char *str) -{ - if (!strncmp(str, "ide", 3)) - combined_mode = IDE; - else if (!strncmp(str, "libata", 6)) - combined_mode = LIBATA; - else /* "combined" or anything else defaults to old behavior */ - combined_mode = COMBINED; - - return 1; -} -__setup("combined_mode=", combined_setup); - -#ifdef CONFIG_SATA_INTEL_COMBINED -static void __devinit quirk_intel_ide_combined(struct pci_dev *pdev) -{ - u8 prog, comb, tmp; - int ich = 0; - - /* - * Narrow down to Intel SATA PCI devices. - */ - switch (pdev->device) { - /* PCI ids taken from drivers/scsi/ata_piix.c */ - case 0x24d1: - case 0x24df: - case 0x25a3: - case 0x25b0: - ich = 5; - break; - case 0x2651: - case 0x2652: - case 0x2653: - case 0x2680: /* ESB2 */ - ich = 6; - break; - case 0x27c0: - case 0x27c4: - ich = 7; - break; - case 0x2828: /* ICH8M */ - ich = 8; - break; - default: - /* we do not handle this PCI device */ - return; - } - - /* - * Read combined mode register. - */ - pci_read_config_byte(pdev, 0x90, &tmp); /* combined mode reg */ - - if (ich == 5) { - tmp &= 0x6; /* interesting bits 2:1, PATA primary/secondary */ - if (tmp == 0x4) /* bits 10x */ - comb = (1 << 0); /* SATA port 0, PATA port 1 */ - else if (tmp == 0x6) /* bits 11x */ - comb = (1 << 2); /* PATA port 0, SATA port 1 */ - else - return; /* not in combined mode */ - } else { - WARN_ON((ich != 6) && (ich != 7) && (ich != 8)); - tmp &= 0x3; /* interesting bits 1:0 */ - if (tmp & (1 << 0)) - comb = (1 << 2); /* PATA port 0, SATA port 1 */ - else if (tmp & (1 << 1)) - comb = (1 << 0); /* SATA port 0, PATA port 1 */ - else - return; /* not in combined mode */ - } - - /* - * Read programming interface register. - * (Tells us if it's legacy or native mode) - */ - pci_read_config_byte(pdev, PCI_CLASS_PROG, &prog); - - /* if SATA port is in native mode, we're ok. */ - if (prog & comb) - return; - - /* Don't reserve any so the IDE driver can get them (but only if - * combined_mode=ide). - */ - if (combined_mode == IDE) - return; - - /* Grab them both for libata if combined_mode=libata. */ - if (combined_mode == LIBATA) { - request_region(0x1f0, 8, "libata"); /* port 0 */ - request_region(0x170, 8, "libata"); /* port 1 */ - return; - } - - /* SATA port is in legacy mode. Reserve port so that - * IDE driver does not attempt to use it. If request_region - * fails, it will be obvious at boot time, so we don't bother - * checking return values. - */ - if (comb == (1 << 0)) - request_region(0x1f0, 8, "libata"); /* port 0 */ - else - request_region(0x170, 8, "libata"); /* port 1 */ -} -DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_ANY_ID, quirk_intel_ide_combined ); -#endif /* CONFIG_SATA_INTEL_COMBINED */ - - int pcie_mch_quirk; EXPORT_SYMBOL(pcie_mch_quirk); @@ -1415,8 +1321,8 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7525_MCH, quir */ static void __devinit quirk_pcie_pxh(struct pci_dev *dev) { - disable_msi_mode(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), - PCI_CAP_ID_MSI); + pci_msi_off(dev); + dev->no_msi = 1; printk(KERN_WARNING "PCI: PXH quirk detected, " diff --git a/drivers/pci/search.c b/drivers/pci/search.c index ff98ead..2dd8681 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -15,7 +15,7 @@ DECLARE_RWSEM(pci_bus_sem); -static struct pci_bus * __devinit +static struct pci_bus * pci_do_find_bus(struct pci_bus* bus, unsigned char busnr) { struct pci_bus* child; diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 89f3036..3554f39 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -36,13 +36,6 @@ #define ROUND_UP(x, a) (((x) + (a) - 1) & ~((a) - 1)) -/* - * FIXME: IO should be max 256 bytes. However, since we may - * have a P2P bridge below a cardbus bridge, we need 4K. - */ -#define CARDBUS_IO_SIZE (256) -#define CARDBUS_MEM_SIZE (64*1024*1024) - static void __devinit pbus_assign_resources_sorted(struct pci_bus *bus) { @@ -415,12 +408,12 @@ pci_bus_size_cardbus(struct pci_bus *bus) * Reserve some resources for CardBus. We reserve * a fixed amount of bus space for CardBus bridges. */ - b_res[0].start = CARDBUS_IO_SIZE; - b_res[0].end = b_res[0].start + CARDBUS_IO_SIZE - 1; + b_res[0].start = pci_cardbus_io_size; + b_res[0].end = b_res[0].start + pci_cardbus_io_size - 1; b_res[0].flags |= IORESOURCE_IO; - b_res[1].start = CARDBUS_IO_SIZE; - b_res[1].end = b_res[1].start + CARDBUS_IO_SIZE - 1; + b_res[1].start = pci_cardbus_io_size; + b_res[1].end = b_res[1].start + pci_cardbus_io_size - 1; b_res[1].flags |= IORESOURCE_IO; /* @@ -440,16 +433,16 @@ pci_bus_size_cardbus(struct pci_bus *bus) * twice the size. */ if (ctrl & PCI_CB_BRIDGE_CTL_PREFETCH_MEM0) { - b_res[2].start = CARDBUS_MEM_SIZE; - b_res[2].end = b_res[2].start + CARDBUS_MEM_SIZE - 1; + b_res[2].start = pci_cardbus_mem_size; + b_res[2].end = b_res[2].start + pci_cardbus_mem_size - 1; b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; - b_res[3].start = CARDBUS_MEM_SIZE; - b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE - 1; + b_res[3].start = pci_cardbus_mem_size; + b_res[3].end = b_res[3].start + pci_cardbus_mem_size - 1; b_res[3].flags |= IORESOURCE_MEM; } else { - b_res[3].start = CARDBUS_MEM_SIZE * 2; - b_res[3].end = b_res[3].start + CARDBUS_MEM_SIZE * 2 - 1; + b_res[3].start = pci_cardbus_mem_size * 2; + b_res[3].end = b_res[3].start + pci_cardbus_mem_size * 2 - 1; b_res[3].flags |= IORESOURCE_MEM; } } diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c index a251289..568f187 100644 --- a/drivers/pci/setup-irq.c +++ b/drivers/pci/setup-irq.c @@ -24,7 +24,7 @@ pdev_fixup_irq(struct pci_dev *dev, int (*map_irq)(struct pci_dev *, u8, u8)) { u8 pin, slot; - int irq; + int irq = 0; /* If this device is not on the primary bus, we need to figure out which interrupt pin it will come in on. We know which slot it @@ -33,16 +33,18 @@ pdev_fixup_irq(struct pci_dev *dev, apply the swizzle function. */ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); - /* Cope with 0 and illegal. */ - if (pin == 0 || pin > 4) + /* Cope with illegal. */ + if (pin > 4) pin = 1; - /* Follow the chain of bridges, swizzling as we go. */ - slot = (*swizzle)(dev, &pin); + if (pin != 0) { + /* Follow the chain of bridges, swizzling as we go. */ + slot = (*swizzle)(dev, &pin); - irq = (*map_irq)(dev, slot, pin); - if (irq == -1) - irq = 0; + irq = (*map_irq)(dev, slot, pin); + if (irq == -1) + irq = 0; + } dev->irq = irq; pr_debug("PCI: fixup irq: (%s) got %d\n", diff --git a/drivers/pcmcia/at91_cf.c b/drivers/pcmcia/at91_cf.c index b318628..99baabc 100644 --- a/drivers/pcmcia/at91_cf.c +++ b/drivers/pcmcia/at91_cf.c @@ -277,7 +277,7 @@ static int __init at91_cf_probe(struct platform_device *pdev) board->det_pin, board->irq_pin); cf->socket.owner = THIS_MODULE; - cf->socket.dev.dev = &pdev->dev; + cf->socket.dev.parent = &pdev->dev; cf->socket.ops = &at91_cf_ops; cf->socket.resource_ops = &pccard_static_ops; cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP diff --git a/drivers/pcmcia/au1000_generic.c b/drivers/pcmcia/au1000_generic.c index 551bde5..b693367 100644 --- a/drivers/pcmcia/au1000_generic.c +++ b/drivers/pcmcia/au1000_generic.c @@ -372,7 +372,7 @@ int au1x00_pcmcia_socket_probe(struct device *dev, struct pcmcia_low_level *ops, skt->socket.resource_ops = &pccard_static_ops; skt->socket.ops = &au1x00_pcmcia_operations; skt->socket.owner = ops->owner; - skt->socket.dev.dev = dev; + skt->socket.dev.parent = dev; init_timer(&skt->poll_timer); skt->poll_timer.function = au1x00_pcmcia_poll_event; diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c index 2d7effe..a1bd763 100644 --- a/drivers/pcmcia/cardbus.c +++ b/drivers/pcmcia/cardbus.c @@ -40,8 +40,6 @@ /*====================================================================*/ -#define FIND_FIRST_BIT(n) ((n) - ((n) & ((n)-1))) - /* Offsets in the Expansion ROM Image Header */ #define ROM_SIGNATURE 0x0000 /* 2 bytes */ #define ROM_DATA_PTR 0x0018 /* 2 bytes */ diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c index caca0dc..f2e810f 100644 --- a/drivers/pcmcia/hd64465_ss.c +++ b/drivers/pcmcia/hd64465_ss.c @@ -907,7 +907,7 @@ static int __init init_hs(void) for (i=0; i<HS_MAX_SOCKETS; i++) { unsigned int ret; - hs_sockets[i].socket.dev.dev = &hd64465_device.dev; + hs_sockets[i].socket.dev.parent = &hd64465_device.dev; hs_sockets[i].number = i; ret = pcmcia_register_socket(&hs_sockets[i].socket); if (ret && i) diff --git a/drivers/pcmcia/m32r_cfc.c b/drivers/pcmcia/m32r_cfc.c index e4a9410..91da15b 100644 --- a/drivers/pcmcia/m32r_cfc.c +++ b/drivers/pcmcia/m32r_cfc.c @@ -760,7 +760,7 @@ static int __init init_m32r_pcc(void) /* Set up interrupt handler(s) */ for (i = 0 ; i < pcc_sockets ; i++) { - socket[i].socket.dev.dev = &pcc_device.dev; + socket[i].socket.dev.parent = &pcc_device.dev; socket[i].socket.ops = &pcc_operations; socket[i].socket.resource_ops = &pccard_nonstatic_ops; socket[i].socket.owner = THIS_MODULE; diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c index d059c91..9721ed7 100644 --- a/drivers/pcmcia/m8xx_pcmcia.c +++ b/drivers/pcmcia/m8xx_pcmcia.c @@ -1321,7 +1321,7 @@ static int __init m8xx_init(void) socket[i].socket.ops = &m8xx_services; socket[i].socket.resource_ops = &pccard_iodyn_ops; socket[i].socket.cb_dev = NULL; - socket[i].socket.dev.dev = &m8xx_device.dev; + socket[i].socket.dev.parent = &m8xx_device.dev; } for (i = 0; i < PCMCIA_SOCKETS_NO; i++) diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 76f7cbc..2df216b0 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -202,15 +202,14 @@ static struct pccard_operations omap_cf_ops = { * "what chipselect is used". Boards could want more. */ -static int __devinit omap_cf_probe(struct device *dev) +static int __init omap_cf_probe(struct platform_device *pdev) { unsigned seg; struct omap_cf_socket *cf; - struct platform_device *pdev = to_platform_device(dev); int irq; int status; - seg = (int) dev->platform_data; + seg = (int) pdev->dev.platform_data; if (seg == 0 || seg > 3) return -ENODEV; @@ -227,7 +226,7 @@ static int __devinit omap_cf_probe(struct device *dev) cf->timer.data = (unsigned long) cf; cf->pdev = pdev; - dev_set_drvdata(dev, cf); + platform_set_drvdata(pdev, cf); /* this primarily just shuts up irq handling noise */ status = request_irq(irq, omap_cf_irq, IRQF_SHARED, @@ -291,7 +290,7 @@ static int __devinit omap_cf_probe(struct device *dev) omap_cf_present() ? "present" : "(not present)"); cf->socket.owner = THIS_MODULE; - cf->socket.dev.dev = dev; + cf->socket.dev.parent = &pdev->dev; cf->socket.ops = &omap_cf_ops; cf->socket.resource_ops = &pccard_static_ops; cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP @@ -318,9 +317,9 @@ fail0: return status; } -static int __devexit omap_cf_remove(struct device *dev) +static int __exit omap_cf_remove(struct platform_device *pdev) { - struct omap_cf_socket *cf = dev_get_drvdata(dev); + struct omap_cf_socket *cf = platform_get_drvdata(pdev); cf->active = 0; pcmcia_unregister_socket(&cf->socket); @@ -332,26 +331,36 @@ static int __devexit omap_cf_remove(struct device *dev) return 0; } -static struct device_driver omap_cf_driver = { - .name = (char *) driver_name, - .bus = &platform_bus_type, - .probe = omap_cf_probe, - .remove = __devexit_p(omap_cf_remove), - .suspend = pcmcia_socket_dev_suspend, - .resume = pcmcia_socket_dev_resume, +static int omap_cf_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + return pcmcia_socket_dev_suspend(&pdev->dev, mesg); +} + +static int omap_cf_resume(struct platform_device *pdev) +{ + return pcmcia_socket_dev_resume(&pdev->dev); +} + +static struct platform_driver omap_cf_driver = { + .driver = { + .name = (char *) driver_name, + }, + .remove = __exit_p(omap_cf_remove), + .suspend = omap_cf_suspend, + .resume = omap_cf_resume, }; static int __init omap_cf_init(void) { if (cpu_is_omap16xx()) - return driver_register(&omap_cf_driver); + return platform_driver_probe(&omap_cf_driver, omap_cf_probe); return -ENODEV; } static void __exit omap_cf_exit(void) { if (cpu_is_omap16xx()) - driver_unregister(&omap_cf_driver); + platform_driver_unregister(&omap_cf_driver); } module_init(omap_cf_init); diff --git a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c index 81dfc2c..ce22262 100644 --- a/drivers/pcmcia/rsrc_mgr.c +++ b/drivers/pcmcia/rsrc_mgr.c @@ -232,7 +232,7 @@ static struct resource *iodyn_find_io_region(unsigned long base, int num, unsigned long align, struct pcmcia_socket *s) { struct resource *res = make_resource(0, num, IORESOURCE_IO, - s->dev.class_id); + s->dev.bus_id); struct pcmcia_align_data data; unsigned long min = base; int ret; diff --git a/drivers/pcmcia/soc_common.c b/drivers/pcmcia/soc_common.c index d2a3bea..aa7779d 100644 --- a/drivers/pcmcia/soc_common.c +++ b/drivers/pcmcia/soc_common.c @@ -478,7 +478,7 @@ dump_bits(char **p, const char *prefix, unsigned int val, struct bittbl *bits, i * * Returns: the number of characters added to the buffer */ -static ssize_t show_status(struct device *dev, char *buf) +static ssize_t show_status(struct device *dev, struct device_attribute *attr, char *buf) { struct soc_pcmcia_socket *skt = container_of(dev, struct soc_pcmcia_socket, socket.dev); @@ -501,7 +501,7 @@ static ssize_t show_status(struct device *dev, char *buf) return p-buf; } -static CLASS_DEVICE_ATTR(status, S_IRUGO, show_status, NULL); +static DEVICE_ATTR(status, S_IRUGO, show_status, NULL); static struct pccard_operations soc_common_pcmcia_operations = { @@ -660,7 +660,7 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops skt->socket.ops = &soc_common_pcmcia_operations; skt->socket.owner = ops->owner; - skt->socket.dev.dev = dev; + skt->socket.dev.parent = dev; init_timer(&skt->poll_timer); skt->poll_timer.function = soc_common_pcmcia_poll_event; @@ -747,7 +747,7 @@ int soc_common_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops add_timer(&skt->poll_timer); - device_create_file(&skt->socket.dev, &device_attr_status); + device_create_file(&skt->socket.dev, &dev_attr_status); } dev_set_drvdata(dev, sinfo); diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c index 206e26c..eee2f1c 100644 --- a/drivers/pcmcia/vrc4171_card.c +++ b/drivers/pcmcia/vrc4171_card.c @@ -596,7 +596,7 @@ static int __devinit vrc4171_add_sockets(void) } sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot); - socket->pcmcia_socket.dev.dev = &vrc4171_card_device.dev; + socket->pcmcia_socket.dev.parent = &vrc4171_card_device.dev; socket->pcmcia_socket.ops = &vrc4171_pccard_operations; socket->pcmcia_socket.owner = THIS_MODULE; diff --git a/drivers/pnp/card.c b/drivers/pnp/card.c index 91c047a..dd6384b 100644 --- a/drivers/pnp/card.c +++ b/drivers/pnp/card.c @@ -311,7 +311,6 @@ done: return NULL; found: - down_write(&dev->dev.bus->subsys.rwsem); dev->card_link = clink; dev->dev.driver = &drv->link.driver; if (pnp_bus_type.probe(&dev->dev)) @@ -319,14 +318,11 @@ found: if (device_bind_driver(&dev->dev)) goto err_out; - up_write(&dev->dev.bus->subsys.rwsem); - return dev; err_out: dev->dev.driver = NULL; dev->card_link = NULL; - up_write(&dev->dev.bus->subsys.rwsem); return NULL; } @@ -340,11 +336,9 @@ void pnp_release_card_device(struct pnp_dev * dev) struct pnp_card_driver * drv = dev->card_link->driver; if (!drv) return; - down_write(&dev->dev.bus->subsys.rwsem); drv->link.remove = &card_remove; device_release_driver(&dev->dev); drv->link.remove = &card_remove_first; - up_write(&dev->dev.bus->subsys.rwsem); } /* diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c index 5026b34..57e6ab1 100644 --- a/drivers/pnp/manager.c +++ b/drivers/pnp/manager.c @@ -451,7 +451,7 @@ int pnp_auto_config_dev(struct pnp_dev *dev) return -EINVAL; if(!pnp_can_configure(dev)) { - pnp_info("Device %s does not support resource configuration.", dev->dev.bus_id); + pnp_dbg("Device %s does not support resource configuration.", dev->dev.bus_id); return -ENODEV; } @@ -482,7 +482,7 @@ int pnp_auto_config_dev(struct pnp_dev *dev) int pnp_start_dev(struct pnp_dev *dev) { if (!pnp_can_write(dev)) { - pnp_info("Device %s does not support activation.", dev->dev.bus_id); + pnp_dbg("Device %s does not support activation.", dev->dev.bus_id); return -EINVAL; } @@ -506,7 +506,7 @@ int pnp_start_dev(struct pnp_dev *dev) int pnp_stop_dev(struct pnp_dev *dev) { if (!pnp_can_disable(dev)) { - pnp_info("Device %s does not support disabling.", dev->dev.bus_id); + pnp_dbg("Device %s does not support disabling.", dev->dev.bus_id); return -EINVAL; } if (dev->protocol->disable(dev)<0) { diff --git a/drivers/pnp/pnpacpi/Kconfig b/drivers/pnp/pnpacpi/Kconfig index ad27e5e..b04767c 100644 --- a/drivers/pnp/pnpacpi/Kconfig +++ b/drivers/pnp/pnpacpi/Kconfig @@ -2,17 +2,5 @@ # Plug and Play ACPI configuration # config PNPACPI - bool "Plug and Play ACPI support" - depends on PNP && ACPI - default y - ---help--- - Linux uses the PNPACPI to autodetect built-in - mainboard resources (e.g. parallel port resources). - - Some features (e.g. real hotplug) are not currently - implemented. - - If you would like the kernel to detect and allocate resources to - your mainboard devices (on some systems they are disabled by the - BIOS) say Y here. Also the PNPACPI can help prevent resource - conflicts between mainboard devices and other bus devices. + bool + default (PNP && ACPI) diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 7a53554..118ac97 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -89,6 +89,7 @@ pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, u32 gsi, return; res->irq_resource[i].flags = IORESOURCE_IRQ; // Also clears _UNSET flag + res->irq_resource[i].flags |= irq_flags(triggering, polarity); irq = acpi_register_gsi(gsi, triggering, polarity); if (irq < 0) { res->irq_resource[i].flags |= IORESOURCE_DISABLED; @@ -103,8 +104,52 @@ pnpacpi_parse_allocated_irqresource(struct pnp_resource_table *res, u32 gsi, pcibios_penalize_isa_irq(irq, 1); } +static int dma_flags(int type, int bus_master, int transfer) +{ + int flags = 0; + + if (bus_master) + flags |= IORESOURCE_DMA_MASTER; + switch (type) { + case ACPI_COMPATIBILITY: + flags |= IORESOURCE_DMA_COMPATIBLE; + break; + case ACPI_TYPE_A: + flags |= IORESOURCE_DMA_TYPEA; + break; + case ACPI_TYPE_B: + flags |= IORESOURCE_DMA_TYPEB; + break; + case ACPI_TYPE_F: + flags |= IORESOURCE_DMA_TYPEF; + break; + default: + /* Set a default value ? */ + flags |= IORESOURCE_DMA_COMPATIBLE; + pnp_err("Invalid DMA type"); + } + switch (transfer) { + case ACPI_TRANSFER_8: + flags |= IORESOURCE_DMA_8BIT; + break; + case ACPI_TRANSFER_8_16: + flags |= IORESOURCE_DMA_8AND16BIT; + break; + case ACPI_TRANSFER_16: + flags |= IORESOURCE_DMA_16BIT; + break; + default: + /* Set a default value ? */ + flags |= IORESOURCE_DMA_8AND16BIT; + pnp_err("Invalid DMA transfer type"); + } + + return flags; +} + static void -pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table *res, u32 dma) +pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table *res, u32 dma, + int type, int bus_master, int transfer) { int i = 0; while (i < PNP_MAX_DMA && @@ -112,6 +157,7 @@ pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table *res, u32 dma) i++; if (i < PNP_MAX_DMA) { res->dma_resource[i].flags = IORESOURCE_DMA; // Also clears _UNSET flag + res->dma_resource[i].flags |= dma_flags(type, bus_master, transfer); if (dma == -1) { res->dma_resource[i].flags |= IORESOURCE_DISABLED; return; @@ -123,7 +169,7 @@ pnpacpi_parse_allocated_dmaresource(struct pnp_resource_table *res, u32 dma) static void pnpacpi_parse_allocated_ioresource(struct pnp_resource_table *res, - u64 io, u64 len) + u64 io, u64 len, int io_decode) { int i = 0; while (!(res->port_resource[i].flags & IORESOURCE_UNSET) && @@ -131,6 +177,8 @@ pnpacpi_parse_allocated_ioresource(struct pnp_resource_table *res, i++; if (i < PNP_MAX_PORT) { res->port_resource[i].flags = IORESOURCE_IO; // Also clears _UNSET flag + if (io_decode == ACPI_DECODE_16) + res->port_resource[i].flags |= PNP_PORT_FLAG_16BITADDR; if (len <= 0 || (io + len -1) >= 0x10003) { res->port_resource[i].flags |= IORESOURCE_DISABLED; return; @@ -142,7 +190,7 @@ pnpacpi_parse_allocated_ioresource(struct pnp_resource_table *res, static void pnpacpi_parse_allocated_memresource(struct pnp_resource_table *res, - u64 mem, u64 len) + u64 mem, u64 len, int write_protect) { int i = 0; while (!(res->mem_resource[i].flags & IORESOURCE_UNSET) && @@ -154,6 +202,9 @@ pnpacpi_parse_allocated_memresource(struct pnp_resource_table *res, res->mem_resource[i].flags |= IORESOURCE_DISABLED; return; } + if(write_protect == ACPI_READ_WRITE_MEMORY) + res->mem_resource[i].flags |= IORESOURCE_MEM_WRITEABLE; + res->mem_resource[i].start = mem; res->mem_resource[i].end = mem + len - 1; } @@ -178,10 +229,11 @@ pnpacpi_parse_allocated_address_space(struct pnp_resource_table *res_table, if (p->resource_type == ACPI_MEMORY_RANGE) pnpacpi_parse_allocated_memresource(res_table, - p->minimum, p->address_length); + p->minimum, p->address_length, p->info.mem.write_protect); else if (p->resource_type == ACPI_IO_RANGE) pnpacpi_parse_allocated_ioresource(res_table, - p->minimum, p->address_length); + p->minimum, p->address_length, + p->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16); } static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, @@ -208,13 +260,17 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, case ACPI_RESOURCE_TYPE_DMA: if (res->data.dma.channel_count > 0) pnpacpi_parse_allocated_dmaresource(res_table, - res->data.dma.channels[0]); + res->data.dma.channels[0], + res->data.dma.type, + res->data.dma.bus_master, + res->data.dma.transfer); break; case ACPI_RESOURCE_TYPE_IO: pnpacpi_parse_allocated_ioresource(res_table, res->data.io.minimum, - res->data.io.address_length); + res->data.io.address_length, + res->data.io.io_decode); break; case ACPI_RESOURCE_TYPE_START_DEPENDENT: @@ -224,7 +280,8 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, case ACPI_RESOURCE_TYPE_FIXED_IO: pnpacpi_parse_allocated_ioresource(res_table, res->data.fixed_io.address, - res->data.fixed_io.address_length); + res->data.fixed_io.address_length, + ACPI_DECODE_10); break; case ACPI_RESOURCE_TYPE_VENDOR: @@ -236,17 +293,20 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, case ACPI_RESOURCE_TYPE_MEMORY24: pnpacpi_parse_allocated_memresource(res_table, res->data.memory24.minimum, - res->data.memory24.address_length); + res->data.memory24.address_length, + res->data.memory24.write_protect); break; case ACPI_RESOURCE_TYPE_MEMORY32: pnpacpi_parse_allocated_memresource(res_table, res->data.memory32.minimum, - res->data.memory32.address_length); + res->data.memory32.address_length, + res->data.memory32.write_protect); break; case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: pnpacpi_parse_allocated_memresource(res_table, res->data.fixed_memory32.address, - res->data.fixed_memory32.address_length); + res->data.fixed_memory32.address_length, + res->data.fixed_memory32.write_protect); break; case ACPI_RESOURCE_TYPE_ADDRESS16: case ACPI_RESOURCE_TYPE_ADDRESS32: @@ -304,42 +364,8 @@ static void pnpacpi_parse_dma_option(struct pnp_option *option, struct acpi_reso for(i = 0; i < p->channel_count; i++) dma->map |= 1 << p->channels[i]; - dma->flags = 0; - if (p->bus_master) - dma->flags |= IORESOURCE_DMA_MASTER; - switch (p->type) { - case ACPI_COMPATIBILITY: - dma->flags |= IORESOURCE_DMA_COMPATIBLE; - break; - case ACPI_TYPE_A: - dma->flags |= IORESOURCE_DMA_TYPEA; - break; - case ACPI_TYPE_B: - dma->flags |= IORESOURCE_DMA_TYPEB; - break; - case ACPI_TYPE_F: - dma->flags |= IORESOURCE_DMA_TYPEF; - break; - default: - /* Set a default value ? */ - dma->flags |= IORESOURCE_DMA_COMPATIBLE; - pnp_err("Invalid DMA type"); - } - switch (p->transfer) { - case ACPI_TRANSFER_8: - dma->flags |= IORESOURCE_DMA_8BIT; - break; - case ACPI_TRANSFER_8_16: - dma->flags |= IORESOURCE_DMA_8AND16BIT; - break; - case ACPI_TRANSFER_16: - dma->flags |= IORESOURCE_DMA_16BIT; - break; - default: - /* Set a default value ? */ - dma->flags |= IORESOURCE_DMA_8AND16BIT; - pnp_err("Invalid DMA transfer type"); - } + + dma->flags = dma_flags(p->type, p->bus_master, p->transfer); pnp_register_dma_resource(option, dma); return; diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 2065e74..a8a9554 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c @@ -22,7 +22,7 @@ static const struct pnp_device_id pnp_dev_table[] = { { "", 0 } }; -static void reserve_range(char *pnpid, int start, int end, int port) +static void reserve_range(const char *pnpid, resource_size_t start, resource_size_t end, int port) { struct resource *res; char *regionid; @@ -32,9 +32,9 @@ static void reserve_range(char *pnpid, int start, int end, int port) return; snprintf(regionid, 16, "pnp %s", pnpid); if (port) - res = request_region(start,end-start+1,regionid); + res = request_region(start, end-start+1, regionid); else - res = request_mem_region(start,end-start+1,regionid); + res = request_mem_region(start, end-start+1, regionid); if (res == NULL) kfree(regionid); else @@ -45,12 +45,13 @@ static void reserve_range(char *pnpid, int start, int end, int port) * have double reservations. */ printk(KERN_INFO - "pnp: %s: %s range 0x%x-0x%x %s reserved\n", - pnpid, port ? "ioport" : "iomem", start, end, + "pnp: %s: %s range 0x%llx-0x%llx %s reserved\n", + pnpid, port ? "ioport" : "iomem", + (unsigned long long)start, (unsigned long long)end, NULL != res ? "has been" : "could not be"); } -static void reserve_resources_of_dev(struct pnp_dev *dev) +static void reserve_resources_of_dev(const struct pnp_dev *dev) { int i; diff --git a/drivers/ps3/Makefile b/drivers/ps3/Makefile index 96958c0..e251d1c 100644 --- a/drivers/ps3/Makefile +++ b/drivers/ps3/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_PS3_VUART) += vuart.o obj-$(CONFIG_PS3_PS3AV) += ps3av.o ps3av_cmd.o +obj-$(CONFIG_PS3_SYS_MANAGER) += sys-manager.o diff --git a/drivers/ps3/ps3av.c b/drivers/ps3/ps3av.c index 1926b4d..d21e04c 100644 --- a/drivers/ps3/ps3av.c +++ b/drivers/ps3/ps3av.c @@ -24,6 +24,8 @@ #include <linux/reboot.h> #include <linux/kernel.h> #include <linux/ioctl.h> + +#include <asm/firmware.h> #include <asm/lv1call.h> #include <asm/ps3av.h> #include <asm/ps3.h> @@ -947,7 +949,12 @@ static struct ps3_vuart_port_driver ps3av_driver = { static int ps3av_module_init(void) { - int error = ps3_vuart_port_driver_register(&ps3av_driver); + int error; + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + error = ps3_vuart_port_driver_register(&ps3av_driver); if (error) { printk(KERN_ERR "%s: ps3_vuart_port_driver_register failed %d\n", diff --git a/drivers/ps3/ps3av_cmd.c b/drivers/ps3/ps3av_cmd.c index 21c97c8..bc70e81f 100644 --- a/drivers/ps3/ps3av_cmd.c +++ b/drivers/ps3/ps3av_cmd.c @@ -485,12 +485,12 @@ static u8 ps3av_cnv_mclk(u32 fs) static const u32 ps3av_ns_table[][5] = { /* D1, D2, D3, D4, D5 */ - [PS3AV_CMD_AUDIO_FS_44K-BASE] { 6272, 6272, 17836, 17836, 8918 }, - [PS3AV_CMD_AUDIO_FS_48K-BASE] { 6144, 6144, 11648, 11648, 5824 }, - [PS3AV_CMD_AUDIO_FS_88K-BASE] { 12544, 12544, 35672, 35672, 17836 }, - [PS3AV_CMD_AUDIO_FS_96K-BASE] { 12288, 12288, 23296, 23296, 11648 }, - [PS3AV_CMD_AUDIO_FS_176K-BASE] { 25088, 25088, 71344, 71344, 35672 }, - [PS3AV_CMD_AUDIO_FS_192K-BASE] { 24576, 24576, 46592, 46592, 23296 } + [PS3AV_CMD_AUDIO_FS_44K-BASE] = { 6272, 6272, 17836, 17836, 8918 }, + [PS3AV_CMD_AUDIO_FS_48K-BASE] = { 6144, 6144, 11648, 11648, 5824 }, + [PS3AV_CMD_AUDIO_FS_88K-BASE] = { 12544, 12544, 35672, 35672, 17836 }, + [PS3AV_CMD_AUDIO_FS_96K-BASE] = { 12288, 12288, 23296, 23296, 11648 }, + [PS3AV_CMD_AUDIO_FS_176K-BASE] = { 25088, 25088, 71344, 71344, 35672 }, + [PS3AV_CMD_AUDIO_FS_192K-BASE] = { 24576, 24576, 46592, 46592, 23296 } }; static void ps3av_cnv_ns(u8 *ns, u32 fs, u32 video_vid) @@ -543,9 +543,10 @@ static void ps3av_cnv_ns(u8 *ns, u32 fs, u32 video_vid) #undef BASE -static u8 ps3av_cnv_enable(u32 source, u8 *enable) +static u8 ps3av_cnv_enable(u32 source, const u8 *enable) { - u8 *p, ret = 0; + const u8 *p; + u8 ret = 0; if (source == PS3AV_CMD_AUDIO_SOURCE_SPDIF) { ret = 0x03; @@ -559,9 +560,10 @@ static u8 ps3av_cnv_enable(u32 source, u8 *enable) return ret; } -static u8 ps3av_cnv_fifomap(u8 *map) +static u8 ps3av_cnv_fifomap(const u8 *map) { - u8 *p, ret = 0; + const u8 *p; + u8 ret = 0; p = map; ret = p[0] + (p[1] << 2) + (p[2] << 4) + (p[3] << 6); @@ -615,7 +617,7 @@ static void ps3av_cnv_info(struct ps3av_audio_info_frame *info, info->pb5.lsv = mode->audio_downmix_level; } -static void ps3av_cnv_chstat(u8 *chstat, u8 *cs_info) +static void ps3av_cnv_chstat(u8 *chstat, const u8 *cs_info) { memcpy(chstat, cs_info, 5); } diff --git a/drivers/ps3/sys-manager.c b/drivers/ps3/sys-manager.c new file mode 100644 index 0000000..3aa2b0d --- /dev/null +++ b/drivers/ps3/sys-manager.c @@ -0,0 +1,610 @@ +/* + * PS3 System Manager. + * + * Copyright (C) 2007 Sony Computer Entertainment Inc. + * Copyright 2007 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/workqueue.h> +#include <linux/reboot.h> + +#include <asm/firmware.h> +#include <asm/ps3.h> + +#include "vuart.h" + +MODULE_AUTHOR("Sony Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("PS3 System Manager"); + +/** + * ps3_sys_manager - PS3 system manager driver. + * + * The system manager provides an asyncronous system event notification + * mechanism for reporting events like thermal alert and button presses to + * guests. It also provides support to control system shutdown and startup. + * + * The actual system manager is implemented as an application running in the + * system policy module in lpar_1. Guests communicate with the system manager + * through port 2 of the vuart using a simple packet message protocol. + * Messages are comprised of a fixed field header followed by a message + * specific payload. + */ + +/** + * struct ps3_sys_manager_header - System manager message header. + * @version: Header version, currently 1. + * @size: Header size in bytes, curently 16. + * @payload_size: Message payload size in bytes. + * @service_id: Message type, one of enum ps3_sys_manager_service_id. + */ + +struct ps3_sys_manager_header { + /* version 1 */ + u8 version; + u8 size; + u16 reserved_1; + u32 payload_size; + u16 service_id; + u16 reserved_2[3]; +}; + +/** + * @PS3_SM_RX_MSG_LEN - System manager received message length. + * + * Currently all messages received from the system manager are the same length + * (16 bytes header + 16 bytes payload = 32 bytes). This knowlege is used to + * simplify the logic. + */ + +enum { + PS3_SM_RX_MSG_LEN = 32, +}; + +/** + * enum ps3_sys_manager_service_id - Message header service_id. + * @PS3_SM_SERVICE_ID_REQUEST: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_COMMAND: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_RESPONSE: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_SET_ATTR: guest --> sys_manager. + * @PS3_SM_SERVICE_ID_EXTERN_EVENT: guest <-- sys_manager. + * @PS3_SM_SERVICE_ID_SET_NEXT_OP: guest --> sys_manager. + */ + +enum ps3_sys_manager_service_id { + /* version 1 */ + PS3_SM_SERVICE_ID_REQUEST = 1, + PS3_SM_SERVICE_ID_RESPONSE = 2, + PS3_SM_SERVICE_ID_COMMAND = 3, + PS3_SM_SERVICE_ID_EXTERN_EVENT = 4, + PS3_SM_SERVICE_ID_SET_NEXT_OP = 5, + PS3_SM_SERVICE_ID_SET_ATTR = 8, +}; + +/** + * enum ps3_sys_manager_attr - Notification attribute (bit position mask). + * @PS3_SM_ATTR_POWER: Power button. + * @PS3_SM_ATTR_RESET: Reset button, not available on retail console. + * @PS3_SM_ATTR_THERMAL: Sytem thermal alert. + * @PS3_SM_ATTR_CONTROLLER: Remote controller event. + * @PS3_SM_ATTR_ALL: Logical OR of all. + * + * The guest tells the system manager which events it is interested in receiving + * notice of by sending the system manager a logical OR of notification + * attributes via the ps3_sys_manager_send_attr() routine. + */ + +enum ps3_sys_manager_attr { + /* version 1 */ + PS3_SM_ATTR_POWER = 1, + PS3_SM_ATTR_RESET = 2, + PS3_SM_ATTR_THERMAL = 4, + PS3_SM_ATTR_CONTROLLER = 8, /* bogus? */ + PS3_SM_ATTR_ALL = 0x0f, +}; + +/** + * enum ps3_sys_manager_event - External event type, reported by system manager. + * @PS3_SM_EVENT_POWER_PRESSED: payload.value not used. + * @PS3_SM_EVENT_POWER_RELEASED: payload.value = time pressed in millisec. + * @PS3_SM_EVENT_RESET_PRESSED: payload.value not used. + * @PS3_SM_EVENT_RESET_RELEASED: payload.value = time pressed in millisec. + * @PS3_SM_EVENT_THERMAL_ALERT: payload.value = thermal zone id. + * @PS3_SM_EVENT_THERMAL_CLEARED: payload.value = thermal zone id. + */ + +enum ps3_sys_manager_event { + /* version 1 */ + PS3_SM_EVENT_POWER_PRESSED = 3, + PS3_SM_EVENT_POWER_RELEASED = 4, + PS3_SM_EVENT_RESET_PRESSED = 5, + PS3_SM_EVENT_RESET_RELEASED = 6, + PS3_SM_EVENT_THERMAL_ALERT = 7, + PS3_SM_EVENT_THERMAL_CLEARED = 8, + /* no info on controller events */ +}; + +/** + * enum ps3_sys_manager_next_op - Operation to perform after lpar is destroyed. + */ + +enum ps3_sys_manager_next_op { + /* version 3 */ + PS3_SM_NEXT_OP_SYS_SHUTDOWN = 1, + PS3_SM_NEXT_OP_SYS_REBOOT = 2, + PS3_SM_NEXT_OP_LPAR_REBOOT = 0x82, +}; + +/** + * enum ps3_sys_manager_wake_source - Next-op wakeup source (bit position mask). + * @PS3_SM_WAKE_DEFAULT: Disk insert, power button, eject button, IR + * controller, and bluetooth controller. + * @PS3_SM_WAKE_RTC: + * @PS3_SM_WAKE_RTC_ERROR: + * @PS3_SM_WAKE_P_O_R: Power on reset. + * + * Additional wakeup sources when specifying PS3_SM_NEXT_OP_SYS_SHUTDOWN. + * System will always wake from the PS3_SM_WAKE_DEFAULT sources. + */ + +enum ps3_sys_manager_wake_source { + /* version 3 */ + PS3_SM_WAKE_DEFAULT = 0, + PS3_SM_WAKE_RTC = 0x00000040, + PS3_SM_WAKE_RTC_ERROR = 0x00000080, + PS3_SM_WAKE_P_O_R = 0x10000000, +}; + +/** + * enum ps3_sys_manager_cmd - Command from system manager to guest. + * + * The guest completes the actions needed, then acks or naks the command via + * ps3_sys_manager_send_response(). In the case of @PS3_SM_CMD_SHUTDOWN, + * the guest must be fully prepared for a system poweroff prior to acking the + * command. + */ + +enum ps3_sys_manager_cmd { + /* version 1 */ + PS3_SM_CMD_SHUTDOWN = 1, /* shutdown guest OS */ +}; + +/** + * ps3_sys_manager_write - Helper to write a two part message to the vuart. + * + */ + +static int ps3_sys_manager_write(struct ps3_vuart_port_device *dev, + const struct ps3_sys_manager_header *header, const void *payload) +{ + int result; + + BUG_ON(header->version != 1); + BUG_ON(header->size != 16); + BUG_ON(header->payload_size != 8 && header->payload_size != 16); + BUG_ON(header->service_id > 8); + + result = ps3_vuart_write(dev, header, + sizeof(struct ps3_sys_manager_header)); + + if (!result) + result = ps3_vuart_write(dev, payload, header->payload_size); + + return result; +} + +/** + * ps3_sys_manager_send_attr - Send a 'set attribute' to the system manager. + * + */ + +static int ps3_sys_manager_send_attr(struct ps3_vuart_port_device *dev, + enum ps3_sys_manager_attr attr) +{ + static const struct ps3_sys_manager_header header = { + .version = 1, + .size = 16, + .payload_size = 16, + .service_id = PS3_SM_SERVICE_ID_SET_ATTR, + }; + struct { + u8 version; + u8 reserved_1[3]; + u32 attribute; + } payload; + + BUILD_BUG_ON(sizeof(payload) != 8); + + dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, attr); + + memset(&payload, 0, sizeof(payload)); + payload.version = 1; + payload.attribute = attr; + + return ps3_sys_manager_write(dev, &header, &payload); +} + +/** + * ps3_sys_manager_send_next_op - Send a 'set next op' to the system manager. + * + * Tell the system manager what to do after this lpar is destroyed. + */ + +static int ps3_sys_manager_send_next_op(struct ps3_vuart_port_device *dev, + enum ps3_sys_manager_next_op op, + enum ps3_sys_manager_wake_source wake_source) +{ + static const struct ps3_sys_manager_header header = { + .version = 1, + .size = 16, + .payload_size = 16, + .service_id = PS3_SM_SERVICE_ID_SET_NEXT_OP, + }; + struct { + u8 version; + u8 type; + u8 gos_id; + u8 reserved_1; + u32 wake_source; + u8 reserved_2[8]; + } payload; + + BUILD_BUG_ON(sizeof(payload) != 16); + + dev_dbg(&dev->core, "%s:%d: (%xh)\n", __func__, __LINE__, op); + + memset(&payload, 0, sizeof(payload)); + payload.version = 3; + payload.type = op; + payload.gos_id = 3; /* other os */ + payload.wake_source = wake_source; + + return ps3_sys_manager_write(dev, &header, &payload); +} + +/** + * ps3_sys_manager_send_request_shutdown - Send 'request' to the system manager. + * + * The guest sends this message to request an operation or action of the system + * manager. The reply is a command message from the system manager. In the + * command handler the guest performs the requested operation. The result of + * the command is then communicated back to the system manager with a response + * message. + * + * Currently, the only supported request it the 'shutdown self' request. + */ + +static int ps3_sys_manager_send_request_shutdown(struct ps3_vuart_port_device *dev) +{ + static const struct ps3_sys_manager_header header = { + .version = 1, + .size = 16, + .payload_size = 16, + .service_id = PS3_SM_SERVICE_ID_REQUEST, + }; + struct { + u8 version; + u8 type; + u8 gos_id; + u8 reserved_1[13]; + } static const payload = { + .version = 1, + .type = 1, /* shutdown */ + .gos_id = 0, /* self */ + }; + + BUILD_BUG_ON(sizeof(payload) != 16); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + return ps3_sys_manager_write(dev, &header, &payload); +} + +/** + * ps3_sys_manager_send_response - Send a 'response' to the system manager. + * @status: zero = success, others fail. + * + * The guest sends this message to the system manager to acnowledge success or + * failure of a command sent by the system manager. + */ + +static int ps3_sys_manager_send_response(struct ps3_vuart_port_device *dev, + u64 status) +{ + static const struct ps3_sys_manager_header header = { + .version = 1, + .size = 16, + .payload_size = 16, + .service_id = PS3_SM_SERVICE_ID_RESPONSE, + }; + struct { + u8 version; + u8 reserved_1[3]; + u8 status; + u8 reserved_2[11]; + } payload; + + BUILD_BUG_ON(sizeof(payload) != 16); + + dev_dbg(&dev->core, "%s:%d: (%s)\n", __func__, __LINE__, + (status ? "nak" : "ack")); + + memset(&payload, 0, sizeof(payload)); + payload.version = 1; + payload.status = status; + + return ps3_sys_manager_write(dev, &header, &payload); +} + +/** + * ps3_sys_manager_handle_event - Second stage event msg handler. + * + */ + +static int ps3_sys_manager_handle_event(struct ps3_vuart_port_device *dev) +{ + int result; + struct { + u8 version; + u8 type; + u8 reserved_1[2]; + u32 value; + u8 reserved_2[8]; + } event; + + BUILD_BUG_ON(sizeof(event) != 16); + + result = ps3_vuart_read(dev, &event, sizeof(event)); + BUG_ON(result); + + if (event.version != 1) { + dev_dbg(&dev->core, "%s:%d: unsupported event version (%u)\n", + __func__, __LINE__, event.version); + return -EIO; + } + + switch (event.type) { + case PS3_SM_EVENT_POWER_PRESSED: + dev_dbg(&dev->core, "%s:%d: POWER_PRESSED\n", + __func__, __LINE__); + break; + case PS3_SM_EVENT_POWER_RELEASED: + dev_dbg(&dev->core, "%s:%d: POWER_RELEASED (%u ms)\n", + __func__, __LINE__, event.value); + kill_cad_pid(SIGINT, 1); + break; + case PS3_SM_EVENT_THERMAL_ALERT: + dev_dbg(&dev->core, "%s:%d: THERMAL_ALERT (zone %u)\n", + __func__, __LINE__, event.value); + printk(KERN_INFO "PS3 Thermal Alert Zone %u\n", event.value); + break; + case PS3_SM_EVENT_THERMAL_CLEARED: + dev_dbg(&dev->core, "%s:%d: THERMAL_CLEARED (zone %u)\n", + __func__, __LINE__, event.value); + break; + default: + dev_dbg(&dev->core, "%s:%d: unknown event (%u)\n", + __func__, __LINE__, event.type); + return -EIO; + } + + return 0; +} +/** + * ps3_sys_manager_handle_cmd - Second stage command msg handler. + * + * The system manager sends this in reply to a 'request' message from the guest. + */ + +static int ps3_sys_manager_handle_cmd(struct ps3_vuart_port_device *dev) +{ + int result; + struct { + u8 version; + u8 type; + u8 reserved_1[14]; + } cmd; + + BUILD_BUG_ON(sizeof(cmd) != 16); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + result = ps3_vuart_read(dev, &cmd, sizeof(cmd)); + + if(result) + return result; + + if (cmd.version != 1) { + dev_dbg(&dev->core, "%s:%d: unsupported cmd version (%u)\n", + __func__, __LINE__, cmd.version); + return -EIO; + } + + if (cmd.type != PS3_SM_CMD_SHUTDOWN) { + dev_dbg(&dev->core, "%s:%d: unknown cmd (%u)\n", + __func__, __LINE__, cmd.type); + return -EIO; + } + + ps3_sys_manager_send_response(dev, 0); + return 0; +} + +/** + * ps3_sys_manager_handle_msg - First stage msg handler. + * + */ + +static int ps3_sys_manager_handle_msg(struct ps3_vuart_port_device *dev) +{ + int result; + struct ps3_sys_manager_header header; + + result = ps3_vuart_read(dev, &header, + sizeof(struct ps3_sys_manager_header)); + + if(result) + return result; + + if (header.version != 1) { + dev_dbg(&dev->core, "%s:%d: unsupported header version (%u)\n", + __func__, __LINE__, header.version); + goto fail_header; + } + + BUILD_BUG_ON(sizeof(header) != 16); + BUG_ON(header.size != 16); + BUG_ON(header.payload_size != 16); + + switch (header.service_id) { + case PS3_SM_SERVICE_ID_EXTERN_EVENT: + dev_dbg(&dev->core, "%s:%d: EVENT\n", __func__, __LINE__); + return ps3_sys_manager_handle_event(dev); + case PS3_SM_SERVICE_ID_COMMAND: + dev_dbg(&dev->core, "%s:%d: COMMAND\n", __func__, __LINE__); + return ps3_sys_manager_handle_cmd(dev); + default: + dev_dbg(&dev->core, "%s:%d: unknown service_id (%u)\n", + __func__, __LINE__, header.service_id); + break; + } + goto fail_id; + +fail_header: + ps3_vuart_clear_rx_bytes(dev, 0); + return -EIO; +fail_id: + ps3_vuart_clear_rx_bytes(dev, header.payload_size); + return -EIO; +} + +/** + * ps3_sys_manager_work - Asyncronous read handler. + * + * Signaled when a complete message arrives at the vuart port. + */ + +static void ps3_sys_manager_work(struct work_struct *work) +{ + struct ps3_vuart_port_device *dev = ps3_vuart_work_to_port_device(work); + + ps3_sys_manager_handle_msg(dev); + ps3_vuart_read_async(dev, ps3_sys_manager_work, PS3_SM_RX_MSG_LEN); +} + +struct { + struct ps3_vuart_port_device *dev; +} static drv_priv; + +/** + * ps3_sys_manager_restart - The final platform machine_restart routine. + * + * This routine never returns. The routine disables asyncronous vuart reads + * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge + * the shutdown command sent from the system manager. Soon after the + * acknowledgement is sent the lpar is destroyed by the HV. This routine + * should only be called from ps3_restart(). + */ + +void ps3_sys_manager_restart(void) +{ + struct ps3_vuart_port_device *dev = drv_priv.dev; + + BUG_ON(!drv_priv.dev); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + ps3_vuart_cancel_async(dev); + + ps3_sys_manager_send_attr(dev, 0); + ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_LPAR_REBOOT, + PS3_SM_WAKE_DEFAULT); + ps3_sys_manager_send_request_shutdown(dev); + + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + + while(1) + ps3_sys_manager_handle_msg(dev); +} + +/** + * ps3_sys_manager_power_off - The final platform machine_power_off routine. + * + * This routine never returns. The routine disables asyncronous vuart reads + * then spins calling ps3_sys_manager_handle_msg() to receive and acknowledge + * the shutdown command sent from the system manager. Soon after the + * acknowledgement is sent the lpar is destroyed by the HV. This routine + * should only be called from ps3_power_off(). + */ + +void ps3_sys_manager_power_off(void) +{ + struct ps3_vuart_port_device *dev = drv_priv.dev; + + BUG_ON(!drv_priv.dev); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + ps3_vuart_cancel_async(dev); + + ps3_sys_manager_send_next_op(dev, PS3_SM_NEXT_OP_SYS_SHUTDOWN, + PS3_SM_WAKE_DEFAULT); + ps3_sys_manager_send_request_shutdown(dev); + + printk(KERN_EMERG "System Halted, OK to turn off power\n"); + + while(1) + ps3_sys_manager_handle_msg(dev); +} + +static int ps3_sys_manager_probe(struct ps3_vuart_port_device *dev) +{ + int result; + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + BUG_ON(drv_priv.dev); + drv_priv.dev = dev; + + result = ps3_sys_manager_send_attr(dev, PS3_SM_ATTR_ALL); + BUG_ON(result); + + result = ps3_vuart_read_async(dev, ps3_sys_manager_work, + PS3_SM_RX_MSG_LEN); + BUG_ON(result); + + return result; +} + +static struct ps3_vuart_port_driver ps3_sys_manager = { + .match_id = PS3_MATCH_ID_SYSTEM_MANAGER, + .core = { + .name = "ps3_sys_manager", + }, + .probe = ps3_sys_manager_probe, +}; + +static int __init ps3_sys_manager_init(void) +{ + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + return ps3_vuart_port_driver_register(&ps3_sys_manager); +} + +module_init(ps3_sys_manager_init); diff --git a/drivers/ps3/vuart.c b/drivers/ps3/vuart.c index ef8fd4c..7d7cab1 100644 --- a/drivers/ps3/vuart.c +++ b/drivers/ps3/vuart.c @@ -21,8 +21,10 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> +#include <linux/workqueue.h> #include <asm/ps3.h> +#include <asm/firmware.h> #include <asm/lv1call.h> #include <asm/bitops.h> @@ -30,7 +32,7 @@ MODULE_AUTHOR("Sony Corporation"); MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("ps3 vuart"); +MODULE_DESCRIPTION("PS3 vuart"); /** * vuart - An inter-partition data link service. @@ -80,14 +82,6 @@ struct ports_bmp { u64 unused[3]; } __attribute__ ((aligned (32))); -/* redefine dev_dbg to do a syntax check */ - -#if !defined(DEBUG) -#undef dev_dbg -static inline int __attribute__ ((format (printf, 2, 3))) dev_dbg( - const struct device *_dev, const char *fmt, ...) {return 0;} -#endif - #define dump_ports_bmp(_b) _dump_ports_bmp(_b, __func__, __LINE__) static void __attribute__ ((unused)) _dump_ports_bmp( const struct ports_bmp* bmp, const char* func, int line) @@ -157,7 +151,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, unsigned long size; unsigned long val; - result = lv1_get_virtual_uart_param(dev->port_number, + result = lv1_get_virtual_uart_param(dev->priv->port_number, PARAM_TX_TRIGGER, &trig->tx); if (result) { @@ -166,7 +160,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->port_number, + result = lv1_get_virtual_uart_param(dev->priv->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -175,7 +169,7 @@ int ps3_vuart_get_triggers(struct ps3_vuart_port_device *dev, return result; } - result = lv1_get_virtual_uart_param(dev->port_number, + result = lv1_get_virtual_uart_param(dev->priv->port_number, PARAM_RX_TRIGGER, &val); if (result) { @@ -198,7 +192,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, int result; unsigned long size; - result = lv1_set_virtual_uart_param(dev->port_number, + result = lv1_set_virtual_uart_param(dev->priv->port_number, PARAM_TX_TRIGGER, tx); if (result) { @@ -207,7 +201,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_get_virtual_uart_param(dev->port_number, + result = lv1_get_virtual_uart_param(dev->priv->port_number, PARAM_RX_BUF_SIZE, &size); if (result) { @@ -216,7 +210,7 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, return result; } - result = lv1_set_virtual_uart_param(dev->port_number, + result = lv1_set_virtual_uart_param(dev->priv->port_number, PARAM_RX_TRIGGER, size - rx); if (result) { @@ -232,9 +226,9 @@ int ps3_vuart_set_triggers(struct ps3_vuart_port_device *dev, unsigned int tx, } static int ps3_vuart_get_rx_bytes_waiting(struct ps3_vuart_port_device *dev, - unsigned long *bytes_waiting) + u64 *bytes_waiting) { - int result = lv1_get_virtual_uart_param(dev->port_number, + int result = lv1_get_virtual_uart_param(dev->priv->port_number, PARAM_RX_BYTES, bytes_waiting); if (result) @@ -253,10 +247,10 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, dev_dbg(&dev->core, "%s:%d: %lxh\n", __func__, __LINE__, mask); - dev->interrupt_mask = mask; + dev->priv->interrupt_mask = mask; - result = lv1_set_virtual_uart_param(dev->port_number, - PARAM_INTERRUPT_MASK, dev->interrupt_mask); + result = lv1_set_virtual_uart_param(dev->priv->port_number, + PARAM_INTERRUPT_MASK, dev->priv->interrupt_mask); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_mask failed: %s\n", @@ -265,62 +259,64 @@ static int ps3_vuart_set_interrupt_mask(struct ps3_vuart_port_device *dev, return result; } -static int ps3_vuart_get_interrupt_mask(struct ps3_vuart_port_device *dev, +static int ps3_vuart_get_interrupt_status(struct ps3_vuart_port_device *dev, unsigned long *status) { - int result = lv1_get_virtual_uart_param(dev->port_number, - PARAM_INTERRUPT_STATUS, status); + u64 tmp; + int result = lv1_get_virtual_uart_param(dev->priv->port_number, + PARAM_INTERRUPT_STATUS, &tmp); if (result) dev_dbg(&dev->core, "%s:%d: interrupt_status failed: %s\n", __func__, __LINE__, ps3_result(result)); + *status = tmp & dev->priv->interrupt_mask; + dev_dbg(&dev->core, "%s:%d: m %lxh, s %lxh, m&s %lxh\n", - __func__, __LINE__, dev->interrupt_mask, *status, - dev->interrupt_mask & *status); + __func__, __LINE__, dev->priv->interrupt_mask, tmp, *status); return result; } int ps3_vuart_enable_interrupt_tx(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_TX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask | INTERRUPT_MASK_TX); } int ps3_vuart_enable_interrupt_rx(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_RX) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask | INTERRUPT_MASK_RX); } int ps3_vuart_enable_interrupt_disconnect(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 - : ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) ? 0 + : ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask | INTERRUPT_MASK_DISCONNECT); } int ps3_vuart_disable_interrupt_tx(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_TX) - ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_TX) + ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask & ~INTERRUPT_MASK_TX) : 0; } int ps3_vuart_disable_interrupt_rx(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_RX) - ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_RX) + ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask & ~INTERRUPT_MASK_RX) : 0; } int ps3_vuart_disable_interrupt_disconnect(struct ps3_vuart_port_device *dev) { - return (dev->interrupt_mask & INTERRUPT_MASK_DISCONNECT) - ? ps3_vuart_set_interrupt_mask(dev, dev->interrupt_mask + return (dev->priv->interrupt_mask & INTERRUPT_MASK_DISCONNECT) + ? ps3_vuart_set_interrupt_mask(dev, dev->priv->interrupt_mask & ~INTERRUPT_MASK_DISCONNECT) : 0; } @@ -335,9 +331,7 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, { int result; - dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); - - result = lv1_write_virtual_uart(dev->port_number, + result = lv1_write_virtual_uart(dev->priv->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_written); if (result) { @@ -346,10 +340,10 @@ static int ps3_vuart_raw_write(struct ps3_vuart_port_device *dev, return result; } - dev->stats.bytes_written += *bytes_written; + dev->priv->stats.bytes_written += *bytes_written; - dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, - __LINE__, *bytes_written, bytes, dev->stats.bytes_written); + dev_dbg(&dev->core, "%s:%d: wrote %lxh/%xh=>%lxh\n", __func__, __LINE__, + *bytes_written, bytes, dev->priv->stats.bytes_written); return result; } @@ -367,7 +361,7 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, dev_dbg(&dev->core, "%s:%d: %xh\n", __func__, __LINE__, bytes); - result = lv1_read_virtual_uart(dev->port_number, + result = lv1_read_virtual_uart(dev->priv->port_number, ps3_mm_phys_to_lpar(__pa(buf)), bytes, bytes_read); if (result) { @@ -376,15 +370,58 @@ static int ps3_vuart_raw_read(struct ps3_vuart_port_device *dev, void* buf, return result; } - dev->stats.bytes_read += *bytes_read; + dev->priv->stats.bytes_read += *bytes_read; dev_dbg(&dev->core, "%s:%d: read %lxh/%xh=>%lxh\n", __func__, __LINE__, - *bytes_read, bytes, dev->stats.bytes_read); + *bytes_read, bytes, dev->priv->stats.bytes_read); return result; } /** + * ps3_vuart_clear_rx_bytes - Discard bytes received. + * @bytes: Max byte count to discard, zero = all pending. + * + * Used to clear pending rx interrupt source. Will not block. + */ + +void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, + unsigned int bytes) +{ + int result; + u64 bytes_waiting; + void* tmp; + + result = ps3_vuart_get_rx_bytes_waiting(dev, &bytes_waiting); + + BUG_ON(result); + + bytes = bytes ? min(bytes, (unsigned int)bytes_waiting) : bytes_waiting; + + dev_dbg(&dev->core, "%s:%d: %u\n", __func__, __LINE__, bytes); + + if (!bytes) + return; + + /* Add some extra space for recently arrived data. */ + + bytes += 128; + + tmp = kmalloc(bytes, GFP_KERNEL); + + if (!tmp) + return; + + ps3_vuart_raw_read(dev, tmp, bytes, &bytes_waiting); + + kfree(tmp); + + /* Don't include these bytes in the stats. */ + + dev->priv->stats.bytes_read -= bytes_waiting; +} + +/** * struct list_buffer - An element for a port device fifo buffer list. */ @@ -416,14 +453,14 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->tx_list.lock, flags); + spin_lock_irqsave(&dev->priv->tx_list.lock, flags); - if (list_empty(&dev->tx_list.head)) { + if (list_empty(&dev->priv->tx_list.head)) { unsigned long bytes_written; result = ps3_vuart_raw_write(dev, buf, bytes, &bytes_written); - spin_unlock_irqrestore(&dev->tx_list.lock, flags); + spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); if (result) { dev_dbg(&dev->core, @@ -441,7 +478,7 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, bytes -= bytes_written; buf += bytes_written; } else - spin_unlock_irqrestore(&dev->tx_list.lock, flags); + spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); lb = kmalloc(sizeof(struct list_buffer) + bytes, GFP_KERNEL); @@ -454,10 +491,10 @@ int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; - spin_lock_irqsave(&dev->tx_list.lock, flags); - list_add_tail(&lb->link, &dev->tx_list.head); + spin_lock_irqsave(&dev->priv->tx_list.lock, flags); + list_add_tail(&lb->link, &dev->priv->tx_list.head); ps3_vuart_enable_interrupt_tx(dev); - spin_unlock_irqrestore(&dev->tx_list.lock, flags); + spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %xh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); @@ -484,47 +521,83 @@ int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, dev_dbg(&dev->core, "%s:%d: %u(%xh) bytes\n", __func__, __LINE__, bytes, bytes); - spin_lock_irqsave(&dev->rx_list.lock, flags); + spin_lock_irqsave(&dev->priv->rx_list.lock, flags); - if (dev->rx_list.bytes_held < bytes) { - spin_unlock_irqrestore(&dev->rx_list.lock, flags); + if (dev->priv->rx_list.bytes_held < bytes) { + spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); dev_dbg(&dev->core, "%s:%d: starved for %lxh bytes\n", - __func__, __LINE__, bytes - dev->rx_list.bytes_held); + __func__, __LINE__, + bytes - dev->priv->rx_list.bytes_held); return -EAGAIN; } - list_for_each_entry_safe(lb, n, &dev->rx_list.head, link) { + list_for_each_entry_safe(lb, n, &dev->priv->rx_list.head, link) { bytes_read = min((unsigned int)(lb->tail - lb->head), bytes); memcpy(buf, lb->head, bytes_read); buf += bytes_read; bytes -= bytes_read; - dev->rx_list.bytes_held -= bytes_read; + dev->priv->rx_list.bytes_held -= bytes_read; if (bytes_read < lb->tail - lb->head) { lb->head += bytes_read; - spin_unlock_irqrestore(&dev->rx_list.lock, flags); - - dev_dbg(&dev->core, - "%s:%d: dequeued buf_%lu, %lxh bytes\n", - __func__, __LINE__, lb->dbg_number, bytes_read); + dev_dbg(&dev->core, "%s:%d: buf_%lu: dequeued %lxh " + "bytes\n", __func__, __LINE__, lb->dbg_number, + bytes_read); + spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); return 0; } - dev_dbg(&dev->core, "%s:%d free buf_%lu\n", __func__, __LINE__, - lb->dbg_number); + dev_dbg(&dev->core, "%s:%d: buf_%lu: free, dequeued %lxh " + "bytes\n", __func__, __LINE__, lb->dbg_number, + bytes_read); list_del(&lb->link); kfree(lb); } - spin_unlock_irqrestore(&dev->rx_list.lock, flags); - dev_dbg(&dev->core, "%s:%d: dequeued buf_%lu, %xh bytes\n", - __func__, __LINE__, lb->dbg_number, bytes); + spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); + return 0; +} + +int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, + unsigned int bytes) +{ + unsigned long flags; + + if(dev->priv->work.trigger) { + dev_dbg(&dev->core, "%s:%d: warning, multiple calls\n", + __func__, __LINE__); + return -EAGAIN; + } + + BUG_ON(!bytes); + + PREPARE_WORK(&dev->priv->work.work, func); + + spin_lock_irqsave(&dev->priv->work.lock, flags); + if(dev->priv->rx_list.bytes_held >= bytes) { + dev_dbg(&dev->core, "%s:%d: schedule_work %xh bytes\n", + __func__, __LINE__, bytes); + schedule_work(&dev->priv->work.work); + spin_unlock_irqrestore(&dev->priv->work.lock, flags); + return 0; + } + + dev->priv->work.trigger = bytes; + spin_unlock_irqrestore(&dev->priv->work.lock, flags); + + dev_dbg(&dev->core, "%s:%d: waiting for %u(%xh) bytes\n", __func__, + __LINE__, bytes, bytes); return 0; } +void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev) +{ + dev->priv->work.trigger = 0; +} + /** * ps3_vuart_handle_interrupt_tx - third stage transmit interrupt handler * @@ -542,9 +615,9 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); - spin_lock_irqsave(&dev->tx_list.lock, flags); + spin_lock_irqsave(&dev->priv->tx_list.lock, flags); - list_for_each_entry_safe(lb, n, &dev->tx_list.head, link) { + list_for_each_entry_safe(lb, n, &dev->priv->tx_list.head, link) { unsigned long bytes_written; @@ -578,7 +651,7 @@ static int ps3_vuart_handle_interrupt_tx(struct ps3_vuart_port_device *dev) ps3_vuart_disable_interrupt_tx(dev); port_full: - spin_unlock_irqrestore(&dev->tx_list.lock, flags); + spin_unlock_irqrestore(&dev->priv->tx_list.lock, flags); dev_dbg(&dev->core, "%s:%d wrote %lxh bytes total\n", __func__, __LINE__, bytes_total); return result; @@ -609,7 +682,7 @@ static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) BUG_ON(!bytes); - /* add some extra space for recently arrived data */ + /* Add some extra space for recently arrived data. */ bytes += 128; @@ -624,14 +697,23 @@ static int ps3_vuart_handle_interrupt_rx(struct ps3_vuart_port_device *dev) lb->tail = lb->data + bytes; lb->dbg_number = ++dbg_number; - spin_lock_irqsave(&dev->rx_list.lock, flags); - list_add_tail(&lb->link, &dev->rx_list.head); - dev->rx_list.bytes_held += bytes; - spin_unlock_irqrestore(&dev->rx_list.lock, flags); + spin_lock_irqsave(&dev->priv->rx_list.lock, flags); + list_add_tail(&lb->link, &dev->priv->rx_list.head); + dev->priv->rx_list.bytes_held += bytes; + spin_unlock_irqrestore(&dev->priv->rx_list.lock, flags); - dev_dbg(&dev->core, "%s:%d: queued buf_%lu, %lxh bytes\n", + dev_dbg(&dev->core, "%s:%d: buf_%lu: queued %lxh bytes\n", __func__, __LINE__, lb->dbg_number, bytes); + spin_lock_irqsave(&dev->priv->work.lock, flags); + if(dev->priv->work.trigger + && dev->priv->rx_list.bytes_held >= dev->priv->work.trigger) { + dev_dbg(&dev->core, "%s:%d: schedule_work %lxh bytes\n", + __func__, __LINE__, dev->priv->work.trigger); + dev->priv->work.trigger = 0; + schedule_work(&dev->priv->work.work); + } + spin_unlock_irqrestore(&dev->priv->work.lock, flags); return 0; } @@ -656,7 +738,7 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) int result; unsigned long status; - result = ps3_vuart_get_interrupt_mask(dev, &status); + result = ps3_vuart_get_interrupt_status(dev, &status); if (result) return result; @@ -665,21 +747,21 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) status); if (status & INTERRUPT_MASK_DISCONNECT) { - dev->stats.disconnect_interrupts++; + dev->priv->stats.disconnect_interrupts++; result = ps3_vuart_handle_interrupt_disconnect(dev); if (result) ps3_vuart_disable_interrupt_disconnect(dev); } if (status & INTERRUPT_MASK_TX) { - dev->stats.tx_interrupts++; + dev->priv->stats.tx_interrupts++; result = ps3_vuart_handle_interrupt_tx(dev); if (result) ps3_vuart_disable_interrupt_tx(dev); } if (status & INTERRUPT_MASK_RX) { - dev->stats.rx_interrupts++; + dev->priv->stats.rx_interrupts++; result = ps3_vuart_handle_interrupt_rx(dev); if (result) ps3_vuart_disable_interrupt_rx(dev); @@ -688,12 +770,13 @@ static int ps3_vuart_handle_port_interrupt(struct ps3_vuart_port_device *dev) return 0; } -struct vuart_private { - unsigned int in_use; +struct vuart_bus_priv { + const struct ports_bmp bmp; unsigned int virq; + struct semaphore probe_mutex; + int use_count; struct ps3_vuart_port_device *devices[PORT_COUNT]; - const struct ports_bmp bmp; -}; +} static vuart_bus_priv; /** * ps3_vuart_irq_handler - first stage interrupt handler @@ -705,25 +788,25 @@ struct vuart_private { static irqreturn_t ps3_vuart_irq_handler(int irq, void *_private) { - struct vuart_private *private; + struct vuart_bus_priv *bus_priv; BUG_ON(!_private); - private = (struct vuart_private *)_private; + bus_priv = (struct vuart_bus_priv *)_private; while (1) { unsigned int port; - dump_ports_bmp(&private->bmp); + dump_ports_bmp(&bus_priv->bmp); - port = (BITS_PER_LONG - 1) - __ilog2(private->bmp.status); + port = (BITS_PER_LONG - 1) - __ilog2(bus_priv->bmp.status); if (port == BITS_PER_LONG) break; BUG_ON(port >= PORT_COUNT); - BUG_ON(!private->devices[port]); + BUG_ON(!bus_priv->devices[port]); - ps3_vuart_handle_port_interrupt(private->devices[port]); + ps3_vuart_handle_port_interrupt(bus_priv->devices[port]); } return IRQ_HANDLED; @@ -744,12 +827,10 @@ static int ps3_vuart_match(struct device *_dev, struct device_driver *_drv) return result; } -static struct vuart_private vuart_private; - static int ps3_vuart_probe(struct device *_dev) { int result; - unsigned long tmp; + unsigned int port_number; struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_dev->driver); @@ -758,7 +839,12 @@ static int ps3_vuart_probe(struct device *_dev) BUG_ON(!drv); - result = ps3_vuart_match_id_to_port(dev->match_id, &dev->port_number); + down(&vuart_bus_priv.probe_mutex); + + /* Setup vuart_bus_priv.devices[]. */ + + result = ps3_vuart_match_id_to_port(dev->match_id, + &port_number); if (result) { dev_dbg(&dev->core, "%s:%d: unknown match_id (%d)\n", @@ -767,24 +853,41 @@ static int ps3_vuart_probe(struct device *_dev) goto fail_match; } - if (vuart_private.devices[dev->port_number]) { + if (vuart_bus_priv.devices[port_number]) { dev_dbg(&dev->core, "%s:%d: port busy (%d)\n", __func__, - __LINE__, dev->port_number); + __LINE__, port_number); result = -EBUSY; goto fail_match; } - vuart_private.devices[dev->port_number] = dev; + vuart_bus_priv.devices[port_number] = dev; + + /* Setup dev->priv. */ + + dev->priv = kzalloc(sizeof(struct ps3_vuart_port_priv), GFP_KERNEL); + + if (!dev->priv) { + result = -ENOMEM; + goto fail_alloc; + } + + dev->priv->port_number = port_number; + + INIT_LIST_HEAD(&dev->priv->tx_list.head); + spin_lock_init(&dev->priv->tx_list.lock); + + INIT_LIST_HEAD(&dev->priv->rx_list.head); + spin_lock_init(&dev->priv->rx_list.lock); + + INIT_WORK(&dev->priv->work.work, NULL); + spin_lock_init(&dev->priv->work.lock); + dev->priv->work.trigger = 0; + dev->priv->work.dev = dev; - INIT_LIST_HEAD(&dev->tx_list.head); - spin_lock_init(&dev->tx_list.lock); - INIT_LIST_HEAD(&dev->rx_list.head); - spin_lock_init(&dev->rx_list.lock); + if (++vuart_bus_priv.use_count == 1) { - vuart_private.in_use++; - if (vuart_private.in_use == 1) { result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY, - (void*)&vuart_private.bmp.status, &vuart_private.virq); + (void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq); if (result) { dev_dbg(&dev->core, @@ -794,8 +897,8 @@ static int ps3_vuart_probe(struct device *_dev) goto fail_alloc_irq; } - result = request_irq(vuart_private.virq, ps3_vuart_irq_handler, - IRQF_DISABLED, "vuart", &vuart_private); + result = request_irq(vuart_bus_priv.virq, ps3_vuart_irq_handler, + IRQF_DISABLED, "vuart", &vuart_bus_priv); if (result) { dev_info(&dev->core, "%s:%d: request_irq failed (%d)\n", @@ -804,10 +907,11 @@ static int ps3_vuart_probe(struct device *_dev) } } - ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX); - /* clear stale pending interrupts */ - ps3_vuart_get_interrupt_mask(dev, &tmp); + + ps3_vuart_clear_rx_bytes(dev, 0); + + ps3_vuart_set_interrupt_mask(dev, INTERRUPT_MASK_RX); ps3_vuart_set_triggers(dev, 1, 1); @@ -822,20 +926,27 @@ static int ps3_vuart_probe(struct device *_dev) if (result) { dev_dbg(&dev->core, "%s:%d: drv->probe failed\n", __func__, __LINE__); + down(&vuart_bus_priv.probe_mutex); goto fail_probe; } + up(&vuart_bus_priv.probe_mutex); + return result; fail_probe: + ps3_vuart_set_interrupt_mask(dev, 0); fail_request_irq: - vuart_private.in_use--; - if (!vuart_private.in_use) { - ps3_free_vuart_irq(vuart_private.virq); - vuart_private.virq = NO_IRQ; - } + ps3_free_vuart_irq(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; fail_alloc_irq: + --vuart_bus_priv.use_count; + kfree(dev->priv); + dev->priv = NULL; +fail_alloc: + vuart_bus_priv.devices[port_number] = NULL; fail_match: + up(&vuart_bus_priv.probe_mutex); dev_dbg(&dev->core, "%s:%d failed\n", __func__, __LINE__); return result; } @@ -846,10 +957,12 @@ static int ps3_vuart_remove(struct device *_dev) struct ps3_vuart_port_driver *drv = to_ps3_vuart_port_driver(_dev->driver); + down(&vuart_bus_priv.probe_mutex); + dev_dbg(&dev->core, "%s:%d: %s\n", __func__, __LINE__, dev->core.bus_id); - BUG_ON(vuart_private.in_use < 1); + BUG_ON(vuart_bus_priv.use_count < 1); if (drv->remove) drv->remove(dev); @@ -857,13 +970,19 @@ static int ps3_vuart_remove(struct device *_dev) dev_dbg(&dev->core, "%s:%d: %s no remove method\n", __func__, __LINE__, dev->core.bus_id); - vuart_private.in_use--; + vuart_bus_priv.devices[dev->priv->port_number] = NULL; - if (!vuart_private.in_use) { - free_irq(vuart_private.virq, &vuart_private); - ps3_free_vuart_irq(vuart_private.virq); - vuart_private.virq = NO_IRQ; + if (--vuart_bus_priv.use_count == 0) { + BUG(); + free_irq(vuart_bus_priv.virq, &vuart_bus_priv); + ps3_free_vuart_irq(vuart_bus_priv.virq); + vuart_bus_priv.virq = NO_IRQ; } + + kfree(dev->priv); + dev->priv = NULL; + + up(&vuart_bus_priv.probe_mutex); return 0; } @@ -884,12 +1003,12 @@ static void ps3_vuart_shutdown(struct device *_dev) } /** - * ps3_vuart - The vuart instance. + * ps3_vuart_bus - The vuart bus instance. * * The vuart is managed as a bus that port devices connect to. */ -struct bus_type ps3_vuart = { +struct bus_type ps3_vuart_bus = { .name = "ps3_vuart", .match = ps3_vuart_match, .probe = ps3_vuart_probe, @@ -897,24 +1016,30 @@ struct bus_type ps3_vuart = { .shutdown = ps3_vuart_shutdown, }; -int __init ps3_vuart_init(void) +int __init ps3_vuart_bus_init(void) { int result; pr_debug("%s:%d:\n", __func__, __LINE__); - result = bus_register(&ps3_vuart); + + if (!firmware_has_feature(FW_FEATURE_PS3_LV1)) + return -ENODEV; + + init_MUTEX(&vuart_bus_priv.probe_mutex); + result = bus_register(&ps3_vuart_bus); BUG_ON(result); + return result; } -void __exit ps3_vuart_exit(void) +void __exit ps3_vuart_bus_exit(void) { pr_debug("%s:%d:\n", __func__, __LINE__); - bus_unregister(&ps3_vuart); + bus_unregister(&ps3_vuart_bus); } -core_initcall(ps3_vuart_init); -module_exit(ps3_vuart_exit); +core_initcall(ps3_vuart_bus_init); +module_exit(ps3_vuart_bus_exit); /** * ps3_vuart_port_release_device - Remove a vuart port device. @@ -922,11 +1047,14 @@ module_exit(ps3_vuart_exit); static void ps3_vuart_port_release_device(struct device *_dev) { - struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); #if defined(DEBUG) - memset(dev, 0xad, sizeof(struct ps3_vuart_port_device)); + struct ps3_vuart_port_device *dev = to_ps3_vuart_port_device(_dev); + + dev_dbg(&dev->core, "%s:%d\n", __func__, __LINE__); + + BUG_ON(dev->priv && "forgot to free"); + memset(&dev->core, 0, sizeof(dev->core)); #endif - kfree(dev); } /** @@ -935,11 +1063,12 @@ static void ps3_vuart_port_release_device(struct device *_dev) int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) { - int result; static unsigned int dev_count = 1; + BUG_ON(dev->priv && "forgot to free"); + dev->core.parent = NULL; - dev->core.bus = &ps3_vuart; + dev->core.bus = &ps3_vuart_bus; dev->core.release = ps3_vuart_port_release_device; snprintf(dev->core.bus_id, sizeof(dev->core.bus_id), "vuart_%02x", @@ -947,9 +1076,7 @@ int ps3_vuart_port_device_register(struct ps3_vuart_port_device *dev) dev_dbg(&dev->core, "%s:%d register\n", __func__, __LINE__); - result = device_register(&dev->core); - - return result; + return device_register(&dev->core); } EXPORT_SYMBOL_GPL(ps3_vuart_port_device_register); @@ -963,7 +1090,7 @@ int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv) int result; pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); - drv->core.bus = &ps3_vuart; + drv->core.bus = &ps3_vuart_bus; result = driver_register(&drv->core); return result; } @@ -976,6 +1103,7 @@ EXPORT_SYMBOL_GPL(ps3_vuart_port_driver_register); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv) { + pr_debug("%s:%d: (%s)\n", __func__, __LINE__, drv->core.name); driver_unregister(&drv->core); } diff --git a/drivers/ps3/vuart.h b/drivers/ps3/vuart.h index 2cbf728..1be992d 100644 --- a/drivers/ps3/vuart.h +++ b/drivers/ps3/vuart.h @@ -21,6 +21,44 @@ #if !defined(_PS3_VUART_H) #define _PS3_VUART_H +#include <asm/ps3.h> + +struct ps3_vuart_stats { + unsigned long bytes_written; + unsigned long bytes_read; + unsigned long tx_interrupts; + unsigned long rx_interrupts; + unsigned long disconnect_interrupts; +}; + +struct ps3_vuart_work { + struct work_struct work; + unsigned long trigger; + spinlock_t lock; + struct ps3_vuart_port_device* dev; /* to convert work to device */ +}; + +/** + * struct ps3_vuart_port_priv - private vuart device data. + */ + +struct ps3_vuart_port_priv { + unsigned int port_number; + u64 interrupt_mask; + + struct { + spinlock_t lock; + struct list_head head; + } tx_list; + struct { + unsigned long bytes_held; + spinlock_t lock; + struct list_head head; + } rx_list; + struct ps3_vuart_stats stats; + struct ps3_vuart_work work; +}; + /** * struct ps3_vuart_port_driver - a driver for a device on a vuart port */ @@ -41,10 +79,6 @@ struct ps3_vuart_port_driver { int ps3_vuart_port_driver_register(struct ps3_vuart_port_driver *drv); void ps3_vuart_port_driver_unregister(struct ps3_vuart_port_driver *drv); -int ps3_vuart_write(struct ps3_vuart_port_device *dev, - const void* buf, unsigned int bytes); -int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, - unsigned int bytes); static inline struct ps3_vuart_port_driver *to_ps3_vuart_port_driver( struct device_driver *_drv) { @@ -55,5 +89,22 @@ static inline struct ps3_vuart_port_device *to_ps3_vuart_port_device( { return container_of(_dev, struct ps3_vuart_port_device, core); } +static inline struct ps3_vuart_port_device *ps3_vuart_work_to_port_device( + struct work_struct *_work) +{ + struct ps3_vuart_work *vw = container_of(_work, struct ps3_vuart_work, + work); + return vw->dev; +} + +int ps3_vuart_write(struct ps3_vuart_port_device *dev, const void* buf, + unsigned int bytes); +int ps3_vuart_read(struct ps3_vuart_port_device *dev, void* buf, + unsigned int bytes); +int ps3_vuart_read_async(struct ps3_vuart_port_device *dev, work_func_t func, + unsigned int bytes); +void ps3_vuart_cancel_async(struct ps3_vuart_port_device *dev); +void ps3_vuart_clear_rx_bytes(struct ps3_vuart_port_device *dev, + unsigned int bytes); #endif diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 4bbca50..95826b9 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -101,7 +101,7 @@ comment "RTC drivers" config RTC_DRV_CMOS tristate "PC-style 'CMOS' real time clock" - depends on RTC_CLASS && (X86_PC || ALPHA || ARM26 || ARM \ + depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \ || M32R || ATARI || POWERPC) help Say "yes" here to get direct support for the real time clock @@ -207,10 +207,12 @@ config RTC_DRV_PCF8563 config RTC_DRV_PCF8583 tristate "Philips PCF8583" - depends on RTC_CLASS && I2C + depends on RTC_CLASS && I2C && ARCH_RPC help - If you say yes here you get support for the - Philips PCF8583 RTC chip. + If you say yes here you get support for the Philips PCF8583 + RTC chip found on Acorn RiscPCs. This driver supports the + platform specific method of retrieving the current year from + the RTC's SRAM. This driver can also be built as a module. If so, the module will be called rtc-pcf8583. diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 7a0d8ee..04aaa63 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -113,10 +113,16 @@ EXPORT_SYMBOL_GPL(rtc_device_register); */ void rtc_device_unregister(struct rtc_device *rtc) { - mutex_lock(&rtc->ops_lock); - rtc->ops = NULL; - mutex_unlock(&rtc->ops_lock); - class_device_unregister(&rtc->class_dev); + if (class_device_get(&rtc->class_dev) != NULL) { + mutex_lock(&rtc->ops_lock); + /* remove innards of this RTC, then disable it, before + * letting any rtc_class_open() users access it again + */ + class_device_unregister(&rtc->class_dev); + rtc->ops = NULL; + mutex_unlock(&rtc->ops_lock); + class_device_put(&rtc->class_dev); + } } EXPORT_SYMBOL_GPL(rtc_device_unregister); diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c index 6f11f6d..ef40df0 100644 --- a/drivers/rtc/interface.c +++ b/drivers/rtc/interface.c @@ -179,7 +179,7 @@ struct class_device *rtc_class_open(char *name) down(&rtc_class->sem); list_for_each_entry(class_dev_tmp, &rtc_class->children, node) { if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) { - class_dev = class_dev_tmp; + class_dev = class_device_get(class_dev_tmp); break; } } @@ -197,6 +197,7 @@ EXPORT_SYMBOL_GPL(rtc_class_open); void rtc_class_close(struct class_device *class_dev) { module_put(to_rtc_device(class_dev)->owner); + class_device_put(class_dev); } EXPORT_SYMBOL_GPL(rtc_class_close); diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index a724ab4..ac0e68e 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -164,6 +164,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) tm.tm_min = alrm->time.tm_min; tm.tm_sec = alrm->time.tm_sec; + at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM); at91_sys_write(AT91_RTC_TIMALR, BIN2BCD(tm.tm_sec) << 0 | BIN2BCD(tm.tm_min) << 8 @@ -174,6 +175,9 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) | BIN2BCD(tm.tm_mday) << 24 | AT91_RTC_DATEEN | AT91_RTC_MTHEN); + if (alrm->enabled) + at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM); + pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__, at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); @@ -303,6 +307,12 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return ret; } + /* cpu init code should really have flagged this device as + * being wake-capable; if it didn't, do that here. + */ + if (!device_can_wakeup(&pdev->dev)) + device_init_wakeup(&pdev->dev, 1); + rtc = rtc_device_register(pdev->name, &pdev->dev, &at91_rtc_ops, THIS_MODULE); if (IS_ERR(rtc)) { @@ -310,7 +320,6 @@ static int __init at91_rtc_probe(struct platform_device *pdev) return PTR_ERR(rtc); } platform_set_drvdata(pdev, rtc); - device_init_wakeup(&pdev->dev, 1); printk(KERN_INFO "AT91 Real Time Clock driver.\n"); return 0; @@ -319,7 +328,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev) /* * Disable and remove the RTC driver */ -static int __devexit at91_rtc_remove(struct platform_device *pdev) +static int __exit at91_rtc_remove(struct platform_device *pdev) { struct rtc_device *rtc = platform_get_drvdata(pdev); @@ -331,7 +340,6 @@ static int __devexit at91_rtc_remove(struct platform_device *pdev) rtc_device_unregister(rtc); platform_set_drvdata(pdev, NULL); - device_init_wakeup(&pdev->dev, 0); return 0; } @@ -404,8 +412,7 @@ static int at91_rtc_resume(struct platform_device *pdev) #endif static struct platform_driver at91_rtc_driver = { - .probe = at91_rtc_probe, - .remove = at91_rtc_remove, + .remove = __exit_p(at91_rtc_remove), .suspend = at91_rtc_suspend, .resume = at91_rtc_resume, .driver = { @@ -416,7 +423,7 @@ static struct platform_driver at91_rtc_driver = { static int __init at91_rtc_init(void) { - return platform_driver_register(&at91_rtc_driver); + return platform_driver_probe(&at91_rtc_driver, at91_rtc_probe); } static void __exit at91_rtc_exit(void) diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c index 85bf795..7c0d609 100644 --- a/drivers/rtc/rtc-cmos.c +++ b/drivers/rtc/rtc-cmos.c @@ -59,6 +59,19 @@ struct cmos_rtc { static const char driver_name[] = "rtc_cmos"; +/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear; + * always mask it against the irq enable bits in RTC_CONTROL. Bit values + * are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both. + */ +#define RTC_IRQMASK (RTC_PF | RTC_AF | RTC_UF) + +static inline int is_intr(u8 rtc_intr) +{ + if (!(rtc_intr & RTC_IRQF)) + return 0; + return rtc_intr & RTC_IRQMASK; +} + /*----------------------------------------------------------------*/ static int cmos_read_time(struct device *dev, struct rtc_time *t) @@ -188,7 +201,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) rtc_control &= ~RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - if (rtc_intr) + rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(rtc_intr)) rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); /* update alarm */ @@ -207,7 +221,8 @@ static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t) rtc_control |= RTC_AIE; CMOS_WRITE(rtc_control, RTC_CONTROL); rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - if (rtc_intr) + rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(rtc_intr)) rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); } @@ -287,7 +302,8 @@ cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) } CMOS_WRITE(rtc_control, RTC_CONTROL); rtc_intr = CMOS_READ(RTC_INTR_FLAGS); - if (rtc_intr) + rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(rtc_intr)) rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr); spin_unlock_irqrestore(&rtc_lock, flags); return 0; @@ -353,12 +369,10 @@ static irqreturn_t cmos_interrupt(int irq, void *p) spin_lock(&rtc_lock); irqstat = CMOS_READ(RTC_INTR_FLAGS); + irqstat &= (CMOS_READ(RTC_CONTROL) & RTC_IRQMASK) | RTC_IRQF; spin_unlock(&rtc_lock); - if (irqstat) { - /* NOTE: irqstat may have e.g. RTC_PF set - * even when RTC_PIE is clear... - */ + if (is_intr(irqstat)) { rtc_update_irq(p, 1, irqstat); return IRQ_HANDLED; } else @@ -525,25 +539,26 @@ static int cmos_suspend(struct device *dev, pm_message_t mesg) { struct cmos_rtc *cmos = dev_get_drvdata(dev); int do_wake = device_may_wakeup(dev); - unsigned char tmp, irqstat; + unsigned char tmp; /* only the alarm might be a wakeup event source */ spin_lock_irq(&rtc_lock); cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL); if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) { + unsigned char irqstat; + if (do_wake) tmp &= ~(RTC_PIE|RTC_UIE); else tmp &= ~(RTC_PIE|RTC_AIE|RTC_UIE); CMOS_WRITE(tmp, RTC_CONTROL); irqstat = CMOS_READ(RTC_INTR_FLAGS); - } else - irqstat = 0; + irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(irqstat)) + rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat); + } spin_unlock_irq(&rtc_lock); - if (irqstat) - rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat); - /* ACPI HOOK: enable ACPI_EVENT_RTC when (tmp & RTC_AIE) * ... it'd be best if we could do that under rtc_lock. */ @@ -573,9 +588,10 @@ static int cmos_resume(struct device *dev) spin_lock_irq(&rtc_lock); CMOS_WRITE(tmp, RTC_CONTROL); tmp = CMOS_READ(RTC_INTR_FLAGS); - spin_unlock_irq(&rtc_lock); - if (tmp) + tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF; + if (is_intr(tmp)) rtc_update_irq(&cmos->rtc->class_dev, 1, tmp); + spin_unlock_irq(&rtc_lock); } pr_debug("%s: resume, ctrl %02x\n", @@ -594,7 +610,7 @@ static int cmos_resume(struct device *dev) /*----------------------------------------------------------------*/ /* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems, - * the device node may alternatively be created as a PNP device. + * the device node will always be created as a PNPACPI device. */ #ifdef CONFIG_PNPACPI @@ -673,7 +689,7 @@ module_exit(cmos_exit); /*----------------------------------------------------------------*/ /* Platform setup should have set up an RTC device, when PNPACPI is - * unavailable ... this is the normal case, common even on PCs. + * unavailable ... this could happen even on (older) PCs. */ static int __init cmos_platform_probe(struct platform_device *pdev) diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c index 5875ebb..d48b033 100644 --- a/drivers/rtc/rtc-pcf8583.c +++ b/drivers/rtc/rtc-pcf8583.c @@ -40,7 +40,7 @@ struct pcf8583 { #define CTRL_ALARM 0x02 #define CTRL_TIMER 0x01 -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END }; /* Module parameters */ I2C_CLIENT_INSMOD; @@ -81,11 +81,11 @@ static int pcf8583_get_datetime(struct i2c_client *client, struct rtc_time *dt) buf[4] &= 0x3f; buf[5] &= 0x1f; - dt->tm_sec = BCD_TO_BIN(buf[1]); - dt->tm_min = BCD_TO_BIN(buf[2]); - dt->tm_hour = BCD_TO_BIN(buf[3]); - dt->tm_mday = BCD_TO_BIN(buf[4]); - dt->tm_mon = BCD_TO_BIN(buf[5]); + dt->tm_sec = BCD2BIN(buf[1]); + dt->tm_min = BCD2BIN(buf[2]); + dt->tm_hour = BCD2BIN(buf[3]); + dt->tm_mday = BCD2BIN(buf[4]); + dt->tm_mon = BCD2BIN(buf[5]) - 1; } return ret == 2 ? 0 : -EIO; @@ -99,14 +99,14 @@ static int pcf8583_set_datetime(struct i2c_client *client, struct rtc_time *dt, buf[0] = 0; buf[1] = get_ctrl(client) | 0x80; buf[2] = 0; - buf[3] = BIN_TO_BCD(dt->tm_sec); - buf[4] = BIN_TO_BCD(dt->tm_min); - buf[5] = BIN_TO_BCD(dt->tm_hour); + buf[3] = BIN2BCD(dt->tm_sec); + buf[4] = BIN2BCD(dt->tm_min); + buf[5] = BIN2BCD(dt->tm_hour); if (datetoo) { len = 8; - buf[6] = BIN_TO_BCD(dt->tm_mday) | (dt->tm_year << 6); - buf[7] = BIN_TO_BCD(dt->tm_mon) | (dt->tm_wday << 5); + buf[6] = BIN2BCD(dt->tm_mday) | (dt->tm_year << 6); + buf[7] = BIN2BCD(dt->tm_mon + 1) | (dt->tm_wday << 5); } ret = i2c_master_send(client, (char *)buf, len); @@ -226,7 +226,7 @@ static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm) */ year_offset += 4; - tm->tm_year = real_year + year_offset + year[1] * 100; + tm->tm_year = (real_year + year_offset + year[1] * 100) - 1900; return 0; } @@ -237,6 +237,7 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm) unsigned char year[2], chk; struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year }; struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk }; + unsigned int proper_year = tm->tm_year + 1900; int ret; /* @@ -258,8 +259,8 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm) chk -= year[1] + year[0]; - year[1] = tm->tm_year / 100; - year[0] = tm->tm_year % 100; + year[1] = proper_year / 100; + year[0] = proper_year % 100; chk += year[1] + year[0]; diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c index 9c8ead4..677bae8 100644 --- a/drivers/rtc/rtc-sa1100.c +++ b/drivers/rtc/rtc-sa1100.c @@ -263,8 +263,12 @@ static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm) static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) { + u32 rtsr; + memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time)); - alrm->pending = RTSR & RTSR_AL ? 1 : 0; + rtsr = RTSR; + alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0; + alrm->pending = (rtsr & RTSR_AL) ? 1 : 0; return 0; } @@ -275,12 +279,10 @@ static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) spin_lock_irq(&sa1100_rtc_lock); ret = rtc_update_alarm(&alrm->time); if (ret == 0) { - memcpy(&rtc_alarm, &alrm->time, sizeof(struct rtc_time)); - if (alrm->enabled) - enable_irq_wake(IRQ_RTCAlrm); + RTSR |= RTSR_ALE; else - disable_irq_wake(IRQ_RTCAlrm); + RTSR &= ~RTSR_ALE; } spin_unlock_irq(&sa1100_rtc_lock); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index eb5dc62..e71929d 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -398,6 +398,9 @@ dasd_change_state(struct dasd_device *device) if (device->state == device->target) wake_up(&dasd_init_waitq); + + /* let user-space know that the device status changed */ + kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE); } /* diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index ed70852..6a89cef 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -19,6 +19,7 @@ #include <asm/debug.h> #include <asm/uaccess.h> +#include <asm/ipl.h> /* This is ugly... */ #define PRINTK_HEADER "dasd_devmap:" @@ -133,6 +134,8 @@ dasd_call_setup(char *str) __setup ("dasd=", dasd_call_setup); #endif /* #ifndef MODULE */ +#define DASD_IPLDEV "ipldev" + /* * Read a device busid/devno from a string. */ @@ -141,6 +144,20 @@ dasd_busid(char **str, int *id0, int *id1, int *devno) { int val, old_style; + /* Interpret ipldev busid */ + if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) { + if (ipl_info.type != IPL_TYPE_CCW) { + MESSAGE(KERN_ERR, "%s", "ipl device is not a ccw " + "device"); + return -EINVAL; + } + *id0 = 0; + *id1 = ipl_info.data.ccw.dev_id.ssid; + *devno = ipl_info.data.ccw.dev_id.devno; + *str += strlen(DASD_IPLDEV); + + return 0; + } /* check for leading '0x' */ old_style = 0; if ((*str)[0] == '0' && (*str)[1] == 'x') { @@ -829,6 +846,46 @@ dasd_discipline_show(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL); static ssize_t +dasd_device_status_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + ssize_t len; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (!IS_ERR(device)) { + switch (device->state) { + case DASD_STATE_NEW: + len = snprintf(buf, PAGE_SIZE, "new\n"); + break; + case DASD_STATE_KNOWN: + len = snprintf(buf, PAGE_SIZE, "detected\n"); + break; + case DASD_STATE_BASIC: + len = snprintf(buf, PAGE_SIZE, "basic\n"); + break; + case DASD_STATE_UNFMT: + len = snprintf(buf, PAGE_SIZE, "unformatted\n"); + break; + case DASD_STATE_READY: + len = snprintf(buf, PAGE_SIZE, "ready\n"); + break; + case DASD_STATE_ONLINE: + len = snprintf(buf, PAGE_SIZE, "online\n"); + break; + default: + len = snprintf(buf, PAGE_SIZE, "no stat\n"); + break; + } + dasd_put_device(device); + } else + len = snprintf(buf, PAGE_SIZE, "unknown\n"); + return len; +} + +static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL); + +static ssize_t dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; @@ -939,6 +996,7 @@ static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store); static struct attribute * dasd_attrs[] = { &dev_attr_readonly.attr, &dev_attr_discipline.attr, + &dev_attr_status.attr, &dev_attr_alias.attr, &dev_attr_vendor.attr, &dev_attr_uid.attr, diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index ab782bb..e810e4a 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -65,7 +65,7 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */ * resulting condition code and DIAG return code. */ static inline int dia250(void *iob, int cmd) { - register unsigned long reg0 asm ("0") = (unsigned long) iob; + register unsigned long reg2 asm ("2") = (unsigned long) iob; typedef union { struct dasd_diag_init_io init_io; struct dasd_diag_rw_io rw_io; @@ -74,15 +74,15 @@ static inline int dia250(void *iob, int cmd) rc = 3; asm volatile( - " diag 0,%2,0x250\n" + " diag 2,%2,0x250\n" "0: ipm %0\n" " srl %0,28\n" - " or %0,1\n" + " or %0,3\n" "1:\n" EX_TABLE(0b,1b) : "+d" (rc), "=m" (*(addr_type *) iob) - : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob) - : "1", "cc"); + : "d" (cmd), "d" (reg2), "m" (*(addr_type *) iob) + : "3", "cc"); return rc; } diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 4b8a95f..a1dc8c4 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -461,6 +461,7 @@ int dasd_eer_enable(struct dasd_device *device) cqr->device = device; cqr->retries = 255; cqr->expires = 10 * HZ; + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->cpaddr->cmd_code = DASD_ECKD_CCW_SNSS; cqr->cpaddr->count = SNSS_DATA_SIZE; diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile index 293e667..c210784 100644 --- a/drivers/s390/char/Makefile +++ b/drivers/s390/char/Makefile @@ -3,7 +3,7 @@ # obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ - sclp_info.o + sclp_info.o sclp_config.o sclp_chp.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o @@ -29,3 +29,6 @@ obj-$(CONFIG_S390_TAPE_34XX) += tape_34xx.o obj-$(CONFIG_S390_TAPE_3590) += tape_3590.o obj-$(CONFIG_MONREADER) += monreader.o obj-$(CONFIG_MONWRITER) += monwriter.o + +zcore_mod-objs := sclp_sdias.o zcore.o +obj-$(CONFIG_ZFCPDUMP) += zcore_mod.o diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 9a328f1..6000bde 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -813,12 +813,6 @@ con3215_unblank(void) spin_unlock_irqrestore(get_ccwdev_lock(raw->cdev), flags); } -static int __init -con3215_consetup(struct console *co, char *options) -{ - return 0; -} - /* * The console structure for the 3215 console */ @@ -827,7 +821,6 @@ static struct console con3215 = { .write = con3215_write, .device = con3215_device, .unblank = con3215_unblank, - .setup = con3215_consetup, .flags = CON_PRINTBUFFER, }; diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 8e7f2d7..fd34791 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -555,12 +555,6 @@ con3270_unblank(void) spin_unlock_irqrestore(&cp->view.lock, flags); } -static int __init -con3270_consetup(struct console *co, char *options) -{ - return 0; -} - /* * The console structure for the 3270 console */ @@ -569,7 +563,6 @@ static struct console con3270 = { .write = con3270_write, .device = con3270_device, .unblank = con3270_unblank, - .setup = con3270_consetup, .flags = CON_PRINTBUFFER, }; diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index f171de3..fa62e69 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -15,6 +15,7 @@ #include <linux/timer.h> #include <linux/reboot.h> #include <linux/jiffies.h> +#include <linux/init.h> #include <asm/types.h> #include <asm/s390_ext.h> @@ -510,7 +511,7 @@ sclp_state_change_cb(struct evbuf_header *evbuf) } static struct sclp_register sclp_state_change_event = { - .receive_mask = EvTyp_StateChange_Mask, + .receive_mask = EVTYP_STATECHANGE_MASK, .receiver_fn = sclp_state_change_cb }; @@ -930,3 +931,10 @@ sclp_init(void) sclp_init_mask(1); return 0; } + +static __init int sclp_initcall(void) +{ + return sclp_init(); +} + +arch_initcall(sclp_initcall); diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h index 7d29ab4..87ac4a3 100644 --- a/drivers/s390/char/sclp.h +++ b/drivers/s390/char/sclp.h @@ -19,33 +19,37 @@ #define MAX_KMEM_PAGES (sizeof(unsigned long) << 3) #define MAX_CONSOLE_PAGES 4 -#define EvTyp_OpCmd 0x01 -#define EvTyp_Msg 0x02 -#define EvTyp_StateChange 0x08 -#define EvTyp_PMsgCmd 0x09 -#define EvTyp_CntlProgOpCmd 0x20 -#define EvTyp_CntlProgIdent 0x0B -#define EvTyp_SigQuiesce 0x1D -#define EvTyp_VT220Msg 0x1A - -#define EvTyp_OpCmd_Mask 0x80000000 -#define EvTyp_Msg_Mask 0x40000000 -#define EvTyp_StateChange_Mask 0x01000000 -#define EvTyp_PMsgCmd_Mask 0x00800000 -#define EvTyp_CtlProgOpCmd_Mask 0x00000001 -#define EvTyp_CtlProgIdent_Mask 0x00200000 -#define EvTyp_SigQuiesce_Mask 0x00000008 -#define EvTyp_VT220Msg_Mask 0x00000040 - -#define GnrlMsgFlgs_DOM 0x8000 -#define GnrlMsgFlgs_SndAlrm 0x4000 -#define GnrlMsgFlgs_HoldMsg 0x2000 - -#define LnTpFlgs_CntlText 0x8000 -#define LnTpFlgs_LabelText 0x4000 -#define LnTpFlgs_DataText 0x2000 -#define LnTpFlgs_EndText 0x1000 -#define LnTpFlgs_PromptText 0x0800 +#define EVTYP_OPCMD 0x01 +#define EVTYP_MSG 0x02 +#define EVTYP_STATECHANGE 0x08 +#define EVTYP_PMSGCMD 0x09 +#define EVTYP_CNTLPROGOPCMD 0x20 +#define EVTYP_CNTLPROGIDENT 0x0B +#define EVTYP_SIGQUIESCE 0x1D +#define EVTYP_VT220MSG 0x1A +#define EVTYP_CONFMGMDATA 0x04 +#define EVTYP_SDIAS 0x1C + +#define EVTYP_OPCMD_MASK 0x80000000 +#define EVTYP_MSG_MASK 0x40000000 +#define EVTYP_STATECHANGE_MASK 0x01000000 +#define EVTYP_PMSGCMD_MASK 0x00800000 +#define EVTYP_CTLPROGOPCMD_MASK 0x00000001 +#define EVTYP_CTLPROGIDENT_MASK 0x00200000 +#define EVTYP_SIGQUIESCE_MASK 0x00000008 +#define EVTYP_VT220MSG_MASK 0x00000040 +#define EVTYP_CONFMGMDATA_MASK 0x10000000 +#define EVTYP_SDIAS_MASK 0x00000010 + +#define GNRLMSGFLGS_DOM 0x8000 +#define GNRLMSGFLGS_SNDALRM 0x4000 +#define GNRLMSGFLGS_HOLDMSG 0x2000 + +#define LNTPFLGS_CNTLTEXT 0x8000 +#define LNTPFLGS_LABELTEXT 0x4000 +#define LNTPFLGS_DATATEXT 0x2000 +#define LNTPFLGS_ENDTEXT 0x1000 +#define LNTPFLGS_PROMPTTEXT 0x0800 typedef unsigned int sclp_cmdw_t; @@ -56,15 +60,15 @@ typedef unsigned int sclp_cmdw_t; #define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001 #define GDS_ID_MDSMU 0x1310 -#define GDS_ID_MDSRouteInfo 0x1311 -#define GDS_ID_AgUnWrkCorr 0x1549 -#define GDS_ID_SNACondReport 0x1532 +#define GDS_ID_MDSROUTEINFO 0x1311 +#define GDS_ID_AGUNWRKCORR 0x1549 +#define GDS_ID_SNACONDREPORT 0x1532 #define GDS_ID_CPMSU 0x1212 -#define GDS_ID_RoutTargInstr 0x154D -#define GDS_ID_OpReq 0x8070 -#define GDS_ID_TextCmd 0x1320 +#define GDS_ID_ROUTTARGINSTR 0x154D +#define GDS_ID_OPREQ 0x8070 +#define GDS_ID_TEXTCMD 0x1320 -#define GDS_KEY_SelfDefTextMsg 0x31 +#define GDS_KEY_SELFDEFTEXTMSG 0x31 typedef u32 sccb_mask_t; /* ATTENTION: assumes 32bit mask !!! */ diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c new file mode 100644 index 0000000..a66b914 --- /dev/null +++ b/drivers/s390/char/sclp_chp.c @@ -0,0 +1,196 @@ +/* + * drivers/s390/char/sclp_chp.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/errno.h> +#include <linux/completion.h> +#include <asm/sclp.h> +#include <asm/chpid.h> + +#include "sclp.h" + +#define TAG "sclp_chp: " + +#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001 +#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001 +#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001 + +static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid) +{ + return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8; +} + +static void chp_callback(struct sclp_req *req, void *data) +{ + struct completion *completion = data; + + complete(completion); +} + +struct chp_cfg_sccb { + struct sccb_header header; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_cfg_data { + struct chp_cfg_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +static int do_configure(sclp_cmdw_t cmd) +{ + struct chp_cfg_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_cfg_sccb); + data->req.command = cmd; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "configure channel-path request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + switch (data->sccb.header.response_code) { + case 0x0020: + case 0x0120: + case 0x0440: + case 0x0450: + break; + default: + printk(KERN_WARNING TAG "configure channel-path failed " + "(cmd=0x%08x, response=0x%04x)\n", cmd, + data->sccb.header.response_code); + rc = -EIO; + break; + } +out: + free_page((unsigned long) data); + + return rc; +} + +/** + * sclp_chp_configure - perform configure channel-path sclp command + * @chpid: channel-path ID + * + * Perform configure channel-path command sclp command for specified chpid. + * Return 0 after command successfully finished, non-zero otherwise. + */ +int sclp_chp_configure(struct chp_id chpid) +{ + return do_configure(get_configure_cmdw(chpid)); +} + +/** + * sclp_chp_deconfigure - perform deconfigure channel-path sclp command + * @chpid: channel-path ID + * + * Perform deconfigure channel-path command sclp command for specified chpid + * and wait for completion. On success return 0. Return non-zero otherwise. + */ +int sclp_chp_deconfigure(struct chp_id chpid) +{ + return do_configure(get_deconfigure_cmdw(chpid)); +} + +struct chp_info_sccb { + struct sccb_header header; + u8 recognized[SCLP_CHP_INFO_MASK_SIZE]; + u8 standby[SCLP_CHP_INFO_MASK_SIZE]; + u8 configured[SCLP_CHP_INFO_MASK_SIZE]; + u8 ccm; + u8 reserved[6]; + u8 cssid; +} __attribute__((packed)); + +struct chp_info_data { + struct chp_info_sccb sccb; + struct sclp_req req; + struct completion completion; +} __attribute__((packed)); + +/** + * sclp_chp_read_info - perform read channel-path information sclp command + * @info: resulting channel-path information data + * + * Perform read channel-path information sclp command and wait for completion. + * On success, store channel-path information in @info and return 0. Return + * non-zero otherwise. + */ +int sclp_chp_read_info(struct sclp_chp_info *info) +{ + struct chp_info_data *data; + int rc; + + /* Prepare sccb. */ + data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + data->sccb.header.length = sizeof(struct chp_info_sccb); + data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION; + data->req.sccb = &(data->sccb); + data->req.status = SCLP_REQ_FILLED; + data->req.callback = chp_callback; + data->req.callback_data = &(data->completion); + init_completion(&data->completion); + + /* Perform sclp request. */ + rc = sclp_add_request(&(data->req)); + if (rc) + goto out; + wait_for_completion(&data->completion); + + /* Check response .*/ + if (data->req.status != SCLP_REQ_DONE) { + printk(KERN_WARNING TAG "read channel-path info request failed " + "(status=0x%02x)\n", data->req.status); + rc = -EIO; + goto out; + } + if (data->sccb.header.response_code != 0x0010) { + printk(KERN_WARNING TAG "read channel-path info failed " + "(response=0x%04x)\n", data->sccb.header.response_code); + rc = -EIO; + goto out; + } + memcpy(info->recognized, data->sccb.recognized, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->standby, data->sccb.standby, + SCLP_CHP_INFO_MASK_SIZE); + memcpy(info->configured, data->sccb.configured, + SCLP_CHP_INFO_MASK_SIZE); +out: + free_page((unsigned long) data); + + return rc; +} diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c new file mode 100644 index 0000000..5322e5e --- /dev/null +++ b/drivers/s390/char/sclp_config.c @@ -0,0 +1,75 @@ +/* + * drivers/s390/char/sclp_config.c + * + * Copyright IBM Corp. 2007 + * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/cpu.h> +#include <linux/sysdev.h> +#include <linux/workqueue.h> +#include "sclp.h" + +#define TAG "sclp_config: " + +struct conf_mgm_data { + u8 reserved; + u8 ev_qualifier; +} __attribute__((packed)); + +#define EV_QUAL_CAP_CHANGE 3 + +static struct work_struct sclp_cpu_capability_work; + +static void sclp_cpu_capability_notify(struct work_struct *work) +{ + int cpu; + struct sys_device *sysdev; + + printk(KERN_WARNING TAG "cpu capability changed.\n"); + lock_cpu_hotplug(); + for_each_online_cpu(cpu) { + sysdev = get_cpu_sysdev(cpu); + kobject_uevent(&sysdev->kobj, KOBJ_CHANGE); + } + unlock_cpu_hotplug(); +} + +static void sclp_conf_receiver_fn(struct evbuf_header *evbuf) +{ + struct conf_mgm_data *cdata; + + cdata = (struct conf_mgm_data *)(evbuf + 1); + if (cdata->ev_qualifier == EV_QUAL_CAP_CHANGE) + schedule_work(&sclp_cpu_capability_work); +} + +static struct sclp_register sclp_conf_register = +{ + .receive_mask = EVTYP_CONFMGMDATA_MASK, + .receiver_fn = sclp_conf_receiver_fn, +}; + +static int __init sclp_conf_init(void) +{ + int rc; + + INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); + + rc = sclp_register(&sclp_conf_register); + if (rc) { + printk(KERN_ERR TAG "failed to register (%d).\n", rc); + return rc; + } + + if (!(sclp_conf_register.sclp_receive_mask & EVTYP_CONFMGMDATA_MASK)) { + printk(KERN_WARNING TAG "no configuration management.\n"); + sclp_unregister(&sclp_conf_register); + rc = -ENOSYS; + } + return rc; +} + +__initcall(sclp_conf_init); diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c index 65aa2c8..29fe2a5 100644 --- a/drivers/s390/char/sclp_cpi.c +++ b/drivers/s390/char/sclp_cpi.c @@ -46,7 +46,7 @@ struct cpi_sccb { /* Event type structure for write message and write priority message */ static struct sclp_register sclp_cpi_event = { - .send_mask = EvTyp_CtlProgIdent_Mask + .send_mask = EVTYP_CTLPROGIDENT_MASK }; MODULE_LICENSE("GPL"); @@ -201,7 +201,7 @@ cpi_module_init(void) "console.\n"); return -EINVAL; } - if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { + if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { printk(KERN_WARNING "cpi: no control program identification " "support\n"); sclp_unregister(&sclp_cpi_event); diff --git a/drivers/s390/char/sclp_quiesce.c b/drivers/s390/char/sclp_quiesce.c index ffa9282..45ff25e 100644 --- a/drivers/s390/char/sclp_quiesce.c +++ b/drivers/s390/char/sclp_quiesce.c @@ -16,6 +16,7 @@ #include <asm/atomic.h> #include <asm/ptrace.h> #include <asm/sigp.h> +#include <asm/smp.h> #include "sclp.h" @@ -42,7 +43,7 @@ sclp_quiesce_handler(struct evbuf_header *evbuf) } static struct sclp_register sclp_quiesce_event = { - .receive_mask = EvTyp_SigQuiesce_Mask, + .receive_mask = EVTYP_SIGQUIESCE_MASK, .receiver_fn = sclp_quiesce_handler }; diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c index 2486783..bbd5b8b 100644 --- a/drivers/s390/char/sclp_rw.c +++ b/drivers/s390/char/sclp_rw.c @@ -30,7 +30,7 @@ /* Event type structure for write message and write priority message */ static struct sclp_register sclp_rw_event = { - .send_mask = EvTyp_Msg_Mask | EvTyp_PMsgCmd_Mask + .send_mask = EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK }; /* @@ -64,7 +64,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab) memset(sccb, 0, sizeof(struct write_sccb)); sccb->header.length = sizeof(struct write_sccb); sccb->msg_buf.header.length = sizeof(struct msg_buf); - sccb->msg_buf.header.type = EvTyp_Msg; + sccb->msg_buf.header.type = EVTYP_MSG; sccb->msg_buf.mdb.header.length = sizeof(struct mdb); sccb->msg_buf.mdb.header.type = 1; sccb->msg_buf.mdb.header.tag = 0xD4C4C240; /* ebcdic "MDB " */ @@ -114,7 +114,7 @@ sclp_initialize_mto(struct sclp_buffer *buffer, int max_len) memset(mto, 0, sizeof(struct mto)); mto->length = sizeof(struct mto); mto->type = 4; /* message text object */ - mto->line_type_flags = LnTpFlgs_EndText; /* end text */ + mto->line_type_flags = LNTPFLGS_ENDTEXT; /* end text */ /* set pointer to first byte after struct mto. */ buffer->current_line = (char *) (mto + 1); @@ -215,7 +215,7 @@ sclp_write(struct sclp_buffer *buffer, const unsigned char *msg, int count) case '\a': /* bell, one for several times */ /* set SCLP sound alarm bit in General Object */ buffer->sccb->msg_buf.mdb.go.general_msg_flags |= - GnrlMsgFlgs_SndAlrm; + GNRLMSGFLGS_SNDALRM; break; case '\t': /* horizontal tabulator */ /* check if new mto needs to be created */ @@ -452,12 +452,12 @@ sclp_emit_buffer(struct sclp_buffer *buffer, return -EIO; sccb = buffer->sccb; - if (sclp_rw_event.sclp_send_mask & EvTyp_Msg_Mask) + if (sclp_rw_event.sclp_send_mask & EVTYP_MSG_MASK) /* Use normal write message */ - sccb->msg_buf.header.type = EvTyp_Msg; - else if (sclp_rw_event.sclp_send_mask & EvTyp_PMsgCmd_Mask) + sccb->msg_buf.header.type = EVTYP_MSG; + else if (sclp_rw_event.sclp_send_mask & EVTYP_PMSGCMD_MASK) /* Use write priority message */ - sccb->msg_buf.header.type = EvTyp_PMsgCmd; + sccb->msg_buf.header.type = EVTYP_PMSGCMD; else return -ENOSYS; buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA; diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c new file mode 100644 index 0000000..52283da --- /dev/null +++ b/drivers/s390/char/sclp_sdias.c @@ -0,0 +1,255 @@ +/* + * Sclp "store data in absolut storage" + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include <linux/sched.h> +#include <asm/sclp.h> +#include <asm/debug.h> +#include <asm/ipl.h> +#include "sclp.h" +#include "sclp_rw.h" + +#define TRACE(x...) debug_sprintf_event(sdias_dbf, 1, x) +#define ERROR_MSG(x...) printk ( KERN_ALERT "SDIAS: " x ) + +#define SDIAS_RETRIES 300 +#define SDIAS_SLEEP_TICKS 50 + +#define EQ_STORE_DATA 0x0 +#define EQ_SIZE 0x1 +#define DI_FCP_DUMP 0x0 +#define ASA_SIZE_32 0x0 +#define ASA_SIZE_64 0x1 +#define EVSTATE_ALL_STORED 0x0 +#define EVSTATE_NO_DATA 0x3 +#define EVSTATE_PART_STORED 0x10 + +static struct debug_info *sdias_dbf; + +static struct sclp_register sclp_sdias_register = { + .send_mask = EVTYP_SDIAS_MASK, +}; + +struct sdias_evbuf { + struct evbuf_header hdr; + u8 event_qual; + u8 data_id; + u64 reserved2; + u32 event_id; + u16 reserved3; + u8 asa_size; + u8 event_status; + u32 reserved4; + u32 blk_cnt; + u64 asa; + u32 reserved5; + u32 fbn; + u32 reserved6; + u32 lbn; + u16 reserved7; + u16 dbs; +} __attribute__((packed)); + +struct sdias_sccb { + struct sccb_header hdr; + struct sdias_evbuf evbuf; +} __attribute__((packed)); + +static struct sdias_sccb sccb __attribute__((aligned(4096))); + +static int sclp_req_done; +static wait_queue_head_t sdias_wq; +static DEFINE_MUTEX(sdias_mutex); + +static void sdias_callback(struct sclp_req *request, void *data) +{ + struct sdias_sccb *sccb; + + sccb = (struct sdias_sccb *) request->sccb; + sclp_req_done = 1; + wake_up(&sdias_wq); /* Inform caller, that request is complete */ + TRACE("callback done\n"); +} + +static int sdias_sclp_send(struct sclp_req *req) +{ + int retries; + int rc; + + for (retries = SDIAS_RETRIES; retries; retries--) { + sclp_req_done = 0; + TRACE("add request\n"); + rc = sclp_add_request(req); + if (rc) { + /* not initiated, wait some time and retry */ + set_current_state(TASK_INTERRUPTIBLE); + TRACE("add request failed: rc = %i\n",rc); + schedule_timeout(SDIAS_SLEEP_TICKS); + continue; + } + /* initiated, wait for completion of service call */ + wait_event(sdias_wq, (sclp_req_done == 1)); + if (req->status == SCLP_REQ_FAILED) { + TRACE("sclp request failed\n"); + rc = -EIO; + continue; + } + TRACE("request done\n"); + break; + } + return rc; +} + +/* + * Get number of blocks (4K) available in the HSA + */ +int sclp_sdias_blk_count(void) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EVTYP_SDIAS; + sccb.evbuf.event_qual = EQ_SIZE; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed for get_nr_blocks\n"); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("send failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case 0: + rc = sccb.evbuf.blk_cnt; + break; + default: + ERROR_MSG("SCLP error: %x\n", sccb.evbuf.event_status); + rc = -EIO; + goto out; + } + TRACE("%i blocks\n", rc); +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +/* + * Copy from HSA to absolute storage (not reentrant): + * + * @dest : Address of buffer where data should be copied + * @start_blk: Start Block (beginning with 1) + * @nr_blks : Number of 4K blocks to copy + * + * Return Value: 0 : Requested 'number' of blocks of data copied + * <0: ERROR - negative event status + */ +int sclp_sdias_copy(void *dest, int start_blk, int nr_blks) +{ + struct sclp_req request; + int rc; + + mutex_lock(&sdias_mutex); + + memset(&sccb, 0, sizeof(sccb)); + memset(&request, 0, sizeof(request)); + + sccb.hdr.length = sizeof(sccb); + sccb.evbuf.hdr.length = sizeof(struct sdias_evbuf); + sccb.evbuf.hdr.type = EVTYP_SDIAS; + sccb.evbuf.hdr.flags = 0; + sccb.evbuf.event_qual = EQ_STORE_DATA; + sccb.evbuf.data_id = DI_FCP_DUMP; + sccb.evbuf.event_id = 4712; +#ifdef __s390x__ + sccb.evbuf.asa_size = ASA_SIZE_64; +#else + sccb.evbuf.asa_size = ASA_SIZE_32; +#endif + sccb.evbuf.event_status = 0; + sccb.evbuf.blk_cnt = nr_blks; + sccb.evbuf.asa = (unsigned long)dest; + sccb.evbuf.fbn = start_blk; + sccb.evbuf.lbn = 0; + sccb.evbuf.dbs = 1; + + request.sccb = &sccb; + request.command = SCLP_CMDW_WRITE_EVENT_DATA; + request.status = SCLP_REQ_FILLED; + request.callback = sdias_callback; + + rc = sdias_sclp_send(&request); + if (rc) { + ERROR_MSG("sclp_send failed: %x\n", rc); + goto out; + } + if (sccb.hdr.response_code != 0x0020) { + TRACE("copy failed: %x\n", sccb.hdr.response_code); + rc = -EIO; + goto out; + } + + switch (sccb.evbuf.event_status) { + case EVSTATE_ALL_STORED: + TRACE("all stored\n"); + case EVSTATE_PART_STORED: + TRACE("part stored: %i\n", sccb.evbuf.blk_cnt); + break; + case EVSTATE_NO_DATA: + TRACE("no data\n"); + default: + ERROR_MSG("Error from SCLP while copying hsa. " + "Event status = %x\n", + sccb.evbuf.event_status); + rc = -EIO; + } +out: + mutex_unlock(&sdias_mutex); + return rc; +} + +int __init sdias_init(void) +{ + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return 0; + sdias_dbf = debug_register("dump_sdias", 4, 1, 4 * sizeof(long)); + debug_register_view(sdias_dbf, &debug_sprintf_view); + debug_set_level(sdias_dbf, 6); + rc = sclp_register(&sclp_sdias_register); + if (rc) { + ERROR_MSG("sclp register failed\n"); + return rc; + } + init_waitqueue_head(&sdias_wq); + TRACE("init done\n"); + return 0; +} + +void __exit sdias_exit(void) +{ + debug_unregister(sdias_dbf); + sclp_unregister(&sclp_sdias_register); +} diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c index 076816b..e3b3d39 100644 --- a/drivers/s390/char/sclp_tty.c +++ b/drivers/s390/char/sclp_tty.c @@ -648,7 +648,7 @@ sclp_eval_textcmd(struct gds_subvector *start, subvec = start; while (subvec < end) { subvec = find_gds_subvector(subvec, end, - GDS_KEY_SelfDefTextMsg); + GDS_KEY_SELFDEFTEXTMSG); if (!subvec) break; sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1), @@ -664,7 +664,7 @@ sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end) vec = start; while (vec < end) { - vec = find_gds_vector(vec, end, GDS_ID_TextCmd); + vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD); if (!vec) break; sclp_eval_textcmd((struct gds_subvector *)(vec + 1), @@ -703,7 +703,7 @@ sclp_tty_state_change(struct sclp_register *reg) static struct sclp_register sclp_input_event = { - .receive_mask = EvTyp_OpCmd_Mask | EvTyp_PMsgCmd_Mask, + .receive_mask = EVTYP_OPCMD_MASK | EVTYP_PMSGCMD_MASK, .state_change_fn = sclp_tty_state_change, .receiver_fn = sclp_tty_receiver }; diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index f77dc33..7263347 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -99,8 +99,8 @@ static void sclp_vt220_emit_current(void); /* Registration structure for our interest in SCLP event buffers */ static struct sclp_register sclp_vt220_register = { - .send_mask = EvTyp_VT220Msg_Mask, - .receive_mask = EvTyp_VT220Msg_Mask, + .send_mask = EVTYP_VT220MSG_MASK, + .receive_mask = EVTYP_VT220MSG_MASK, .state_change_fn = NULL, .receiver_fn = sclp_vt220_receiver_fn }; @@ -202,7 +202,7 @@ sclp_vt220_callback(struct sclp_req *request, void *data) static int __sclp_vt220_emit(struct sclp_vt220_request *request) { - if (!(sclp_vt220_register.sclp_send_mask & EvTyp_VT220Msg_Mask)) { + if (!(sclp_vt220_register.sclp_send_mask & EVTYP_VT220MSG_MASK)) { request->sclp_req.status = SCLP_REQ_FAILED; return -EIO; } @@ -284,7 +284,7 @@ sclp_vt220_initialize_page(void *page) sccb->header.length = sizeof(struct sclp_vt220_sccb); sccb->header.function_code = SCLP_NORMAL_WRITE; sccb->header.response_code = 0x0000; - sccb->evbuf.type = EvTyp_VT220Msg; + sccb->evbuf.type = EVTYP_VT220MSG; sccb->evbuf.length = sizeof(struct evbuf_header); return request; diff --git a/drivers/s390/char/tape_std.c b/drivers/s390/char/tape_std.c index 7a76ec4..2a1af4e 100644 --- a/drivers/s390/char/tape_std.c +++ b/drivers/s390/char/tape_std.c @@ -647,7 +647,10 @@ tape_std_mtcompression(struct tape_device *device, int mt_count) return PTR_ERR(request); request->op = TO_NOP; /* setup ccws */ - *device->modeset_byte = (mt_count == 0) ? 0x00 : 0x08; + if (mt_count == 0) + *device->modeset_byte &= ~0x08; + else + *device->modeset_byte |= 0x08; tape_ccw_cc(request->cpaddr, MODE_SET_DB, 1, device->modeset_byte); tape_ccw_end(request->cpaddr + 1, NOP, 0, NULL); /* execute it */ diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index b87d3b0..a5a00e9 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -125,7 +125,7 @@ static struct vmlogrdr_priv_t sys_ser[] = { .recording_name = "EREP", .minor_num = 0, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[0].priv_lock), .autorecording = 1, .autopurge = 1, }, @@ -134,7 +134,7 @@ static struct vmlogrdr_priv_t sys_ser[] = { .recording_name = "ACCOUNT", .minor_num = 1, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[1].priv_lock), .autorecording = 1, .autopurge = 1, }, @@ -143,7 +143,7 @@ static struct vmlogrdr_priv_t sys_ser[] = { .recording_name = "SYMPTOM", .minor_num = 2, .buffer_free = 1, - .priv_lock = SPIN_LOCK_UNLOCKED, + .priv_lock = __SPIN_LOCK_UNLOCKED(sys_ser[2].priv_lock), .autorecording = 1, .autopurge = 1, } @@ -385,6 +385,9 @@ static int vmlogrdr_release (struct inode *inode, struct file *filp) struct vmlogrdr_priv_t * logptr = filp->private_data; + iucv_path_sever(logptr->path, NULL); + kfree(logptr->path); + logptr->path = NULL; if (logptr->autorecording) { ret = vmlogrdr_recording(logptr,0,logptr->autopurge); if (ret) diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c new file mode 100644 index 0000000..89d43931 --- /dev/null +++ b/drivers/s390/char/zcore.c @@ -0,0 +1,651 @@ +/* + * zcore module to export memory content and register sets for creating system + * dumps on SCSI disks (zfcpdump). The "zcore/mem" debugfs file shows the same + * dump format as s390 standalone dumps. + * + * For more information please refer to Documentation/s390/zfcpdump.txt + * + * Copyright IBM Corp. 2003,2007 + * Author(s): Michael Holzheu + */ + +#include <linux/init.h> +#include <linux/miscdevice.h> +#include <linux/utsname.h> +#include <linux/debugfs.h> +#include <asm/ipl.h> +#include <asm/sclp.h> +#include <asm/setup.h> +#include <asm/sigp.h> +#include <asm/uaccess.h> +#include <asm/debug.h> +#include <asm/processor.h> +#include <asm/irqflags.h> + +#define TRACE(x...) debug_sprintf_event(zcore_dbf, 1, x) +#define MSG(x...) printk( KERN_ALERT x ) +#define ERROR_MSG(x...) printk ( KERN_ALERT "DUMP: " x ) + +#define TO_USER 0 +#define TO_KERNEL 1 + +enum arch_id { + ARCH_S390 = 0, + ARCH_S390X = 1, +}; + +/* dump system info */ + +struct sys_info { + enum arch_id arch; + unsigned long sa_base; + u32 sa_size; + int cpu_map[NR_CPUS]; + unsigned long mem_size; + union save_area lc_mask; +}; + +static struct sys_info sys_info; +static struct debug_info *zcore_dbf; +static int hsa_available; +static struct dentry *zcore_dir; +static struct dentry *zcore_file; + +/* + * Copy memory from HSA to kernel or user memory (not reentrant): + * + * @dest: Kernel or user buffer where memory should be copied to + * @src: Start address within HSA where data should be copied + * @count: Size of buffer, which should be copied + * @mode: Either TO_KERNEL or TO_USER + */ +static int memcpy_hsa(void *dest, unsigned long src, size_t count, int mode) +{ + int offs, blk_num; + static char buf[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE))); + + if (count == 0) + return 0; + + /* copy first block */ + offs = 0; + if ((src % PAGE_SIZE) != 0) { + blk_num = src / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + offs = min((PAGE_SIZE - (src % PAGE_SIZE)), count); + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest, + buf + (src % PAGE_SIZE), offs)) + return -EFAULT; + } else + memcpy(dest, buf + (src % PAGE_SIZE), offs); + } + if (offs == count) + goto out; + + /* copy middle */ + for (; (offs + PAGE_SIZE) <= count; offs += PAGE_SIZE) { + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, + buf, PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, PAGE_SIZE); + } + if (offs == count) + goto out; + + /* copy last block */ + blk_num = (src + offs) / PAGE_SIZE + 2; + if (sclp_sdias_copy(buf, blk_num, 1)) { + TRACE("sclp_sdias_copy() failed\n"); + return -EIO; + } + if (mode == TO_USER) { + if (copy_to_user((__force __user void*) dest + offs, buf, + PAGE_SIZE)) + return -EFAULT; + } else + memcpy(dest + offs, buf, count - offs); +out: + return 0; +} + +static int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count) +{ + return memcpy_hsa((void __force *) dest, src, count, TO_USER); +} + +static int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count) +{ + return memcpy_hsa(dest, src, count, TO_KERNEL); +} + +static int memcpy_real(void *dest, unsigned long src, size_t count) +{ + unsigned long flags; + int rc = -EFAULT; + register unsigned long _dest asm("2") = (unsigned long) dest; + register unsigned long _len1 asm("3") = (unsigned long) count; + register unsigned long _src asm("4") = src; + register unsigned long _len2 asm("5") = (unsigned long) count; + + if (count == 0) + return 0; + flags = __raw_local_irq_stnsm(0xf8); /* switch to real mode */ + asm volatile ( + "0: mvcle %1,%2,0x0\n" + "1: jo 0b\n" + " lhi %0,0x0\n" + "2:\n" + EX_TABLE(1b,2b) + : "+d" (rc) + : "d" (_dest), "d" (_src), "d" (_len1), "d" (_len2) + : "cc", "memory"); + __raw_local_irq_ssm(flags); + + return rc; +} + +static int memcpy_real_user(__user void *dest, unsigned long src, size_t count) +{ + static char buf[4096]; + int offs = 0, size; + + while (offs < count) { + size = min(sizeof(buf), count - offs); + if (memcpy_real(buf, src + offs, size)) + return -EFAULT; + if (copy_to_user(dest + offs, buf, size)) + return -EFAULT; + offs += size; + } + return 0; +} + +#ifdef __s390x__ +/* + * Convert s390x (64 bit) cpu info to s390 (32 bit) cpu info + */ +static void __init s390x_to_s390_regs(union save_area *out, union save_area *in, + int cpu) +{ + int i; + + for (i = 0; i < 16; i++) { + out->s390.gp_regs[i] = in->s390x.gp_regs[i] & 0x00000000ffffffff; + out->s390.acc_regs[i] = in->s390x.acc_regs[i]; + out->s390.ctrl_regs[i] = + in->s390x.ctrl_regs[i] & 0x00000000ffffffff; + } + /* locore for 31 bit has only space for fpregs 0,2,4,6 */ + out->s390.fp_regs[0] = in->s390x.fp_regs[0]; + out->s390.fp_regs[1] = in->s390x.fp_regs[2]; + out->s390.fp_regs[2] = in->s390x.fp_regs[4]; + out->s390.fp_regs[3] = in->s390x.fp_regs[6]; + memcpy(&(out->s390.psw[0]), &(in->s390x.psw[0]), 4); + out->s390.psw[1] |= 0x8; /* set bit 12 */ + memcpy(&(out->s390.psw[4]),&(in->s390x.psw[12]), 4); + out->s390.psw[4] |= 0x80; /* set (31bit) addressing bit */ + out->s390.pref_reg = in->s390x.pref_reg; + out->s390.timer = in->s390x.timer; + out->s390.clk_cmp = in->s390x.clk_cmp; +} + +static void __init s390x_to_s390_save_areas(void) +{ + int i = 1; + static union save_area tmp; + + while (zfcpdump_save_areas[i]) { + s390x_to_s390_regs(&tmp, zfcpdump_save_areas[i], i); + memcpy(zfcpdump_save_areas[i], &tmp, sizeof(tmp)); + i++; + } +} + +#endif /* __s390x__ */ + +static int __init init_cpu_info(enum arch_id arch) +{ + union save_area *sa; + + /* get info for boot cpu from lowcore, stored in the HSA */ + + sa = kmalloc(sizeof(*sa), GFP_KERNEL); + if (!sa) { + ERROR_MSG("kmalloc failed: %s: %i\n",__FUNCTION__, __LINE__); + return -ENOMEM; + } + if (memcpy_hsa_kernel(sa, sys_info.sa_base, sys_info.sa_size) < 0) { + ERROR_MSG("could not copy from HSA\n"); + kfree(sa); + return -EIO; + } + zfcpdump_save_areas[0] = sa; + +#ifdef __s390x__ + /* convert s390x regs to s390, if we are dumping an s390 Linux */ + + if (arch == ARCH_S390) + s390x_to_s390_save_areas(); +#endif + + return 0; +} + +static DEFINE_MUTEX(zcore_mutex); + +#define DUMP_VERSION 0x3 +#define DUMP_MAGIC 0xa8190173618f23fdULL +#define DUMP_ARCH_S390X 2 +#define DUMP_ARCH_S390 1 +#define HEADER_SIZE 4096 + +/* dump header dumped according to s390 crash dump format */ + +struct zcore_header { + u64 magic; + u32 version; + u32 header_size; + u32 dump_level; + u32 page_size; + u64 mem_size; + u64 mem_start; + u64 mem_end; + u32 num_pages; + u32 pad1; + u64 tod; + cpuid_t cpu_id; + u32 arch_id; + u32 build_arch; + char pad2[4016]; +} __attribute__((packed,__aligned__(16))); + +static struct zcore_header zcore_header = { + .magic = DUMP_MAGIC, + .version = DUMP_VERSION, + .header_size = 4096, + .dump_level = 0, + .page_size = PAGE_SIZE, + .mem_start = 0, +#ifdef __s390x__ + .build_arch = DUMP_ARCH_S390X, +#else + .build_arch = DUMP_ARCH_S390, +#endif +}; + +/* + * Copy lowcore info to buffer. Use map in order to copy only register parts. + * + * @buf: User buffer + * @sa: Pointer to save area + * @sa_off: Offset in save area to copy + * @len: Number of bytes to copy + */ +static int copy_lc(void __user *buf, void *sa, int sa_off, int len) +{ + int i; + char *lc_mask = (char*)&sys_info.lc_mask; + + for (i = 0; i < len; i++) { + if (!lc_mask[i + sa_off]) + continue; + if (copy_to_user(buf + i, sa + sa_off + i, 1)) + return -EFAULT; + } + return 0; +} + +/* + * Copy lowcores info to memory, if necessary + * + * @buf: User buffer + * @addr: Start address of buffer in dump memory + * @count: Size of buffer + */ +static int zcore_add_lc(char __user *buf, unsigned long start, size_t count) +{ + unsigned long end; + int i = 0; + + if (count == 0) + return 0; + + end = start + count; + while (zfcpdump_save_areas[i]) { + unsigned long cp_start, cp_end; /* copy range */ + unsigned long sa_start, sa_end; /* save area range */ + unsigned long prefix; + unsigned long sa_off, len, buf_off; + + if (sys_info.arch == ARCH_S390) + prefix = zfcpdump_save_areas[i]->s390.pref_reg; + else + prefix = zfcpdump_save_areas[i]->s390x.pref_reg; + + sa_start = prefix + sys_info.sa_base; + sa_end = prefix + sys_info.sa_base + sys_info.sa_size; + + if ((end < sa_start) || (start > sa_end)) + goto next; + cp_start = max(start, sa_start); + cp_end = min(end, sa_end); + + buf_off = cp_start - start; + sa_off = cp_start - sa_start; + len = cp_end - cp_start; + + TRACE("copy_lc for: %lx\n", start); + if (copy_lc(buf + buf_off, zfcpdump_save_areas[i], sa_off, len)) + return -EFAULT; +next: + i++; + } + return 0; +} + +/* + * Read routine for zcore character device + * First 4K are dump header + * Next 32MB are HSA Memory + * Rest is read from absolute Memory + */ +static ssize_t zcore_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned long mem_start; /* Start address in memory */ + size_t mem_offs; /* Offset in dump memory */ + size_t hdr_count; /* Size of header part of output buffer */ + size_t size; + int rc; + + mutex_lock(&zcore_mutex); + + if (*ppos > (sys_info.mem_size + HEADER_SIZE)) { + rc = -EINVAL; + goto fail; + } + + count = min(count, (size_t) (sys_info.mem_size + HEADER_SIZE - *ppos)); + + /* Copy dump header */ + if (*ppos < HEADER_SIZE) { + size = min(count, (size_t) (HEADER_SIZE - *ppos)); + if (copy_to_user(buf, &zcore_header + *ppos, size)) { + rc = -EFAULT; + goto fail; + } + hdr_count = size; + mem_start = 0; + } else { + hdr_count = 0; + mem_start = *ppos - HEADER_SIZE; + } + + mem_offs = 0; + + /* Copy from HSA data */ + if (*ppos < (ZFCPDUMP_HSA_SIZE + HEADER_SIZE)) { + size = min((count - hdr_count), (size_t) (ZFCPDUMP_HSA_SIZE + - mem_start)); + rc = memcpy_hsa_user(buf + hdr_count, mem_start, size); + if (rc) + goto fail; + + mem_offs += size; + } + + /* Copy from real mem */ + size = count - mem_offs - hdr_count; + rc = memcpy_real_user(buf + hdr_count + mem_offs, mem_start + mem_offs, + size); + if (rc) + goto fail; + + /* + * Since s390 dump analysis tools like lcrash or crash + * expect register sets in the prefix pages of the cpus, + * we copy them into the read buffer, if necessary. + * buf + hdr_count: Start of memory part of output buffer + * mem_start: Start memory address to copy from + * count - hdr_count: Size of memory area to copy + */ + if (zcore_add_lc(buf + hdr_count, mem_start, count - hdr_count)) { + rc = -EFAULT; + goto fail; + } + *ppos += count; +fail: + mutex_unlock(&zcore_mutex); + return (rc < 0) ? rc : count; +} + +static int zcore_open(struct inode *inode, struct file *filp) +{ + if (!hsa_available) + return -ENODATA; + else + return capable(CAP_SYS_RAWIO) ? 0 : -EPERM; +} + +static int zcore_release(struct inode *inode, struct file *filep) +{ + diag308(DIAG308_REL_HSA, NULL); + hsa_available = 0; + return 0; +} + +static loff_t zcore_lseek(struct file *file, loff_t offset, int orig) +{ + loff_t rc; + + mutex_lock(&zcore_mutex); + switch (orig) { + case 0: + file->f_pos = offset; + rc = file->f_pos; + break; + case 1: + file->f_pos += offset; + rc = file->f_pos; + break; + default: + rc = -EINVAL; + } + mutex_unlock(&zcore_mutex); + return rc; +} + +static struct file_operations zcore_fops = { + .owner = THIS_MODULE, + .llseek = zcore_lseek, + .read = zcore_read, + .open = zcore_open, + .release = zcore_release, +}; + + +static void __init set_s390_lc_mask(union save_area *map) +{ + memset(&map->s390.ext_save, 0xff, sizeof(map->s390.ext_save)); + memset(&map->s390.timer, 0xff, sizeof(map->s390.timer)); + memset(&map->s390.clk_cmp, 0xff, sizeof(map->s390.clk_cmp)); + memset(&map->s390.psw, 0xff, sizeof(map->s390.psw)); + memset(&map->s390.pref_reg, 0xff, sizeof(map->s390.pref_reg)); + memset(&map->s390.acc_regs, 0xff, sizeof(map->s390.acc_regs)); + memset(&map->s390.fp_regs, 0xff, sizeof(map->s390.fp_regs)); + memset(&map->s390.gp_regs, 0xff, sizeof(map->s390.gp_regs)); + memset(&map->s390.ctrl_regs, 0xff, sizeof(map->s390.ctrl_regs)); +} + +static void __init set_s390x_lc_mask(union save_area *map) +{ + memset(&map->s390x.fp_regs, 0xff, sizeof(map->s390x.fp_regs)); + memset(&map->s390x.gp_regs, 0xff, sizeof(map->s390x.gp_regs)); + memset(&map->s390x.psw, 0xff, sizeof(map->s390x.psw)); + memset(&map->s390x.pref_reg, 0xff, sizeof(map->s390x.pref_reg)); + memset(&map->s390x.fp_ctrl_reg, 0xff, sizeof(map->s390x.fp_ctrl_reg)); + memset(&map->s390x.tod_reg, 0xff, sizeof(map->s390x.tod_reg)); + memset(&map->s390x.timer, 0xff, sizeof(map->s390x.timer)); + memset(&map->s390x.clk_cmp, 0xff, sizeof(map->s390x.clk_cmp)); + memset(&map->s390x.acc_regs, 0xff, sizeof(map->s390x.acc_regs)); + memset(&map->s390x.ctrl_regs, 0xff, sizeof(map->s390x.ctrl_regs)); +} + +/* + * Initialize dump globals for a given architecture + */ +static int __init sys_info_init(enum arch_id arch) +{ + switch (arch) { + case ARCH_S390X: + MSG("DETECTED 'S390X (64 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390X; + sys_info.sa_size = sizeof(struct save_area_s390x); + set_s390x_lc_mask(&sys_info.lc_mask); + break; + case ARCH_S390: + MSG("DETECTED 'S390 (32 bit) OS'\n"); + sys_info.sa_base = SAVE_AREA_BASE_S390; + sys_info.sa_size = sizeof(struct save_area_s390); + set_s390_lc_mask(&sys_info.lc_mask); + break; + default: + ERROR_MSG("unknown architecture 0x%x.\n",arch); + return -EINVAL; + } + sys_info.arch = arch; + if (init_cpu_info(arch)) { + ERROR_MSG("get cpu info failed\n"); + return -ENOMEM; + } + sys_info.mem_size = real_memory_size; + + return 0; +} + +static int __init check_sdias(void) +{ + int rc, act_hsa_size; + + rc = sclp_sdias_blk_count(); + if (rc < 0) { + ERROR_MSG("Could not determine HSA size\n"); + return rc; + } + act_hsa_size = (rc - 1) * PAGE_SIZE; + if (act_hsa_size < ZFCPDUMP_HSA_SIZE) { + ERROR_MSG("HSA size too small: %i\n", act_hsa_size); + return -EINVAL; + } + return 0; +} + +static void __init zcore_header_init(int arch, struct zcore_header *hdr) +{ + if (arch == ARCH_S390X) + hdr->arch_id = DUMP_ARCH_S390X; + else + hdr->arch_id = DUMP_ARCH_S390; + hdr->mem_size = sys_info.mem_size; + hdr->mem_end = sys_info.mem_size; + hdr->num_pages = sys_info.mem_size / PAGE_SIZE; + hdr->tod = get_clock(); + get_cpu_id(&hdr->cpu_id); +} + +extern int sdias_init(void); + +static int __init zcore_init(void) +{ + unsigned char arch; + int rc; + + if (ipl_info.type != IPL_TYPE_FCP_DUMP) + return -ENODATA; + + zcore_dbf = debug_register("zcore", 4, 1, 4 * sizeof(long)); + debug_register_view(zcore_dbf, &debug_sprintf_view); + debug_set_level(zcore_dbf, 6); + + TRACE("devno: %x\n", ipl_info.data.fcp.dev_id.devno); + TRACE("wwpn: %llx\n", (unsigned long long) ipl_info.data.fcp.wwpn); + TRACE("lun: %llx\n", (unsigned long long) ipl_info.data.fcp.lun); + + rc = sdias_init(); + if (rc) + goto fail; + + rc = check_sdias(); + if (rc) { + ERROR_MSG("Dump initialization failed\n"); + goto fail; + } + + rc = memcpy_hsa_kernel(&arch, __LC_AR_MODE_ID, 1); + if (rc) { + ERROR_MSG("sdial memcpy for arch id failed\n"); + goto fail; + } + +#ifndef __s390x__ + if (arch == ARCH_S390X) { + ERROR_MSG("32 bit dumper can't dump 64 bit system!\n"); + rc = -EINVAL; + goto fail; + } +#endif + + rc = sys_info_init(arch); + if (rc) { + ERROR_MSG("arch init failed\n"); + goto fail; + } + + zcore_header_init(arch, &zcore_header); + + zcore_dir = debugfs_create_dir("zcore" , NULL); + if (!zcore_dir) { + rc = -ENOMEM; + goto fail; + } + zcore_file = debugfs_create_file("mem", S_IRUSR, zcore_dir, NULL, + &zcore_fops); + if (!zcore_file) { + debugfs_remove(zcore_dir); + rc = -ENOMEM; + goto fail; + } + hsa_available = 1; + return 0; + +fail: + diag308(DIAG308_REL_HSA, NULL); + return rc; +} + +extern void sdias_exit(void); + +static void __exit zcore_exit(void) +{ + debug_unregister(zcore_dbf); + sdias_exit(); + diag308(DIAG308_REL_HSA, NULL); +} + +MODULE_AUTHOR("Copyright IBM Corp. 2003,2007"); +MODULE_DESCRIPTION("zcore module for zfcpdump support"); +MODULE_LICENSE("GPL"); + +subsys_initcall(zcore_init); +module_exit(zcore_exit); diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile index c490c2a..cfaf77b 100644 --- a/drivers/s390/cio/Makefile +++ b/drivers/s390/cio/Makefile @@ -2,7 +2,7 @@ # Makefile for the S/390 common i/o drivers # -obj-y += airq.o blacklist.o chsc.o cio.o css.o +obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device_id.o device_pgid.o device_status.o obj-y += ccw_device.o cmf.o diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index d48e3ca..e5ccda6 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -71,19 +71,33 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ +static void ccwgroup_ungroup_callback(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + mutex_lock(&gdev->reg_mutex); + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); +} + static ssize_t ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccwgroup_device *gdev; + int rc; gdev = to_ccwgroupdev(dev); if (gdev->state != CCWGROUP_OFFLINE) return -EINVAL; - __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); - + /* Note that we cannot unregister the device from one of its + * attribute methods, so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); + if (rc) + count = rc; return count; } @@ -161,7 +175,8 @@ ccwgroup_create(struct device *root, return -ENOMEM; atomic_set(&gdev->onoff, 0); - + mutex_init(&gdev->reg_mutex); + mutex_lock(&gdev->reg_mutex); for (i = 0; i < argc; i++) { gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); @@ -171,12 +186,12 @@ ccwgroup_create(struct device *root, || gdev->cdev[i]->id.driver_info != gdev->cdev[0]->id.driver_info) { rc = -EINVAL; - goto free_dev; + goto error; } /* Don't allow a device to belong to more than one group. */ if (gdev->cdev[i]->dev.driver_data) { rc = -EINVAL; - goto free_dev; + goto error; } gdev->cdev[i]->dev.driver_data = gdev; } @@ -191,9 +206,8 @@ ccwgroup_create(struct device *root, gdev->cdev[0]->dev.bus_id); rc = device_register(&gdev->dev); - if (rc) - goto free_dev; + goto error; get_device(&gdev->dev); rc = device_create_file(&gdev->dev, &dev_attr_ungroup); @@ -204,6 +218,7 @@ ccwgroup_create(struct device *root, rc = __ccwgroup_create_symlinks(gdev); if (!rc) { + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); return 0; } @@ -212,19 +227,12 @@ ccwgroup_create(struct device *root, error: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { - put_device(&gdev->cdev[i]->dev); - gdev->cdev[i]->dev.driver_data = NULL; - } - put_device(&gdev->dev); - return rc; -free_dev: - for (i = 0; i < argc; i++) - if (gdev->cdev[i]) { if (gdev->cdev[i]->dev.driver_data == gdev) gdev->cdev[i]->dev.driver_data = NULL; put_device(&gdev->cdev[i]->dev); } - kfree(gdev); + mutex_unlock(&gdev->reg_mutex); + put_device(&gdev->dev); return rc; } @@ -410,8 +418,12 @@ ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) get_driver(&cdriver->driver); while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, __ccwgroup_match_all))) { - __ccwgroup_remove_symlinks(to_ccwgroupdev(dev)); + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + mutex_lock(&gdev->reg_mutex); + __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + mutex_unlock(&gdev->reg_mutex); put_device(dev); } put_driver(&cdriver->driver); @@ -432,8 +444,10 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) if (cdev->dev.driver_data) { gdev = (struct ccwgroup_device *)cdev->dev.driver_data; if (get_device(&gdev->dev)) { + mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) return gdev; + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } return NULL; @@ -453,6 +467,7 @@ ccwgroup_remove_ccwdev(struct ccw_device *cdev) if (gdev) { __ccwgroup_remove_symlinks(gdev); device_unregister(&gdev->dev); + mutex_unlock(&gdev->reg_mutex); put_device(&gdev->dev); } } diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c new file mode 100644 index 0000000..ac289e6 --- /dev/null +++ b/drivers/s390/cio/chp.c @@ -0,0 +1,683 @@ +/* + * drivers/s390/cio/chp.c + * + * Copyright IBM Corp. 1999,2007 + * Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) + * Arnd Bergmann (arndb@de.ibm.com) + * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/bug.h> +#include <linux/workqueue.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <asm/errno.h> +#include <asm/chpid.h> +#include <asm/sclp.h> + +#include "cio.h" +#include "css.h" +#include "ioasm.h" +#include "cio_debug.h" +#include "chp.h" + +#define to_channelpath(device) container_of(device, struct channel_path, dev) +#define CHP_INFO_UPDATE_INTERVAL 1*HZ + +enum cfg_task_t { + cfg_none, + cfg_configure, + cfg_deconfigure +}; + +/* Map for pending configure tasks. */ +static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; +static DEFINE_MUTEX(cfg_lock); +static int cfg_busy; + +/* Map for channel-path status. */ +static struct sclp_chp_info chp_info; +static DEFINE_MUTEX(info_lock); + +/* Time after which channel-path status may be outdated. */ +static unsigned long chp_info_expires; + +/* Workqueue to perform pending configure tasks. */ +static struct workqueue_struct *chp_wq; +static struct work_struct cfg_work; + +/* Wait queue for configure completion events. */ +static wait_queue_head_t cfg_wait_queue; + +/* Return channel_path struct for given chpid. */ +static inline struct channel_path *chpid_to_chp(struct chp_id chpid) +{ + return css[chpid.cssid]->chps[chpid.id]; +} + +/* Set vary state for given chpid. */ +static void set_chp_logically_online(struct chp_id chpid, int onoff) +{ + chpid_to_chp(chpid)->state = onoff; +} + +/* On succes return 0 if channel-path is varied offline, 1 if it is varied + * online. Return -ENODEV if channel-path is not registered. */ +int chp_get_status(struct chp_id chpid) +{ + return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); +} + +/** + * chp_get_sch_opm - return opm for subchannel + * @sch: subchannel + * + * Calculate and return the operational path mask (opm) based on the chpids + * used by the subchannel and the status of the associated channel-paths. + */ +u8 chp_get_sch_opm(struct subchannel *sch) +{ + struct chp_id chpid; + int opm; + int i; + + opm = 0; + chp_id_init(&chpid); + for (i=0; i < 8; i++) { + opm <<= 1; + chpid.id = sch->schib.pmcw.chpid[i]; + if (chp_get_status(chpid) != 0) + opm |= 1; + } + return opm; +} + +/** + * chp_is_registered - check if a channel-path is registered + * @chpid: channel-path ID + * + * Return non-zero if a channel-path with the given chpid is registered, + * zero otherwise. + */ +int chp_is_registered(struct chp_id chpid) +{ + return chpid_to_chp(chpid) != NULL; +} + +/* + * Function: s390_vary_chpid + * Varies the specified chpid online or offline + */ +static int s390_vary_chpid(struct chp_id chpid, int on) +{ + char dbf_text[15]; + int status; + + sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, + chpid.id); + CIO_TRACE_EVENT( 2, dbf_text); + + status = chp_get_status(chpid); + if (status < 0) { + printk(KERN_ERR "Can't vary unknown chpid %x.%02x\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + if (!on && !status) { + printk(KERN_ERR "chpid %x.%02x is already offline\n", + chpid.cssid, chpid.id); + return -EINVAL; + } + + set_chp_logically_online(chpid, on); + chsc_chp_vary(chpid, on); + return 0; +} + +/* + * Channel measurement related functions + */ +static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + if (!chp->cmg_chars) + return 0; + + size = sizeof(struct cmg_chars); + + if (off > size) + return 0; + if (off + count > size) + count = size - off; + memcpy(buf, chp->cmg_chars + off, count); + return count; +} + +static struct bin_attribute chp_measurement_chars_attr = { + .attr = { + .name = "measurement_chars", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_chars), + .read = chp_measurement_chars_read, +}; + +static void chp_measurement_copy_block(struct cmg_entry *buf, + struct channel_subsystem *css, + struct chp_id chpid) +{ + void *area; + struct cmg_entry *entry, reference_buf; + int idx; + + if (chpid.id < 128) { + area = css->cub_addr1; + idx = chpid.id; + } else { + area = css->cub_addr2; + idx = chpid.id - 128; + } + entry = area + (idx * sizeof(struct cmg_entry)); + do { + memcpy(buf, entry, sizeof(*entry)); + memcpy(&reference_buf, entry, sizeof(*entry)); + } while (reference_buf.values[0] != buf->values[0]); +} + +static ssize_t chp_measurement_read(struct kobject *kobj, char *buf, + loff_t off, size_t count) +{ + struct channel_path *chp; + struct channel_subsystem *css; + unsigned int size; + + chp = to_channelpath(container_of(kobj, struct device, kobj)); + css = to_css(chp->dev.parent); + + size = sizeof(struct cmg_entry); + + /* Only allow single reads. */ + if (off || count < size) + return 0; + chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); + count = size; + return count; +} + +static struct bin_attribute chp_measurement_attr = { + .attr = { + .name = "measurement", + .mode = S_IRUSR, + .owner = THIS_MODULE, + }, + .size = sizeof(struct cmg_entry), + .read = chp_measurement_read, +}; + +void chp_remove_cmg_attr(struct channel_path *chp) +{ + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + device_remove_bin_file(&chp->dev, &chp_measurement_attr); +} + +int chp_add_cmg_attr(struct channel_path *chp) +{ + int ret; + + ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); + if (ret) + return ret; + ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); + if (ret) + device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); + return ret; +} + +/* + * Files for the channel path entries. + */ +static ssize_t chp_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return (chp_get_status(chp->chpid) ? sprintf(buf, "online\n") : + sprintf(buf, "offline\n")); +} + +static ssize_t chp_status_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp = container_of(dev, struct channel_path, dev); + char cmd[10]; + int num_args; + int error; + + num_args = sscanf(buf, "%5s", cmd); + if (!num_args) + return count; + + if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) + error = s390_vary_chpid(cp->chpid, 1); + else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) + error = s390_vary_chpid(cp->chpid, 0); + else + error = -EINVAL; + + return error < 0 ? error : count; + +} + +static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); + +static ssize_t chp_configure_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *cp; + int status; + + cp = container_of(dev, struct channel_path, dev); + status = chp_info_get_status(cp->chpid); + if (status < 0) + return status; + + return snprintf(buf, PAGE_SIZE, "%d\n", status); +} + +static int cfg_wait_idle(void); + +static ssize_t chp_configure_write(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct channel_path *cp; + int val; + char delim; + + if (sscanf(buf, "%d %c", &val, &delim) != 1) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + cp = container_of(dev, struct channel_path, dev); + chp_cfg_schedule(cp->chpid, val); + cfg_wait_idle(); + + return count; +} + +static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); + +static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = container_of(dev, struct channel_path, dev); + + if (!chp) + return 0; + return sprintf(buf, "%x\n", chp->desc.desc); +} + +static DEVICE_ATTR(type, 0444, chp_type_show, NULL); + +static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->cmg == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->cmg); +} + +static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); + +static ssize_t chp_shared_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct channel_path *chp = to_channelpath(dev); + + if (!chp) + return 0; + if (chp->shared == -1) /* channel measurements not available */ + return sprintf(buf, "unknown\n"); + return sprintf(buf, "%x\n", chp->shared); +} + +static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); + +static struct attribute * chp_attrs[] = { + &dev_attr_status.attr, + &dev_attr_configure.attr, + &dev_attr_type.attr, + &dev_attr_cmg.attr, + &dev_attr_shared.attr, + NULL, +}; + +static struct attribute_group chp_attr_group = { + .attrs = chp_attrs, +}; + +static void chp_release(struct device *dev) +{ + struct channel_path *cp; + + cp = container_of(dev, struct channel_path, dev); + kfree(cp); +} + +/** + * chp_new - register a new channel-path + * @chpid - channel-path ID + * + * Create and register data structure representing new channel-path. Return + * zero on success, non-zero otherwise. + */ +int chp_new(struct chp_id chpid) +{ + struct channel_path *chp; + int ret; + + if (chp_is_registered(chpid)) + return 0; + chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); + if (!chp) + return -ENOMEM; + + /* fill in status, etc. */ + chp->chpid = chpid; + chp->state = 1; + chp->dev.parent = &css[chpid.cssid]->device; + chp->dev.release = chp_release; + snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp%x.%02x", chpid.cssid, + chpid.id); + + /* Obtain channel path description and fill it in. */ + ret = chsc_determine_channel_path_description(chpid, &chp->desc); + if (ret) + goto out_free; + if ((chp->desc.flags & 0x80) == 0) { + ret = -ENODEV; + goto out_free; + } + /* Get channel-measurement characteristics. */ + if (css_characteristics_avail && css_chsc_characteristics.scmc + && css_chsc_characteristics.secm) { + ret = chsc_get_channel_measurement_chars(chp); + if (ret) + goto out_free; + } else { + static int msg_done; + + if (!msg_done) { + printk(KERN_WARNING "cio: Channel measurements not " + "available, continuing.\n"); + msg_done = 1; + } + chp->cmg = -1; + } + + /* make it known to the system */ + ret = device_register(&chp->dev); + if (ret) { + printk(KERN_WARNING "%s: could not register %x.%02x\n", + __func__, chpid.cssid, chpid.id); + goto out_free; + } + ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); + if (ret) { + device_unregister(&chp->dev); + goto out_free; + } + mutex_lock(&css[chpid.cssid]->mutex); + if (css[chpid.cssid]->cm_enabled) { + ret = chp_add_cmg_attr(chp); + if (ret) { + sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); + device_unregister(&chp->dev); + mutex_unlock(&css[chpid.cssid]->mutex); + goto out_free; + } + } + css[chpid.cssid]->chps[chpid.id] = chp; + mutex_unlock(&css[chpid.cssid]->mutex); + return ret; +out_free: + kfree(chp); + return ret; +} + +/** + * chp_get_chp_desc - return newly allocated channel-path description + * @chpid: channel-path ID + * + * On success return a newly allocated copy of the channel-path description + * data associated with the given channel-path ID. Return %NULL on error. + */ +void *chp_get_chp_desc(struct chp_id chpid) +{ + struct channel_path *chp; + struct channel_path_desc *desc; + + chp = chpid_to_chp(chpid); + if (!chp) + return NULL; + desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); + if (!desc) + return NULL; + memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); + return desc; +} + +/** + * chp_process_crw - process channel-path status change + * @id: channel-path ID number + * @status: non-zero if channel-path has become available, zero otherwise + * + * Handle channel-report-words indicating that the status of a channel-path + * has changed. + */ +void chp_process_crw(int id, int status) +{ + struct chp_id chpid; + + chp_id_init(&chpid); + chpid.id = id; + if (status) { + if (!chp_is_registered(chpid)) + chp_new(chpid); + chsc_chp_online(chpid); + } else + chsc_chp_offline(chpid); +} + +static inline int info_bit_num(struct chp_id id) +{ + return id.id + id.cssid * (__MAX_CHPID + 1); +} + +/* Force chp_info refresh on next call to info_validate(). */ +static void info_expire(void) +{ + mutex_lock(&info_lock); + chp_info_expires = jiffies - 1; + mutex_unlock(&info_lock); +} + +/* Ensure that chp_info is up-to-date. */ +static int info_update(void) +{ + int rc; + + mutex_lock(&info_lock); + rc = 0; + if (time_after(jiffies, chp_info_expires)) { + /* Data is too old, update. */ + rc = sclp_chp_read_info(&chp_info); + chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; + } + mutex_unlock(&info_lock); + + return rc; +} + +/** + * chp_info_get_status - retrieve configure status of a channel-path + * @chpid: channel-path ID + * + * On success, return 0 for standby, 1 for configured, 2 for reserved, + * 3 for not recognized. Return negative error code on error. + */ +int chp_info_get_status(struct chp_id chpid) +{ + int rc; + int bit; + + rc = info_update(); + if (rc) + return rc; + + bit = info_bit_num(chpid); + mutex_lock(&info_lock); + if (!chp_test_bit(chp_info.recognized, bit)) + rc = CHP_STATUS_NOT_RECOGNIZED; + else if (chp_test_bit(chp_info.configured, bit)) + rc = CHP_STATUS_CONFIGURED; + else if (chp_test_bit(chp_info.standby, bit)) + rc = CHP_STATUS_STANDBY; + else + rc = CHP_STATUS_RESERVED; + mutex_unlock(&info_lock); + + return rc; +} + +/* Return configure task for chpid. */ +static enum cfg_task_t cfg_get_task(struct chp_id chpid) +{ + return chp_cfg_task[chpid.cssid][chpid.id]; +} + +/* Set configure task for chpid. */ +static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) +{ + chp_cfg_task[chpid.cssid][chpid.id] = cfg; +} + +/* Perform one configure/deconfigure request. Reschedule work function until + * last request. */ +static void cfg_func(struct work_struct *work) +{ + struct chp_id chpid; + enum cfg_task_t t; + + mutex_lock(&cfg_lock); + t = cfg_none; + chp_id_for_each(&chpid) { + t = cfg_get_task(chpid); + if (t != cfg_none) { + cfg_set_task(chpid, cfg_none); + break; + } + } + mutex_unlock(&cfg_lock); + + switch (t) { + case cfg_configure: + sclp_chp_configure(chpid); + info_expire(); + chsc_chp_online(chpid); + break; + case cfg_deconfigure: + sclp_chp_deconfigure(chpid); + info_expire(); + chsc_chp_offline(chpid); + break; + case cfg_none: + /* Get updated information after last change. */ + info_update(); + mutex_lock(&cfg_lock); + cfg_busy = 0; + mutex_unlock(&cfg_lock); + wake_up_interruptible(&cfg_wait_queue); + return; + } + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_schedule - schedule chpid configuration request + * @chpid - channel-path ID + * @configure - Non-zero for configure, zero for deconfigure + * + * Schedule a channel-path configuration/deconfiguration request. + */ +void chp_cfg_schedule(struct chp_id chpid, int configure) +{ + CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, + configure); + mutex_lock(&cfg_lock); + cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); + cfg_busy = 1; + mutex_unlock(&cfg_lock); + queue_work(chp_wq, &cfg_work); +} + +/** + * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request + * @chpid - channel-path ID + * + * Cancel an active channel-path deconfiguration request if it has not yet + * been performed. + */ +void chp_cfg_cancel_deconfigure(struct chp_id chpid) +{ + CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); + mutex_lock(&cfg_lock); + if (cfg_get_task(chpid) == cfg_deconfigure) + cfg_set_task(chpid, cfg_none); + mutex_unlock(&cfg_lock); +} + +static int cfg_wait_idle(void) +{ + if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) + return -ERESTARTSYS; + return 0; +} + +static int __init chp_init(void) +{ + struct chp_id chpid; + + chp_wq = create_singlethread_workqueue("cio_chp"); + if (!chp_wq) + return -ENOMEM; + INIT_WORK(&cfg_work, cfg_func); + init_waitqueue_head(&cfg_wait_queue); + if (info_update()) + return 0; + /* Register available channel-paths. */ + chp_id_for_each(&chpid) { + if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) + chp_new(chpid); + } + + return 0; +} + +subsys_initcall(chp_init); diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h new file mode 100644 index 0000000..6528656 --- /dev/null +++ b/drivers/s390/cio/chp.h @@ -0,0 +1,53 @@ +/* + * drivers/s390/cio/chp.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#ifndef S390_CHP_H +#define S390_CHP_H S390_CHP_H + +#include <linux/types.h> +#include <linux/device.h> +#include <asm/chpid.h> +#include "chsc.h" + +#define CHP_STATUS_STANDBY 0 +#define CHP_STATUS_CONFIGURED 1 +#define CHP_STATUS_RESERVED 2 +#define CHP_STATUS_NOT_RECOGNIZED 3 + +static inline int chp_test_bit(u8 *bitmap, int num) +{ + int byte = num >> 3; + int mask = 128 >> (num & 7); + + return (bitmap[byte] & mask) ? 1 : 0; +} + + +struct channel_path { + struct chp_id chpid; + int state; + struct channel_path_desc desc; + /* Channel-measurement related stuff: */ + int cmg; + int shared; + void *cmg_chars; + struct device dev; +}; + +int chp_get_status(struct chp_id chpid); +u8 chp_get_sch_opm(struct subchannel *sch); +int chp_is_registered(struct chp_id chpid); +void *chp_get_chp_desc(struct chp_id chpid); +void chp_process_crw(int id, int available); +void chp_remove_cmg_attr(struct channel_path *chp); +int chp_add_cmg_attr(struct channel_path *chp); +int chp_new(struct chp_id chpid); +void chp_cfg_schedule(struct chp_id chpid, int configure); +void chp_cfg_cancel_deconfigure(struct chp_id chpid); +int chp_info_get_status(struct chp_id chpid); + +#endif /* S390_CHP_H */ diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 6f05a44..ea92ac4 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -15,202 +15,124 @@ #include <linux/device.h> #include <asm/cio.h> +#include <asm/chpid.h> #include "css.h" #include "cio.h" #include "cio_debug.h" #include "ioasm.h" +#include "chp.h" #include "chsc.h" static void *sei_page; -static int new_channel_path(int chpid); - -static inline void -set_chp_logically_online(int chp, int onoff) -{ - css[0]->chps[chp]->state = onoff; -} - -static int -get_chp_status(int chp) -{ - return (css[0]->chps[chp] ? css[0]->chps[chp]->state : -ENODEV); -} - -void -chsc_validate_chpids(struct subchannel *sch) -{ - int mask, chp; - - for (chp = 0; chp <= 7; chp++) { - mask = 0x80 >> chp; - if (!get_chp_status(sch->schib.pmcw.chpid[chp])) - /* disable using this path */ - sch->opm &= ~mask; - } -} - -void -chpid_is_actually_online(int chp) -{ - int state; - - state = get_chp_status(chp); - if (state < 0) { - need_rescan = 1; - queue_work(slow_path_wq, &slow_path_work); - } else - WARN_ON(!state); -} +struct chsc_ssd_area { + struct chsc_header request; + u16 :10; + u16 ssid:2; + u16 :4; + u16 f_sch; /* first subchannel */ + u16 :16; + u16 l_sch; /* last subchannel */ + u32 :32; + struct chsc_header response; + u32 :32; + u8 sch_valid : 1; + u8 dev_valid : 1; + u8 st : 3; /* subchannel type */ + u8 zeroes : 3; + u8 unit_addr; /* unit address */ + u16 devno; /* device number */ + u8 path_mask; + u8 fla_valid_mask; + u16 sch; /* subchannel */ + u8 chpid[8]; /* chpids 0-7 */ + u16 fla[8]; /* full link addresses 0-7 */ +} __attribute__ ((packed)); -/* FIXME: this is _always_ called for every subchannel. shouldn't we - * process more than one at a time? */ -static int -chsc_get_sch_desc_irq(struct subchannel *sch, void *page) +int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd) { - int ccode, j; - - struct { - struct chsc_header request; - u16 reserved1a:10; - u16 ssid:2; - u16 reserved1b:4; - u16 f_sch; /* first subchannel */ - u16 reserved2; - u16 l_sch; /* last subchannel */ - u32 reserved3; - struct chsc_header response; - u32 reserved4; - u8 sch_valid : 1; - u8 dev_valid : 1; - u8 st : 3; /* subchannel type */ - u8 zeroes : 3; - u8 unit_addr; /* unit address */ - u16 devno; /* device number */ - u8 path_mask; - u8 fla_valid_mask; - u16 sch; /* subchannel */ - u8 chpid[8]; /* chpids 0-7 */ - u16 fla[8]; /* full link addresses 0-7 */ - } __attribute__ ((packed)) *ssd_area; - - ssd_area = page; + unsigned long page; + struct chsc_ssd_area *ssd_area; + int ccode; + int ret; + int i; + int mask; + page = get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!page) + return -ENOMEM; + ssd_area = (struct chsc_ssd_area *) page; ssd_area->request.length = 0x0010; ssd_area->request.code = 0x0004; - - ssd_area->ssid = sch->schid.ssid; - ssd_area->f_sch = sch->schid.sch_no; - ssd_area->l_sch = sch->schid.sch_no; + ssd_area->ssid = schid.ssid; + ssd_area->f_sch = schid.sch_no; + ssd_area->l_sch = schid.sch_no; ccode = chsc(ssd_area); + /* Check response. */ if (ccode > 0) { - pr_debug("chsc returned with ccode = %d\n", ccode); - return (ccode == 3) ? -ENODEV : -EBUSY; + ret = (ccode == 3) ? -ENODEV : -EBUSY; + goto out_free; } - - switch (ssd_area->response.code) { - case 0x0001: /* everything ok */ - break; - case 0x0002: - CIO_CRW_EVENT(2, "Invalid command!\n"); - return -EINVAL; - case 0x0003: - CIO_CRW_EVENT(2, "Error in chsc request block!\n"); - return -EINVAL; - case 0x0004: - CIO_CRW_EVENT(2, "Model does not provide ssd\n"); - return -EOPNOTSUPP; - default: - CIO_CRW_EVENT(2, "Unknown CHSC response %d\n", + if (ssd_area->response.code != 0x0001) { + CIO_MSG_EVENT(2, "chsc: ssd failed for 0.%x.%04x (rc=%04x)\n", + schid.ssid, schid.sch_no, ssd_area->response.code); - return -EIO; + ret = -EIO; + goto out_free; } - - /* - * ssd_area->st stores the type of the detected - * subchannel, with the following definitions: - * - * 0: I/O subchannel: All fields have meaning - * 1: CHSC subchannel: Only sch_val, st and sch - * have meaning - * 2: Message subchannel: All fields except unit_addr - * have meaning - * 3: ADM subchannel: Only sch_val, st and sch - * have meaning - * - * Other types are currently undefined. - */ - if (ssd_area->st > 3) { /* uhm, that looks strange... */ - CIO_CRW_EVENT(0, "Strange subchannel type %d" - " for sch 0.%x.%04x\n", ssd_area->st, - sch->schid.ssid, sch->schid.sch_no); - /* - * There may have been a new subchannel type defined in the - * time since this code was written; since we don't know which - * fields have meaning and what to do with it we just jump out - */ - return 0; - } else { - const char *type[4] = {"I/O", "chsc", "message", "ADM"}; - CIO_CRW_EVENT(6, "ssd: sch 0.%x.%04x is %s subchannel\n", - sch->schid.ssid, sch->schid.sch_no, - type[ssd_area->st]); - - sch->ssd_info.valid = 1; - sch->ssd_info.type = ssd_area->st; + if (!ssd_area->sch_valid) { + ret = -ENODEV; + goto out_free; } - - if (ssd_area->st == 0 || ssd_area->st == 2) { - for (j = 0; j < 8; j++) { - if (!((0x80 >> j) & ssd_area->path_mask & - ssd_area->fla_valid_mask)) - continue; - sch->ssd_info.chpid[j] = ssd_area->chpid[j]; - sch->ssd_info.fla[j] = ssd_area->fla[j]; + /* Copy data */ + ret = 0; + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + if ((ssd_area->st != 0) && (ssd_area->st != 2)) + goto out_free; + ssd->path_mask = ssd_area->path_mask; + ssd->fla_valid_mask = ssd_area->fla_valid_mask; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd_area->path_mask & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = ssd_area->chpid[i]; } + if (ssd_area->fla_valid_mask & mask) + ssd->fla[i] = ssd_area->fla[i]; } - return 0; +out_free: + free_page(page); + return ret; } -int -css_get_ssd_info(struct subchannel *sch) +static int check_for_io_on_path(struct subchannel *sch, int mask) { - int ret; - void *page; + int cc; - page = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA); - if (!page) - return -ENOMEM; - spin_lock_irq(sch->lock); - ret = chsc_get_sch_desc_irq(sch, page); - if (ret) { - static int cio_chsc_err_msg; - - if (!cio_chsc_err_msg) { - printk(KERN_ERR - "chsc_get_sch_descriptions:" - " Error %d while doing chsc; " - "processing some machine checks may " - "not work\n", ret); - cio_chsc_err_msg = 1; - } - } - spin_unlock_irq(sch->lock); - free_page((unsigned long)page); - if (!ret) { - int j, chpid, mask; - /* Allocate channel path structures, if needed. */ - for (j = 0; j < 8; j++) { - mask = 0x80 >> j; - chpid = sch->ssd_info.chpid[j]; - if ((sch->schib.pmcw.pim & mask) && - (get_chp_status(chpid) < 0)) - new_channel_path(chpid); - } + cc = stsch(sch->schid, &sch->schib); + if (cc) + return 0; + if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == mask) + return 1; + return 0; +} + +static void terminate_internal_io(struct subchannel *sch) +{ + if (cio_clear(sch)) { + /* Recheck device in case clear failed. */ + sch->lpm = 0; + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); + return; } - return ret; + /* Request retry of internal operation. */ + device_set_intretry(sch); + /* Call handler. */ + if (sch->driver && sch->driver->termination) + sch->driver->termination(&sch->dev); } static int @@ -219,7 +141,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) int j; int mask; struct subchannel *sch; - struct channel_path *chpid; + struct chp_id *chpid; struct schib schib; sch = to_subchannel(dev); @@ -243,106 +165,50 @@ s390_subchannel_remove_chpid(struct device *dev, void *data) if (sch->schib.pmcw.pim == 0x80) goto out_unreg; - if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) && - (sch->schib.scsw.actl & SCSW_ACTL_SCHACT) && - (sch->schib.pmcw.lpum == mask)) { - int cc; - - cc = cio_clear(sch); - if (cc == -ENODEV) + if (check_for_io_on_path(sch, mask)) { + if (device_is_online(sch)) + device_kill_io(sch); + else { + terminate_internal_io(sch); + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + } + } else { + /* trigger path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); + else if (sch->lpm == mask) goto out_unreg; - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); - goto out_unlock; } - /* trigger path verification. */ - if (sch->driver && sch->driver->verify) - sch->driver->verify(&sch->dev); - else if (sch->lpm == mask) - goto out_unreg; -out_unlock: spin_unlock_irq(sch->lock); return 0; + out_unreg: - spin_unlock_irq(sch->lock); sch->lpm = 0; - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } + spin_unlock_irq(sch->lock); + css_schedule_eval(sch->schid); return 0; } -static void -s390_set_chpid_offline( __u8 chpid) +void chsc_chp_offline(struct chp_id chpid) { char dbf_txt[15]; - struct device *dev; - sprintf(dbf_txt, "chpr%x", chpid); + sprintf(dbf_txt, "chpr%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - if (get_chp_status(chpid) <= 0) + if (chp_get_status(chpid) <= 0) return; - dev = get_device(&css[0]->chps[chpid]->dev); - bus_for_each_dev(&css_bus_type, NULL, to_channelpath(dev), + bus_for_each_dev(&css_bus_type, NULL, &chpid, s390_subchannel_remove_chpid); - - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); - put_device(dev); -} - -struct res_acc_data { - struct channel_path *chp; - u32 fla_mask; - u16 fla; -}; - -static int -s390_process_res_acc_sch(struct res_acc_data *res_data, struct subchannel *sch) -{ - int found; - int chp; - int ccode; - - found = 0; - for (chp = 0; chp <= 7; chp++) - /* - * check if chpid is in information updated by ssd - */ - if (sch->ssd_info.valid && - sch->ssd_info.chpid[chp] == res_data->chp->id && - (sch->ssd_info.fla[chp] & res_data->fla_mask) - == res_data->fla) { - found = 1; - break; - } - - if (found == 0) - return 0; - - /* - * Do a stsch to update our subchannel structure with the - * new path information and eventually check for logically - * offline chpids. - */ - ccode = stsch(sch->schid, &sch->schib); - if (ccode > 0) - return 0; - - return 0x80 >> chp; } static int s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; /* * We don't know the device yet, but since a path * may be available now to the device we'll have @@ -353,14 +219,35 @@ s390_process_res_acc_new_sch(struct subchannel_id schid) */ if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; + css_schedule_eval(schid); + return 0; +} + +struct res_acc_data { + struct chp_id chpid; + u32 fla_mask; + u16 fla; +}; + +static int get_res_chpid_mask(struct chsc_ssd_info *ssd, + struct res_acc_data *data) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (!(ssd->path_mask & mask)) + continue; + if (!chp_id_is_equal(&ssd->chpid[i], &data->chpid)) + continue; + if ((ssd->fla_valid_mask & mask) && + ((ssd->fla[i] & data->fla_mask) != data->fla)) + continue; + return mask; } return 0; } @@ -379,14 +266,11 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) return s390_process_res_acc_new_sch(schid); spin_lock_irq(sch->lock); - - chp_mask = s390_process_res_acc_sch(res_data, sch); - - if (chp_mask == 0) { - spin_unlock_irq(sch->lock); - put_device(&sch->dev); - return 0; - } + chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); + if (chp_mask == 0) + goto out; + if (stsch(sch->schid, &sch->schib)) + goto out; old_lpm = sch->lpm; sch->lpm = ((sch->schib.pmcw.pim & sch->schib.pmcw.pam & @@ -396,20 +280,18 @@ __s390_process_res_acc(struct subchannel_id schid, void *data) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); - +out: spin_unlock_irq(sch->lock); put_device(&sch->dev); return 0; } - -static int -s390_process_res_acc (struct res_acc_data *res_data) +static void s390_process_res_acc (struct res_acc_data *res_data) { - int rc; char dbf_txt[15]; - sprintf(dbf_txt, "accpr%x", res_data->chp->id); + sprintf(dbf_txt, "accpr%x.%02x", res_data->chpid.cssid, + res_data->chpid.id); CIO_TRACE_EVENT( 2, dbf_txt); if (res_data->fla != 0) { sprintf(dbf_txt, "fla%x", res_data->fla); @@ -423,12 +305,7 @@ s390_process_res_acc (struct res_acc_data *res_data) * The more information we have (info), the less scanning * will we have to do. */ - rc = for_each_subchannel(__s390_process_res_acc, res_data); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - else if (rc != -EAGAIN) - rc = 0; - return rc; + for_each_subchannel(__s390_process_res_acc, res_data); } static int @@ -480,43 +357,45 @@ struct chsc_sei_area { /* ccdf has to be big enough for a link-incident record */ } __attribute__ ((packed)); -static int chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) +static void chsc_process_sei_link_incident(struct chsc_sei_area *sei_area) { - int chpid; + struct chp_id chpid; + int id; CIO_CRW_EVENT(4, "chsc: link incident (rs=%02x, rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; - chpid = __get_chpid_from_lir(sei_area->ccdf); - if (chpid < 0) + return; + id = __get_chpid_from_lir(sei_area->ccdf); + if (id < 0) CIO_CRW_EVENT(4, "chsc: link incident - invalid LIR\n"); - else - s390_set_chpid_offline(chpid); - - return 0; + else { + chp_id_init(&chpid); + chpid.id = id; + chsc_chp_offline(chpid); + } } -static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) +static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) { struct res_acc_data res_data; - struct device *dev; + struct chp_id chpid; int status; - int rc; CIO_CRW_EVENT(4, "chsc: resource accessibility event (rs=%02x, " "rs_id=%04x)\n", sei_area->rs, sei_area->rsid); if (sei_area->rs != 4) - return 0; + return; + chp_id_init(&chpid); + chpid.id = sei_area->rsid; /* allocate a new channel path structure, if needed */ - status = get_chp_status(sei_area->rsid); + status = chp_get_status(chpid); if (status < 0) - new_channel_path(sei_area->rsid); + chp_new(chpid); else if (!status) - return 0; - dev = get_device(&css[0]->chps[sei_area->rsid]->dev); + return; memset(&res_data, 0, sizeof(struct res_acc_data)); - res_data.chp = to_channelpath(dev); + res_data.chpid = chpid; if ((sei_area->vf & 0xc0) != 0) { res_data.fla = sei_area->fla; if ((sei_area->vf & 0xc0) == 0xc0) @@ -526,51 +405,82 @@ static int chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) /* link address */ res_data.fla_mask = 0xff00; } - rc = s390_process_res_acc(&res_data); - put_device(dev); - - return rc; + s390_process_res_acc(&res_data); } -static int chsc_process_sei(struct chsc_sei_area *sei_area) +struct chp_config_data { + u8 map[32]; + u8 op; + u8 pc; +}; + +static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area) { - int rc; + struct chp_config_data *data; + struct chp_id chpid; + int num; + + CIO_CRW_EVENT(4, "chsc: channel-path-configuration notification\n"); + if (sei_area->rs != 0) + return; + data = (struct chp_config_data *) &(sei_area->ccdf); + chp_id_init(&chpid); + for (num = 0; num <= __MAX_CHPID; num++) { + if (!chp_test_bit(data->map, num)) + continue; + chpid.id = num; + printk(KERN_WARNING "cio: processing configure event %d for " + "chpid %x.%02x\n", data->op, chpid.cssid, chpid.id); + switch (data->op) { + case 0: + chp_cfg_schedule(chpid, 1); + break; + case 1: + chp_cfg_schedule(chpid, 0); + break; + case 2: + chp_cfg_cancel_deconfigure(chpid); + break; + } + } +} +static void chsc_process_sei(struct chsc_sei_area *sei_area) +{ /* Check if we might have lost some information. */ - if (sei_area->flags & 0x40) + if (sei_area->flags & 0x40) { CIO_CRW_EVENT(2, "chsc: event overflow\n"); + css_schedule_eval_all(); + } /* which kind of information was stored? */ - rc = 0; switch (sei_area->cc) { case 1: /* link incident*/ - rc = chsc_process_sei_link_incident(sei_area); + chsc_process_sei_link_incident(sei_area); break; case 2: /* i/o resource accessibiliy */ - rc = chsc_process_sei_res_acc(sei_area); + chsc_process_sei_res_acc(sei_area); + break; + case 8: /* channel-path-configuration notification */ + chsc_process_sei_chp_config(sei_area); break; default: /* other stuff */ CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n", sei_area->cc); break; } - - return rc; } -int chsc_process_crw(void) +void chsc_process_crw(void) { struct chsc_sei_area *sei_area; - int ret; - int rc; if (!sei_page) - return 0; + return; /* Access to sei_page is serialized through machine check handler * thread, so no need for locking. */ sei_area = sei_page; CIO_TRACE_EVENT( 2, "prcss"); - ret = 0; do { memset(sei_area, 0, sizeof(*sei_area)); sei_area->request.length = 0x0010; @@ -580,37 +490,26 @@ int chsc_process_crw(void) if (sei_area->response.code == 0x0001) { CIO_CRW_EVENT(4, "chsc: sei successful\n"); - rc = chsc_process_sei(sei_area); - if (rc) - ret = rc; + chsc_process_sei(sei_area); } else { CIO_CRW_EVENT(2, "chsc: sei failed (rc=%04x)\n", sei_area->response.code); - ret = 0; break; } } while (sei_area->flags & 0x80); - - return ret; } static int __chp_add_new_sch(struct subchannel_id schid) { struct schib schib; - int ret; if (stsch_err(schid, &schib)) /* We're through */ - return need_rescan ? -EAGAIN : -ENXIO; + return -ENXIO; /* Put it on the slow path. */ - ret = css_enqueue_subchannel_slow(schid); - if (ret) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } @@ -619,10 +518,10 @@ static int __chp_add(struct subchannel_id schid, void *data) { int i, mask; - struct channel_path *chp; + struct chp_id *chpid; struct subchannel *sch; - chp = data; + chpid = data; sch = get_subchannel_by_schid(schid); if (!sch) /* Check if the subchannel is now available. */ @@ -631,7 +530,7 @@ __chp_add(struct subchannel_id schid, void *data) for (i=0; i<8; i++) { mask = 0x80 >> i; if ((sch->schib.pmcw.pim & mask) && - (sch->schib.pmcw.chpid[i] == chp->id)) { + (sch->schib.pmcw.chpid[i] == chpid->id)) { if (stsch(sch->schid, &sch->schib) != 0) { /* Endgame. */ spin_unlock_irq(sch->lock); @@ -657,122 +556,58 @@ __chp_add(struct subchannel_id schid, void *data) return 0; } -static int -chp_add(int chpid) +void chsc_chp_online(struct chp_id chpid) { - int rc; char dbf_txt[15]; - struct device *dev; - if (!get_chp_status(chpid)) - return 0; /* no need to do the rest */ - - sprintf(dbf_txt, "cadd%x", chpid); + sprintf(dbf_txt, "cadd%x.%02x", chpid.cssid, chpid.id); CIO_TRACE_EVENT(2, dbf_txt); - dev = get_device(&css[0]->chps[chpid]->dev); - rc = for_each_subchannel(__chp_add, to_channelpath(dev)); - if (css_slow_subchannels_exist()) - rc = -EAGAIN; - if (rc != -EAGAIN) - rc = 0; - put_device(dev); - return rc; + if (chp_get_status(chpid) != 0) + for_each_subchannel(__chp_add, &chpid); } -/* - * Handling of crw machine checks with channel path source. - */ -int -chp_process_crw(int chpid, int on) -{ - if (on == 0) { - /* Path has gone. We use the link incident routine.*/ - s390_set_chpid_offline(chpid); - return 0; /* De-register is async anyway. */ - } - /* - * Path has come. Allocate a new channel path structure, - * if needed. - */ - if (get_chp_status(chpid) < 0) - new_channel_path(chpid); - /* Avoid the extra overhead in process_rec_acc. */ - return chp_add(chpid); -} - -static int check_for_io_on_path(struct subchannel *sch, int index) -{ - int cc; - - cc = stsch(sch->schid, &sch->schib); - if (cc) - return 0; - if (sch->schib.scsw.actl && sch->schib.pmcw.lpum == (0x80 >> index)) - return 1; - return 0; -} - -static void terminate_internal_io(struct subchannel *sch) -{ - if (cio_clear(sch)) { - /* Recheck device in case clear failed. */ - sch->lpm = 0; - if (device_trigger_verify(sch) != 0) { - if(css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return; - } - /* Request retry of internal operation. */ - device_set_intretry(sch); - /* Call handler. */ - if (sch->driver && sch->driver->termination) - sch->driver->termination(&sch->dev); -} - -static void -__s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) +static void __s390_subchannel_vary_chpid(struct subchannel *sch, + struct chp_id chpid, int on) { int chp, old_lpm; + int mask; unsigned long flags; - if (!sch->ssd_info.valid) - return; - spin_lock_irqsave(sch->lock, flags); old_lpm = sch->lpm; for (chp = 0; chp < 8; chp++) { - if (sch->ssd_info.chpid[chp] != chpid) + mask = 0x80 >> chp; + if (!(sch->ssd_info.path_mask & mask)) + continue; + if (!chp_id_is_equal(&sch->ssd_info.chpid[chp], &chpid)) continue; if (on) { - sch->opm |= (0x80 >> chp); - sch->lpm |= (0x80 >> chp); + sch->opm |= mask; + sch->lpm |= mask; if (!old_lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; } - sch->opm &= ~(0x80 >> chp); - sch->lpm &= ~(0x80 >> chp); - if (check_for_io_on_path(sch, chp)) { + sch->opm &= ~mask; + sch->lpm &= ~mask; + if (check_for_io_on_path(sch, mask)) { if (device_is_online(sch)) /* Path verification is done after killing. */ device_kill_io(sch); - else + else { /* Kill and retry internal I/O. */ terminate_internal_io(sch); - } else if (!sch->lpm) { - if (device_trigger_verify(sch) != 0) { - if (css_enqueue_subchannel_slow(sch->schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } + /* Re-start path verification. */ + if (sch->driver && sch->driver->verify) + sch->driver->verify(&sch->dev); } + } else if (!sch->lpm) { + if (device_trigger_verify(sch) != 0) + css_schedule_eval(sch->schid); } else if (sch->driver && sch->driver->verify) sch->driver->verify(&sch->dev); break; @@ -780,11 +615,10 @@ __s390_subchannel_vary_chpid(struct subchannel *sch, __u8 chpid, int on) spin_unlock_irqrestore(sch->lock, flags); } -static int -s390_subchannel_vary_chpid_off(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -793,11 +627,10 @@ s390_subchannel_vary_chpid_off(struct device *dev, void *data) return 0; } -static int -s390_subchannel_vary_chpid_on(struct device *dev, void *data) +static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) { struct subchannel *sch; - __u8 *chpid; + struct chp_id *chpid; sch = to_subchannel(dev); chpid = data; @@ -821,40 +654,17 @@ __s390_vary_chpid_on(struct subchannel_id schid, void *data) /* We're through */ return -ENXIO; /* Put it on the slow path. */ - if (css_enqueue_subchannel_slow(schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - return -EAGAIN; - } + css_schedule_eval(schid); return 0; } -/* - * Function: s390_vary_chpid - * Varies the specified chpid online or offline +/** + * chsc_chp_vary - propagate channel-path vary operation to subchannels + * @chpid: channl-path ID + * @on: non-zero for vary online, zero for vary offline */ -static int -s390_vary_chpid( __u8 chpid, int on) +int chsc_chp_vary(struct chp_id chpid, int on) { - char dbf_text[15]; - int status; - - sprintf(dbf_text, on?"varyon%x":"varyoff%x", chpid); - CIO_TRACE_EVENT( 2, dbf_text); - - status = get_chp_status(chpid); - if (status < 0) { - printk(KERN_ERR "Can't vary unknown chpid %02X\n", chpid); - return -EINVAL; - } - - if (!on && !status) { - printk(KERN_ERR "chpid %x is already offline\n", chpid); - return -EINVAL; - } - - set_chp_logically_online(chpid, on); - /* * Redo PathVerification on the devices the chpid connects to */ @@ -865,118 +675,9 @@ s390_vary_chpid( __u8 chpid, int on) if (on) /* Scan for new devices on varied on path. */ for_each_subchannel(__s390_vary_chpid_on, NULL); - if (need_rescan || css_slow_subchannels_exist()) - queue_work(slow_path_wq, &slow_path_work); return 0; } -/* - * Channel measurement related functions - */ -static ssize_t -chp_measurement_chars_read(struct kobject *kobj, char *buf, loff_t off, - size_t count) -{ - struct channel_path *chp; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - if (!chp->cmg_chars) - return 0; - - size = sizeof(struct cmg_chars); - - if (off > size) - return 0; - if (off + count > size) - count = size - off; - memcpy(buf, chp->cmg_chars + off, count); - return count; -} - -static struct bin_attribute chp_measurement_chars_attr = { - .attr = { - .name = "measurement_chars", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_chars), - .read = chp_measurement_chars_read, -}; - -static void -chp_measurement_copy_block(struct cmg_entry *buf, - struct channel_subsystem *css, int chpid) -{ - void *area; - struct cmg_entry *entry, reference_buf; - int idx; - - if (chpid < 128) { - area = css->cub_addr1; - idx = chpid; - } else { - area = css->cub_addr2; - idx = chpid - 128; - } - entry = area + (idx * sizeof(struct cmg_entry)); - do { - memcpy(buf, entry, sizeof(*entry)); - memcpy(&reference_buf, entry, sizeof(*entry)); - } while (reference_buf.values[0] != buf->values[0]); -} - -static ssize_t -chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count) -{ - struct channel_path *chp; - struct channel_subsystem *css; - unsigned int size; - - chp = to_channelpath(container_of(kobj, struct device, kobj)); - css = to_css(chp->dev.parent); - - size = sizeof(struct cmg_entry); - - /* Only allow single reads. */ - if (off || count < size) - return 0; - chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id); - count = size; - return count; -} - -static struct bin_attribute chp_measurement_attr = { - .attr = { - .name = "measurement", - .mode = S_IRUSR, - .owner = THIS_MODULE, - }, - .size = sizeof(struct cmg_entry), - .read = chp_measurement_read, -}; - -static void -chsc_remove_chp_cmg_attr(struct channel_path *chp) -{ - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - device_remove_bin_file(&chp->dev, &chp_measurement_attr); -} - -static int -chsc_add_chp_cmg_attr(struct channel_path *chp) -{ - int ret; - - ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); - if (ret) - return ret; - ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); - if (ret) - device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); - return ret; -} - static void chsc_remove_cmg_attr(struct channel_subsystem *css) { @@ -985,7 +686,7 @@ chsc_remove_cmg_attr(struct channel_subsystem *css) for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } } @@ -998,7 +699,7 @@ chsc_add_cmg_attr(struct channel_subsystem *css) for (i = 0; i <= __MAX_CHPID; i++) { if (!css->chps[i]) continue; - ret = chsc_add_chp_cmg_attr(css->chps[i]); + ret = chp_add_cmg_attr(css->chps[i]); if (ret) goto cleanup; } @@ -1007,12 +708,11 @@ cleanup: for (--i; i >= 0; i--) { if (!css->chps[i]) continue; - chsc_remove_chp_cmg_attr(css->chps[i]); + chp_remove_cmg_attr(css->chps[i]); } return ret; } - static int __chsc_do_secm(struct channel_subsystem *css, int enable, void *page) { @@ -1118,7 +818,7 @@ chsc_secm(struct channel_subsystem *css, int enable) } else chsc_remove_cmg_attr(css); } - if (enable && !css->cm_enabled) { + if (!css->cm_enabled) { free_page((unsigned long)css->cub_addr1); free_page((unsigned long)css->cub_addr2); } @@ -1127,109 +827,8 @@ chsc_secm(struct channel_subsystem *css, int enable) return ret; } -/* - * Files for the channel path entries. - */ -static ssize_t -chp_status_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return (get_chp_status(chp->id) ? sprintf(buf, "online\n") : - sprintf(buf, "offline\n")); -} - -static ssize_t -chp_status_write(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct channel_path *cp = container_of(dev, struct channel_path, dev); - char cmd[10]; - int num_args; - int error; - - num_args = sscanf(buf, "%5s", cmd); - if (!num_args) - return count; - - if (!strnicmp(cmd, "on", 2)) - error = s390_vary_chpid(cp->id, 1); - else if (!strnicmp(cmd, "off", 3)) - error = s390_vary_chpid(cp->id, 0); - else - error = -EINVAL; - - return error < 0 ? error : count; - -} - -static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); - -static ssize_t -chp_type_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = container_of(dev, struct channel_path, dev); - - if (!chp) - return 0; - return sprintf(buf, "%x\n", chp->desc.desc); -} - -static DEVICE_ATTR(type, 0444, chp_type_show, NULL); - -static ssize_t -chp_cmg_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->cmg == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->cmg); -} - -static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); - -static ssize_t -chp_shared_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct channel_path *chp = to_channelpath(dev); - - if (!chp) - return 0; - if (chp->shared == -1) /* channel measurements not available */ - return sprintf(buf, "unknown\n"); - return sprintf(buf, "%x\n", chp->shared); -} - -static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); - -static struct attribute * chp_attrs[] = { - &dev_attr_status.attr, - &dev_attr_type.attr, - &dev_attr_cmg.attr, - &dev_attr_shared.attr, - NULL, -}; - -static struct attribute_group chp_attr_group = { - .attrs = chp_attrs, -}; - -static void -chp_release(struct device *dev) -{ - struct channel_path *cp; - - cp = container_of(dev, struct channel_path, dev); - kfree(cp); -} - -static int -chsc_determine_channel_path_description(int chpid, - struct channel_path_desc *desc) +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc) { int ccode, ret; @@ -1252,8 +851,8 @@ chsc_determine_channel_path_description(int chpid, scpd_area->request.length = 0x0010; scpd_area->request.code = 0x0002; - scpd_area->first_chpid = chpid; - scpd_area->last_chpid = chpid; + scpd_area->first_chpid = chpid.id; + scpd_area->last_chpid = chpid.id; ccode = chsc(scpd_area); if (ccode > 0) { @@ -1316,8 +915,7 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, } } -static int -chsc_get_channel_measurement_chars(struct channel_path *chp) +int chsc_get_channel_measurement_chars(struct channel_path *chp) { int ccode, ret; @@ -1349,8 +947,8 @@ chsc_get_channel_measurement_chars(struct channel_path *chp) scmc_area->request.length = 0x0010; scmc_area->request.code = 0x0022; - scmc_area->first_chpid = chp->id; - scmc_area->last_chpid = chp->id; + scmc_area->first_chpid = chp->chpid.id; + scmc_area->last_chpid = chp->chpid.id; ccode = chsc(scmc_area); if (ccode > 0) { @@ -1392,94 +990,6 @@ out: return ret; } -/* - * Entries for chpids on the system bus. - * This replaces /proc/chpids. - */ -static int -new_channel_path(int chpid) -{ - struct channel_path *chp; - int ret; - - chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); - if (!chp) - return -ENOMEM; - - /* fill in status, etc. */ - chp->id = chpid; - chp->state = 1; - chp->dev.parent = &css[0]->device; - chp->dev.release = chp_release; - snprintf(chp->dev.bus_id, BUS_ID_SIZE, "chp0.%x", chpid); - - /* Obtain channel path description and fill it in. */ - ret = chsc_determine_channel_path_description(chpid, &chp->desc); - if (ret) - goto out_free; - /* Get channel-measurement characteristics. */ - if (css_characteristics_avail && css_chsc_characteristics.scmc - && css_chsc_characteristics.secm) { - ret = chsc_get_channel_measurement_chars(chp); - if (ret) - goto out_free; - } else { - static int msg_done; - - if (!msg_done) { - printk(KERN_WARNING "cio: Channel measurements not " - "available, continuing.\n"); - msg_done = 1; - } - chp->cmg = -1; - } - - /* make it known to the system */ - ret = device_register(&chp->dev); - if (ret) { - printk(KERN_WARNING "%s: could not register %02x\n", - __func__, chpid); - goto out_free; - } - ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); - if (ret) { - device_unregister(&chp->dev); - goto out_free; - } - mutex_lock(&css[0]->mutex); - if (css[0]->cm_enabled) { - ret = chsc_add_chp_cmg_attr(chp); - if (ret) { - sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); - device_unregister(&chp->dev); - mutex_unlock(&css[0]->mutex); - goto out_free; - } - } - css[0]->chps[chpid] = chp; - mutex_unlock(&css[0]->mutex); - return ret; -out_free: - kfree(chp); - return ret; -} - -void * -chsc_get_chp_desc(struct subchannel *sch, int chp_no) -{ - struct channel_path *chp; - struct channel_path_desc *desc; - - chp = css[0]->chps[sch->schib.pmcw.chpid[chp_no]]; - if (!chp) - return NULL; - desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); - if (!desc) - return NULL; - memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); - return desc; -} - static int __init chsc_alloc_sei_area(void) { diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 0fb2b02..2ad81d1 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -1,9 +1,10 @@ #ifndef S390_CHSC_H #define S390_CHSC_H -#define CHSC_SEI_ACC_CHPID 1 -#define CHSC_SEI_ACC_LINKADDR 2 -#define CHSC_SEI_ACC_FULLLINKADDR 3 +#include <linux/types.h> +#include <linux/device.h> +#include <asm/chpid.h> +#include "schid.h" #define CHSC_SDA_OC_MSS 0x2 @@ -33,23 +34,9 @@ struct channel_path_desc { u8 chpp; } __attribute__ ((packed)); -struct channel_path { - int id; - int state; - struct channel_path_desc desc; - /* Channel-measurement related stuff: */ - int cmg; - int shared; - void *cmg_chars; - struct device dev; -}; +struct channel_path; -extern void s390_process_css( void ); -extern void chsc_validate_chpids(struct subchannel *); -extern void chpid_is_actually_online(int); -extern int css_get_ssd_info(struct subchannel *); -extern int chsc_process_crw(void); -extern int chp_process_crw(int, int); +extern void chsc_process_crw(void); struct css_general_char { u64 : 41; @@ -82,15 +69,26 @@ struct css_chsc_char { extern struct css_general_char css_general_characteristics; extern struct css_chsc_char css_chsc_characteristics; +struct chsc_ssd_info { + u8 path_mask; + u8 fla_valid_mask; + struct chp_id chpid[8]; + u16 fla[8]; +}; +extern int chsc_get_ssd_info(struct subchannel_id schid, + struct chsc_ssd_info *ssd); extern int chsc_determine_css_characteristics(void); extern int css_characteristics_avail; -extern void *chsc_get_chp_desc(struct subchannel*, int); - extern int chsc_enable_facility(int); struct channel_subsystem; extern int chsc_secm(struct channel_subsystem *, int); -#define to_channelpath(device) container_of(device, struct channel_path, dev) +int chsc_chp_vary(struct chp_id chpid, int on); +int chsc_determine_channel_path_description(struct chp_id chpid, + struct channel_path_desc *desc); +void chsc_chp_online(struct chp_id chpid); +void chsc_chp_offline(struct chp_id chpid); +int chsc_get_channel_measurement_chars(struct channel_path *chp); #endif diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c index b3a56dc..ea1defb 100644 --- a/drivers/s390/cio/cio.c +++ b/drivers/s390/cio/cio.c @@ -21,6 +21,8 @@ #include <asm/irq_regs.h> #include <asm/setup.h> #include <asm/reset.h> +#include <asm/ipl.h> +#include <asm/chpid.h> #include "airq.h" #include "cio.h" #include "css.h" @@ -28,6 +30,7 @@ #include "ioasm.h" #include "blacklist.h" #include "cio_debug.h" +#include "chp.h" #include "../s390mach.h" debug_info_t *cio_debug_msg_id; @@ -591,9 +594,10 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) err = -ENODEV; goto out; } - sch->opm = 0xff; - if (!cio_is_console(sch->schid)) - chsc_validate_chpids(sch); + if (cio_is_console(sch->schid)) + sch->opm = 0xff; + else + sch->opm = chp_get_sch_opm(sch); sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, @@ -953,6 +957,7 @@ static void css_reset(void) { int i, ret; unsigned long long timeout; + struct chp_id chpid; /* Reset subchannels. */ for_each_subchannel(__shutdown_subchannel_easy, NULL); @@ -962,8 +967,10 @@ static void css_reset(void) __ctl_set_bit(14, 28); /* Temporarily reenable machine checks. */ local_mcck_enable(); + chp_id_init(&chpid); for (i = 0; i <= __MAX_CHPID; i++) { - ret = rchp(i); + chpid.id = i; + ret = rchp(chpid); if ((ret == 0) || (ret == 2)) /* * rchp either succeeded, or another rchp is already @@ -1047,37 +1054,19 @@ void reipl_ccw_dev(struct ccw_dev_id *devid) do_reipl_asm(*((__u32*)&schid)); } -extern struct schib ipl_schib; - -/* - * ipl_save_parameters gets called very early. It is not allowed to access - * anything in the bss section at all. The bss section is not cleared yet, - * but may contain some ipl parameters written by the firmware. - * These parameters (if present) are copied to 0x2000. - * To avoid corruption of the ipl parameters, all variables used by this - * function must reside on the stack or in the data section. - */ -void ipl_save_parameters(void) +int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo) { struct subchannel_id schid; - unsigned int *ipl_ptr; - void *src, *dst; + struct schib schib; schid = *(struct subchannel_id *)__LC_SUBCHANNEL_ID; if (!schid.one) - return; - if (stsch(schid, &ipl_schib)) - return; - if (!ipl_schib.pmcw.dnv) - return; - ipl_devno = ipl_schib.pmcw.dev; - ipl_flags |= IPL_DEVNO_VALID; - if (!ipl_schib.pmcw.qf) - return; - ipl_flags |= IPL_PARMBLOCK_VALID; - ipl_ptr = (unsigned int *)__LC_IPL_PARMBLOCK_PTR; - src = (void *)(unsigned long)*ipl_ptr; - dst = (void *)IPL_PARMBLOCK_ORIGIN; - memmove(dst, src, PAGE_SIZE); - *ipl_ptr = IPL_PARMBLOCK_ORIGIN; + return -ENODEV; + if (stsch(schid, &schib)) + return -ENODEV; + if (!schib.pmcw.dnv) + return -ENODEV; + iplinfo->devno = schib.pmcw.dev; + iplinfo->is_qdio = schib.pmcw.qf; + return 0; } diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h index 35154a2..7446c399 100644 --- a/drivers/s390/cio/cio.h +++ b/drivers/s390/cio/cio.h @@ -1,18 +1,11 @@ #ifndef S390_CIO_H #define S390_CIO_H -#include "schid.h" #include <linux/mutex.h> - -/* - * where we put the ssd info - */ -struct ssd_info { - __u8 valid:1; - __u8 type:7; /* subchannel type */ - __u8 chpid[8]; /* chpids */ - __u16 fla[8]; /* full link addresses */ -} __attribute__ ((packed)); +#include <linux/device.h> +#include <asm/chpid.h> +#include "chsc.h" +#include "schid.h" /* * path management control word @@ -108,7 +101,7 @@ struct subchannel { struct schib schib; /* subchannel information block */ struct orb orb; /* operation request block */ struct ccw1 sense_ccw; /* static ccw for sense command */ - struct ssd_info ssd_info; /* subchannel description */ + struct chsc_ssd_info ssd_info; /* subchannel description */ struct device dev; /* entry in device tree */ struct css_driver *driver; } __attribute__ ((aligned(8))); diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c index 90b22fa..28abd69 100644 --- a/drivers/s390/cio/cmf.c +++ b/drivers/s390/cio/cmf.c @@ -476,7 +476,7 @@ struct cmb_area { }; static struct cmb_area cmb_area = { - .lock = SPIN_LOCK_UNLOCKED, + .lock = __SPIN_LOCK_UNLOCKED(cmb_area.lock), .list = LIST_HEAD_INIT(cmb_area.list), .num_channels = 1024, }; diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index fe0ace7..27c6d9e 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -20,8 +20,9 @@ #include "ioasm.h" #include "chsc.h" #include "device.h" +#include "idset.h" +#include "chp.h" -int need_rescan = 0; int css_init_done = 0; static int need_reprobe = 0; static int max_ssid = 0; @@ -125,8 +126,52 @@ void css_sch_device_unregister(struct subchannel *sch) mutex_unlock(&sch->reg_mutex); } -static int -css_register_subchannel(struct subchannel *sch) +static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) +{ + int i; + int mask; + + memset(ssd, 0, sizeof(struct chsc_ssd_info)); + ssd->path_mask = pmcw->pim; + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (pmcw->pim & mask) { + chp_id_init(&ssd->chpid[i]); + ssd->chpid[i].id = pmcw->chpid[i]; + } + } +} + +static void ssd_register_chpids(struct chsc_ssd_info *ssd) +{ + int i; + int mask; + + for (i = 0; i < 8; i++) { + mask = 0x80 >> i; + if (ssd->path_mask & mask) + if (!chp_is_registered(ssd->chpid[i])) + chp_new(ssd->chpid[i]); + } +} + +void css_update_ssd_info(struct subchannel *sch) +{ + int ret; + + if (cio_is_console(sch->schid)) { + /* Console is initialized too early for functions requiring + * memory allocation. */ + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + } else { + ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info); + if (ret) + ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw); + ssd_register_chpids(&sch->ssd_info); + } +} + +static int css_register_subchannel(struct subchannel *sch) { int ret; @@ -135,9 +180,7 @@ css_register_subchannel(struct subchannel *sch) sch->dev.bus = &css_bus_type; sch->dev.release = &css_subchannel_release; sch->dev.groups = subch_attr_groups; - - css_get_ssd_info(sch); - + css_update_ssd_info(sch); /* make it known to the system */ ret = css_sch_device_register(sch); if (ret) { @@ -306,7 +349,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) return css_probe_device(schid); } -static int css_evaluate_subchannel(struct subchannel_id schid, int slow) +static void css_evaluate_subchannel(struct subchannel_id schid, int slow) { struct subchannel *sch; int ret; @@ -317,53 +360,66 @@ static int css_evaluate_subchannel(struct subchannel_id schid, int slow) put_device(&sch->dev); } else ret = css_evaluate_new_subchannel(schid, slow); - - return ret; + if (ret == -EAGAIN) + css_schedule_eval(schid); } -static int -css_rescan_devices(struct subchannel_id schid, void *data) +static struct idset *slow_subchannel_set; +static spinlock_t slow_subchannel_lock; + +static int __init slow_subchannel_init(void) { - return css_evaluate_subchannel(schid, 1); + spin_lock_init(&slow_subchannel_lock); + slow_subchannel_set = idset_sch_new(); + if (!slow_subchannel_set) { + printk(KERN_WARNING "cio: could not allocate slow subchannel " + "set\n"); + return -ENOMEM; + } + return 0; } -struct slow_subchannel { - struct list_head slow_list; - struct subchannel_id schid; -}; - -static LIST_HEAD(slow_subchannels_head); -static DEFINE_SPINLOCK(slow_subchannel_lock); +subsys_initcall(slow_subchannel_init); -static void -css_trigger_slow_path(struct work_struct *unused) +static void css_slow_path_func(struct work_struct *unused) { - CIO_TRACE_EVENT(4, "slowpath"); - - if (need_rescan) { - need_rescan = 0; - for_each_subchannel(css_rescan_devices, NULL); - return; - } + struct subchannel_id schid; + CIO_TRACE_EVENT(4, "slowpath"); spin_lock_irq(&slow_subchannel_lock); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); + init_subchannel_id(&schid); + while (idset_sch_get_first(slow_subchannel_set, &schid)) { + idset_sch_del(slow_subchannel_set, schid); spin_unlock_irq(&slow_subchannel_lock); - css_evaluate_subchannel(slow_sch->schid, 1); + css_evaluate_subchannel(schid, 1); spin_lock_irq(&slow_subchannel_lock); - kfree(slow_sch); } spin_unlock_irq(&slow_subchannel_lock); } -DECLARE_WORK(slow_path_work, css_trigger_slow_path); +static DECLARE_WORK(slow_path_work, css_slow_path_func); struct workqueue_struct *slow_path_wq; +void css_schedule_eval(struct subchannel_id schid) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_sch_add(slow_subchannel_set, schid); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + +void css_schedule_eval_all(void) +{ + unsigned long flags; + + spin_lock_irqsave(&slow_subchannel_lock, flags); + idset_fill(slow_subchannel_set); + queue_work(slow_path_wq, &slow_path_work); + spin_unlock_irqrestore(&slow_subchannel_lock, flags); +} + /* Reprobe subchannel if unregistered. */ static int reprobe_subchannel(struct subchannel_id schid, void *data) { @@ -426,33 +482,14 @@ void css_schedule_reprobe(void) EXPORT_SYMBOL_GPL(css_schedule_reprobe); /* - * Rescan for new devices. FIXME: This is slow. - * This function is called when we have lost CRWs due to overflows and we have - * to do subchannel housekeeping. - */ -void -css_reiterate_subchannels(void) -{ - css_clear_subchannel_slow_list(); - need_rescan = 1; -} - -/* * Called from the machine check handler for subchannel report words. */ -int -css_process_crw(int rsid1, int rsid2) +void css_process_crw(int rsid1, int rsid2) { - int ret; struct subchannel_id mchk_schid; CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n", rsid1, rsid2); - - if (need_rescan) - /* We need to iterate all subchannels anyway. */ - return -EAGAIN; - init_subchannel_id(&mchk_schid); mchk_schid.sch_no = rsid1; if (rsid2 != 0) @@ -463,14 +500,7 @@ css_process_crw(int rsid1, int rsid2) * use stsch() to find out if the subchannel in question has come * or gone. */ - ret = css_evaluate_subchannel(mchk_schid, 0); - if (ret == -EAGAIN) { - if (css_enqueue_subchannel_slow(mchk_schid)) { - css_clear_subchannel_slow_list(); - need_rescan = 1; - } - } - return ret; + css_evaluate_subchannel(mchk_schid, 0); } static int __init @@ -745,47 +775,6 @@ struct bus_type css_bus_type = { subsys_initcall(init_channel_subsystem); -int -css_enqueue_subchannel_slow(struct subchannel_id schid) -{ - struct slow_subchannel *new_slow_sch; - unsigned long flags; - - new_slow_sch = kzalloc(sizeof(struct slow_subchannel), GFP_ATOMIC); - if (!new_slow_sch) - return -ENOMEM; - new_slow_sch->schid = schid; - spin_lock_irqsave(&slow_subchannel_lock, flags); - list_add_tail(&new_slow_sch->slow_list, &slow_subchannels_head); - spin_unlock_irqrestore(&slow_subchannel_lock, flags); - return 0; -} - -void -css_clear_subchannel_slow_list(void) -{ - unsigned long flags; - - spin_lock_irqsave(&slow_subchannel_lock, flags); - while (!list_empty(&slow_subchannels_head)) { - struct slow_subchannel *slow_sch = - list_entry(slow_subchannels_head.next, - struct slow_subchannel, slow_list); - - list_del_init(slow_subchannels_head.next); - kfree(slow_sch); - } - spin_unlock_irqrestore(&slow_subchannel_lock, flags); -} - - - -int -css_slow_subchannels_exist(void) -{ - return (!list_empty(&slow_subchannels_head)); -} - MODULE_LICENSE("GPL"); EXPORT_SYMBOL(css_bus_type); EXPORT_SYMBOL_GPL(css_characteristics_avail); diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h index ca2bab9..71fcfdc 100644 --- a/drivers/s390/cio/css.h +++ b/drivers/s390/cio/css.h @@ -4,8 +4,11 @@ #include <linux/mutex.h> #include <linux/wait.h> #include <linux/workqueue.h> +#include <linux/device.h> +#include <linux/types.h> #include <asm/cio.h> +#include <asm/chpid.h> #include "schid.h" @@ -143,13 +146,12 @@ extern void css_sch_device_unregister(struct subchannel *); extern struct subchannel * get_subchannel_by_schid(struct subchannel_id); extern int css_init_done; extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *); -extern int css_process_crw(int, int); +extern void css_process_crw(int, int); extern void css_reiterate_subchannels(void); +void css_update_ssd_info(struct subchannel *sch); #define __MAX_SUBCHANNEL 65535 #define __MAX_SSID 3 -#define __MAX_CHPID 255 -#define __MAX_CSSID 0 struct channel_subsystem { u8 cssid; @@ -185,16 +187,12 @@ int device_trigger_verify(struct subchannel *sch); void device_kill_pending_timer(struct subchannel *); /* Helper functions to build lists for the slow path. */ -extern int css_enqueue_subchannel_slow(struct subchannel_id schid); -void css_walk_subchannel_slow_list(void (*fn)(unsigned long)); -void css_clear_subchannel_slow_list(void); -int css_slow_subchannels_exist(void); -extern int need_rescan; +void css_schedule_eval(struct subchannel_id schid); +void css_schedule_eval_all(void); int sch_is_pseudo_sch(struct subchannel *); extern struct workqueue_struct *slow_path_wq; -extern struct work_struct slow_path_work; int subchannel_add_files (struct device *); extern struct attribute_group *subch_attr_groups[]; diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e322111..a23ff58 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, struct device_driver * drv) /* Store modalias string delimited by prefix/suffix string into buffer with * specified size. Return length of resulting string (excluding trailing '\0') * even if string doesn't fit buffer (snprintf semantics). */ -static int snprint_alias(char *buf, size_t size, const char *prefix, +static int snprint_alias(char *buf, size_t size, struct ccw_device_id *id, const char *suffix) { int len; - len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type, - id->cu_model); + len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model); if (len > size) return len; buf += len; @@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp, struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device_id *id = &(cdev->id); int i = 0; - int len; + int len = 0; + int ret; + char modalias_buf[30]; /* CU_TYPE= */ - len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_TYPE=%04X", id->cu_type); + if (ret) + return ret; /* CU_MODEL= */ - len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "CU_MODEL=%02X", id->cu_model); + if (ret) + return ret; /* The next two can be zero, that's ok for us */ /* DEV_TYPE= */ - len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_TYPE=%04X", id->dev_type); + if (ret) + return ret; /* DEV_MODEL= */ - len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X", - (unsigned char) id->dev_model) + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "DEV_MODEL=%02X", id->dev_model); + if (ret) + return ret; /* MODALIAS= */ - len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1; - if (len > buffer_size || i >= num_envp) - return -ENOMEM; - envp[i++] = buffer; - buffer += len; - buffer_size -= len; - - envp[i] = NULL; - - return 0; + snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); + ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, + "MODALIAS=%s", modalias_buf); + return ret; } struct bus_type ccw_bus_type; @@ -230,12 +216,18 @@ static ssize_t chpids_show (struct device * dev, struct device_attribute *attr, char * buf) { struct subchannel *sch = to_subchannel(dev); - struct ssd_info *ssd = &sch->ssd_info; + struct chsc_ssd_info *ssd = &sch->ssd_info; ssize_t ret = 0; int chp; + int mask; - for (chp = 0; chp < 8; chp++) - ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); + for (chp = 0; chp < 8; chp++) { + mask = 0x80 >> chp; + if (ssd->path_mask & mask) + ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id); + else + ret += sprintf(buf + ret, "00 "); + } ret += sprintf (buf+ret, "\n"); return min((ssize_t)PAGE_SIZE, ret); } @@ -280,7 +272,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf) struct ccw_device_id *id = &(cdev->id); int len; - len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1; + len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1; return len > PAGE_SIZE ? PAGE_SIZE : len; } @@ -298,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_device *cdev) return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); } -static void ccw_device_unregister(struct work_struct *work) +static void ccw_device_unregister(struct ccw_device *cdev) { - struct ccw_device_private *priv; - struct ccw_device *cdev; - - priv = container_of(work, struct ccw_device_private, kick_work); - cdev = priv->cdev; if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - put_device(&cdev->dev); + device_del(&cdev->dev); } static void @@ -324,11 +310,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) spin_lock_irqsave(cdev->ccwlock, flags); cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return ; } sch = to_subchannel(cdev->dev.parent); @@ -413,11 +396,60 @@ ccw_device_set_online(struct ccw_device *cdev) return (ret == 0) ? -ENODEV : ret; } -static ssize_t -online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +static void online_store_handle_offline(struct ccw_device *cdev) +{ + if (cdev->private->state == DEV_STATE_DISCONNECTED) + ccw_device_remove_disconnected(cdev); + else if (cdev->drv && cdev->drv->set_offline) + ccw_device_set_offline(cdev); +} + +static int online_store_recog_and_online(struct ccw_device *cdev) +{ + int ret; + + /* Do device recognition, if needed. */ + if (cdev->id.cu_type == 0) { + ret = ccw_device_recognition(cdev); + if (ret) { + printk(KERN_WARNING"Couldn't start recognition " + "for device %s (ret=%d)\n", + cdev->dev.bus_id, ret); + return ret; + } + wait_event(cdev->private->wait_q, + cdev->private->flags.recog_done); + } + if (cdev->drv && cdev->drv->set_online) + ccw_device_set_online(cdev); + return 0; +} +static void online_store_handle_online(struct ccw_device *cdev, int force) +{ + int ret; + + ret = online_store_recog_and_online(cdev); + if (ret) + return; + if (force && cdev->private->state == DEV_STATE_BOXED) { + ret = ccw_device_stlck(cdev); + if (ret) { + printk(KERN_WARNING"ccw_device_stlck for device %s " + "returned %d!\n", cdev->dev.bus_id, ret); + return; + } + if (cdev->id.cu_type == 0) + cdev->private->state = DEV_STATE_NOT_OPER; + online_store_recog_and_online(cdev); + } + +} + +static ssize_t online_store (struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct ccw_device *cdev = to_ccwdev(dev); - int i, force, ret; + int i, force; char *tmp; if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) @@ -434,51 +466,17 @@ online_store (struct device *dev, struct device_attribute *attr, const char *buf force = 0; i = simple_strtoul(buf, &tmp, 16); } - if (i == 1) { - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); - } else if (i == 0) { - if (cdev->private->state == DEV_STATE_DISCONNECTED) - ccw_device_remove_disconnected(cdev); - else if (cdev->drv && cdev->drv->set_offline) - ccw_device_set_offline(cdev); - } - if (force && cdev->private->state == DEV_STATE_BOXED) { - ret = ccw_device_stlck(cdev); - if (ret) { - printk(KERN_WARNING"ccw_device_stlck for device %s " - "returned %d!\n", cdev->dev.bus_id, ret); - goto out; - } - /* Do device recognition, if needed. */ - if (cdev->id.cu_type == 0) { - cdev->private->state = DEV_STATE_NOT_OPER; - ret = ccw_device_recognition(cdev); - if (ret) { - printk(KERN_WARNING"Couldn't start recognition " - "for device %s (ret=%d)\n", - cdev->dev.bus_id, ret); - goto out; - } - wait_event(cdev->private->wait_q, - cdev->private->flags.recog_done); - } - if (cdev->drv && cdev->drv->set_online) - ccw_device_set_online(cdev); + + switch (i) { + case 0: + online_store_handle_offline(cdev); + break; + case 1: + online_store_handle_online(cdev, force); + break; + default: + count = -EINVAL; } - out: if (cdev->drv) module_put(cdev->drv->owner); atomic_set(&cdev->private->onoff, 0); @@ -548,17 +546,10 @@ static struct attribute_group ccwdev_attr_group = { .attrs = ccwdev_attrs, }; -static int -device_add_files (struct device *dev) -{ - return sysfs_create_group(&dev->kobj, &ccwdev_attr_group); -} - -static void -device_remove_files(struct device *dev) -{ - sysfs_remove_group(&dev->kobj, &ccwdev_attr_group); -} +struct attribute_group *ccwdev_attr_groups[] = { + &ccwdev_attr_group, + NULL, +}; /* this is a simple abstraction for device_register that sets the * correct bus type and adds the bus specific files */ @@ -573,10 +564,6 @@ static int ccw_device_register(struct ccw_device *cdev) return ret; set_bit(1, &cdev->private->registered); - if ((ret = device_add_files(dev))) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(dev); - } return ret; } @@ -648,10 +635,6 @@ ccw_device_add_changed(struct work_struct *work) return; } set_bit(1, &cdev->private->registered); - if (device_add_files(&cdev->dev)) { - if (test_and_clear_bit(1, &cdev->private->registered)) - device_unregister(&cdev->dev); - } } void ccw_device_do_unreg_rereg(struct work_struct *work) @@ -664,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct work_struct *work) cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - device_remove_files(&cdev->dev); - if (test_and_clear_bit(1, &cdev->private->registered)) - device_del(&cdev->dev); + ccw_device_unregister(cdev); PREPARE_WORK(&cdev->private->kick_work, ccw_device_add_changed); queue_work(ccw_device_work, &cdev->private->kick_work); @@ -705,6 +686,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, cdev->dev.parent = &sch->dev; cdev->dev.release = ccw_device_release; INIT_LIST_HEAD(&cdev->private->kick_work.entry); + cdev->dev.groups = ccwdev_attr_groups; /* Do first half of device_register. */ device_initialize(&cdev->dev); if (!get_device(&sch->dev)) { @@ -736,6 +718,7 @@ static int io_subchannel_recog(struct ccw_device *, struct subchannel *); static void sch_attach_device(struct subchannel *sch, struct ccw_device *cdev) { + css_update_ssd_info(sch); spin_lock_irq(sch->lock); sch->dev.driver_data = cdev; cdev->private->schid = sch->schid; @@ -871,7 +854,7 @@ io_subchannel_register(struct work_struct *work) priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; sch = to_subchannel(cdev->dev.parent); - + css_update_ssd_info(sch); /* * io_subchannel_register() will also be called after device * recognition has been done for a boxed device (which will already @@ -888,6 +871,12 @@ io_subchannel_register(struct work_struct *work) } goto out; } + /* + * Now we know this subchannel will stay, we can throw + * our delayed uevent. + */ + sch->dev.uevent_suppress = 0; + kobject_uevent(&sch->dev.kobj, KOBJ_ADD); /* make it known to the system */ ret = ccw_device_register(cdev); if (ret) { @@ -1133,15 +1122,8 @@ io_subchannel_remove (struct subchannel *sch) sch->dev.driver_data = NULL; cdev->private->state = DEV_STATE_NOT_OPER; spin_unlock_irqrestore(cdev->ccwlock, flags); - /* - * Put unregistration on workqueue to avoid livelocks on the css bus - * semaphore. - */ - if (get_device(&cdev->dev)) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_unregister); - queue_work(ccw_device_work, &cdev->private->kick_work); - } + ccw_device_unregister(cdev); + put_device(&cdev->dev); return 0; } diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 51238e7..898ec3b 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -15,6 +15,7 @@ #include <asm/ccwdev.h> #include <asm/cio.h> +#include <asm/chpid.h> #include "cio.h" #include "cio_debug.h" @@ -22,6 +23,7 @@ #include "device.h" #include "chsc.h" #include "ioasm.h" +#include "chp.h" int device_is_online(struct subchannel *sch) @@ -144,8 +146,8 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev) ret = stsch(sch->schid, &sch->schib); if (ret || !sch->schib.pmcw.dnv) return -ENODEV; - if (!sch->schib.pmcw.ena || sch->schib.scsw.actl == 0) - /* Not operational or no activity -> done. */ + if (!sch->schib.pmcw.ena) + /* Not operational -> done. */ return 0; /* Stage 1: cancel io. */ if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) && @@ -210,14 +212,18 @@ static void __recover_lost_chpids(struct subchannel *sch, int old_lpm) { int mask, i; + struct chp_id chpid; + chp_id_init(&chpid); for (i = 0; i<8; i++) { mask = 0x80 >> i; if (!(sch->lpm & mask)) continue; if (old_lpm & mask) continue; - chpid_is_actually_online(sch->schib.pmcw.chpid[i]); + chpid.id = sch->schib.pmcw.chpid[i]; + if (!chp_is_registered(chpid)) + css_schedule_eval_all(); } } @@ -334,20 +340,29 @@ ccw_device_oper_notify(struct work_struct *work) struct ccw_device *cdev; struct subchannel *sch; int ret; + unsigned long flags; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; + spin_lock_irqsave(cdev->ccwlock, flags); sch = to_subchannel(cdev->dev.parent); - ret = (sch->driver && sch->driver->notify) ? - sch->driver->notify(&sch->dev, CIO_OPER) : 0; - if (!ret) - /* Driver doesn't want device back. */ - ccw_device_do_unreg_rereg(work); - else { + if (sch->driver && sch->driver->notify) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + ret = sch->driver->notify(&sch->dev, CIO_OPER); + spin_lock_irqsave(cdev->ccwlock, flags); + } else + ret = 0; + if (ret) { /* Reenable channel measurements, if needed. */ + spin_unlock_irqrestore(cdev->ccwlock, flags); cmf_reenable(cdev); + spin_lock_irqsave(cdev->ccwlock, flags); wake_up(&cdev->private->wait_q); } + spin_unlock_irqrestore(cdev->ccwlock, flags); + if (!ret) + /* Driver doesn't want device back. */ + ccw_device_do_unreg_rereg(work); } /* @@ -534,15 +549,21 @@ ccw_device_nopath_notify(struct work_struct *work) struct ccw_device *cdev; struct subchannel *sch; int ret; + unsigned long flags; priv = container_of(work, struct ccw_device_private, kick_work); cdev = priv->cdev; + spin_lock_irqsave(cdev->ccwlock, flags); sch = to_subchannel(cdev->dev.parent); /* Extra sanity. */ if (sch->lpm) - return; - ret = (sch->driver && sch->driver->notify) ? - sch->driver->notify(&sch->dev, CIO_NO_PATH) : 0; + goto out_unlock; + if (sch->driver && sch->driver->notify) { + spin_unlock_irqrestore(cdev->ccwlock, flags); + ret = sch->driver->notify(&sch->dev, CIO_NO_PATH); + spin_lock_irqsave(cdev->ccwlock, flags); + } else + ret = 0; if (!ret) { if (get_device(&sch->dev)) { /* Driver doesn't want to keep device. */ @@ -562,6 +583,8 @@ ccw_device_nopath_notify(struct work_struct *work) cdev->private->state = DEV_STATE_DISCONNECTED; wake_up(&cdev->private->wait_q); } +out_unlock: + spin_unlock_irqrestore(cdev->ccwlock, flags); } void @@ -607,10 +630,13 @@ ccw_device_verify_done(struct ccw_device *cdev, int err) default: /* Reset oper notify indication after verify error. */ cdev->private->flags.donotify = 0; - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - ccw_device_done(cdev, DEV_STATE_NOT_OPER); + if (cdev->online) { + PREPARE_WORK(&cdev->private->kick_work, + ccw_device_nopath_notify); + queue_work(ccw_device_notify_work, + &cdev->private->kick_work); + } else + ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } } @@ -756,15 +782,22 @@ static void ccw_device_online_notoper(struct ccw_device *cdev, enum dev_event dev_event) { struct subchannel *sch; + int ret; sch = to_subchannel(cdev->dev.parent); - if (sch->driver->notify && - sch->driver->notify(&sch->dev, sch->lpm ? CIO_GONE : CIO_NO_PATH)) { - ccw_device_set_timeout(cdev, 0); - cdev->private->flags.fake_irb = 0; - cdev->private->state = DEV_STATE_DISCONNECTED; - wake_up(&cdev->private->wait_q); - return; + if (sch->driver->notify) { + spin_unlock_irq(cdev->ccwlock); + ret = sch->driver->notify(&sch->dev, + sch->lpm ? CIO_GONE : CIO_NO_PATH); + spin_lock_irq(cdev->ccwlock); + } else + ret = 0; + if (ret) { + ccw_device_set_timeout(cdev, 0); + cdev->private->flags.fake_irb = 0; + cdev->private->state = DEV_STATE_DISCONNECTED; + wake_up(&cdev->private->wait_q); + return; } cdev->private->state = DEV_STATE_NOT_OPER; cio_disable_subchannel(sch); @@ -969,18 +1002,12 @@ ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) sch = to_subchannel(cdev->dev.parent); ccw_device_set_timeout(cdev, 0); + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); /* OK, i/o is dead now. Call interrupt handler. */ - cdev->private->state = DEV_STATE_ONLINE; if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - } else if (cdev->private->flags.doverify) - /* Start delayed path verification. */ - ccw_device_online_verify(cdev, 0); } static void @@ -993,21 +1020,8 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_set_timeout(cdev, 3*HZ); return; } - if (ret == -ENODEV) { - struct subchannel *sch; - - sch = to_subchannel(cdev->dev.parent); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - return; - } - //FIXME: Can we get here? - cdev->private->state = DEV_STATE_ONLINE; + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); @@ -1025,26 +1039,11 @@ void device_kill_io(struct subchannel *sch) cdev->private->state = DEV_STATE_TIMEOUT_KILL; return; } - if (ret == -ENODEV) { - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, - &cdev->private->kick_work); - } else - dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - return; - } + /* Start delayed path verification. */ + ccw_device_online_verify(cdev, 0); if (cdev->handler) cdev->handler(cdev, cdev->private->intparm, ERR_PTR(-EIO)); - if (!sch->lpm) { - PREPARE_WORK(&cdev->private->kick_work, - ccw_device_nopath_notify); - queue_work(ccw_device_notify_work, &cdev->private->kick_work); - } else - /* Start delayed path verification. */ - ccw_device_online_verify(cdev, 0); } static void diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 7c7775a..16f59fc 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -16,12 +16,14 @@ #include <asm/ccwdev.h> #include <asm/idals.h> +#include <asm/chpid.h> #include "cio.h" #include "cio_debug.h" #include "css.h" #include "chsc.h" #include "device.h" +#include "chp.h" int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) { @@ -606,9 +608,12 @@ void * ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) { struct subchannel *sch; + struct chp_id chpid; sch = to_subchannel(cdev->dev.parent); - return chsc_get_chp_desc(sch, chp_no); + chp_id_init(&chpid); + chpid.id = sch->schib.pmcw.chpid[chp_no]; + return chp_get_chp_desc(chpid); } // FIXME: these have to go: diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c index 6b1caea..aa96e67 100644 --- a/drivers/s390/cio/device_status.c +++ b/drivers/s390/cio/device_status.c @@ -221,6 +221,14 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) cdev_irb = &cdev->private->irb; + /* + * If the clear function had been performed, all formerly pending + * status at the subchannel has been cleared and we must not pass + * intermediate accumulated status to the device driver. + */ + if (irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) + memset(&cdev->private->irb, 0, sizeof(struct irb)); + /* Copy bits which are valid only for the start function. */ if (irb->scsw.fctl & SCSW_FCTL_START_FUNC) { /* Copy key. */ @@ -263,7 +271,11 @@ ccw_device_accumulate_irb(struct ccw_device *cdev, struct irb *irb) cdev_irb->scsw.cpa = irb->scsw.cpa; /* Accumulate device status, but not the device busy flag. */ cdev_irb->scsw.dstat &= ~DEV_STAT_BUSY; - cdev_irb->scsw.dstat |= irb->scsw.dstat; + /* dstat is not always valid. */ + if (irb->scsw.stctl & + (SCSW_STCTL_PRIM_STATUS | SCSW_STCTL_SEC_STATUS + | SCSW_STCTL_INTER_STATUS | SCSW_STCTL_ALERT_STATUS)) + cdev_irb->scsw.dstat |= irb->scsw.dstat; /* Accumulate subchannel status. */ cdev_irb->scsw.cstat |= irb->scsw.cstat; /* Copy residual count if it is valid. */ diff --git a/drivers/s390/cio/idset.c b/drivers/s390/cio/idset.c new file mode 100644 index 0000000..16ea828 --- /dev/null +++ b/drivers/s390/cio/idset.c @@ -0,0 +1,112 @@ +/* + * drivers/s390/cio/idset.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#include <linux/slab.h> +#include <asm/bitops.h> +#include "idset.h" +#include "css.h" + +struct idset { + int num_ssid; + int num_id; + unsigned long bitmap[0]; +}; + +static inline unsigned long bitmap_size(int num_ssid, int num_id) +{ + return __BITOPS_WORDS(num_ssid * num_id) * sizeof(unsigned long); +} + +static struct idset *idset_new(int num_ssid, int num_id) +{ + struct idset *set; + + set = kzalloc(sizeof(struct idset) + bitmap_size(num_ssid, num_id), + GFP_KERNEL); + if (set) { + set->num_ssid = num_ssid; + set->num_id = num_id; + } + return set; +} + +void idset_free(struct idset *set) +{ + kfree(set); +} + +void idset_clear(struct idset *set) +{ + memset(set->bitmap, 0, bitmap_size(set->num_ssid, set->num_id)); +} + +void idset_fill(struct idset *set) +{ + memset(set->bitmap, 0xff, bitmap_size(set->num_ssid, set->num_id)); +} + +static inline void idset_add(struct idset *set, int ssid, int id) +{ + set_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline void idset_del(struct idset *set, int ssid, int id) +{ + clear_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_contains(struct idset *set, int ssid, int id) +{ + return test_bit(ssid * set->num_id + id, set->bitmap); +} + +static inline int idset_get_first(struct idset *set, int *ssid, int *id) +{ + int bitnum; + + bitnum = find_first_bit(set->bitmap, set->num_ssid * set->num_id); + if (bitnum >= set->num_ssid * set->num_id) + return 0; + *ssid = bitnum / set->num_id; + *id = bitnum % set->num_id; + return 1; +} + +struct idset *idset_sch_new(void) +{ + return idset_new(__MAX_SSID + 1, __MAX_SUBCHANNEL + 1); +} + +void idset_sch_add(struct idset *set, struct subchannel_id schid) +{ + idset_add(set, schid.ssid, schid.sch_no); +} + +void idset_sch_del(struct idset *set, struct subchannel_id schid) +{ + idset_del(set, schid.ssid, schid.sch_no); +} + +int idset_sch_contains(struct idset *set, struct subchannel_id schid) +{ + return idset_contains(set, schid.ssid, schid.sch_no); +} + +int idset_sch_get_first(struct idset *set, struct subchannel_id *schid) +{ + int ssid = 0; + int id = 0; + int rc; + + rc = idset_get_first(set, &ssid, &id); + if (rc) { + init_subchannel_id(schid); + schid->ssid = ssid; + schid->sch_no = id; + } + return rc; +} diff --git a/drivers/s390/cio/idset.h b/drivers/s390/cio/idset.h new file mode 100644 index 0000000..144466a --- /dev/null +++ b/drivers/s390/cio/idset.h @@ -0,0 +1,25 @@ +/* + * drivers/s390/cio/idset.h + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com> + */ + +#ifndef S390_IDSET_H +#define S390_IDSET_H S390_IDSET_H + +#include "schid.h" + +struct idset; + +void idset_free(struct idset *set); +void idset_clear(struct idset *set); +void idset_fill(struct idset *set); + +struct idset *idset_sch_new(void); +void idset_sch_add(struct idset *set, struct subchannel_id id); +void idset_sch_del(struct idset *set, struct subchannel_id id); +int idset_sch_contains(struct idset *set, struct subchannel_id id); +int idset_sch_get_first(struct idset *set, struct subchannel_id *id); + +#endif /* S390_IDSET_H */ diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h index ad6d829..7153dd9 100644 --- a/drivers/s390/cio/ioasm.h +++ b/drivers/s390/cio/ioasm.h @@ -1,6 +1,7 @@ #ifndef S390_CIO_IOASM_H #define S390_CIO_IOASM_H +#include <asm/chpid.h> #include "schid.h" /* @@ -189,9 +190,9 @@ static inline int chsc(void *chsc_area) return cc; } -static inline int rchp(int chpid) +static inline int rchp(struct chp_id chpid) { - register unsigned int reg1 asm ("1") = chpid; + register struct chp_id reg1 asm ("1") = chpid; int ccode; asm volatile( diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c index 5b1e3ff..05fac07 100644 --- a/drivers/s390/cio/qdio.c +++ b/drivers/s390/cio/qdio.c @@ -210,9 +210,11 @@ again: goto again; } if (rc < 0) { - QDIO_DBF_TEXT3(1,trace,"sqberr"); - sprintf(dbf_text,"%2x,%2x,%d,%d",tmp_cnt,*cnt,ccq,q_no); - QDIO_DBF_TEXT3(1,trace,dbf_text); + QDIO_DBF_TEXT3(1,trace,"sqberr"); + sprintf(dbf_text,"%2x,%2x",tmp_cnt,*cnt); + QDIO_DBF_TEXT3(1,trace,dbf_text); + sprintf(dbf_text,"%d,%d",ccq,q_no); + QDIO_DBF_TEXT3(1,trace,dbf_text); q->handler(q->cdev,QDIO_STATUS_ACTIVATE_CHECK_CONDITION| QDIO_STATUS_LOOK_FOR_ERROR, 0, 0, 0, -1, -1, q->int_parm); @@ -1250,7 +1252,6 @@ qdio_is_inbound_q_done(struct qdio_q *q) if (!no_used) { QDIO_DBF_TEXT4(0,trace,"inqisdnA"); QDIO_DBF_HEX4(0,trace,&q,sizeof(void*)); - QDIO_DBF_TEXT4(0,trace,dbf_text); return 1; } if (irq->is_qebsm) { @@ -3371,10 +3372,15 @@ qdio_do_qdio_fill_input(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + int tmp = 0; + qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); if (irq->is_qebsm) { - while (count) - set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); + while (count) { + tmp = set_slsb(q, &qidx, SLSB_CU_INPUT_EMPTY, &count); + if (!tmp) + return; + } return; } for (;;) { @@ -3390,11 +3396,15 @@ qdio_do_qdio_fill_output(struct qdio_q *q, unsigned int qidx, unsigned int count, struct qdio_buffer *buffers) { struct qdio_irq *irq = (struct qdio_irq *) q->irq_ptr; + int tmp = 0; qidx &= (QDIO_MAX_BUFFERS_PER_Q - 1); if (irq->is_qebsm) { - while (count) - set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); + while (count) { + tmp = set_slsb(q, &qidx, SLSB_CU_OUTPUT_PRIMED, &count); + if (!tmp) + return; + } return; } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index c7d1355..5aac0ec 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -65,6 +65,8 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000); MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 1 (on)."); static struct device *ap_root_device = NULL; +static DEFINE_SPINLOCK(ap_device_lock); +static LIST_HEAD(ap_device_list); /** * Workqueue & timer for bus rescan. @@ -421,27 +423,25 @@ static int ap_uevent (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { struct ap_device *ap_dev = to_ap_dev(dev); - int length; + int retval = 0, length = 0, i = 0; if (!ap_dev) return -ENODEV; /* Set up DEV_TYPE environment variable. */ - envp[0] = buffer; - length = scnprintf(buffer, buffer_size, "DEV_TYPE=%04X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - buffer += length; - buffer_size -= length; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "DEV_TYPE=%04X", ap_dev->device_type); + if (retval) + return retval; + /* Add MODALIAS= */ - envp[1] = buffer; - length = scnprintf(buffer, buffer_size, "MODALIAS=ap:t%02X", - ap_dev->device_type); - if (buffer_size - length <= 0) - return -ENOMEM; - envp[2] = NULL; - return 0; + retval = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=ap:t%02X", ap_dev->device_type); + + envp[i] = NULL; + return retval; } static struct bus_type ap_bus_type = { @@ -457,6 +457,9 @@ static int ap_device_probe(struct device *dev) int rc; ap_dev->drv = ap_drv; + spin_lock_bh(&ap_device_lock); + list_add(&ap_dev->list, &ap_device_list); + spin_unlock_bh(&ap_device_lock); rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV; return rc; } @@ -497,6 +500,12 @@ static int ap_device_remove(struct device *dev) ap_flush_queue(ap_dev); if (ap_drv->remove) ap_drv->remove(ap_dev); + spin_lock_bh(&ap_device_lock); + list_del_init(&ap_dev->list); + spin_unlock_bh(&ap_device_lock); + spin_lock_bh(&ap_dev->lock); + atomic_sub(ap_dev->queue_count, &ap_poll_requests); + spin_unlock_bh(&ap_dev->lock); return 0; } @@ -749,10 +758,16 @@ static void ap_scan_bus(struct work_struct *unused) (void *)(unsigned long)qid, __ap_scan_bus); rc = ap_query_queue(qid, &queue_depth, &device_type); - if (dev && rc) { - put_device(dev); - device_unregister(dev); - continue; + if (dev) { + ap_dev = to_ap_dev(dev); + spin_lock_bh(&ap_dev->lock); + if (rc || ap_dev->unregistered) { + spin_unlock_bh(&ap_dev->lock); + put_device(dev); + device_unregister(dev); + continue; + } else + spin_unlock_bh(&ap_dev->lock); } if (dev) { put_device(dev); @@ -772,6 +787,7 @@ static void ap_scan_bus(struct work_struct *unused) spin_lock_init(&ap_dev->lock); INIT_LIST_HEAD(&ap_dev->pendingq); INIT_LIST_HEAD(&ap_dev->requestq); + INIT_LIST_HEAD(&ap_dev->list); if (device_type == 0) ap_probe_device_type(ap_dev); else @@ -852,6 +868,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags) case AP_RESPONSE_NO_PENDING_REPLY: if (status.queue_empty) { /* The card shouldn't forget requests but who knows. */ + atomic_sub(ap_dev->queue_count, &ap_poll_requests); ap_dev->queue_count = 0; list_splice_init(&ap_dev->pendingq, &ap_dev->requestq); ap_dev->requestq_count += ap_dev->pendingq_count; @@ -985,7 +1002,7 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg) ap_dev->unregistered = 1; } else { ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-ENODEV)); - rc = 0; + rc = -ENODEV; } spin_unlock_bh(&ap_dev->lock); if (rc == -ENODEV) @@ -1033,31 +1050,29 @@ static void ap_poll_timeout(unsigned long unused) * polling until bit 2^0 of the control flags is not set. If bit 2^1 * of the control flags has been set arm the poll timer. */ -static int __ap_poll_all(struct device *dev, void *data) +static int __ap_poll_all(struct ap_device *ap_dev, unsigned long *flags) { - struct ap_device *ap_dev = to_ap_dev(dev); - int rc; - spin_lock(&ap_dev->lock); if (!ap_dev->unregistered) { - rc = ap_poll_queue(to_ap_dev(dev), (unsigned long *) data); - if (rc) + if (ap_poll_queue(ap_dev, flags)) ap_dev->unregistered = 1; - } else - rc = 0; + } spin_unlock(&ap_dev->lock); - if (rc) - device_unregister(&ap_dev->device); return 0; } static void ap_poll_all(unsigned long dummy) { unsigned long flags; + struct ap_device *ap_dev; do { flags = 0; - bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); + spin_lock(&ap_device_lock); + list_for_each_entry(ap_dev, &ap_device_list, list) { + __ap_poll_all(ap_dev, &flags); + } + spin_unlock(&ap_device_lock); } while (flags & 1); if (flags & 2) ap_schedule_poll_timer(); @@ -1075,6 +1090,7 @@ static int ap_poll_thread(void *data) DECLARE_WAITQUEUE(wait, current); unsigned long flags; int requests; + struct ap_device *ap_dev; set_user_nice(current, 19); while (1) { @@ -1092,10 +1108,12 @@ static int ap_poll_thread(void *data) set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); - local_bh_disable(); flags = 0; - bus_for_each_dev(&ap_bus_type, NULL, &flags, __ap_poll_all); - local_bh_enable(); + spin_lock_bh(&ap_device_lock); + list_for_each_entry(ap_dev, &ap_device_list, list) { + __ap_poll_all(ap_dev, &flags); + } + spin_unlock_bh(&ap_device_lock); } set_current_state(TASK_RUNNING); remove_wait_queue(&ap_poll_wait, &wait); diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 83b69c0..008559e 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -106,6 +106,7 @@ struct ap_device { struct device device; struct ap_driver *drv; /* Pointer to AP device driver. */ spinlock_t lock; /* Per device lock. */ + struct list_head list; /* private list of all AP devices. */ ap_qid_t qid; /* AP queue id. */ int queue_depth; /* AP queue depth.*/ diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 9976139..e3625a4 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -298,14 +298,14 @@ static long zcrypt_rsa_modexpo(struct ica_rsa_modexpo *mex) get_device(&zdev->ap_dev->device); zdev->request_count++; __zcrypt_decrease_preference(zdev); - spin_unlock_bh(&zcrypt_device_lock); if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + spin_unlock_bh(&zcrypt_device_lock); rc = zdev->ops->rsa_modexpo(zdev, mex); + spin_lock_bh(&zcrypt_device_lock); module_put(zdev->ap_dev->drv->driver.owner); } else rc = -EAGAIN; - spin_lock_bh(&zcrypt_device_lock); zdev->request_count--; __zcrypt_increase_preference(zdev); put_device(&zdev->ap_dev->device); @@ -373,14 +373,14 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) get_device(&zdev->ap_dev->device); zdev->request_count++; __zcrypt_decrease_preference(zdev); - spin_unlock_bh(&zcrypt_device_lock); if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + spin_unlock_bh(&zcrypt_device_lock); rc = zdev->ops->rsa_modexpo_crt(zdev, crt); + spin_lock_bh(&zcrypt_device_lock); module_put(zdev->ap_dev->drv->driver.owner); } else rc = -EAGAIN; - spin_lock_bh(&zcrypt_device_lock); zdev->request_count--; __zcrypt_increase_preference(zdev); put_device(&zdev->ap_dev->device); @@ -408,14 +408,14 @@ static long zcrypt_send_cprb(struct ica_xcRB *xcRB) get_device(&zdev->ap_dev->device); zdev->request_count++; __zcrypt_decrease_preference(zdev); - spin_unlock_bh(&zcrypt_device_lock); if (try_module_get(zdev->ap_dev->drv->driver.owner)) { + spin_unlock_bh(&zcrypt_device_lock); rc = zdev->ops->send_cprb(zdev, xcRB); + spin_lock_bh(&zcrypt_device_lock); module_put(zdev->ap_dev->drv->driver.owner); } else rc = -EAGAIN; - spin_lock_bh(&zcrypt_device_lock); zdev->request_count--; __zcrypt_increase_preference(zdev); put_device(&zdev->ap_dev->device); diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 7809a79..6dd64d0 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -3525,8 +3525,8 @@ unpack_next: memcpy(skb_put(skb,len_of_data), privptr->p_mtc_envelope, len_of_data); - skb->mac.raw=skb->data; skb->dev=dev; + skb_reset_mac_header(skb); skb->protocol=htons(ETH_P_IP); skb->ip_summed=CHECKSUM_UNNECESSARY; privptr->stats.rx_packets++; diff --git a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c index 0d6d5fc..b20fd06 100644 --- a/drivers/s390/net/ctcmain.c +++ b/drivers/s390/net/ctcmain.c @@ -455,7 +455,7 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb) return; } skb_put(pskb, header->length); - pskb->mac.raw = pskb->data; + skb_reset_mac_header(pskb); len -= header->length; skb = dev_alloc_skb(pskb->len); if (!skb) { @@ -472,8 +472,9 @@ ctc_unpack_skb(struct channel *ch, struct sk_buff *pskb) privptr->stats.rx_dropped++; return; } - memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len); - skb->mac.raw = skb->data; + skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), + pskb->len); + skb_reset_mac_header(skb); skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; @@ -706,7 +707,8 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) spin_unlock(&ch->collect_lock); return; } - ch->trans_skb->tail = ch->trans_skb->data = ch->trans_skb_data; + ch->trans_skb->data = ch->trans_skb_data; + skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; if (ch->prof.maxmulti < (ch->collect_len + 2)) ch->prof.maxmulti = ch->collect_len + 2; @@ -715,8 +717,9 @@ ch_action_txdone(fsm_instance * fi, int event, void *arg) *((__u16 *) skb_put(ch->trans_skb, 2)) = ch->collect_len + 2; i = 0; while ((skb = skb_dequeue(&ch->collect_queue))) { - memcpy(skb_put(ch->trans_skb, skb->len), skb->data, - skb->len); + skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, + skb->len), + skb->len); privptr->stats.tx_packets++; privptr->stats.tx_bytes += skb->len - LL_HEADER_LENGTH; atomic_dec(&skb->users); @@ -831,7 +834,8 @@ ch_action_rx(fsm_instance * fi, int event, void *arg) ctc_unpack_skb(ch, skb); } again: - skb->data = skb->tail = ch->trans_skb_data; + skb->data = ch->trans_skb_data; + skb_reset_tail_pointer(skb); skb->len = 0; if (ctc_checkalloc_buffer(ch, 1)) return; @@ -1638,21 +1642,19 @@ add_channel(struct ccw_device *cdev, enum channel_types type) struct channel *ch; DBF_TEXT(trace, 2, __FUNCTION__); - if ((ch = - (struct channel *) kmalloc(sizeof (struct channel), - GFP_KERNEL)) == NULL) { + ch = kzalloc(sizeof(struct channel), GFP_KERNEL); + if (!ch) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } - memset(ch, 0, sizeof (struct channel)); - if ((ch->ccw = kmalloc(8*sizeof(struct ccw1), - GFP_KERNEL | GFP_DMA)) == NULL) { + /* assure all flags and counters are reset */ + ch->ccw = kzalloc(8 * sizeof(struct ccw1), GFP_KERNEL | GFP_DMA); + if (!ch->ccw) { kfree(ch); ctc_pr_warn("ctc: Out of memory in add_channel\n"); return -1; } - memset(ch->ccw, 0, 8*sizeof(struct ccw1)); // assure all flags and counters are reset /** * "static" ccws are used in the following way: @@ -1692,15 +1694,14 @@ add_channel(struct ccw_device *cdev, enum channel_types type) return -1; } fsm_newstate(ch->fsm, CH_STATE_IDLE); - if ((ch->irb = kmalloc(sizeof (struct irb), - GFP_KERNEL)) == NULL) { + ch->irb = kzalloc(sizeof(struct irb), GFP_KERNEL); + if (!ch->irb) { ctc_pr_warn("ctc: Out of memory in add_channel\n"); kfree_fsm(ch->fsm); kfree(ch->ccw); kfree(ch); return -1; } - memset(ch->irb, 0, sizeof (struct irb)); while (*c && less_than((*c)->id, ch->id)) c = &(*c)->next; if (*c && (!strncmp((*c)->id, ch->id, CTC_ID_SIZE))) { @@ -2226,7 +2227,8 @@ transmit_skb(struct channel *ch, struct sk_buff *skb) * IDAL support in CTC is broken, so we have to * care about skb's above 2G ourselves. */ - hi = ((unsigned long) skb->tail + LL_HEADER_LENGTH) >> 31; + hi = ((unsigned long)skb_tail_pointer(skb) + + LL_HEADER_LENGTH) >> 31; if (hi) { nskb = alloc_skb(skb->len, GFP_ATOMIC | GFP_DMA); if (!nskb) { @@ -2262,11 +2264,12 @@ transmit_skb(struct channel *ch, struct sk_buff *skb) return -EBUSY; } - ch->trans_skb->tail = ch->trans_skb->data; + skb_reset_tail_pointer(ch->trans_skb); ch->trans_skb->len = 0; ch->ccw[1].count = skb->len; - memcpy(skb_put(ch->trans_skb, skb->len), skb->data, - skb->len); + skb_copy_from_linear_data(skb, skb_put(ch->trans_skb, + skb->len), + skb->len); atomic_dec(&skb->users); dev_kfree_skb_irq(skb); ccw_idx = 0; @@ -2745,14 +2748,13 @@ ctc_probe_device(struct ccwgroup_device *cgdev) if (!get_device(&cgdev->dev)) return -ENODEV; - priv = kmalloc(sizeof (struct ctc_priv), GFP_KERNEL); + priv = kzalloc(sizeof(struct ctc_priv), GFP_KERNEL); if (!priv) { ctc_pr_err("%s: Out of memory\n", __func__); put_device(&cgdev->dev); return -ENOMEM; } - memset(priv, 0, sizeof (struct ctc_priv)); rc = ctc_add_files(&cgdev->dev); if (rc) { kfree(priv); @@ -2793,10 +2795,9 @@ ctc_init_netdevice(struct net_device * dev, int alloc_device, DBF_TEXT(setup, 3, __FUNCTION__); if (alloc_device) { - dev = kmalloc(sizeof (struct net_device), GFP_KERNEL); + dev = kzalloc(sizeof(struct net_device), GFP_KERNEL); if (!dev) return NULL; - memset(dev, 0, sizeof (struct net_device)); } dev->priv = privptr; diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index ecca104..08a994f 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -1576,7 +1576,7 @@ __lcs_start_xmit(struct lcs_card *card, struct sk_buff *skb, header->offset = card->tx_buffer->count; header->type = card->lan_type; header->slot = card->portno; - memcpy(header + 1, skb->data, skb->len); + skb_copy_from_linear_data(skb, header + 1, skb->len); spin_unlock(&card->lock); card->stats.tx_bytes += skb->len; card->stats.tx_packets++; @@ -1784,7 +1784,6 @@ lcs_get_skb(struct lcs_card *card, char *skb_data, unsigned int skb_len) card->stats.rx_dropped++; return; } - skb->dev = card->dev; memcpy(skb_put(skb, skb_len), skb_data, skb_len); skb->protocol = card->lan_type_trans(skb, card->dev); card->stats.rx_bytes += skb_len; diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 594320c..e10e85e 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -635,7 +635,7 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, return; } skb_put(pskb, header->next); - pskb->mac.raw = pskb->data; + skb_reset_mac_header(pskb); skb = dev_alloc_skb(pskb->len); if (!skb) { PRINT_WARN("%s Out of memory in netiucv_unpack_skb\n", @@ -645,8 +645,9 @@ static void netiucv_unpack_skb(struct iucv_connection *conn, privptr->stats.rx_dropped++; return; } - memcpy(skb_put(skb, pskb->len), pskb->data, pskb->len); - skb->mac.raw = skb->data; + skb_copy_from_linear_data(pskb, skb_put(skb, pskb->len), + pskb->len); + skb_reset_mac_header(skb); skb->dev = pskb->dev; skb->protocol = pskb->protocol; pskb->ip_summed = CHECKSUM_UNNECESSARY; @@ -689,7 +690,8 @@ static void conn_action_rx(fsm_instance *fi, int event, void *arg) msg->length, conn->max_buffsize); return; } - conn->rx_buff->data = conn->rx_buff->tail = conn->rx_buff->head; + conn->rx_buff->data = conn->rx_buff->head; + skb_reset_tail_pointer(conn->rx_buff); conn->rx_buff->len = 0; rc = iucv_message_receive(conn->path, msg, 0, conn->rx_buff->data, msg->length, NULL); @@ -735,14 +737,17 @@ static void conn_action_txdone(fsm_instance *fi, int event, void *arg) } } } - conn->tx_buff->data = conn->tx_buff->tail = conn->tx_buff->head; + conn->tx_buff->data = conn->tx_buff->head; + skb_reset_tail_pointer(conn->tx_buff); conn->tx_buff->len = 0; spin_lock_irqsave(&conn->collect_lock, saveflags); while ((skb = skb_dequeue(&conn->collect_queue))) { header.next = conn->tx_buff->len + skb->len + NETIUCV_HDRLEN; memcpy(skb_put(conn->tx_buff, NETIUCV_HDRLEN), &header, NETIUCV_HDRLEN); - memcpy(skb_put(conn->tx_buff, skb->len), skb->data, skb->len); + skb_copy_from_linear_data(skb, + skb_put(conn->tx_buff, skb->len), + skb->len); txbytes += skb->len; txpackets++; stat_maxcq++; @@ -1164,8 +1169,8 @@ static int netiucv_transmit_skb(struct iucv_connection *conn, * Copy the skb to a new allocated skb in lowmem only if the * data is located above 2G in memory or tailroom is < 2. */ - unsigned long hi = - ((unsigned long)(skb->tail + NETIUCV_HDRLEN)) >> 31; + unsigned long hi = ((unsigned long)(skb_tail_pointer(skb) + + NETIUCV_HDRLEN)) >> 31; int copied = 0; if (hi || (skb_tailroom(skb) < 2)) { nskb = alloc_skb(skb->len + NETIUCV_HDRLEN + diff --git a/drivers/s390/net/qeth.h b/drivers/s390/net/qeth.h index e95c281..84b108d 100644 --- a/drivers/s390/net/qeth.h +++ b/drivers/s390/net/qeth.h @@ -873,7 +873,7 @@ qeth_realloc_headroom(struct qeth_card *card, struct sk_buff *skb, int size) } static inline struct sk_buff * -qeth_pskb_unshare(struct sk_buff *skb, int pri) +qeth_pskb_unshare(struct sk_buff *skb, gfp_t pri) { struct sk_buff *nskb; if (!skb_cloned(skb)) diff --git a/drivers/s390/net/qeth_eddp.c b/drivers/s390/net/qeth_eddp.c index 7c735e1..dd7034f 100644 --- a/drivers/s390/net/qeth_eddp.c +++ b/drivers/s390/net/qeth_eddp.c @@ -267,7 +267,8 @@ qeth_eddp_copy_data_tcp(char *dst, struct qeth_eddp_data *eddp, int len, QETH_DBF_TEXT(trace, 5, "eddpcdtc"); if (skb_shinfo(eddp->skb)->nr_frags == 0) { - memcpy(dst, eddp->skb->data + eddp->skb_offset, len); + skb_copy_from_linear_data_offset(eddp->skb, eddp->skb_offset, + dst, len); *hcsum = csum_partial(eddp->skb->data + eddp->skb_offset, len, *hcsum); eddp->skb_offset += len; @@ -416,7 +417,7 @@ __qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, eddp->skb_offset += VLAN_HLEN; #endif /* CONFIG_QETH_VLAN */ } - tcph = eddp->skb->h.th; + tcph = tcp_hdr(eddp->skb); while (eddp->skb_offset < eddp->skb->len) { data_len = min((int)skb_shinfo(eddp->skb)->gso_size, (int)(eddp->skb->len - eddp->skb_offset)); @@ -473,20 +474,24 @@ qeth_eddp_fill_context_tcp(struct qeth_eddp_context *ctx, QETH_DBF_TEXT(trace, 5, "eddpficx"); /* create our segmentation headers and copy original headers */ if (skb->protocol == htons(ETH_P_IP)) - eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.iph, - skb->nh.iph->ihl*4, - (u8 *)skb->h.th, skb->h.th->doff*4); + eddp = qeth_eddp_create_eddp_data(qhdr, + skb_network_header(skb), + ip_hdrlen(skb), + skb_transport_header(skb), + tcp_hdrlen(skb)); else - eddp = qeth_eddp_create_eddp_data(qhdr, (u8 *)skb->nh.ipv6h, - sizeof(struct ipv6hdr), - (u8 *)skb->h.th, skb->h.th->doff*4); + eddp = qeth_eddp_create_eddp_data(qhdr, + skb_network_header(skb), + sizeof(struct ipv6hdr), + skb_transport_header(skb), + tcp_hdrlen(skb)); if (eddp == NULL) { QETH_DBF_TEXT(trace, 2, "eddpfcnm"); return -ENOMEM; } if (qhdr->hdr.l2.id == QETH_HEADER_TYPE_LAYER2) { - skb->mac.raw = (skb->data) + sizeof(struct qeth_hdr); + skb_set_mac_header(skb, sizeof(struct qeth_hdr)); memcpy(&eddp->mac, eth_hdr(skb), ETH_HLEN); #ifdef CONFIG_QETH_VLAN if (eddp->mac.h_proto == __constant_htons(ETH_P_8021Q)) { @@ -590,12 +595,13 @@ qeth_eddp_create_context_tcp(struct qeth_card *card, struct sk_buff *skb, QETH_DBF_TEXT(trace, 5, "creddpct"); if (skb->protocol == htons(ETH_P_IP)) ctx = qeth_eddp_create_context_generic(card, skb, - sizeof(struct qeth_hdr) + skb->nh.iph->ihl*4 + - skb->h.th->doff*4); + (sizeof(struct qeth_hdr) + + ip_hdrlen(skb) + + tcp_hdrlen(skb))); else if (skb->protocol == htons(ETH_P_IPV6)) ctx = qeth_eddp_create_context_generic(card, skb, sizeof(struct qeth_hdr) + sizeof(struct ipv6hdr) + - skb->h.th->doff*4); + tcp_hdrlen(skb)); else QETH_DBF_TEXT(trace, 2, "cetcpinv"); diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c index 2257e45..ad7792d 100644 --- a/drivers/s390/net/qeth_main.c +++ b/drivers/s390/net/qeth_main.c @@ -2278,7 +2278,7 @@ qeth_type_trans(struct sk_buff *skb, struct net_device *dev) (card->info.link_type == QETH_LINK_TYPE_LANE_TR)) return tr_type_trans(skb,dev); #endif /* CONFIG_TR */ - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb_pull(skb, ETH_HLEN ); eth = eth_hdr(skb); @@ -2306,9 +2306,9 @@ qeth_rebuild_skb_fake_ll_tr(struct qeth_card *card, struct sk_buff *skb, struct iphdr *ip_hdr; QETH_DBF_TEXT(trace,5,"skbfktr"); - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_TR; + skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_TR); /* this is a fake ethernet header */ - fake_hdr = (struct trh_hdr *) skb->mac.raw; + fake_hdr = tr_hdr(skb); /* the destination MAC address */ switch (skb->pkt_type){ @@ -2359,9 +2359,9 @@ qeth_rebuild_skb_fake_ll_eth(struct qeth_card *card, struct sk_buff *skb, struct iphdr *ip_hdr; QETH_DBF_TEXT(trace,5,"skbfketh"); - skb->mac.raw = skb->data - QETH_FAKE_LL_LEN_ETH; + skb_set_mac_header(skb, -QETH_FAKE_LL_LEN_ETH); /* this is a fake ethernet header */ - fake_hdr = (struct ethhdr *) skb->mac.raw; + fake_hdr = eth_hdr(skb); /* the destination MAC address */ switch (skb->pkt_type){ @@ -2461,7 +2461,7 @@ qeth_rebuild_skb(struct qeth_card *card, struct sk_buff *skb, if (card->options.fake_ll) qeth_rebuild_skb_fake_ll(card, skb, hdr); else - skb->mac.raw = skb->data; + skb_reset_mac_header(skb); skb->ip_summed = card->options.checksum_type; if (card->options.checksum_type == HW_CHECKSUMMING){ if ( (hdr->hdr.l3.ext_flags & @@ -2501,7 +2501,8 @@ qeth_process_inbound_buffer(struct qeth_card *card, vlan_tag = qeth_rebuild_skb(card, skb, hdr); else { /*in case of OSN*/ skb_push(skb, sizeof(struct qeth_hdr)); - memcpy(skb->data, hdr, sizeof(struct qeth_hdr)); + skb_copy_to_linear_data(skb, hdr, + sizeof(struct qeth_hdr)); } /* is device UP ? */ if (!(card->dev->flags & IFF_UP)){ @@ -3654,7 +3655,7 @@ qeth_verify_vlan_dev(struct net_device *dev, struct qeth_card *card) return rc; for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++){ - if (vg->vlan_devices[i] == dev){ + if (vlan_group_get_device(vg, i) == dev){ rc = QETH_VLAN_CARD; break; } @@ -3778,9 +3779,11 @@ qeth_get_cast_type(struct qeth_card *card, struct sk_buff *skb) } /* try something else */ if (skb->protocol == ETH_P_IPV6) - return (skb->nh.raw[24] == 0xff) ? RTN_MULTICAST : 0; + return (skb_network_header(skb)[24] == 0xff) ? + RTN_MULTICAST : 0; else if (skb->protocol == ETH_P_IP) - return ((skb->nh.raw[16] & 0xf0) == 0xe0) ? RTN_MULTICAST : 0; + return ((skb_network_header(skb)[16] & 0xf0) == 0xe0) ? + RTN_MULTICAST : 0; /* ... */ if (!memcmp(skb->data, skb->dev->broadcast, 6)) return RTN_BROADCAST; @@ -3818,18 +3821,20 @@ qeth_get_priority_queue(struct qeth_card *card, struct sk_buff *skb, return card->info.is_multicast_different & (card->qdio.no_out_queues - 1); if (card->qdio.do_prio_queueing && (ipv == 4)) { + const u8 tos = ip_hdr(skb)->tos; + if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_TOS){ - if (skb->nh.iph->tos & IP_TOS_NOTIMPORTANT) + if (tos & IP_TOS_NOTIMPORTANT) return 3; - if (skb->nh.iph->tos & IP_TOS_HIGHRELIABILITY) + if (tos & IP_TOS_HIGHRELIABILITY) return 2; - if (skb->nh.iph->tos & IP_TOS_HIGHTHROUGHPUT) + if (tos & IP_TOS_HIGHTHROUGHPUT) return 1; - if (skb->nh.iph->tos & IP_TOS_LOWDELAY) + if (tos & IP_TOS_LOWDELAY) return 0; } if (card->qdio.do_prio_queueing==QETH_PRIO_Q_ING_PREC) - return 3 - (skb->nh.iph->tos >> 6); + return 3 - (tos >> 6); } else if (card->qdio.do_prio_queueing && (ipv == 6)) { /* TODO: IPv6!!! */ } @@ -3866,9 +3871,9 @@ __qeth_prepare_skb(struct qeth_card *card, struct sk_buff *skb, int ipv) * memcpys instead of one memmove to save cycles. */ skb_push(skb, VLAN_HLEN); - memcpy(skb->data, skb->data + 4, 4); - memcpy(skb->data + 4, skb->data + 8, 4); - memcpy(skb->data + 8, skb->data + 12, 4); + skb_copy_to_linear_data(skb, skb->data + 4, 4); + skb_copy_to_linear_data_offset(skb, 4, skb->data + 8, 4); + skb_copy_to_linear_data_offset(skb, 8, skb->data + 12, 4); tag = (u16 *)(skb->data + 12); /* * first two bytes = ETH_P_8021Q (0x8100) @@ -4039,7 +4044,8 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, *((u32 *) skb->dst->neighbour->primary_key); } else { /* fill in destination address used in ip header */ - *((u32 *) (&hdr->hdr.l3.dest_addr[12])) = skb->nh.iph->daddr; + *((u32 *)(&hdr->hdr.l3.dest_addr[12])) = + ip_hdr(skb)->daddr; } } else if (ipv == 6) { /* IPv6 or passthru */ hdr->hdr.l3.flags = qeth_get_qeth_hdr_flags6(cast_type); @@ -4048,7 +4054,8 @@ qeth_fill_header(struct qeth_card *card, struct qeth_hdr *hdr, skb->dst->neighbour->primary_key, 16); } else { /* fill in destination address used in ip header */ - memcpy(hdr->hdr.l3.dest_addr, &skb->nh.ipv6h->daddr, 16); + memcpy(hdr->hdr.l3.dest_addr, + &ipv6_hdr(skb)->daddr, 16); } } else { /* passthrough */ if((skb->dev->type == ARPHRD_IEEE802_TR) && @@ -5261,7 +5268,7 @@ qeth_free_vlan_addresses4(struct qeth_card *card, unsigned short vid) QETH_DBF_TEXT(trace, 4, "frvaddr4"); rcu_read_lock(); - in_dev = __in_dev_get_rcu(card->vlangrp->vlan_devices[vid]); + in_dev = __in_dev_get_rcu(vlan_group_get_device(card->vlangrp, vid)); if (!in_dev) goto out; for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { @@ -5288,7 +5295,7 @@ qeth_free_vlan_addresses6(struct qeth_card *card, unsigned short vid) QETH_DBF_TEXT(trace, 4, "frvaddr6"); - in6_dev = in6_dev_get(card->vlangrp->vlan_devices[vid]); + in6_dev = in6_dev_get(vlan_group_get_device(card->vlangrp, vid)); if (!in6_dev) return; for (ifa = in6_dev->addr_list; ifa; ifa = ifa->lst_next){ @@ -5360,7 +5367,7 @@ qeth_layer2_process_vlans(struct qeth_card *card, int clear) if (!card->vlangrp) return; for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (card->vlangrp->vlan_devices[i] == NULL) + if (vlan_group_get_device(card->vlangrp, i) == NULL) continue; if (clear) qeth_layer2_send_setdelvlan(card, i, IPA_CMD_DELVLAN); @@ -5398,8 +5405,7 @@ qeth_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid) spin_lock_irqsave(&card->vlanlock, flags); /* unregister IP addresses of vlan device */ qeth_free_vlan_addresses(card, vid); - if (card->vlangrp) - card->vlangrp->vlan_devices[vid] = NULL; + vlan_group_set_device(card->vlangrp, vid, NULL); spin_unlock_irqrestore(&card->vlanlock, flags); if (card->options.layer2) qeth_layer2_send_setdelvlan(card, vid, IPA_CMD_DELVLAN); @@ -5662,10 +5668,11 @@ qeth_add_vlan_mc(struct qeth_card *card) vg = card->vlangrp; for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (vg->vlan_devices[i] == NULL || - !(vg->vlan_devices[i]->flags & IFF_UP)) + struct net_device *netdev = vlan_group_get_device(vg, i); + if (netdev == NULL || + !(netdev->flags & IFF_UP)) continue; - in_dev = in_dev_get(vg->vlan_devices[i]); + in_dev = in_dev_get(netdev); if (!in_dev) continue; read_lock(&in_dev->mc_list_lock); @@ -5749,10 +5756,11 @@ qeth_add_vlan_mc6(struct qeth_card *card) vg = card->vlangrp; for (i = 0; i < VLAN_GROUP_ARRAY_LEN; i++) { - if (vg->vlan_devices[i] == NULL || - !(vg->vlan_devices[i]->flags & IFF_UP)) + struct net_device *netdev = vlan_group_get_device(vg, i); + if (netdev == NULL || + !(netdev->flags & IFF_UP)) continue; - in_dev = in6_dev_get(vg->vlan_devices[i]); + in_dev = in6_dev_get(netdev); if (!in_dev) continue; read_lock(&in_dev->lock); diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c index 81f805c..89d56c8 100644 --- a/drivers/s390/net/qeth_proc.c +++ b/drivers/s390/net/qeth_proc.c @@ -37,7 +37,6 @@ qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) struct device *dev = NULL; loff_t nr = 0; - down_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); if (*offset == 0) return SEQ_START_TOKEN; while (1) { @@ -53,7 +52,6 @@ qeth_procfile_seq_start(struct seq_file *s, loff_t *offset) static void qeth_procfile_seq_stop(struct seq_file *s, void* it) { - up_read(&qeth_ccwgroup_driver.driver.bus->subsys.rwsem); } static void * diff --git a/drivers/s390/net/qeth_tso.h b/drivers/s390/net/qeth_tso.h index 14504af..c20e923 100644 --- a/drivers/s390/net/qeth_tso.h +++ b/drivers/s390/net/qeth_tso.h @@ -40,8 +40,8 @@ qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) QETH_DBF_TEXT(trace, 5, "tsofhdr"); hdr = (struct qeth_hdr_tso *) skb->data; - iph = skb->nh.iph; - tcph = skb->h.th; + iph = ip_hdr(skb); + tcph = tcp_hdr(skb); /*fix header to TSO values ...*/ hdr->hdr.hdr.l3.id = QETH_HEADER_TYPE_TSO; /*set values which are fix for the first approach ...*/ @@ -63,13 +63,9 @@ qeth_tso_fill_header(struct qeth_card *card, struct sk_buff *skb) static inline void qeth_tso_set_tcpip_header(struct qeth_card *card, struct sk_buff *skb) { - struct iphdr *iph; - struct ipv6hdr *ip6h; - struct tcphdr *tcph; - - iph = skb->nh.iph; - ip6h = skb->nh.ipv6h; - tcph = skb->h.th; + struct iphdr *iph = ip_hdr(skb); + struct ipv6hdr *ip6h = ipv6_hdr(skb); + struct tcphdr *tcph = tcp_hdr(skb); tcph->check = 0; if (skb->protocol == ETH_P_IPV6) { diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c index 806bb1a..644a06e 100644 --- a/drivers/s390/s390mach.c +++ b/drivers/s390/s390mach.c @@ -21,6 +21,7 @@ #include "cio/cio.h" #include "cio/chsc.h" #include "cio/css.h" +#include "cio/chp.h" #include "s390mach.h" static struct semaphore m_sem; @@ -44,14 +45,13 @@ static int s390_collect_crw_info(void *param) { struct crw crw[2]; - int ccode, ret, slow; + int ccode; struct semaphore *sem; unsigned int chain; sem = (struct semaphore *)param; repeat: down_interruptible(sem); - slow = 0; chain = 0; while (1) { if (unlikely(chain > 1)) { @@ -84,9 +84,8 @@ repeat: /* Check for overflows. */ if (crw[chain].oflw) { pr_debug("%s: crw overflow detected!\n", __FUNCTION__); - css_reiterate_subchannels(); + css_schedule_eval_all(); chain = 0; - slow = 1; continue; } switch (crw[chain].rsc) { @@ -94,10 +93,7 @@ repeat: if (crw[0].chn && !chain) break; pr_debug("source is subchannel %04X\n", crw[0].rsid); - ret = css_process_crw (crw[0].rsid, - chain ? crw[1].rsid : 0); - if (ret == -EAGAIN) - slow = 1; + css_process_crw(crw[0].rsid, chain ? crw[1].rsid : 0); break; case CRW_RSC_MONITOR: pr_debug("source is monitoring facility\n"); @@ -116,28 +112,23 @@ repeat: } switch (crw[0].erc) { case CRW_ERC_IPARM: /* Path has come. */ - ret = chp_process_crw(crw[0].rsid, 1); + chp_process_crw(crw[0].rsid, 1); break; case CRW_ERC_PERRI: /* Path has gone. */ case CRW_ERC_PERRN: - ret = chp_process_crw(crw[0].rsid, 0); + chp_process_crw(crw[0].rsid, 0); break; default: pr_debug("Don't know how to handle erc=%x\n", crw[0].erc); - ret = 0; } - if (ret == -EAGAIN) - slow = 1; break; case CRW_RSC_CONFIG: pr_debug("source is configuration-alert facility\n"); break; case CRW_RSC_CSS: pr_debug("source is channel subsystem\n"); - ret = chsc_process_crw(); - if (ret == -EAGAIN) - slow = 1; + chsc_process_crw(); break; default: pr_debug("unknown source\n"); @@ -146,8 +137,6 @@ repeat: /* chain is always 0 or 1 here. */ chain = crw[chain].chn ? chain + 1 : 0; } - if (slow) - queue_work(slow_path_wq, &slow_path_work); goto repeat; return 0; } diff --git a/drivers/s390/sysinfo.c b/drivers/s390/sysinfo.c index 090743d..19343f9 100644 --- a/drivers/s390/sysinfo.c +++ b/drivers/s390/sysinfo.c @@ -357,6 +357,24 @@ static __init int create_proc_sysinfo(void) __initcall(create_proc_sysinfo); +int get_cpu_capability(unsigned int *capability) +{ + struct sysinfo_1_2_2 *info; + int rc; + + info = (void *) get_zeroed_page(GFP_KERNEL); + if (!info) + return -ENOMEM; + rc = stsi(info, 1, 2, 2); + if (rc == -ENOSYS) + goto out; + rc = 0; + *capability = info->capability; +out: + free_page((unsigned long) info); + return rc; +} + /* * CPU capability might have changed. Therefore recalculate loops_per_jiffy. */ diff --git a/drivers/sbus/char/Kconfig b/drivers/sbus/char/Kconfig index 3a81529..35a7316 100644 --- a/drivers/sbus/char/Kconfig +++ b/drivers/sbus/char/Kconfig @@ -46,13 +46,6 @@ config SUN_VIDEOPIX based on the Phillips SAA9051, can handle NTSC and PAL/SECAM and SVIDEO signals. -config SUN_AURORA - tristate "Aurora Multiboard 1600se (EXPERIMENTAL)" - depends on EXPERIMENTAL && BROKEN - help - The Aurora Multiboard is a multi-port high-speed serial controller. - If you have one of these, say Y. - config TADPOLE_TS102_UCTRL tristate "Tadpole TS102 Microcontroller support (EXPERIMENTAL)" depends on EXPERIMENTAL && SPARC32 diff --git a/drivers/sbus/char/Makefile b/drivers/sbus/char/Makefile index 3a5ea1d..7ab060e 100644 --- a/drivers/sbus/char/Makefile +++ b/drivers/sbus/char/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_SUN_OPENPROMIO) += openprom.o obj-$(CONFIG_SUN_MOSTEK_RTC) += rtc.o obj-$(CONFIG_SUN_BPP) += bpp.o obj-$(CONFIG_SUN_VIDEOPIX) += vfc.o -obj-$(CONFIG_SUN_AURORA) += aurora.o obj-$(CONFIG_TADPOLE_TS102_UCTRL) += uctrl.o obj-$(CONFIG_SUN_JSFLASH) += jsflash.o obj-$(CONFIG_BBC_I2C) += bbc.o diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c deleted file mode 100644 index a54b4ac..0000000 --- a/drivers/sbus/char/aurora.c +++ /dev/null @@ -1,2364 +0,0 @@ -/* $Id: aurora.c,v 1.19 2002/01/08 16:00:16 davem Exp $ - * linux/drivers/sbus/char/aurora.c -- Aurora multiport driver - * - * Copyright (c) 1999 by Oliver Aldulea (oli at bv dot ro) - * - * This code is based on the RISCom/8 multiport serial driver written - * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial - * driver, written by Linus Torvalds, Theodore T'so and others. - * The Aurora multiport programming info was obtained mainly from the - * Cirrus Logic CD180 documentation (available on the web), and by - * doing heavy tests on the board. Many thanks to Eddie C. Dost for the - * help on the sbus interface. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Revision 1.0 - * - * This is the first public release. - * - * Most of the information you need is in the aurora.h file. Please - * read that file before reading this one. - * - * Several parts of the code do not have comments yet. - * - * n.b. The board can support 115.2 bit rates, but only on a few - * ports. The total badwidth of one chip (ports 0-7 or 8-15) is equal - * to OSC_FREQ div 16. In case of my board, each chip can take 6 - * channels of 115.2 kbaud. This information is not well-tested. - * - * Fixed to use tty_get_baud_rate(). - * Theodore Ts'o <tytso@mit.edu>, 2001-Oct-12 - */ - -#include <linux/module.h> - -#include <linux/errno.h> -#include <linux/sched.h> -#ifdef AURORA_INT_DEBUG -#include <linux/timer.h> -#endif -#include <linux/interrupt.h> -#include <linux/tty.h> -#include <linux/tty_flip.h> -#include <linux/major.h> -#include <linux/string.h> -#include <linux/fcntl.h> -#include <linux/mm.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/delay.h> -#include <linux/bitops.h> - -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/oplib.h> -#include <asm/system.h> -#include <asm/kdebug.h> -#include <asm/sbus.h> -#include <asm/uaccess.h> - -#include "aurora.h" -#include "cd180.h" - -unsigned char irqs[4] = { - 0, 0, 0, 0 -}; - -#ifdef AURORA_INT_DEBUG -int irqhit=0; -#endif - -static struct tty_driver *aurora_driver; -static struct Aurora_board aurora_board[AURORA_NBOARD] = { - {0,}, -}; - -static struct Aurora_port aurora_port[AURORA_TNPORTS] = { - { 0, }, -}; - -/* no longer used. static struct Aurora_board * IRQ_to_board[16] = { NULL, } ;*/ -static unsigned char * tmp_buf = NULL; - -DECLARE_TASK_QUEUE(tq_aurora); - -static inline int aurora_paranoia_check(struct Aurora_port const * port, - char *name, const char *routine) -{ -#ifdef AURORA_PARANOIA_CHECK - static const char *badmagic = - KERN_DEBUG "aurora: Warning: bad aurora port magic number for device %s in %s\n"; - static const char *badinfo = - KERN_DEBUG "aurora: Warning: null aurora port for device %s in %s\n"; - - if (!port) { - printk(badinfo, name, routine); - return 1; - } - if (port->magic != AURORA_MAGIC) { - printk(badmagic, name, routine); - return 1; - } -#endif - return 0; -} - -/* - * - * Service functions for aurora driver. - * - */ - -/* Get board number from pointer */ -static inline int board_No (struct Aurora_board const * bp) -{ - return bp - aurora_board; -} - -/* Get port number from pointer */ -static inline int port_No (struct Aurora_port const * port) -{ - return AURORA_PORT(port - aurora_port); -} - -/* Get pointer to board from pointer to port */ -static inline struct Aurora_board * port_Board(struct Aurora_port const * port) -{ - return &aurora_board[AURORA_BOARD(port - aurora_port)]; -} - -/* Wait for Channel Command Register ready */ -static inline void aurora_wait_CCR(struct aurora_reg128 * r) -{ - unsigned long delay; - -#ifdef AURORA_DEBUG -printk("aurora_wait_CCR\n"); -#endif - /* FIXME: need something more descriptive than 100000 :) */ - for (delay = 100000; delay; delay--) - if (!sbus_readb(&r->r[CD180_CCR])) - return; - printk(KERN_DEBUG "aurora: Timeout waiting for CCR.\n"); -} - -/* - * aurora probe functions. - */ - -/* Must be called with enabled interrupts */ -static inline void aurora_long_delay(unsigned long delay) -{ - unsigned long i; - -#ifdef AURORA_DEBUG - printk("aurora_long_delay: start\n"); -#endif - for (i = jiffies + delay; time_before(jiffies, i); ) ; -#ifdef AURORA_DEBUG - printk("aurora_long_delay: end\n"); -#endif -} - -/* Reset and setup CD180 chip */ -static int aurora_init_CD180(struct Aurora_board * bp, int chip) -{ - unsigned long flags; - int id; - -#ifdef AURORA_DEBUG - printk("aurora_init_CD180: start %d:%d\n", - board_No(bp), chip); -#endif - save_flags(flags); cli(); - sbus_writeb(0, &bp->r[chip]->r[CD180_CAR]); - sbus_writeb(0, &bp->r[chip]->r[CD180_GSVR]); - - /* Wait for CCR ready */ - aurora_wait_CCR(bp->r[chip]); - - /* Reset CD180 chip */ - sbus_writeb(CCR_HARDRESET, &bp->r[chip]->r[CD180_CCR]); - udelay(1); - sti(); - id=1000; - while((--id) && - (sbus_readb(&bp->r[chip]->r[CD180_GSVR])!=0xff))udelay(100); - if(!id) { - printk(KERN_ERR "aurora%d: Chip %d failed init.\n", - board_No(bp), chip); - restore_flags(flags); - return(-1); - } - cli(); - sbus_writeb((board_No(bp)<<5)|((chip+1)<<3), - &bp->r[chip]->r[CD180_GSVR]); /* Set ID for this chip */ - sbus_writeb(0x80|bp->ACK_MINT, - &bp->r[chip]->r[CD180_MSMR]); /* Prio for modem intr */ - sbus_writeb(0x80|bp->ACK_TINT, - &bp->r[chip]->r[CD180_TSMR]); /* Prio for transmitter intr */ - sbus_writeb(0x80|bp->ACK_RINT, - &bp->r[chip]->r[CD180_RSMR]); /* Prio for receiver intr */ - /* Setting up prescaler. We need 4 tick per 1 ms */ - sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) >> 8, - &bp->r[chip]->r[CD180_PPRH]); - sbus_writeb((bp->oscfreq/(1000000/AURORA_TPS)) & 0xff, - &bp->r[chip]->r[CD180_PPRL]); - - sbus_writeb(SRCR_AUTOPRI|SRCR_GLOBPRI, - &bp->r[chip]->r[CD180_SRCR]); - - id = sbus_readb(&bp->r[chip]->r[CD180_GFRCR]); - printk(KERN_INFO "aurora%d: Chip %d id %02x: ", - board_No(bp), chip,id); - if(sbus_readb(&bp->r[chip]->r[CD180_SRCR]) & 128) { - switch (id) { - case 0x82:printk("CL-CD1864 rev A\n");break; - case 0x83:printk("CL-CD1865 rev A\n");break; - case 0x84:printk("CL-CD1865 rev B\n");break; - case 0x85:printk("CL-CD1865 rev C\n");break; - default:printk("Unknown.\n"); - }; - } else { - switch (id) { - case 0x81:printk("CL-CD180 rev B\n");break; - case 0x82:printk("CL-CD180 rev C\n");break; - default:printk("Unknown.\n"); - }; - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_init_CD180: end\n"); -#endif - return 0; -} - -static int valid_irq(unsigned char irq) -{ -int i; -for(i=0;i<TYPE_1_IRQS;i++) - if (type_1_irq[i]==irq) return 1; -return 0; -} - -static irqreturn_t aurora_interrupt(int irq, void * dev_id); - -/* Main probing routine, also sets irq. */ -static int aurora_probe(void) -{ - struct sbus_bus *sbus; - struct sbus_dev *sdev; - int grrr; - char buf[30]; - int bn = 0; - struct Aurora_board *bp; - - for_each_sbus(sbus) { - for_each_sbusdev(sdev, sbus) { -/* printk("Try: %x %s\n",sdev,sdev->prom_name);*/ - if (!strcmp(sdev->prom_name, "sio16")) { -#ifdef AURORA_DEBUG - printk(KERN_INFO "aurora: sio16 at %p\n",sdev); -#endif - if((sdev->reg_addrs[0].reg_size!=1) && - (sdev->reg_addrs[1].reg_size!=128) && - (sdev->reg_addrs[2].reg_size!=128) && - (sdev->reg_addrs[3].reg_size!=4)) { - printk(KERN_ERR "aurora%d: registers' sizes " - "do not match.\n", bn); - break; - } - bp = &aurora_board[bn]; - bp->r0 = (struct aurora_reg1 *) - sbus_ioremap(&sdev->resource[0], 0, - sdev->reg_addrs[0].reg_size, - "sio16"); - if (bp->r0 == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[0]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 0: %p\n", bp->r0); -#endif - bp->r[0] = (struct aurora_reg128 *) - sbus_ioremap(&sdev->resource[1], 0, - sdev->reg_addrs[1].reg_size, - "sio16"); - if (bp->r[0] == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[1]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 1: %p\n", bp->r[0]); -#endif - bp->r[1] = (struct aurora_reg128 *) - sbus_ioremap(&sdev->resource[2], 0, - sdev->reg_addrs[2].reg_size, - "sio16"); - if (bp->r[1] == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[2]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 2: %p\n", bp->r[1]); -#endif - bp->r3 = (struct aurora_reg4 *) - sbus_ioremap(&sdev->resource[3], 0, - sdev->reg_addrs[3].reg_size, - "sio16"); - if (bp->r3 == NULL) { - printk(KERN_ERR "aurora%d: can't map " - "reg_addrs[3]\n", bn); - break; - } -#ifdef AURORA_DEBUG - printk("Map reg 3: %p\n", bp->r3); -#endif - /* Variables setup */ - bp->flags = 0; -#ifdef AURORA_DEBUG - grrr=prom_getint(sdev->prom_node,"intr"); - printk("intr pri %d\n", grrr); -#endif - if ((bp->irq=irqs[bn]) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - if ((bp->irq=prom_getint(sdev->prom_node, "bintr")) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - if ((bp->irq=prom_getint(sdev->prom_node, "intr")) && valid_irq(bp->irq) && - !request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - } else - for(grrr=0;grrr<TYPE_1_IRQS;grrr++) { - if ((bp->irq=type_1_irq[grrr])&&!request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, "sio16", bp)) { - free_irq(bp->irq|0x30, bp); - break; - } else { - printk(KERN_ERR "aurora%d: Could not get an irq for this board !!!\n",bn); - bp->flags=0xff; - } - } - if(bp->flags==0xff)break; - printk(KERN_INFO "aurora%d: irq %d\n",bn,bp->irq&0x0f); - buf[0]=0; - grrr=prom_getproperty(sdev->prom_node,"dtr_rts",buf,sizeof(buf)); - if(!strcmp(buf,"swapped")){ - printk(KERN_INFO "aurora%d: Swapped DTR and RTS\n",bn); - bp->DTR=MSVR_RTS; - bp->RTS=MSVR_DTR; - bp->MSVDTR=CD180_MSVRTS; - bp->MSVRTS=CD180_MSVDTR; - bp->flags|=AURORA_BOARD_DTR_FLOW_OK; - }else{ - #ifdef AURORA_FORCE_DTR_FLOW - printk(KERN_INFO "aurora%d: Forcing swapped DTR-RTS\n",bn); - bp->DTR=MSVR_RTS; - bp->RTS=MSVR_DTR; - bp->MSVDTR=CD180_MSVRTS; - bp->MSVRTS=CD180_MSVDTR; - bp->flags|=AURORA_BOARD_DTR_FLOW_OK; - #else - printk(KERN_INFO "aurora%d: Normal DTR and RTS\n",bn); - bp->DTR=MSVR_DTR; - bp->RTS=MSVR_RTS; - bp->MSVDTR=CD180_MSVDTR; - bp->MSVRTS=CD180_MSVRTS; - #endif - } - bp->oscfreq=prom_getint(sdev->prom_node,"clk")*100; - printk(KERN_INFO "aurora%d: Oscillator: %d Hz\n",bn,bp->oscfreq); - grrr=prom_getproperty(sdev->prom_node,"chip",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Chips: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"manu",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Manufacturer: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"model",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Model: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"rev",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Revision: %s\n",bn,buf); - grrr=prom_getproperty(sdev->prom_node,"mode",buf,sizeof(buf)); - printk(KERN_INFO "aurora%d: Mode: %s\n",bn,buf); - #ifdef MODULE - bp->count=0; - #endif - bp->flags = AURORA_BOARD_PRESENT; - /* hardware ack */ - bp->ACK_MINT=1; - bp->ACK_TINT=2; - bp->ACK_RINT=3; - bn++; - } - } - } - return bn; -} - -static void aurora_release_io_range(struct Aurora_board *bp) -{ - sbus_iounmap((unsigned long)bp->r0, 1); - sbus_iounmap((unsigned long)bp->r[0], 128); - sbus_iounmap((unsigned long)bp->r[1], 128); - sbus_iounmap((unsigned long)bp->r3, 4); -} - -static inline void aurora_mark_event(struct Aurora_port * port, int event) -{ -#ifdef AURORA_DEBUG - printk("aurora_mark_event: start\n"); -#endif - set_bit(event, &port->event); - queue_task(&port->tqueue, &tq_aurora); - mark_bh(AURORA_BH); -#ifdef AURORA_DEBUG - printk("aurora_mark_event: end\n"); -#endif -} - -static __inline__ struct Aurora_port * aurora_get_port(struct Aurora_board const * bp, - int chip, - unsigned char const *what) -{ - unsigned char channel; - struct Aurora_port * port; - - channel = ((chip << 3) | - ((sbus_readb(&bp->r[chip]->r[CD180_GSCR]) & GSCR_CHAN) >> GSCR_CHAN_OFF)); - port = &aurora_port[board_No(bp) * AURORA_NPORT * AURORA_NCD180 + channel]; - if (port->flags & ASYNC_INITIALIZED) - return port; - - printk(KERN_DEBUG "aurora%d: %s interrupt from invalid port %d\n", - board_No(bp), what, channel); - return NULL; -} - -static void aurora_receive_exc(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char status; - unsigned char ch; - - if (!(port = aurora_get_port(bp, chip, "Receive_x"))) - return; - - tty = port->tty; - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -#ifdef AURORA_INTNORM - printk("aurora%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); -#endif - return; - } - -#ifdef AURORA_REPORT_OVERRUN - status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]); - if (status & RCSR_OE) { - port->overrun++; -#if 1 - printk("aurora%d: port %d: Overrun. Total %ld overruns.\n", - board_No(bp), port_No(port), port->overrun); -#endif - } - status &= port->mark_mask; -#else - status = sbus_readb(&bp->r[chip]->r[CD180_RCSR]) & port->mark_mask; -#endif - ch = sbus_readb(&bp->r[chip]->r[CD180_RDR]); - if (!status) - return; - - if (status & RCSR_TOUT) { -/* printk("aurora%d: port %d: Receiver timeout. Hardware problems ?\n", - board_No(bp), port_No(port));*/ - return; - - } else if (status & RCSR_BREAK) { - printk(KERN_DEBUG "aurora%d: port %d: Handling break...\n", - board_No(bp), port_No(port)); - *tty->flip.flag_buf_ptr++ = TTY_BREAK; - if (port->flags & ASYNC_SAK) - do_SAK(tty); - - } else if (status & RCSR_PE) - *tty->flip.flag_buf_ptr++ = TTY_PARITY; - - else if (status & RCSR_FE) - *tty->flip.flag_buf_ptr++ = TTY_FRAME; - - else if (status & RCSR_OE) - *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; - - else - *tty->flip.flag_buf_ptr++ = 0; - - *tty->flip.char_buf_ptr++ = ch; - tty->flip.count++; - queue_task(&tty->flip.tqueue, &tq_timer); -} - -static void aurora_receive(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char count,cnt; - - if (!(port = aurora_get_port(bp, chip, "Receive"))) - return; - - tty = port->tty; - - count = sbus_readb(&bp->r[chip]->r[CD180_RDCR]); - -#ifdef AURORA_REPORT_FIFO - port->hits[count > 8 ? 9 : count]++; -#endif - - while (count--) { - if (tty->flip.count >= TTY_FLIPBUF_SIZE) { -#ifdef AURORA_INTNORM - printk("aurora%d: port %d: Working around flip buffer overflow.\n", - board_No(bp), port_No(port)); -#endif - break; - } - cnt = sbus_readb(&bp->r[chip]->r[CD180_RDR]); - *tty->flip.char_buf_ptr++ = cnt; - *tty->flip.flag_buf_ptr++ = 0; - tty->flip.count++; - } - queue_task(&tty->flip.tqueue, &tq_timer); -} - -static void aurora_transmit(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char count; - - if (!(port = aurora_get_port(bp, chip, "Transmit"))) - return; - - tty = port->tty; - - if (port->SRER & SRER_TXEMPTY) { - /* FIFO drained */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXEMPTY; - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - return; - } - - if ((port->xmit_cnt <= 0 && !port->break_length) - || tty->stopped || tty->hw_stopped) { - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - return; - } - - if (port->break_length) { - if (port->break_length > 0) { - if (port->COR2 & COR2_ETC) { - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_SBRK, - &bp->r[chip]->r[CD180_TDR]); - port->COR2 &= ~COR2_ETC; - } - count = min(port->break_length, 0xff); - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_DELAY, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(count, - &bp->r[chip]->r[CD180_TDR]); - if (!(port->break_length -= count)) - port->break_length--; - } else { - sbus_writeb(CD180_C_ESC, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(CD180_C_EBRK, - &bp->r[chip]->r[CD180_TDR]); - sbus_writeb(port->COR2, - &bp->r[chip]->r[CD180_COR2]); - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_CORCHG2, - &bp->r[chip]->r[CD180_CCR]); - port->break_length = 0; - } - return; - } - - count = CD180_NFIFO; - do { - u8 byte = port->xmit_buf[port->xmit_tail++]; - - sbus_writeb(byte, &bp->r[chip]->r[CD180_TDR]); - port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); - if (--port->xmit_cnt <= 0) - break; - } while (--count > 0); - - if (port->xmit_cnt <= 0) { - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - } - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); -} - -static void aurora_check_modem(struct Aurora_board const * bp, int chip) -{ - struct Aurora_port *port; - struct tty_struct *tty; - unsigned char mcr; - - if (!(port = aurora_get_port(bp, chip, "Modem"))) - return; - - tty = port->tty; - - mcr = sbus_readb(&bp->r[chip]->r[CD180_MCR]); - if (mcr & MCR_CDCHG) { - if (sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD) - wake_up_interruptible(&port->open_wait); - else - schedule_task(&port->tqueue_hangup); - } - -/* We don't have such things yet. My aurora board has DTR and RTS swapped, but that doesn't count in this driver. Let's hope - * Aurora didn't made any boards with CTS or DSR broken... - */ -/* #ifdef AURORA_BRAIN_DAMAGED_CTS - if (mcr & MCR_CTSCHG) { - if (aurora_in(bp, CD180_MSVR) & MSVR_CTS) { - tty->hw_stopped = 0; - port->SRER |= SRER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); - } else { - tty->hw_stopped = 1; - port->SRER &= ~SRER_TXRDY; - } - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } - if (mcr & MCR_DSRCHG) { - if (aurora_in(bp, CD180_MSVR) & MSVR_DSR) { - tty->hw_stopped = 0; - port->SRER |= SRER_TXRDY; - if (port->xmit_cnt <= port->wakeup_chars) - aurora_mark_event(port, RS_EVENT_WRITE_WAKEUP); - } else { - tty->hw_stopped = 1; - port->SRER &= ~SRER_TXRDY; - } - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } -#endif AURORA_BRAIN_DAMAGED_CTS */ - - /* Clear change bits */ - sbus_writeb(0, &bp->r[chip]->r[CD180_MCR]); -} - -/* The main interrupt processing routine */ -static irqreturn_t aurora_interrupt(int irq, void * dev_id) -{ - unsigned char status; - unsigned char ack,chip/*,chip_id*/; - struct Aurora_board * bp = (struct Aurora_board *) dev_id; - unsigned long loop = 0; - -#ifdef AURORA_INT_DEBUG - printk("IRQ%d %d\n",irq,++irqhit); -#ifdef AURORA_FLOODPRO - if (irqhit>=AURORA_FLOODPRO) - sbus_writeb(8, &bp->r0->r); -#endif -#endif - -/* old bp = IRQ_to_board[irq&0x0f];*/ - - if (!bp || !(bp->flags & AURORA_BOARD_ACTIVE)) - return IRQ_NONE; - -/* The while() below takes care of this. - status = sbus_readb(&bp->r[0]->r[CD180_SRSR]); -#ifdef AURORA_INT_DEBUG - printk("mumu: %02x\n", status); -#endif - if (!(status&SRSR_ANYINT)) - return IRQ_NONE; * Nobody has anything to say, so exit * -*/ - while ((loop++ < 48) && - (status = sbus_readb(&bp->r[0]->r[CD180_SRSR]) & SRSR_ANYINT)){ -#ifdef AURORA_INT_DEBUG - printk("SRSR: %02x\n", status); -#endif - if (status & SRSR_REXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); -#ifdef AURORA_INT_DEBUG - printk("R-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_RGD) { - aurora_receive(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if ((ack & GSVR_ITMASK) == GSVR_IT_REXC) { - aurora_receive_exc(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } else if (status & SRSR_TEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); -#ifdef AURORA_INT_DEBUG - printk("T-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_TX) { - aurora_transmit(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } else if (status & SRSR_MEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); -#ifdef AURORA_INT_DEBUG - printk("M-ACK %02x\n", ack); -#endif - if ((ack >> 5) == board_No(bp)) { - if ((chip = ((ack>>3)&3)-1) < AURORA_NCD180) { - if ((ack&GSVR_ITMASK)==GSVR_IT_MDM) { - aurora_check_modem(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } - } -/* I guess this faster code can be used with CD1865, using AUROPRI and GLOBPRI. */ -#if 0 - while ((loop++ < 48)&&(status=bp->r[0]->r[CD180_SRSR]&SRSR_ANYINT)){ -#ifdef AURORA_INT_DEBUG - printk("SRSR: %02x\n",status); -#endif - ack = sbus_readb(&bp->r3->r[0]); -#ifdef AURORA_INT_DEBUG - printk("ACK: %02x\n",ack); -#endif - if ((ack>>5)==board_No(bp)) { - if ((chip=((ack>>3)&3)-1) < AURORA_NCD180) { - ack&=GSVR_ITMASK; - if (ack==GSVR_IT_RGD) { - aurora_receive(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_REXC) { - aurora_receive_exc(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_TX) { - aurora_transmit(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } else if (ack==GSVR_IT_MDM) { - aurora_check_modem(bp,chip); - sbus_writeb(0, - &bp->r[chip]->r[CD180_EOSRR]); - } - } - } - } -#endif - -/* This is the old handling routine, used in riscom8 for only one CD180. I keep it here for reference. */ -#if 0 - for(chip=0;chip<AURORA_NCD180;chip++){ - chip_id=(board_No(bp)<<5)|((chip+1)<<3); - loop=0; - while ((loop++ < 1) && - ((status = sbus_readb(&bp->r[chip]->r[CD180_SRSR])) & - (SRSR_TEXT | SRSR_MEXT | SRSR_REXT))) { - - if (status & SRSR_REXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_RINT]); - if (ack == (chip_id | GSVR_IT_RGD)) { -#ifdef AURORA_INTMSG - printk("RX ACK\n"); -#endif - aurora_receive(bp,chip); - } else if (ack == (chip_id | GSVR_IT_REXC)) { -#ifdef AURORA_INTMSG - printk("RXC ACK\n"); -#endif - aurora_receive_exc(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad receive ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } else if (status & SRSR_TEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_TINT]); - if (ack == (chip_id | GSVR_IT_TX)){ -#ifdef AURORA_INTMSG - printk("TX ACK\n"); -#endif - aurora_transmit(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad transmit ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } else if (status & SRSR_MEXT) { - ack = sbus_readb(&bp->r3->r[bp->ACK_MINT]); - if (ack == (chip_id | GSVR_IT_MDM)){ -#ifdef AURORA_INTMSG - printk("MDM ACK\n"); -#endif - aurora_check_modem(bp,chip); - } else { -#ifdef AURORA_INTNORM - printk("aurora%d-%d: Bad modem ack 0x%02x.\n", - board_No(bp), chip, ack); -#endif - } - } - sbus_writeb(0, &bp->r[chip]->r[CD180_EOSRR]); - } - } -#endif - - return IRQ_HANDLED; -} - -#ifdef AURORA_INT_DEBUG -static void aurora_timer (unsigned long ignored); - -static DEFINE_TIMER(aurora_poll_timer, aurora_timer, 0, 0); - -static void -aurora_timer (unsigned long ignored) -{ - unsigned long flags; - int i; - - save_flags(flags); cli(); - - printk("SRSR: %02x,%02x - ", - sbus_readb(&aurora_board[0].r[0]->r[CD180_SRSR]), - sbus_readb(&aurora_board[0].r[1]->r[CD180_SRSR])); - for (i = 0; i < 4; i++) { - udelay(1); - printk("%02x ", - sbus_readb(&aurora_board[0].r3->r[i])); - } - printk("\n"); - - aurora_poll_timer.expires = jiffies + 300; - add_timer (&aurora_poll_timer); - - restore_flags(flags); -} -#endif - -/* - * Routines for open & close processing. - */ - -/* Called with disabled interrupts */ -static int aurora_setup_board(struct Aurora_board * bp) -{ - int error; - -#ifdef AURORA_ALLIRQ - int i; - for (i = 0; i < AURORA_ALLIRQ; i++) { - error = request_irq(allirq[i]|0x30, aurora_interrupt, IRQF_SHARED, - "sio16", bp); - if (error) - printk(KERN_ERR "IRQ%d request error %d\n", - allirq[i], error); - } -#else - error = request_irq(bp->irq|0x30, aurora_interrupt, IRQF_SHARED, - "sio16", bp); - if (error) { - printk(KERN_ERR "IRQ request error %d\n", error); - return error; - } -#endif - /* Board reset */ - sbus_writeb(0, &bp->r0->r); - udelay(1); - if (bp->flags & AURORA_BOARD_TYPE_2) { - /* unknown yet */ - } else { - sbus_writeb((AURORA_CFG_ENABLE_IO | AURORA_CFG_ENABLE_IRQ | - (((bp->irq)&0x0f)>>2)), - &bp->r0->r); - } - udelay(10000); - - if (aurora_init_CD180(bp,0))error=1;error=0; - if (aurora_init_CD180(bp,1))error++; - if (error == AURORA_NCD180) { - printk(KERN_ERR "Both chips failed initialisation.\n"); - return -EIO; - } - -#ifdef AURORA_INT_DEBUG - aurora_poll_timer.expires= jiffies + 1; - add_timer(&aurora_poll_timer); -#endif -#ifdef AURORA_DEBUG - printk("aurora_setup_board: end\n"); -#endif - return 0; -} - -/* Called with disabled interrupts */ -static void aurora_shutdown_board(struct Aurora_board *bp) -{ - int i; - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_board: start\n"); -#endif - -#ifdef AURORA_INT_DEBUG - del_timer(&aurora_poll_timer); -#endif - -#ifdef AURORA_ALLIRQ - for(i=0;i<AURORA_ALLIRQ;i++){ - free_irq(allirq[i]|0x30, bp); -/* IRQ_to_board[allirq[i]&0xf] = NULL;*/ - } -#else - free_irq(bp->irq|0x30, bp); -/* IRQ_to_board[bp->irq&0xf] = NULL;*/ -#endif - /* Drop all DTR's */ - for(i=0;i<16;i++){ - sbus_writeb(i & 7, &bp->r[i>>3]->r[CD180_CAR]); - udelay(1); - sbus_writeb(0, &bp->r[i>>3]->r[CD180_MSVR]); - udelay(1); - } - /* Board shutdown */ - sbus_writeb(0, &bp->r0->r); - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_board: end\n"); -#endif -} - -/* Setting up port characteristics. - * Must be called with disabled interrupts - */ -static void aurora_change_speed(struct Aurora_board *bp, struct Aurora_port *port) -{ - struct tty_struct *tty; - unsigned long baud; - long tmp; - unsigned char cor1 = 0, cor3 = 0; - unsigned char mcor1 = 0, mcor2 = 0,chip; - -#ifdef AURORA_DEBUG - printk("aurora_change_speed: start\n"); -#endif - if (!(tty = port->tty) || !tty->termios) - return; - - chip = AURORA_CD180(port_No(port)); - - port->SRER = 0; - port->COR2 = 0; - port->MSVR = MSVR_RTS|MSVR_DTR; - - baud = tty_get_baud_rate(tty); - - /* Select port on the board */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - if (!baud) { - /* Drop DTR & exit */ - port->MSVR &= ~(bp->DTR|bp->RTS); - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - return; - } else { - /* Set DTR on */ - port->MSVR |= bp->DTR; - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - } - - /* Now we must calculate some speed dependent things. */ - - /* Set baud rate for port. */ - tmp = (((bp->oscfreq + baud/2) / baud + - CD180_TPC/2) / CD180_TPC); - -/* tmp = (bp->oscfreq/7)/baud; - if((tmp%10)>4)tmp=tmp/10+1;else tmp=tmp/10;*/ -/* printk("Prescaler period: %d\n",tmp);*/ - - sbus_writeb((tmp >> 8) & 0xff, - &bp->r[chip]->r[CD180_RBPRH]); - sbus_writeb((tmp >> 8) & 0xff, - &bp->r[chip]->r[CD180_TBPRH]); - sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_RBPRL]); - sbus_writeb(tmp & 0xff, &bp->r[chip]->r[CD180_TBPRL]); - - baud = (baud + 5) / 10; /* Estimated CPS */ - - /* Two timer ticks seems enough to wakeup something like SLIP driver */ - tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO; - port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? - SERIAL_XMIT_SIZE - 1 : tmp); - - /* Receiver timeout will be transmission time for 1.5 chars */ - tmp = (AURORA_TPS + AURORA_TPS/2 + baud/2) / baud; - tmp = (tmp > 0xff) ? 0xff : tmp; - sbus_writeb(tmp, &bp->r[chip]->r[CD180_RTPR]); - - switch (C_CSIZE(tty)) { - case CS5: - cor1 |= COR1_5BITS; - break; - case CS6: - cor1 |= COR1_6BITS; - break; - case CS7: - cor1 |= COR1_7BITS; - break; - case CS8: - cor1 |= COR1_8BITS; - break; - } - - if (C_CSTOPB(tty)) - cor1 |= COR1_2SB; - - cor1 |= COR1_IGNORE; - if (C_PARENB(tty)) { - cor1 |= COR1_NORMPAR; - if (C_PARODD(tty)) - cor1 |= COR1_ODDP; - if (I_INPCK(tty)) - cor1 &= ~COR1_IGNORE; - } - /* Set marking of some errors */ - port->mark_mask = RCSR_OE | RCSR_TOUT; - if (I_INPCK(tty)) - port->mark_mask |= RCSR_FE | RCSR_PE; - if (I_BRKINT(tty) || I_PARMRK(tty)) - port->mark_mask |= RCSR_BREAK; - if (I_IGNPAR(tty)) - port->mark_mask &= ~(RCSR_FE | RCSR_PE); - if (I_IGNBRK(tty)) { - port->mark_mask &= ~RCSR_BREAK; - if (I_IGNPAR(tty)) - /* Real raw mode. Ignore all */ - port->mark_mask &= ~RCSR_OE; - } - /* Enable Hardware Flow Control */ - if (C_CRTSCTS(tty)) { -/*#ifdef AURORA_BRAIN_DAMAGED_CTS - port->SRER |= SRER_DSR | SRER_CTS; - mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; - mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; - tty->hw_stopped = !(aurora_in(bp, CD180_MSVR) & (MSVR_CTS|MSVR_DSR)); -#else*/ - port->COR2 |= COR2_CTSAE; -/*#endif*/ - if (bp->flags&AURORA_BOARD_DTR_FLOW_OK) { - mcor1 |= AURORA_RXTH; - } - } - /* Enable Software Flow Control. FIXME: I'm not sure about this */ - /* Some people reported that it works, but I still doubt */ - if (I_IXON(tty)) { - port->COR2 |= COR2_TXIBE; - cor3 |= (COR3_FCT | COR3_SCDE); - if (I_IXANY(tty)) - port->COR2 |= COR2_IXM; - sbus_writeb(START_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR1]); - sbus_writeb(STOP_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR2]); - sbus_writeb(START_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR3]); - sbus_writeb(STOP_CHAR(tty), - &bp->r[chip]->r[CD180_SCHR4]); - } - if (!C_CLOCAL(tty)) { - /* Enable CD check */ - port->SRER |= SRER_CD; - mcor1 |= MCOR1_CDZD; - mcor2 |= MCOR2_CDOD; - } - - if (C_CREAD(tty)) - /* Enable receiver */ - port->SRER |= SRER_RXD; - - /* Set input FIFO size (1-8 bytes) */ - cor3 |= AURORA_RXFIFO; - /* Setting up CD180 channel registers */ - sbus_writeb(cor1, &bp->r[chip]->r[CD180_COR1]); - sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); - sbus_writeb(cor3, &bp->r[chip]->r[CD180_COR3]); - /* Make CD180 know about registers change */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3, - &bp->r[chip]->r[CD180_CCR]); - /* Setting up modem option registers */ - sbus_writeb(mcor1, &bp->r[chip]->r[CD180_MCOR1]); - sbus_writeb(mcor2, &bp->r[chip]->r[CD180_MCOR2]); - /* Enable CD180 transmitter & receiver */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_TXEN | CCR_RXEN, &bp->r[chip]->r[CD180_CCR]); - /* Enable interrupts */ - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - /* And finally set RTS on */ - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); -#ifdef AURORA_DEBUG - printk("aurora_change_speed: end\n"); -#endif -} - -/* Must be called with interrupts enabled */ -static int aurora_setup_port(struct Aurora_board *bp, struct Aurora_port *port) -{ - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_setup_port: start %d\n",port_No(port)); -#endif - if (port->flags & ASYNC_INITIALIZED) - return 0; - - if (!port->xmit_buf) { - /* We may sleep in get_zeroed_page() */ - unsigned long tmp; - - if (!(tmp = get_zeroed_page(GFP_KERNEL))) - return -ENOMEM; - - if (port->xmit_buf) { - free_page(tmp); - return -ERESTARTSYS; - } - port->xmit_buf = (unsigned char *) tmp; - } - - save_flags(flags); cli(); - - if (port->tty) - clear_bit(TTY_IO_ERROR, &port->tty->flags); - -#ifdef MODULE - if ((port->count == 1) && ((++bp->count) == 1)) - bp->flags |= AURORA_BOARD_ACTIVE; -#endif - - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - aurora_change_speed(bp, port); - port->flags |= ASYNC_INITIALIZED; - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_setup_port: end\n"); -#endif - return 0; -} - -/* Must be called with interrupts disabled */ -static void aurora_shutdown_port(struct Aurora_board *bp, struct Aurora_port *port) -{ - struct tty_struct *tty; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_port: start\n"); -#endif - if (!(port->flags & ASYNC_INITIALIZED)) - return; - - chip = AURORA_CD180(port_No(port)); - -#ifdef AURORA_REPORT_OVERRUN - printk("aurora%d: port %d: Total %ld overruns were detected.\n", - board_No(bp), port_No(port), port->overrun); -#endif -#ifdef AURORA_REPORT_FIFO - { - int i; - - printk("aurora%d: port %d: FIFO hits [ ", - board_No(bp), port_No(port)); - for (i = 0; i < 10; i++) { - printk("%ld ", port->hits[i]); - } - printk("].\n"); - } -#endif - if (port->xmit_buf) { - free_page((unsigned long) port->xmit_buf); - port->xmit_buf = NULL; - } - - if (!(tty = port->tty) || C_HUPCL(tty)) { - /* Drop DTR */ - port->MSVR &= ~(bp->DTR|bp->RTS); - sbus_writeb(port->MSVR, - &bp->r[chip]->r[CD180_MSVR]); - } - - /* Select port */ - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - /* Reset port */ - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SOFTRESET, &bp->r[chip]->r[CD180_CCR]); - - /* Disable all interrupts from this port */ - port->SRER = 0; - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - - if (tty) - set_bit(TTY_IO_ERROR, &tty->flags); - port->flags &= ~ASYNC_INITIALIZED; - -#ifdef MODULE - if (--bp->count < 0) { - printk(KERN_DEBUG "aurora%d: aurora_shutdown_port: " - "bad board count: %d\n", - board_No(bp), bp->count); - bp->count = 0; - } - - if (!bp->count) - bp->flags &= ~AURORA_BOARD_ACTIVE; -#endif - -#ifdef AURORA_DEBUG - printk("aurora_shutdown_port: end\n"); -#endif -} - - -static int block_til_ready(struct tty_struct *tty, struct file * filp, - struct Aurora_port *port) -{ - DECLARE_WAITQUEUE(wait, current); - struct Aurora_board *bp = port_Board(port); - int retval; - int do_clocal = 0; - int CD; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("block_til_ready: start\n"); -#endif - chip = AURORA_CD180(port_No(port)); - - /* 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) - return -EAGAIN; - else - return -ERESTARTSYS; - } - - /* If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if ((filp->f_flags & O_NONBLOCK) || - (tty->flags & (1 << TTY_IO_ERROR))) { - port->flags |= ASYNC_NORMAL_ACTIVE; - return 0; - } - - if (C_CLOCAL(tty)) - do_clocal = 1; - - /* Block waiting for the carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, info->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); - cli(); - if (!tty_hung_up_p(filp)) - port->count--; - sti(); - port->blocked_open++; - while (1) { - cli(); - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - CD = sbus_readb(&bp->r[chip]->r[CD180_MSVR]) & MSVR_CD; - port->MSVR=bp->RTS; - - /* auto drops DTR */ - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - sti(); - set_current_state(TASK_INTERRUPTIBLE); - if (tty_hung_up_p(filp) || - !(port->flags & ASYNC_INITIALIZED)) { - if (port->flags & ASYNC_HUP_NOTIFY) - retval = -EAGAIN; - else - retval = -ERESTARTSYS; - break; - } - if (!(port->flags & ASYNC_CLOSING) && - (do_clocal || CD)) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - current->state = TASK_RUNNING; - remove_wait_queue(&port->open_wait, &wait); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - if (retval) - return retval; - - port->flags |= ASYNC_NORMAL_ACTIVE; -#ifdef AURORA_DEBUG - printk("block_til_ready: end\n"); -#endif - return 0; -} - -static int aurora_open(struct tty_struct * tty, struct file * filp) -{ - int board; - int error; - struct Aurora_port * port; - struct Aurora_board * bp; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_open: start\n"); -#endif - - board = AURORA_BOARD(tty->index); - if (board > AURORA_NBOARD || - !(aurora_board[board].flags & AURORA_BOARD_PRESENT)) { -#ifdef AURORA_DEBUG - printk("aurora_open: error board %d present %d\n", - board, aurora_board[board].flags & AURORA_BOARD_PRESENT); -#endif - return -ENODEV; - } - - bp = &aurora_board[board]; - port = aurora_port + board * AURORA_NPORT * AURORA_NCD180 + AURORA_PORT(tty->index); - if ((aurora_paranoia_check(port, tty->name, "aurora_open")) { -#ifdef AURORA_DEBUG - printk("aurora_open: error paranoia check\n"); -#endif - return -ENODEV; - } - - port->count++; - tty->driver_data = port; - port->tty = tty; - - if ((error = aurora_setup_port(bp, port))) { -#ifdef AURORA_DEBUG - printk("aurora_open: error aurora_setup_port ret %d\n",error); -#endif - return error; - } - - if ((error = block_til_ready(tty, filp, port))) { -#ifdef AURORA_DEBUG - printk("aurora_open: error block_til_ready ret %d\n",error); -#endif - return error; - } - -#ifdef AURORA_DEBUG - printk("aurora_open: end\n"); -#endif - return 0; -} - -static void aurora_close(struct tty_struct * tty, struct file * filp) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned long timeout; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_close: start\n"); -#endif - - if (!port || (aurora_paranoia_check(port, tty->name, "close")) - return; - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (tty_hung_up_p(filp)) { - restore_flags(flags); - return; - } - - bp = port_Board(port); - if ((tty->count == 1) && (port->count != 1)) { - printk(KERN_DEBUG "aurora%d: aurora_close: bad port count; " - "tty->count is 1, port count is %d\n", - board_No(bp), port->count); - port->count = 1; - } - if (--port->count < 0) { - printk(KERN_DEBUG "aurora%d: aurora_close: bad port " - "count for tty%d: %d\n", - board_No(bp), port_No(port), port->count); - port->count = 0; - } - if (port->count) { - restore_flags(flags); - return; - } - 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. - */ - tty->closing = 1; - if (port->closing_wait != ASYNC_CLOSING_WAIT_NONE){ -#ifdef AURORA_DEBUG - printk("aurora_close: waiting to flush...\n"); -#endif - tty_wait_until_sent(tty, port->closing_wait); - } - - /* At this point we stop accepting input. To do this, we - * disable the receive line status interrupts, and tell the - * interrupt driver to stop checking the data ready bit in the - * line status register. - */ - port->SRER &= ~SRER_RXD; - if (port->flags & ASYNC_INITIALIZED) { - port->SRER &= ~SRER_TXRDY; - port->SRER |= SRER_TXEMPTY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - /* - * Before we drop DTR, make sure the UART transmitter - * has completely drained; this is especially - * important if there is a transmit FIFO! - */ - timeout = jiffies+HZ; - while(port->SRER & SRER_TXEMPTY) { - msleep_interruptible(jiffies_to_msecs(port->timeout)); - if (time_after(jiffies, timeout)) - break; - } - } -#ifdef AURORA_DEBUG - printk("aurora_close: shutdown_port\n"); -#endif - aurora_shutdown_port(bp, port); - if (tty->driver->flush_buffer) - tty->driver->flush_buffer(tty); - tty_ldisc_flush(tty); - tty->closing = 0; - port->event = 0; - port->tty = 0; - if (port->blocked_open) { - if (port->close_delay) { - msleep_interruptible(jiffies_to_msecs(port->close_delay)); - } - wake_up_interruptible(&port->open_wait); - } - port->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); - wake_up_interruptible(&port->close_wait); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_close: end\n"); -#endif -} - -static int aurora_write(struct tty_struct * tty, - const unsigned char *buf, int count) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - int c, total = 0; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_write: start %d\n",count); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_write")) - return 0; - - chip = AURORA_CD180(port_No(port)); - - bp = port_Board(port); - - if (!tty || !port->xmit_buf || !tmp_buf) - return 0; - - save_flags(flags); - while (1) { - cli(); - c = min(count, min(SERIAL_XMIT_SIZE - port->xmit_cnt - 1, - SERIAL_XMIT_SIZE - port->xmit_head)); - if (c <= 0) { - restore_flags(flags); - break; - } - memcpy(port->xmit_buf + port->xmit_head, buf, c); - port->xmit_head = (port->xmit_head + c) & (SERIAL_XMIT_SIZE-1); - port->xmit_cnt += c; - restore_flags(flags); - - buf += c; - count -= c; - total += c; - } - - cli(); - if (port->xmit_cnt && !tty->stopped && !tty->hw_stopped && - !(port->SRER & SRER_TXRDY)) { - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_write: end %d\n",total); -#endif - return total; -} - -static void aurora_put_char(struct tty_struct * tty, unsigned char ch) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_put_char: start %c\n",ch); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_put_char")) - return; - - if (!tty || !port->xmit_buf) - return; - - save_flags(flags); cli(); - - if (port->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { - restore_flags(flags); - return; - } - - port->xmit_buf[port->xmit_head++] = ch; - port->xmit_head &= SERIAL_XMIT_SIZE - 1; - port->xmit_cnt++; - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_put_char: end\n"); -#endif -} - -static void aurora_flush_chars(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - unsigned char chip; - -/*#ifdef AURORA_DEBUG - printk("aurora_flush_chars: start\n"); -#endif*/ - if ((aurora_paranoia_check(port, tty->name, "aurora_flush_chars")) - return; - - chip = AURORA_CD180(port_No(port)); - - if (port->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || - !port->xmit_buf) - return; - - save_flags(flags); cli(); - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &port_Board(port)->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &port_Board(port)->r[chip]->r[CD180_SRER]); - restore_flags(flags); -/*#ifdef AURORA_DEBUG - printk("aurora_flush_chars: end\n"); -#endif*/ -} - -static int aurora_write_room(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - int ret; - -#ifdef AURORA_DEBUG - printk("aurora_write_room: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_write_room")) - return 0; - - ret = SERIAL_XMIT_SIZE - port->xmit_cnt - 1; - if (ret < 0) - ret = 0; -#ifdef AURORA_DEBUG - printk("aurora_write_room: end\n"); -#endif - return ret; -} - -static int aurora_chars_in_buffer(struct tty_struct *tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - - if ((aurora_paranoia_check(port, tty->name, "aurora_chars_in_buffer")) - return 0; - - return port->xmit_cnt; -} - -static void aurora_flush_buffer(struct tty_struct *tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_flush_buffer: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_flush_buffer")) - return; - - save_flags(flags); cli(); - port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; - restore_flags(flags); - - tty_wakeup(tty); -#ifdef AURORA_DEBUG - printk("aurora_flush_buffer: end\n"); -#endif -} - -static int aurora_tiocmget(struct tty_struct *tty, struct file *file) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board * bp; - unsigned char status,chip; - unsigned int result; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_get_modem_info: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) - return -ENODEV; - - chip = AURORA_CD180(port_No(port)); - - bp = port_Board(port); - - save_flags(flags); cli(); - - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - status = sbus_readb(&bp->r[chip]->r[CD180_MSVR]); - result = 0/*bp->r[chip]->r[AURORA_RI] & (1u << port_No(port)) ? 0 : TIOCM_RNG*/; - - restore_flags(flags); - - result |= ((status & bp->RTS) ? TIOCM_RTS : 0) - | ((status & bp->DTR) ? TIOCM_DTR : 0) - | ((status & MSVR_CD) ? TIOCM_CAR : 0) - | ((status & MSVR_DSR) ? TIOCM_DSR : 0) - | ((status & MSVR_CTS) ? TIOCM_CTS : 0); - -#ifdef AURORA_DEBUG - printk("aurora_get_modem_info: end\n"); -#endif - return result; -} - -static int aurora_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned int arg; - unsigned long flags; - struct Aurora_board *bp = port_Board(port); - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_set_modem_info: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, __FUNCTION__)) - return -ENODEV; - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (set & TIOCM_RTS) - port->MSVR |= bp->RTS; - if (set & TIOCM_DTR) - port->MSVR |= bp->DTR; - if (clear & TIOCM_RTS) - port->MSVR &= ~bp->RTS; - if (clear & TIOCM_DTR) - port->MSVR &= ~bp->DTR; - - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_set_modem_info: end\n"); -#endif - return 0; -} - -static void aurora_send_break(struct Aurora_port * port, unsigned long length) -{ - struct Aurora_board *bp = port_Board(port); - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_send_break: start\n"); -#endif - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - - port->break_length = AURORA_TPS / HZ * length; - port->COR2 |= COR2_ETC; - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - - sbus_writeb(port->COR2, &bp->r[chip]->r[CD180_COR2]); - sbus_writeb(port->SRER, &bp->r[chip]->r[CD180_SRER]); - aurora_wait_CCR(bp->r[chip]); - - sbus_writeb(CCR_CORCHG2, &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_send_break: end\n"); -#endif -} - -static int aurora_set_serial_info(struct Aurora_port * port, - struct serial_struct * newinfo) -{ - struct serial_struct tmp; - struct Aurora_board *bp = port_Board(port); - int change_speed; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_set_serial_info: start\n"); -#endif - if (copy_from_user(&tmp, newinfo, sizeof(tmp))) - return -EFAULT; -#if 0 - if ((tmp.irq != bp->irq) || - (tmp.port != bp->base) || - (tmp.type != PORT_CIRRUS) || - (tmp.baud_base != (bp->oscfreq + CD180_TPC/2) / CD180_TPC) || - (tmp.custom_divisor != 0) || - (tmp.xmit_fifo_size != CD180_NFIFO) || - (tmp.flags & ~AURORA_LEGAL_FLAGS)) - return -EINVAL; -#endif - - change_speed = ((port->flags & ASYNC_SPD_MASK) != - (tmp.flags & ASYNC_SPD_MASK)); - - if (!capable(CAP_SYS_ADMIN)) { - if ((tmp.close_delay != port->close_delay) || - (tmp.closing_wait != port->closing_wait) || - ((tmp.flags & ~ASYNC_USR_MASK) != - (port->flags & ~ASYNC_USR_MASK))) - return -EPERM; - port->flags = ((port->flags & ~ASYNC_USR_MASK) | - (tmp.flags & ASYNC_USR_MASK)); - } else { - port->flags = ((port->flags & ~ASYNC_FLAGS) | - (tmp.flags & ASYNC_FLAGS)); - port->close_delay = tmp.close_delay; - port->closing_wait = tmp.closing_wait; - } - if (change_speed) { - save_flags(flags); cli(); - aurora_change_speed(bp, port); - restore_flags(flags); - } -#ifdef AURORA_DEBUG - printk("aurora_set_serial_info: end\n"); -#endif - return 0; -} - -extern int aurora_get_serial_info(struct Aurora_port * port, - struct serial_struct * retinfo) -{ - struct serial_struct tmp; - struct Aurora_board *bp = port_Board(port); - -#ifdef AURORA_DEBUG - printk("aurora_get_serial_info: start\n"); -#endif - if (!access_ok(VERIFY_WRITE, (void *) retinfo, sizeof(tmp))) - return -EFAULT; - - memset(&tmp, 0, sizeof(tmp)); - tmp.type = PORT_CIRRUS; - tmp.line = port - aurora_port; - tmp.port = 0; - tmp.irq = bp->irq; - tmp.flags = port->flags; - tmp.baud_base = (bp->oscfreq + CD180_TPC/2) / CD180_TPC; - tmp.close_delay = port->close_delay * HZ/100; - tmp.closing_wait = port->closing_wait * HZ/100; - tmp.xmit_fifo_size = CD180_NFIFO; - copy_to_user(retinfo, &tmp, sizeof(tmp)); -#ifdef AURORA_DEBUG -printk("aurora_get_serial_info: end\n"); -#endif - return 0; -} - -static int aurora_ioctl(struct tty_struct * tty, struct file * filp, - unsigned int cmd, unsigned long arg) - -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - int retval; - -#ifdef AURORA_DEBUG - printk("aurora_ioctl: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_ioctl")) - return -ENODEV; - - switch (cmd) { - case TCSBRK: /* SVID version: non-zero arg --> no break */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - if (!arg) - aurora_send_break(port, HZ/4); /* 1/4 second */ - return 0; - case TCSBRKP: /* support for POSIX tcsendbreak() */ - retval = tty_check_change(tty); - if (retval) - return retval; - tty_wait_until_sent(tty, 0); - aurora_send_break(port, arg ? arg*(HZ/10) : HZ/4); - return 0; - case TIOCGSOFTCAR: - return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *)arg); - case TIOCSSOFTCAR: - if (get_user(arg,(unsigned long *)arg)) - return -EFAULT; - tty->termios->c_cflag = - ((tty->termios->c_cflag & ~CLOCAL) | - (arg ? CLOCAL : 0)); - return 0; - case TIOCGSERIAL: - return aurora_get_serial_info(port, (struct serial_struct *) arg); - case TIOCSSERIAL: - return aurora_set_serial_info(port, (struct serial_struct *) arg); - default: - return -ENOIOCTLCMD; - }; -#ifdef AURORA_DEBUG - printk("aurora_ioctl: end\n"); -#endif - return 0; -} - -static void aurora_throttle(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_throttle: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_throttle")) - return; - - bp = port_Board(port); - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->MSVR &= ~bp->RTS; - sbus_writeb(port_No(port) & 7, &bp->r[chip]->r[CD180_CAR]); - udelay(1); - if (I_IXOFF(tty)) { - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SSCH2, &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - } - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_throttle: end\n"); -#endif -} - -static void aurora_unthrottle(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_unthrottle: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_unthrottle")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->MSVR |= bp->RTS; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - if (I_IXOFF(tty)) { - aurora_wait_CCR(bp->r[chip]); - sbus_writeb(CCR_SSCH1, - &bp->r[chip]->r[CD180_CCR]); - aurora_wait_CCR(bp->r[chip]); - } - sbus_writeb(port->MSVR, &bp->r[chip]->r[CD180_MSVR]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_unthrottle: end\n"); -#endif -} - -static void aurora_stop(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_stop: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_stop")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - port->SRER &= ~SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_stop: end\n"); -#endif -} - -static void aurora_start(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - unsigned long flags; - unsigned char chip; - -#ifdef AURORA_DEBUG - printk("aurora_start: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_start")) - return; - - bp = port_Board(port); - - chip = AURORA_CD180(port_No(port)); - - save_flags(flags); cli(); - if (port->xmit_cnt && port->xmit_buf && !(port->SRER & SRER_TXRDY)) { - port->SRER |= SRER_TXRDY; - sbus_writeb(port_No(port) & 7, - &bp->r[chip]->r[CD180_CAR]); - udelay(1); - sbus_writeb(port->SRER, - &bp->r[chip]->r[CD180_SRER]); - } - restore_flags(flags); -#ifdef AURORA_DEBUG - printk("aurora_start: end\n"); -#endif -} - -/* - * This routine is called from the scheduler tqueue when the interrupt - * routine has signalled that a hangup has occurred. The path of - * hangup processing is: - * - * serial interrupt routine -> (scheduler tqueue) -> - * do_aurora_hangup() -> tty->hangup() -> aurora_hangup() - * - */ -static void do_aurora_hangup(void *private_) -{ - struct Aurora_port *port = (struct Aurora_port *) private_; - struct tty_struct *tty; - -#ifdef AURORA_DEBUG - printk("do_aurora_hangup: start\n"); -#endif - tty = port->tty; - if (tty != NULL) { - tty_hangup(tty); /* FIXME: module removal race - AKPM */ -#ifdef AURORA_DEBUG - printk("do_aurora_hangup: end\n"); -#endif - } -} - -static void aurora_hangup(struct tty_struct * tty) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - struct Aurora_board *bp; - -#ifdef AURORA_DEBUG - printk("aurora_hangup: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_hangup")) - return; - - bp = port_Board(port); - - aurora_shutdown_port(bp, port); - port->event = 0; - port->count = 0; - port->flags &= ~ASYNC_NORMAL_ACTIVE; - port->tty = 0; - wake_up_interruptible(&port->open_wait); -#ifdef AURORA_DEBUG - printk("aurora_hangup: end\n"); -#endif -} - -static void aurora_set_termios(struct tty_struct * tty, struct termios * old_termios) -{ - struct Aurora_port *port = (struct Aurora_port *) tty->driver_data; - unsigned long flags; - -#ifdef AURORA_DEBUG - printk("aurora_set_termios: start\n"); -#endif - if ((aurora_paranoia_check(port, tty->name, "aurora_set_termios")) - return; - - if (tty->termios->c_cflag == old_termios->c_cflag && - tty->termios->c_iflag == old_termios->c_iflag) - return; - - save_flags(flags); cli(); - aurora_change_speed(port_Board(port), port); - restore_flags(flags); - - if ((old_termios->c_cflag & CRTSCTS) && - !(tty->termios->c_cflag & CRTSCTS)) { - tty->hw_stopped = 0; - aurora_start(tty); - } -#ifdef AURORA_DEBUG - printk("aurora_set_termios: end\n"); -#endif -} - -static void do_aurora_bh(void) -{ - run_task_queue(&tq_aurora); -} - -static void do_softint(void *private_) -{ - struct Aurora_port *port = (struct Aurora_port *) private_; - struct tty_struct *tty; - -#ifdef AURORA_DEBUG - printk("do_softint: start\n"); -#endif - tty = port->tty; - if (tty == NULL) - return; - - if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &port->event)) { - tty_wakeup(tty); - } -#ifdef AURORA_DEBUG - printk("do_softint: end\n"); -#endif -} - -static const struct tty_operations aurora_ops = { - .open = aurora_open, - .close = aurora_close, - .write = aurora_write, - .put_char = aurora_put_char, - .flush_chars = aurora_flush_chars, - .write_room = aurora_write_room, - .chars_in_buffer = aurora_chars_in_buffer, - .flush_buffer = aurora_flush_buffer, - .ioctl = aurora_ioctl, - .throttle = aurora_throttle, - .unthrottle = aurora_unthrottle, - .set_termios = aurora_set_termios, - .stop = aurora_stop, - .start = aurora_start, - .hangup = aurora_hangup, - .tiocmget = aurora_tiocmget, - .tiocmset = aurora_tiocmset, -}; - -static int aurora_init_drivers(void) -{ - int error; - int i; - -#ifdef AURORA_DEBUG - printk("aurora_init_drivers: start\n"); -#endif - tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL); - if (tmp_buf == NULL) { - printk(KERN_ERR "aurora: Couldn't get free page.\n"); - return 1; - } - init_bh(AURORA_BH, do_aurora_bh); - aurora_driver = alloc_tty_driver(AURORA_INPORTS); - if (!aurora_driver) { - printk(KERN_ERR "aurora: Couldn't allocate tty driver.\n"); - free_page((unsigned long) tmp_buf); - return 1; - } - aurora_driver->owner = THIS_MODULE; - aurora_driver->name = "ttyA"; - aurora_driver->major = AURORA_MAJOR; - aurora_driver->type = TTY_DRIVER_TYPE_SERIAL; - aurora_driver->subtype = SERIAL_TYPE_NORMAL; - aurora_driver->init_termios = tty_std_termios; - aurora_driver->init_termios.c_cflag = - B9600 | CS8 | CREAD | HUPCL | CLOCAL; - aurora_driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(aurora_driver, &aurora_ops); - error = tty_register_driver(aurora_driver); - if (error) { - put_tty_driver(aurora_driver); - free_page((unsigned long) tmp_buf); - printk(KERN_ERR "aurora: Couldn't register aurora driver, error = %d\n", - error); - return 1; - } - - memset(aurora_port, 0, sizeof(aurora_port)); - for (i = 0; i < AURORA_TNPORTS; i++) { - aurora_port[i].magic = AURORA_MAGIC; - aurora_port[i].tqueue.routine = do_softint; - aurora_port[i].tqueue.data = &aurora_port[i]; - aurora_port[i].tqueue_hangup.routine = do_aurora_hangup; - aurora_port[i].tqueue_hangup.data = &aurora_port[i]; - aurora_port[i].close_delay = 50 * HZ/100; - aurora_port[i].closing_wait = 3000 * HZ/100; - init_waitqueue_head(&aurora_port[i].open_wait); - init_waitqueue_head(&aurora_port[i].close_wait); - } -#ifdef AURORA_DEBUG - printk("aurora_init_drivers: end\n"); -#endif - return 0; -} - -static void aurora_release_drivers(void) -{ -#ifdef AURORA_DEBUG - printk("aurora_release_drivers: start\n"); -#endif - free_page((unsigned long)tmp_buf); - tty_unregister_driver(aurora_driver); - put_tty_driver(aurora_driver); -#ifdef AURORA_DEBUG - printk("aurora_release_drivers: end\n"); -#endif -} - -/* - * Called at boot time. - * - * You can specify IO base for up to RC_NBOARD cards, - * using line "riscom8=0xiobase1,0xiobase2,.." at LILO prompt. - * Note that there will be no probing at default - * addresses in this case. - * - */ -void __init aurora_setup(char *str, int *ints) -{ - int i; - - for(i=0;(i<ints[0])&&(i<4);i++) { - if (ints[i+1]) irqs[i]=ints[i+1]; - } -} - -static int __init aurora_real_init(void) -{ - int found; - int i; - - printk(KERN_INFO "aurora: Driver starting.\n"); - if(aurora_init_drivers()) - return -EIO; - found = aurora_probe(); - if(!found) { - aurora_release_drivers(); - printk(KERN_INFO "aurora: No Aurora Multiport boards detected.\n"); - return -EIO; - } else { - printk(KERN_INFO "aurora: %d boards found.\n", found); - } - for (i = 0; i < found; i++) { - int ret = aurora_setup_board(&aurora_board[i]); - - if (ret) { -#ifdef AURORA_DEBUG - printk(KERN_ERR "aurora_init: error aurora_setup_board ret %d\n", - ret); -#endif - return ret; - } - } - return 0; -} - -int irq = 0; -int irq1 = 0; -int irq2 = 0; -int irq3 = 0; -module_param(irq , int, 0); -module_param(irq1, int, 0); -module_param(irq2, int, 0); -module_param(irq3, int, 0); - -static int __init aurora_init(void) -{ - if (irq ) irqs[0]=irq ; - if (irq1) irqs[1]=irq1; - if (irq2) irqs[2]=irq2; - if (irq3) irqs[3]=irq3; - return aurora_real_init(); -} - -static void __exit aurora_cleanup(void) -{ - int i; - -#ifdef AURORA_DEBUG -printk("cleanup_module: aurora_release_drivers\n"); -#endif - - aurora_release_drivers(); - for (i = 0; i < AURORA_NBOARD; i++) - if (aurora_board[i].flags & AURORA_BOARD_PRESENT) { - aurora_shutdown_board(&aurora_board[i]); - aurora_release_io_range(&aurora_board[i]); - } -} - -module_init(aurora_init); -module_exit(aurora_cleanup); -MODULE_LICENSE("GPL"); diff --git a/drivers/sbus/char/aurora.h b/drivers/sbus/char/aurora.h deleted file mode 100644 index b8b5476d..0000000 --- a/drivers/sbus/char/aurora.h +++ /dev/null @@ -1,276 +0,0 @@ -/* $Id: aurora.h,v 1.6 2001/06/05 12:23:38 davem Exp $ - * linux/drivers/sbus/char/aurora.h -- Aurora multiport driver - * - * Copyright (c) 1999 by Oliver Aldulea (oli@bv.ro) - * - * This code is based on the RISCom/8 multiport serial driver written - * by Dmitry Gorodchanin (pgmdsg@ibi.com), based on the Linux serial - * driver, written by Linus Torvalds, Theodore T'so and others. - * The Aurora multiport programming info was obtained mainly from the - * Cirrus Logic CD180 documentation (available on the web), and by - * doing heavy tests on the board. Many thanks to Eddie C. Dost for the - * help on the sbus interface. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Revision 1.0 - * - * This is the first public release. - * - * This version needs a lot of feedback. This is the version that works - * with _my_ board. My board is model 1600se, revision '@(#)1600se.fth - * 1.2 3/28/95 1'. The driver might work with your board, but I do not - * guarantee it. If you have _any_ type of board, I need to know if the - * driver works or not, I need to know exactly your board parameters - * (get them with 'cd /proc/openprom/iommu/sbus/sio16/; ls *; cat *') - * Also, I need your board revision code, which is written on the board. - * Send me the output of my driver too (it outputs through klogd). - * - * If the driver does not work, you can try enabling the debug options - * to see what's wrong or what should be done. - * - * I'm sorry about the alignment of the code. It was written in a - * 128x48 environment. - * - * I must say that I do not like Aurora Technologies' policy. I asked - * them to help me do this driver faster, but they ended by something - * like "don't call us, we'll call you", and I never heard anything - * from them. They told me "knowing the way the board works, I don't - * doubt you and others on the net will make the driver." - * The truth about this board is that it has nothing intelligent on it. - * If you want to say to somebody what kind of board you have, say that - * it uses Cirrus Logic processors (CD180). The power of the board is - * in those two chips. The rest of the board is the interface to the - * sbus and to the peripherals. Still, they did something smart: they - * reversed DTR and RTS to make on-board automatic hardware flow - * control usable. - * Thanks to Aurora Technologies for wasting my time, nerves and money. - */ - -#ifndef __LINUX_AURORA_H -#define __LINUX_AURORA_H - -#include <linux/serial.h> -#include <linux/serialP.h> - -#ifdef __KERNEL__ - -/* This is the number of boards to support. I've only tested this driver with - * one board, so it might not work. - */ -#define AURORA_NBOARD 1 - -/* Useful ? Yes. But you can safely comment the warnings if they annoy you - * (let me say that again: the warnings in the code, not this define). - */ -#define AURORA_PARANOIA_CHECK - -/* Well, after many lost nights, I found that the IRQ for this board is - * selected from four built-in values by writing some bits in the - * configuration register. This causes a little problem to occur: which - * IRQ to select ? Which one is the best for the user ? Well, I finally - * decided for the following algorithm: if the "bintr" value is not acceptable - * (not within type_1_irq[], then test the "intr" value, if that fails too, - * try each value from type_1_irq until succeded. Hope it's ok. - * You can safely reorder the irq's. - */ -#define TYPE_1_IRQS 4 -unsigned char type_1_irq[TYPE_1_IRQS] = { - 3, 5, 9, 13 -}; -/* I know something about another method of interrupt setting, but not enough. - * Also, this is for another type of board, so I first have to learn how to - * detect it. -#define TYPE_2_IRQS 3 -unsigned char type_2_irq[TYPE_2_IRQS] = { - 0, 0, 0 ** could anyone find these for me ? (see AURORA_ALLIRQ below) ** - }; -unsigned char type_2_mask[TYPE_2_IRQS] = { - 32, 64, 128 - }; -*/ - -/* The following section should only be modified by those who know what - * they're doing (or don't, but want to help with some feedback). Modifying - * anything raises a _big_ probability for your system to hang, but the - * sacrifice worths. (I sacrificed my ext2fs many, many times...) - */ - -/* This one tries to dump to console the name of almost every function called, - * and many other debugging info. - */ -#undef AURORA_DEBUG - -/* These are the most dangerous and useful defines. They do printk() during - * the interrupt processing routine(s), so if you manage to get "flooded" by - * irq's, start thinking about the "Power off/on" button... - */ -#undef AURORA_INTNORM /* This one enables the "normal" messages, but some - * of them cause flood, so I preffered putting - * them under a define */ -#undef AURORA_INT_DEBUG /* This one is really bad. */ - -/* Here's something helpful: after n irq's, the board will be disabled. This - * prevents irq flooding during debug (no need to think about power - * off/on anymore...) - */ -#define AURORA_FLOODPRO 10 - -/* This one helps finding which irq the board calls, in case of a strange/ - * unsupported board. AURORA_INT_DEBUG should be enabled, because I don't - * think /proc/interrupts or any command will be available in case of an irq - * flood... "allirq" is the list of all free irq's. - */ -/* -#define AURORA_ALLIRQ 6 -int allirq[AURORA_ALLIRQ]={ - 2,3,5,7,9,13 - }; -*/ - -/* These must not be modified. These values are assumed during the code for - * performance optimisations. - */ -#define AURORA_NCD180 2 /* two chips per board */ -#define AURORA_NPORT 8 /* 8 ports per chip */ - -/* several utilities */ -#define AURORA_BOARD(line) (((line) >> 4) & 0x01) -#define AURORA_CD180(line) (((line) >> 3) & 0x01) -#define AURORA_PORT(line) ((line) & 15) - -#define AURORA_TNPORTS (AURORA_NBOARD*AURORA_NCD180*AURORA_NPORT) - -/* Ticks per sec. Used for setting receiver timeout and break length */ -#define AURORA_TPS 4000 - -#define AURORA_MAGIC 0x0A18 - -/* Yeah, after heavy testing I decided it must be 6. - * Sure, You can change it if needed. - */ -#define AURORA_RXFIFO 6 /* Max. receiver FIFO size (1-8) */ - -#define AURORA_RXTH 7 - -struct aurora_reg1 { - __volatile__ unsigned char r; -}; - -struct aurora_reg128 { - __volatile__ unsigned char r[128]; -}; - -struct aurora_reg4 { - __volatile__ unsigned char r[4]; -}; - -struct Aurora_board { - unsigned long flags; - struct aurora_reg1 * r0; /* This is the board configuration - * register (write-only). */ - struct aurora_reg128 * r[2]; /* These are the registers for the - * two chips. */ - struct aurora_reg4 * r3; /* These are used for hardware-based - * acknowledge. Software-based ack is - * not supported by CD180. */ - unsigned int oscfreq; /* The on-board oscillator - * frequency, in Hz. */ - unsigned char irq; -#ifdef MODULE - signed char count; /* counts the use of the board */ -#endif - /* Values for the dtr_rts swapped mode. */ - unsigned char DTR; - unsigned char RTS; - unsigned char MSVDTR; - unsigned char MSVRTS; - /* Values for hardware acknowledge. */ - unsigned char ACK_MINT, ACK_TINT, ACK_RINT; -}; - -/* Board configuration register */ -#define AURORA_CFG_ENABLE_IO 8 -#define AURORA_CFG_ENABLE_IRQ 4 - -/* Board flags */ -#define AURORA_BOARD_PRESENT 0x00000001 -#define AURORA_BOARD_ACTIVE 0x00000002 -#define AURORA_BOARD_TYPE_2 0x00000004 /* don't know how to - * detect this yet */ -#define AURORA_BOARD_DTR_FLOW_OK 0x00000008 - -/* The story goes like this: Cirrus programmed the CD-180 chip to do automatic - * hardware flow control, and do it using CTS and DTR. CTS is ok, but, if you - * have a modem and the chip drops DTR, then the modem will drop the carrier - * (ain't that cute...). Luckily, the guys at Aurora decided to swap DTR and - * RTS, which makes the flow control usable. I hope that all the boards made - * by Aurora have these two signals swapped. If your's doesn't but you have a - * breakout box, you can try to reverse them yourself, then set the following - * flag. - */ -#undef AURORA_FORCE_DTR_FLOW - -/* In fact, a few more words have to be said about hardware flow control. - * This driver handles "output" flow control through the on-board facility - * CTS Auto Enable. For the "input" flow control there are two cases when - * the flow should be controlled. The first case is when the kernel is so - * busy that it cannot process IRQ's in time; this flow control can only be - * activated by the on-board chip, and if the board has RTS and DTR swapped, - * this facility is usable. The second case is when the application is so - * busy that it cannot receive bytes from the kernel, and this flow must be - * activated by software. This second case is not yet implemented in this - * driver. Unfortunately, I estimate that the second case is the one that - * occurs the most. - */ - - -struct Aurora_port { - int magic; - int baud_base; - int flags; - struct tty_struct * tty; - int count; - int blocked_open; - long event; - 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; - struct tq_struct tqueue; - struct tq_struct tqueue_hangup; - short wakeup_chars; - short break_length; - unsigned short closing_wait; - unsigned char mark_mask; - unsigned char SRER; - unsigned char MSVR; - unsigned char COR2; -#ifdef AURORA_REPORT_OVERRUN - unsigned long overrun; -#endif -#ifdef AURORA_REPORT_FIFO - unsigned long hits[10]; -#endif -}; - -#endif -#endif /*__LINUX_AURORA_H*/ - diff --git a/drivers/sbus/char/bbc_i2c.c b/drivers/sbus/char/bbc_i2c.c index 22631f8..8410587 100644 --- a/drivers/sbus/char/bbc_i2c.c +++ b/drivers/sbus/char/bbc_i2c.c @@ -187,19 +187,20 @@ static int wait_for_pin(struct bbc_i2c_bus *bp, u8 *status) bp->waiting = 1; add_wait_queue(&bp->wq, &wait); while (limit-- > 0) { - u8 val; - - set_current_state(TASK_INTERRUPTIBLE); - *status = val = readb(bp->i2c_control_regs + 0); - if ((val & I2C_PCF_PIN) == 0) { + unsigned long val; + + val = wait_event_interruptible_timeout( + bp->wq, + (((*status = readb(bp->i2c_control_regs + 0)) + & I2C_PCF_PIN) == 0), + msecs_to_jiffies(250)); + if (val > 0) { ret = 0; break; } - msleep_interruptible(250); } remove_wait_queue(&bp->wq, &wait); bp->waiting = 0; - current->state = TASK_RUNNING; return ret; } @@ -340,7 +341,7 @@ static irqreturn_t bbc_i2c_interrupt(int irq, void *dev_id) */ if (bp->waiting && !(readb(bp->i2c_control_regs + 0x0) & I2C_PCF_PIN)) - wake_up(&bp->wq); + wake_up_interruptible(&bp->wq); return IRQ_HANDLED; } diff --git a/drivers/sbus/char/cd180.h b/drivers/sbus/char/cd180.h deleted file mode 100644 index 445b86c..0000000 --- a/drivers/sbus/char/cd180.h +++ /dev/null @@ -1,240 +0,0 @@ - -/* Definitions for Cirrus Logic CL-CD180 8-port async mux chip */ -#define CD180_NCH 8 /* Total number of channels */ -#define CD180_TPC 16 /* Ticks per character */ -#define CD180_NFIFO 8 /* TX FIFO size */ - -/* Global registers */ -#define CD180_GFRCR 0x6b /* Global Firmware Revision Code Register */ -#define CD180_SRCR 0x66 /* Service Request Configuration Register */ -#define CD180_PPRH 0x70 /* Prescaler Period Register High */ -#define CD180_PPRL 0x71 /* Prescaler Period Register Low */ -#define CD180_MSMR 0x61 /* Modem Service Match Register */ -#define CD180_TSMR 0x62 /* Transmit Service Match Register */ -#define CD180_RSMR 0x63 /* Receive Service Match Register */ -#define CD180_GSVR 0x40 /* Global Service Vector Register */ -#define CD180_SRSR 0x65 /* Service Request Status Register */ -#define CD180_GSCR 0x41 /* Global Service Channel Register */ -#define CD180_CAR 0x64 /* Channel Access Register */ - -/* Indexed registers */ -#define CD180_RDCR 0x07 /* Receive Data Count Register */ -#define CD180_RDR 0x78 /* Receiver Data Register */ -#define CD180_RCSR 0x7a /* Receiver Character Status Register */ -#define CD180_TDR 0x7b /* Transmit Data Register */ -#define CD180_EOSRR 0x7f /* End of Service Request Register */ - -/* Channel Registers */ -#define CD180_SRER 0x02 /* Service Request Enable Register */ -#define CD180_CCR 0x01 /* Channel Command Register */ -#define CD180_COR1 0x03 /* Channel Option Register 1 */ -#define CD180_COR2 0x04 /* Channel Option Register 2 */ -#define CD180_COR3 0x05 /* Channel Option Register 3 */ -#define CD180_CCSR 0x06 /* Channel Control Status Register */ -#define CD180_RTPR 0x18 /* Receive Timeout Period Register */ -#define CD180_RBPRH 0x31 /* Receive Bit Rate Period Register High */ -#define CD180_RBPRL 0x32 /* Receive Bit Rate Period Register Low */ -#define CD180_TBPRH 0x39 /* Transmit Bit Rate Period Register High */ -#define CD180_TBPRL 0x3a /* Transmit Bit Rate Period Register Low */ -#define CD180_SCHR1 0x09 /* Special Character Register 1 */ -#define CD180_SCHR2 0x0a /* Special Character Register 2 */ -#define CD180_SCHR3 0x0b /* Special Character Register 3 */ -#define CD180_SCHR4 0x0c /* Special Character Register 4 */ -#define CD180_MCR 0x12 /* Modem Change Register */ -#define CD180_MCOR1 0x10 /* Modem Change Option 1 Register */ -#define CD180_MCOR2 0x11 /* Modem Change Option 2 Register */ -#define CD180_MSVR 0x28 /* Modem Signal Value Register */ -#define CD180_MSVRTS 0x29 /* Modem Signal Value RTS */ -#define CD180_MSVDTR 0x2a /* Modem Signal Value DTR */ - -/* Global Interrupt Vector Register (R/W) */ - -#define GSVR_ITMASK 0x07 /* Interrupt type mask */ -#define GSVR_IT_MDM 0x01 /* Modem Signal Change Interrupt */ -#define GSVR_IT_TX 0x02 /* Transmit Data Interrupt */ -#define GSVR_IT_RGD 0x03 /* Receive Good Data Interrupt */ -#define GSVR_IT_REXC 0x07 /* Receive Exception Interrupt */ - - -/* Global Interrupt Channel Register (R/W) */ - -#define GSCR_CHAN 0x1c /* Channel Number Mask */ -#define GSCR_CHAN_OFF 2 /* Channel Number Offset */ - - -/* Channel Address Register (R/W) */ - -#define CAR_CHAN 0x07 /* Channel Number Mask */ - - -/* Receive Character Status Register (R/O) */ - -#define RCSR_TOUT 0x80 /* Rx Timeout */ -#define RCSR_SCDET 0x70 /* Special Character Detected Mask */ -#define RCSR_NO_SC 0x00 /* No Special Characters Detected */ -#define RCSR_SC_1 0x10 /* Special Char 1 (or 1 & 3) Detected */ -#define RCSR_SC_2 0x20 /* Special Char 2 (or 2 & 4) Detected */ -#define RCSR_SC_3 0x30 /* Special Char 3 Detected */ -#define RCSR_SC_4 0x40 /* Special Char 4 Detected */ -#define RCSR_BREAK 0x08 /* Break has been detected */ -#define RCSR_PE 0x04 /* Parity Error */ -#define RCSR_FE 0x02 /* Frame Error */ -#define RCSR_OE 0x01 /* Overrun Error */ - - -/* Channel Command Register (R/W) (commands in groups can be OR-ed) */ - -#define CCR_HARDRESET 0x81 /* Reset the chip */ - -#define CCR_SOFTRESET 0x80 /* Soft Channel Reset */ - -#define CCR_CORCHG1 0x42 /* Channel Option Register 1 Changed */ -#define CCR_CORCHG2 0x44 /* Channel Option Register 2 Changed */ -#define CCR_CORCHG3 0x48 /* Channel Option Register 3 Changed */ - -#define CCR_SSCH1 0x21 /* Send Special Character 1 */ - -#define CCR_SSCH2 0x22 /* Send Special Character 2 */ - -#define CCR_SSCH3 0x23 /* Send Special Character 3 */ - -#define CCR_SSCH4 0x24 /* Send Special Character 4 */ - -#define CCR_TXEN 0x18 /* Enable Transmitter */ -#define CCR_RXEN 0x12 /* Enable Receiver */ - -#define CCR_TXDIS 0x14 /* Disable Transmitter */ -#define CCR_RXDIS 0x11 /* Disable Receiver */ - - -/* Service Request Enable Register (R/W) */ - -#define SRER_DSR 0x80 /* Enable interrupt on DSR change */ -#define SRER_CD 0x40 /* Enable interrupt on CD change */ -#define SRER_CTS 0x20 /* Enable interrupt on CTS change */ -#define SRER_RXD 0x10 /* Enable interrupt on Receive Data */ -#define SRER_RXSC 0x08 /* Enable interrupt on Receive Spec. Char */ -#define SRER_TXRDY 0x04 /* Enable interrupt on TX FIFO empty */ -#define SRER_TXEMPTY 0x02 /* Enable interrupt on TX completely empty */ -#define SRER_RET 0x01 /* Enable interrupt on RX Exc. Timeout */ - - -/* Channel Option Register 1 (R/W) */ - -#define COR1_ODDP 0x80 /* Odd Parity */ -#define COR1_PARMODE 0x60 /* Parity Mode mask */ -#define COR1_NOPAR 0x00 /* No Parity */ -#define COR1_FORCEPAR 0x20 /* Force Parity */ -#define COR1_NORMPAR 0x40 /* Normal Parity */ -#define COR1_IGNORE 0x10 /* Ignore Parity on RX */ -#define COR1_STOPBITS 0x0c /* Number of Stop Bits */ -#define COR1_1SB 0x00 /* 1 Stop Bit */ -#define COR1_15SB 0x04 /* 1.5 Stop Bits */ -#define COR1_2SB 0x08 /* 2 Stop Bits */ -#define COR1_CHARLEN 0x03 /* Character Length */ -#define COR1_5BITS 0x00 /* 5 bits */ -#define COR1_6BITS 0x01 /* 6 bits */ -#define COR1_7BITS 0x02 /* 7 bits */ -#define COR1_8BITS 0x03 /* 8 bits */ - - -/* Channel Option Register 2 (R/W) */ - -#define COR2_IXM 0x80 /* Implied XON mode */ -#define COR2_TXIBE 0x40 /* Enable In-Band (XON/XOFF) Flow Control */ -#define COR2_ETC 0x20 /* Embedded Tx Commands Enable */ -#define COR2_LLM 0x10 /* Local Loopback Mode */ -#define COR2_RLM 0x08 /* Remote Loopback Mode */ -#define COR2_RTSAO 0x04 /* RTS Automatic Output Enable */ -#define COR2_CTSAE 0x02 /* CTS Automatic Enable */ -#define COR2_DSRAE 0x01 /* DSR Automatic Enable */ - - -/* Channel Option Register 3 (R/W) */ - -#define COR3_XONCH 0x80 /* XON is a pair of characters (1 & 3) */ -#define COR3_XOFFCH 0x40 /* XOFF is a pair of characters (2 & 4) */ -#define COR3_FCT 0x20 /* Flow-Control Transparency Mode */ -#define COR3_SCDE 0x10 /* Special Character Detection Enable */ -#define COR3_RXTH 0x0f /* RX FIFO Threshold value (1-8) */ - - -/* Channel Control Status Register (R/O) */ - -#define CCSR_RXEN 0x80 /* Receiver Enabled */ -#define CCSR_RXFLOFF 0x40 /* Receive Flow Off (XOFF was sent) */ -#define CCSR_RXFLON 0x20 /* Receive Flow On (XON was sent) */ -#define CCSR_TXEN 0x08 /* Transmitter Enabled */ -#define CCSR_TXFLOFF 0x04 /* Transmit Flow Off (got XOFF) */ -#define CCSR_TXFLON 0x02 /* Transmit Flow On (got XON) */ - - -/* Modem Change Option Register 1 (R/W) */ - -#define MCOR1_DSRZD 0x80 /* Detect 0->1 transition of DSR */ -#define MCOR1_CDZD 0x40 /* Detect 0->1 transition of CD */ -#define MCOR1_CTSZD 0x20 /* Detect 0->1 transition of CTS */ -#define MCOR1_DTRTH 0x0f /* Auto DTR flow control Threshold (1-8) */ -#define MCOR1_NODTRFC 0x0 /* Automatic DTR flow control disabled */ - - -/* Modem Change Option Register 2 (R/W) */ - -#define MCOR2_DSROD 0x80 /* Detect 1->0 transition of DSR */ -#define MCOR2_CDOD 0x40 /* Detect 1->0 transition of CD */ -#define MCOR2_CTSOD 0x20 /* Detect 1->0 transition of CTS */ - - -/* Modem Change Register (R/W) */ - -#define MCR_DSRCHG 0x80 /* DSR Changed */ -#define MCR_CDCHG 0x40 /* CD Changed */ -#define MCR_CTSCHG 0x20 /* CTS Changed */ - - -/* Modem Signal Value Register (R/W) */ - -#define MSVR_DSR 0x80 /* Current state of DSR input */ -#define MSVR_CD 0x40 /* Current state of CD input */ -#define MSVR_CTS 0x20 /* Current state of CTS input */ -#define MSVR_DTR 0x02 /* Current state of DTR output */ -#define MSVR_RTS 0x01 /* Current state of RTS output */ - - -/* Service Request Status Register */ - -#define SRSR_CMASK 0xC0 /* Current Service Context Mask */ -#define SRSR_CNONE 0x00 /* Not in a service context */ -#define SRSR_CRX 0x40 /* Rx Context */ -#define SRSR_CTX 0x80 /* Tx Context */ -#define SRSR_CMDM 0xC0 /* Modem Context */ -#define SRSR_ANYINT 0x6F /* Any interrupt flag */ -#define SRSR_RINT 0x10 /* Receive Interrupt */ -#define SRSR_TINT 0x04 /* Transmit Interrupt */ -#define SRSR_MINT 0x01 /* Modem Interrupt */ -#define SRSR_REXT 0x20 /* Receive External Interrupt */ -#define SRSR_TEXT 0x08 /* Transmit External Interrupt */ -#define SRSR_MEXT 0x02 /* Modem External Interrupt */ - - -/* Service Request Configuration Register */ - -#define SRCR_PKGTYPE 0x80 -#define SRCR_REGACKEN 0x40 -#define SRCR_DAISYEN 0x20 -#define SRCR_GLOBPRI 0x10 -#define SRCR_UNFAIR 0x08 -#define SRCR_AUTOPRI 0x02 -#define SRCR_PRISEL 0x01 - -/* Values for register-based Interrupt ACKs */ -#define CD180_ACK_MINT 0x75 /* goes to MSMR */ -#define CD180_ACK_TINT 0x76 /* goes to TSMR */ -#define CD180_ACK_RINT 0x77 /* goes to RSMR */ - -/* Escape characters */ - -#define CD180_C_ESC 0x00 /* Escape character */ -#define CD180_C_SBRK 0x81 /* Start sending BREAK */ -#define CD180_C_DELAY 0x82 /* Delay output */ -#define CD180_C_EBRK 0x83 /* Stop sending BREAK */ diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c index 2cea4f5..f2be2ea 100644 --- a/drivers/sbus/char/envctrl.c +++ b/drivers/sbus/char/envctrl.c @@ -726,7 +726,7 @@ static struct miscdevice envctrl_dev = { * Return: None. */ static void envctrl_set_mon(struct i2c_child_t *pchild, - char *chnl_desc, + const char *chnl_desc, int chnl_no) { /* Firmware only has temperature type. It does not distinguish @@ -763,8 +763,8 @@ static void envctrl_set_mon(struct i2c_child_t *pchild, static void envctrl_init_adc(struct i2c_child_t *pchild, struct device_node *dp) { int i = 0, len; - char *pos; - unsigned int *pval; + const char *pos; + const unsigned int *pval; /* Firmware describe channels into a stream separated by a '\0'. */ pos = of_get_property(dp, "channels-description", &len); @@ -859,7 +859,7 @@ static void envctrl_init_i2c_child(struct linux_ebus_child *edev_child, { int len, i, tbls_size = 0; struct device_node *dp = edev_child->prom_node; - void *pval; + const void *pval; /* Get device address. */ pval = of_get_property(dp, "reg", &len); diff --git a/drivers/sbus/char/flash.c b/drivers/sbus/char/flash.c index 6e99507..262f01e 100644 --- a/drivers/sbus/char/flash.c +++ b/drivers/sbus/char/flash.c @@ -190,7 +190,7 @@ static int __init flash_init(void) } if (!sdev) { #ifdef CONFIG_PCI - struct linux_prom_registers *ebus_regs; + const struct linux_prom_registers *ebus_regs; for_each_ebus(ebus) { for_each_ebusdev(edev, ebus) { diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c index eec28c1..fbfeb89 100644 --- a/drivers/sbus/char/openprom.c +++ b/drivers/sbus/char/openprom.c @@ -44,7 +44,6 @@ #include <asm/openpromio.h> #ifdef CONFIG_PCI #include <linux/pci.h> -#include <asm/pbm.h> #endif MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)"); @@ -141,7 +140,7 @@ static int copyout(void __user *info, struct openpromio *opp, int len) static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize) { - void *pval; + const void *pval; int len; if (!dp || @@ -248,18 +247,18 @@ static int oprompci2node(void __user *argp, struct device_node *dp, struct openp if (bufsize >= 2*sizeof(int)) { #ifdef CONFIG_PCI struct pci_dev *pdev; - struct pcidev_cookie *pcp; - pdev = pci_find_slot (((int *) op->oprom_array)[0], + struct device_node *dp; + + pdev = pci_get_bus_and_slot (((int *) op->oprom_array)[0], ((int *) op->oprom_array)[1]); - pcp = pdev->sysdata; - if (pcp != NULL) { - dp = pcp->prom_node; - data->current_node = dp; - *((int *)op->oprom_array) = dp->node; - op->oprom_size = sizeof(int); - err = copyout(argp, op, bufsize + sizeof(int)); - } + dp = pci_device_to_OF_node(pdev); + data->current_node = dp; + *((int *)op->oprom_array) = dp->node; + op->oprom_size = sizeof(int); + err = copyout(argp, op, bufsize + sizeof(int)); + + pci_dev_put(pdev); #endif } @@ -409,7 +408,7 @@ static int opiocget(void __user *argp, DATA *data) struct opiocdesc op; struct device_node *dp; char *str; - void *pval; + const void *pval; int err, len; if (copy_from_user(&op, argp, sizeof(op))) diff --git a/drivers/sbus/char/uctrl.c b/drivers/sbus/char/uctrl.c index 45cf5bc..44d2ef9 100644 --- a/drivers/sbus/char/uctrl.c +++ b/drivers/sbus/char/uctrl.c @@ -364,6 +364,7 @@ static int __init ts102_uctrl_init(void) struct linux_prom_irqs tmp_irq[2]; unsigned int vaddr[2] = { 0, 0 }; int tmpnode, uctrlnode = prom_getchild(prom_root_node); + int err; tmpnode = prom_searchsiblings(uctrlnode, "obio"); @@ -389,7 +390,12 @@ static int __init ts102_uctrl_init(void) if(!driver->irq) driver->irq = tmp_irq[0].pri; - request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); + err = request_irq(driver->irq, uctrl_interrupt, 0, "uctrl", driver); + if (err) { + printk("%s: unable to register irq %d\n", + __FUNCTION__, driver->irq); + return err; + } if (misc_register(&uctrl_dev)) { printk("%s: unable to get misc minor %d\n", diff --git a/drivers/sbus/char/vfc_dev.c b/drivers/sbus/char/vfc_dev.c index 8bfb67c..c3135e2 100644 --- a/drivers/sbus/char/vfc_dev.c +++ b/drivers/sbus/char/vfc_dev.c @@ -259,11 +259,10 @@ static int vfc_debug(struct vfc_dev *dev, int cmd, void __user *argp) if (copy_from_user(&inout, argp, sizeof(inout))) return -EFAULT; - buffer = kmalloc(inout.len, GFP_KERNEL); + buffer = kzalloc(inout.len, GFP_KERNEL); if (buffer == NULL) return -ENOMEM; - memset(buffer,0,inout.len); vfc_lock_device(dev); inout.ret= vfc_i2c_recvbuf(dev,inout.addr & 0xff diff --git a/drivers/sbus/char/vfc_i2c.c b/drivers/sbus/char/vfc_i2c.c index ceec306..9efed77 100644 --- a/drivers/sbus/char/vfc_i2c.c +++ b/drivers/sbus/char/vfc_i2c.c @@ -14,7 +14,7 @@ /* NOTE: It seems to me that the documentation regarding the pcd8584t/pcf8584 does not show the correct way to address the i2c bus. Based on the information on the I2C bus itself and the remainder of -the Phillips docs the following algorithims apper to be correct. I am +the Phillips docs the following algorithms appear to be correct. I am fairly certain that the flowcharts in the phillips docs are wrong. */ diff --git a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c index 6349dd6..eee590a 100644 --- a/drivers/sbus/sbus.c +++ b/drivers/sbus/sbus.c @@ -35,7 +35,7 @@ struct sbus_bus *sbus_root; static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sdev) { unsigned long base; - void *pval; + const void *pval; int len, err; sdev->prom_node = dp->node; @@ -86,7 +86,7 @@ static void __init fill_sbus_device(struct device_node *dp, struct sbus_dev *sde static void __init sbus_bus_ranges_init(struct device_node *dp, struct sbus_bus *sbus) { - void *pval; + const void *pval; int len; pval = of_get_property(dp, "ranges", &len); diff --git a/drivers/scsi/3w-xxxx.c b/drivers/scsi/3w-xxxx.c index bf5d63e..656bdb1 100644 --- a/drivers/scsi/3w-xxxx.c +++ b/drivers/scsi/3w-xxxx.c @@ -1864,10 +1864,17 @@ static int tw_scsiop_read_write(TW_Device_Extension *tw_dev, int request_id) /* This function will handle the request sense scsi command */ static int tw_scsiop_request_sense(TW_Device_Extension *tw_dev, int request_id) { + char request_buffer[18]; + dprintk(KERN_NOTICE "3w-xxxx: tw_scsiop_request_sense()\n"); - /* For now we just zero the request buffer */ - memset(tw_dev->srb[request_id]->request_buffer, 0, tw_dev->srb[request_id]->request_bufflen); + memset(request_buffer, 0, sizeof(request_buffer)); + request_buffer[0] = 0x70; /* Immediate fixed format */ + request_buffer[7] = 10; /* minimum size per SPC: 18 bytes */ + /* leave all other fields zero, giving effectively NO_SENSE return */ + tw_transfer_internal(tw_dev, request_id, request_buffer, + sizeof(request_buffer)); + tw_dev->state[request_id] = TW_S_COMPLETED; tw_state_request_finish(tw_dev, request_id); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 5bf3f07..fcc4cb6 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -230,6 +230,7 @@ config SCSI_SCAN_ASYNC The SCSI subsystem can probe for devices while the rest of the system continues booting, and even probe devices on different busses in parallel, leading to a significant speed-up. + If you have built SCSI as modules, enabling this option can be a problem as the devices may not have been found by the time your system expects them to have been. You can load the @@ -237,8 +238,8 @@ config SCSI_SCAN_ASYNC If you build your SCSI drivers into the kernel, then everything will work fine if you say Y here. - You can override this choice by specifying scsi_mod.scan="sync" - or "async" on the kernel's command line. + You can override this choice by specifying "scsi_mod.scan=sync" + or async on the kernel's command line. menu "SCSI Transports" depends on SCSI @@ -1762,9 +1763,15 @@ config SUN3X_ESP The ESP was an on-board SCSI controller used on Sun 3/80 machines. Say Y here to compile in support for it. +config SCSI_ESP_CORE + tristate "ESP Scsi Driver Core" + depends on SCSI + select SCSI_SPI_ATTRS + config SCSI_SUNESP tristate "Sparc ESP Scsi Driver" depends on SBUS && SCSI + select SCSI_ESP_CORE help This is the driver for the Sun ESP SCSI host adapter. The ESP chipset is present in most SPARC SBUS-based computers. diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 79ecf4e..70cff4c 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -106,7 +106,8 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/ obj-$(CONFIG_MEGARAID_SAS) += megaraid/ obj-$(CONFIG_SCSI_ACARD) += atp870u.o -obj-$(CONFIG_SCSI_SUNESP) += esp.o +obj-$(CONFIG_SCSI_ESP_CORE) += esp_scsi.o +obj-$(CONFIG_SCSI_SUNESP) += sun_esp.o obj-$(CONFIG_SCSI_GDTH) += gdth.o obj-$(CONFIG_SCSI_INITIO) += initio.o obj-$(CONFIG_SCSI_INIA100) += a100u2w.o diff --git a/drivers/scsi/a100u2w.c b/drivers/scsi/a100u2w.c index 2650a5d..7f4241b 100644 --- a/drivers/scsi/a100u2w.c +++ b/drivers/scsi/a100u2w.c @@ -1067,7 +1067,7 @@ static int __devinit inia100_probe_one(struct pci_dev *pdev, goto out_disable_device; } - /* <02> read from base address + 0x50 offset to get the bios balue. */ + /* <02> read from base address + 0x50 offset to get the bios value. */ bios = ORC_RDWORD(port, 0x50); diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c index c7fe478..2be03e9 100644 --- a/drivers/scsi/aic7xxx/aic79xx_osm.c +++ b/drivers/scsi/aic7xxx/aic79xx_osm.c @@ -418,7 +418,6 @@ ahd_linux_info(struct Scsi_Host *host) strcat(bp, " "); ahd_controller_info(ahd, ahd_info); strcat(bp, ahd_info); - strcat(bp, "\n"); return (bp); } diff --git a/drivers/scsi/aic94xx/aic94xx_seq.c b/drivers/scsi/aic94xx/aic94xx_seq.c index eae7a24..c750fbf 100644 --- a/drivers/scsi/aic94xx/aic94xx_seq.c +++ b/drivers/scsi/aic94xx/aic94xx_seq.c @@ -44,7 +44,6 @@ #define PAUSE_TRIES 1000 static const struct firmware *sequencer_fw; -static const char *sequencer_version; static u16 cseq_vecs[CSEQ_NUM_VECS], lseq_vecs[LSEQ_NUM_VECS], mode2_task, cseq_idle_loop, lseq_idle_loop; static u8 *cseq_code, *lseq_code; @@ -1276,7 +1275,6 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha) header.csum = le32_to_cpu(hdr_ptr->csum); header.major = le32_to_cpu(hdr_ptr->major); header.minor = le32_to_cpu(hdr_ptr->minor); - sequencer_version = hdr_ptr->version; header.cseq_table_offset = le32_to_cpu(hdr_ptr->cseq_table_offset); header.cseq_table_size = le32_to_cpu(hdr_ptr->cseq_table_size); header.lseq_table_offset = le32_to_cpu(hdr_ptr->lseq_table_offset); @@ -1303,6 +1301,16 @@ static int asd_request_firmware(struct asd_ha_struct *asd_ha) return -EINVAL; } + asd_printk("Found sequencer Firmware version %d.%d (%s)\n", + header.major, header.minor, hdr_ptr->version); + + if (header.major != SAS_RAZOR_SEQUENCER_FW_MAJOR) { + asd_printk("Firmware Major Version Mismatch;" + "driver requires version %d.X", + SAS_RAZOR_SEQUENCER_FW_MAJOR); + return -EINVAL; + } + ptr_cseq_vecs = (u16 *)&sequencer_fw->data[header.cseq_table_offset]; ptr_lseq_vecs = (u16 *)&sequencer_fw->data[header.lseq_table_offset]; mode2_task = header.mode2_task; @@ -1335,7 +1343,6 @@ int asd_init_seqs(struct asd_ha_struct *asd_ha) return err; } - asd_printk("using sequencer %s\n", sequencer_version); err = asd_seq_download_seqs(asd_ha); if (err) { asd_printk("couldn't download sequencers for %s\n", diff --git a/drivers/scsi/aic94xx/aic94xx_seq.h b/drivers/scsi/aic94xx/aic94xx_seq.h index 9437ff0..2ea6a0d 100644 --- a/drivers/scsi/aic94xx/aic94xx_seq.h +++ b/drivers/scsi/aic94xx/aic94xx_seq.h @@ -31,6 +31,7 @@ #define LSEQ_NUM_VECS 11 #define SAS_RAZOR_SEQUENCER_FW_FILE "aic94xx-seq.fw" +#define SAS_RAZOR_SEQUENCER_FW_MAJOR 1 /* Note: All quantites in the sequencer file are little endian */ struct sequencer_file_header { diff --git a/drivers/scsi/arm/cumana_2.c b/drivers/scsi/arm/cumana_2.c index d2d51dc..82add77 100644 --- a/drivers/scsi/arm/cumana_2.c +++ b/drivers/scsi/arm/cumana_2.c @@ -178,10 +178,10 @@ cumanascsi_2_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, dma_dir = DMA_MODE_READ, alatch_dir = ALATCH_DMA_IN; - dma_map_sg(dev, info->sg, bufs + 1, map_dir); + dma_map_sg(dev, info->sg, bufs, map_dir); disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_sg(dmach, info->sg, bufs); writeb(alatch_dir, info->base + CUMANASCSI2_ALATCH); set_dma_mode(dmach, dma_dir); enable_dma(dmach); diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c index 4677152..ed06a8c 100644 --- a/drivers/scsi/arm/eesox.c +++ b/drivers/scsi/arm/eesox.c @@ -175,10 +175,10 @@ eesoxscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, map_dir = DMA_FROM_DEVICE, dma_dir = DMA_MODE_READ; - dma_map_sg(dev, info->sg, bufs + 1, map_dir); + dma_map_sg(dev, info->sg, bufs, map_dir); disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_sg(dmach, info->sg, bufs); set_dma_mode(dmach, dma_dir); enable_dma(dmach); return fasdma_real_all; @@ -196,7 +196,7 @@ static void eesoxscsi_buffer_in(void *buf, int length, void __iomem *base) const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET; const void __iomem *reg_dmastat = base + EESOX_DMASTAT; const void __iomem *reg_dmadata = base + EESOX_DMADATA; - const register unsigned long mask = 0xffff; + register const unsigned long mask = 0xffff; do { unsigned int status; diff --git a/drivers/scsi/arm/fas216.c b/drivers/scsi/arm/fas216.c index 2969cc0..fb5f202 100644 --- a/drivers/scsi/arm/fas216.c +++ b/drivers/scsi/arm/fas216.c @@ -633,7 +633,7 @@ static void fas216_updateptrs(FAS216_Info *info, int bytes_transferred) BUG_ON(bytes_transferred < 0); - info->SCpnt->request_bufflen -= bytes_transferred; + SCp->phase -= bytes_transferred; while (bytes_transferred != 0) { if (SCp->this_residual > bytes_transferred) @@ -715,7 +715,7 @@ static void fas216_cleanuptransfer(FAS216_Info *info) return; if (dmatype == fasdma_real_all) - total = info->SCpnt->request_bufflen; + total = info->scsi.SCp.phase; else total = info->scsi.SCp.this_residual; @@ -753,7 +753,7 @@ static void fas216_transfer(FAS216_Info *info) fas216_log(info, LOG_BUFFER, "starttransfer: buffer %p length 0x%06x reqlen 0x%06x", info->scsi.SCp.ptr, info->scsi.SCp.this_residual, - info->SCpnt->request_bufflen); + info->scsi.SCp.phase); if (!info->scsi.SCp.ptr) { fas216_log(info, LOG_ERROR, "null buffer passed to " @@ -784,7 +784,7 @@ static void fas216_transfer(FAS216_Info *info) info->dma.transfer_type = dmatype; if (dmatype == fasdma_real_all) - fas216_set_stc(info, info->SCpnt->request_bufflen); + fas216_set_stc(info, info->scsi.SCp.phase); else fas216_set_stc(info, info->scsi.SCp.this_residual); @@ -2114,6 +2114,7 @@ request_sense: SCpnt->SCp.buffers_residual = 0; SCpnt->SCp.ptr = (char *)SCpnt->sense_buffer; SCpnt->SCp.this_residual = sizeof(SCpnt->sense_buffer); + SCpnt->SCp.phase = sizeof(SCpnt->sense_buffer); SCpnt->SCp.Message = 0; SCpnt->SCp.Status = 0; SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer); diff --git a/drivers/scsi/arm/powertec.c b/drivers/scsi/arm/powertec.c index f9cd20b..159047a 100644 --- a/drivers/scsi/arm/powertec.c +++ b/drivers/scsi/arm/powertec.c @@ -148,10 +148,10 @@ powertecscsi_dma_setup(struct Scsi_Host *host, struct scsi_pointer *SCp, map_dir = DMA_FROM_DEVICE, dma_dir = DMA_MODE_READ; - dma_map_sg(dev, info->sg, bufs + 1, map_dir); + dma_map_sg(dev, info->sg, bufs, map_dir); disable_dma(dmach); - set_dma_sg(dmach, info->sg, bufs + 1); + set_dma_sg(dmach, info->sg, bufs); set_dma_mode(dmach, dma_dir); enable_dma(dmach); return fasdma_real_all; @@ -342,6 +342,7 @@ powertecscsi_probe(struct expansion_card *ec, const struct ecard_id *id) info->base = base; powertecscsi_terminator_ctl(host, term[ec->slot_no]); + info->ec = ec; info->info.scsi.io_base = base + POWERTEC_FAS216_OFFSET; info->info.scsi.io_shift = POWERTEC_FAS216_SHIFT; info->info.scsi.irq = ec->irq; diff --git a/drivers/scsi/arm/scsi.h b/drivers/scsi/arm/scsi.h index 3a39579..21ba571 100644 --- a/drivers/scsi/arm/scsi.h +++ b/drivers/scsi/arm/scsi.h @@ -80,6 +80,7 @@ static inline void init_SCp(struct scsi_cmnd *SCpnt) (page_address(SCpnt->SCp.buffer->page) + SCpnt->SCp.buffer->offset); SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length; + SCpnt->SCp.phase = SCpnt->request_bufflen; #ifdef BELT_AND_BRACES /* @@ -98,6 +99,7 @@ static inline void init_SCp(struct scsi_cmnd *SCpnt) } else { SCpnt->SCp.ptr = (unsigned char *)SCpnt->request_buffer; SCpnt->SCp.this_residual = SCpnt->request_bufflen; + SCpnt->SCp.phase = SCpnt->request_bufflen; } /* diff --git a/drivers/scsi/esp.c b/drivers/scsi/esp.c deleted file mode 100644 index 2c2fe80..0000000 --- a/drivers/scsi/esp.c +++ /dev/null @@ -1,4394 +0,0 @@ -/* esp.c: ESP Sun SCSI driver. - * - * Copyright (C) 1995, 1998, 2006 David S. Miller (davem@davemloft.net) - */ - -/* TODO: - * - * 1) Maybe disable parity checking in config register one for SCSI1 - * targets. (Gilmore says parity error on the SBus can lock up - * old sun4c's) - * 2) Add support for DMA2 pipelining. - * 3) Add tagged queueing. - */ - -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/types.h> -#include <linux/string.h> -#include <linux/slab.h> -#include <linux/blkdev.h> -#include <linux/proc_fs.h> -#include <linux/stat.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/interrupt.h> -#include <linux/module.h> - -#include "esp.h" - -#include <asm/sbus.h> -#include <asm/dma.h> -#include <asm/system.h> -#include <asm/ptrace.h> -#include <asm/pgtable.h> -#include <asm/oplib.h> -#include <asm/io.h> -#include <asm/irq.h> -#ifndef __sparc_v9__ -#include <asm/machines.h> -#include <asm/idprom.h> -#endif - -#include <scsi/scsi.h> -#include <scsi/scsi_cmnd.h> -#include <scsi/scsi_device.h> -#include <scsi/scsi_eh.h> -#include <scsi/scsi_host.h> -#include <scsi/scsi_tcq.h> - -#define DRV_VERSION "1.101" - -#define DEBUG_ESP -/* #define DEBUG_ESP_HME */ -/* #define DEBUG_ESP_DATA */ -/* #define DEBUG_ESP_QUEUE */ -/* #define DEBUG_ESP_DISCONNECT */ -/* #define DEBUG_ESP_STATUS */ -/* #define DEBUG_ESP_PHASES */ -/* #define DEBUG_ESP_WORKBUS */ -/* #define DEBUG_STATE_MACHINE */ -/* #define DEBUG_ESP_CMDS */ -/* #define DEBUG_ESP_IRQS */ -/* #define DEBUG_SDTR */ -/* #define DEBUG_ESP_SG */ - -/* Use the following to sprinkle debugging messages in a way which - * suits you if combinations of the above become too verbose when - * trying to track down a specific problem. - */ -/* #define DEBUG_ESP_MISC */ - -#if defined(DEBUG_ESP) -#define ESPLOG(foo) printk foo -#else -#define ESPLOG(foo) -#endif /* (DEBUG_ESP) */ - -#if defined(DEBUG_ESP_HME) -#define ESPHME(foo) printk foo -#else -#define ESPHME(foo) -#endif - -#if defined(DEBUG_ESP_DATA) -#define ESPDATA(foo) printk foo -#else -#define ESPDATA(foo) -#endif - -#if defined(DEBUG_ESP_QUEUE) -#define ESPQUEUE(foo) printk foo -#else -#define ESPQUEUE(foo) -#endif - -#if defined(DEBUG_ESP_DISCONNECT) -#define ESPDISC(foo) printk foo -#else -#define ESPDISC(foo) -#endif - -#if defined(DEBUG_ESP_STATUS) -#define ESPSTAT(foo) printk foo -#else -#define ESPSTAT(foo) -#endif - -#if defined(DEBUG_ESP_PHASES) -#define ESPPHASE(foo) printk foo -#else -#define ESPPHASE(foo) -#endif - -#if defined(DEBUG_ESP_WORKBUS) -#define ESPBUS(foo) printk foo -#else -#define ESPBUS(foo) -#endif - -#if defined(DEBUG_ESP_IRQS) -#define ESPIRQ(foo) printk foo -#else -#define ESPIRQ(foo) -#endif - -#if defined(DEBUG_SDTR) -#define ESPSDTR(foo) printk foo -#else -#define ESPSDTR(foo) -#endif - -#if defined(DEBUG_ESP_MISC) -#define ESPMISC(foo) printk foo -#else -#define ESPMISC(foo) -#endif - -/* Command phase enumeration. */ -enum { - not_issued = 0x00, /* Still in the issue_SC queue. */ - - /* Various forms of selecting a target. */ -#define in_slct_mask 0x10 - in_slct_norm = 0x10, /* ESP is arbitrating, normal selection */ - in_slct_stop = 0x11, /* ESP will select, then stop with IRQ */ - in_slct_msg = 0x12, /* select, then send a message */ - in_slct_tag = 0x13, /* select and send tagged queue msg */ - in_slct_sneg = 0x14, /* select and acquire sync capabilities */ - - /* Any post selection activity. */ -#define in_phases_mask 0x20 - in_datain = 0x20, /* Data is transferring from the bus */ - in_dataout = 0x21, /* Data is transferring to the bus */ - in_data_done = 0x22, /* Last DMA data operation done (maybe) */ - in_msgin = 0x23, /* Eating message from target */ - in_msgincont = 0x24, /* Eating more msg bytes from target */ - in_msgindone = 0x25, /* Decide what to do with what we got */ - in_msgout = 0x26, /* Sending message to target */ - in_msgoutdone = 0x27, /* Done sending msg out */ - in_cmdbegin = 0x28, /* Sending cmd after abnormal selection */ - in_cmdend = 0x29, /* Done sending slow cmd */ - in_status = 0x2a, /* Was in status phase, finishing cmd */ - in_freeing = 0x2b, /* freeing the bus for cmd cmplt or disc */ - in_the_dark = 0x2c, /* Don't know what bus phase we are in */ - - /* Special states, ie. not normal bus transitions... */ -#define in_spec_mask 0x80 - in_abortone = 0x80, /* Aborting one command currently */ - in_abortall = 0x81, /* Blowing away all commands we have */ - in_resetdev = 0x82, /* SCSI target reset in progress */ - in_resetbus = 0x83, /* SCSI bus reset in progress */ - in_tgterror = 0x84, /* Target did something stupid */ -}; - -enum { - /* Zero has special meaning, see skipahead[12]. */ -/*0*/ do_never, - -/*1*/ do_phase_determine, -/*2*/ do_reset_bus, -/*3*/ do_reset_complete, -/*4*/ do_work_bus, -/*5*/ do_intr_end -}; - -/* Forward declarations. */ -static irqreturn_t esp_intr(int irq, void *dev_id); - -/* Debugging routines */ -struct esp_cmdstrings { - u8 cmdchar; - char *text; -} esp_cmd_strings[] = { - /* Miscellaneous */ - { ESP_CMD_NULL, "ESP_NOP", }, - { ESP_CMD_FLUSH, "FIFO_FLUSH", }, - { ESP_CMD_RC, "RSTESP", }, - { ESP_CMD_RS, "RSTSCSI", }, - /* Disconnected State Group */ - { ESP_CMD_RSEL, "RESLCTSEQ", }, - { ESP_CMD_SEL, "SLCTNATN", }, - { ESP_CMD_SELA, "SLCTATN", }, - { ESP_CMD_SELAS, "SLCTATNSTOP", }, - { ESP_CMD_ESEL, "ENSLCTRESEL", }, - { ESP_CMD_DSEL, "DISSELRESEL", }, - { ESP_CMD_SA3, "SLCTATN3", }, - { ESP_CMD_RSEL3, "RESLCTSEQ", }, - /* Target State Group */ - { ESP_CMD_SMSG, "SNDMSG", }, - { ESP_CMD_SSTAT, "SNDSTATUS", }, - { ESP_CMD_SDATA, "SNDDATA", }, - { ESP_CMD_DSEQ, "DISCSEQ", }, - { ESP_CMD_TSEQ, "TERMSEQ", }, - { ESP_CMD_TCCSEQ, "TRGTCMDCOMPSEQ", }, - { ESP_CMD_DCNCT, "DISC", }, - { ESP_CMD_RMSG, "RCVMSG", }, - { ESP_CMD_RCMD, "RCVCMD", }, - { ESP_CMD_RDATA, "RCVDATA", }, - { ESP_CMD_RCSEQ, "RCVCMDSEQ", }, - /* Initiator State Group */ - { ESP_CMD_TI, "TRANSINFO", }, - { ESP_CMD_ICCSEQ, "INICMDSEQCOMP", }, - { ESP_CMD_MOK, "MSGACCEPTED", }, - { ESP_CMD_TPAD, "TPAD", }, - { ESP_CMD_SATN, "SATN", }, - { ESP_CMD_RATN, "RATN", }, -}; -#define NUM_ESP_COMMANDS ((sizeof(esp_cmd_strings)) / (sizeof(struct esp_cmdstrings))) - -/* Print textual representation of an ESP command */ -static inline void esp_print_cmd(u8 espcmd) -{ - u8 dma_bit = espcmd & ESP_CMD_DMA; - int i; - - espcmd &= ~dma_bit; - for (i = 0; i < NUM_ESP_COMMANDS; i++) - if (esp_cmd_strings[i].cmdchar == espcmd) - break; - if (i == NUM_ESP_COMMANDS) - printk("ESP_Unknown"); - else - printk("%s%s", esp_cmd_strings[i].text, - ((dma_bit) ? "+DMA" : "")); -} - -/* Print the status register's value */ -static inline void esp_print_statreg(u8 statreg) -{ - u8 phase; - - printk("STATUS<"); - phase = statreg & ESP_STAT_PMASK; - printk("%s,", (phase == ESP_DOP ? "DATA-OUT" : - (phase == ESP_DIP ? "DATA-IN" : - (phase == ESP_CMDP ? "COMMAND" : - (phase == ESP_STATP ? "STATUS" : - (phase == ESP_MOP ? "MSG-OUT" : - (phase == ESP_MIP ? "MSG_IN" : - "unknown"))))))); - if (statreg & ESP_STAT_TDONE) - printk("TRANS_DONE,"); - if (statreg & ESP_STAT_TCNT) - printk("TCOUNT_ZERO,"); - if (statreg & ESP_STAT_PERR) - printk("P_ERROR,"); - if (statreg & ESP_STAT_SPAM) - printk("SPAM,"); - if (statreg & ESP_STAT_INTR) - printk("IRQ,"); - printk(">"); -} - -/* Print the interrupt register's value */ -static inline void esp_print_ireg(u8 intreg) -{ - printk("INTREG< "); - if (intreg & ESP_INTR_S) - printk("SLCT_NATN "); - if (intreg & ESP_INTR_SATN) - printk("SLCT_ATN "); - if (intreg & ESP_INTR_RSEL) - printk("RSLCT "); - if (intreg & ESP_INTR_FDONE) - printk("FDONE "); - if (intreg & ESP_INTR_BSERV) - printk("BSERV "); - if (intreg & ESP_INTR_DC) - printk("DISCNCT "); - if (intreg & ESP_INTR_IC) - printk("ILL_CMD "); - if (intreg & ESP_INTR_SR) - printk("SCSI_BUS_RESET "); - printk(">"); -} - -/* Print the sequence step registers contents */ -static inline void esp_print_seqreg(u8 stepreg) -{ - stepreg &= ESP_STEP_VBITS; - printk("STEP<%s>", - (stepreg == ESP_STEP_ASEL ? "SLCT_ARB_CMPLT" : - (stepreg == ESP_STEP_SID ? "1BYTE_MSG_SENT" : - (stepreg == ESP_STEP_NCMD ? "NOT_IN_CMD_PHASE" : - (stepreg == ESP_STEP_PPC ? "CMD_BYTES_LOST" : - (stepreg == ESP_STEP_FINI4 ? "CMD_SENT_OK" : - "UNKNOWN")))))); -} - -static char *phase_string(int phase) -{ - switch (phase) { - case not_issued: - return "UNISSUED"; - case in_slct_norm: - return "SLCTNORM"; - case in_slct_stop: - return "SLCTSTOP"; - case in_slct_msg: - return "SLCTMSG"; - case in_slct_tag: - return "SLCTTAG"; - case in_slct_sneg: - return "SLCTSNEG"; - case in_datain: - return "DATAIN"; - case in_dataout: - return "DATAOUT"; - case in_data_done: - return "DATADONE"; - case in_msgin: - return "MSGIN"; - case in_msgincont: - return "MSGINCONT"; - case in_msgindone: - return "MSGINDONE"; - case in_msgout: - return "MSGOUT"; - case in_msgoutdone: - return "MSGOUTDONE"; - case in_cmdbegin: - return "CMDBEGIN"; - case in_cmdend: - return "CMDEND"; - case in_status: - return "STATUS"; - case in_freeing: - return "FREEING"; - case in_the_dark: - return "CLUELESS"; - case in_abortone: - return "ABORTONE"; - case in_abortall: - return "ABORTALL"; - case in_resetdev: - return "RESETDEV"; - case in_resetbus: - return "RESETBUS"; - case in_tgterror: - return "TGTERROR"; - default: - return "UNKNOWN"; - }; -} - -#ifdef DEBUG_STATE_MACHINE -static inline void esp_advance_phase(struct scsi_cmnd *s, int newphase) -{ - ESPLOG(("<%s>", phase_string(newphase))); - s->SCp.sent_command = s->SCp.phase; - s->SCp.phase = newphase; -} -#else -#define esp_advance_phase(__s, __newphase) \ - (__s)->SCp.sent_command = (__s)->SCp.phase; \ - (__s)->SCp.phase = (__newphase); -#endif - -#ifdef DEBUG_ESP_CMDS -static inline void esp_cmd(struct esp *esp, u8 cmd) -{ - esp->espcmdlog[esp->espcmdent] = cmd; - esp->espcmdent = (esp->espcmdent + 1) & 31; - sbus_writeb(cmd, esp->eregs + ESP_CMD); -} -#else -#define esp_cmd(__esp, __cmd) \ - sbus_writeb((__cmd), ((__esp)->eregs) + ESP_CMD) -#endif - -#define ESP_INTSOFF(__dregs) \ - sbus_writel(sbus_readl((__dregs)+DMA_CSR)&~(DMA_INT_ENAB), (__dregs)+DMA_CSR) -#define ESP_INTSON(__dregs) \ - sbus_writel(sbus_readl((__dregs)+DMA_CSR)|DMA_INT_ENAB, (__dregs)+DMA_CSR) -#define ESP_IRQ_P(__dregs) \ - (sbus_readl((__dregs)+DMA_CSR) & (DMA_HNDL_INTR|DMA_HNDL_ERROR)) - -/* How we use the various Linux SCSI data structures for operation. - * - * struct scsi_cmnd: - * - * We keep track of the synchronous capabilities of a target - * in the device member, using sync_min_period and - * sync_max_offset. These are the values we directly write - * into the ESP registers while running a command. If offset - * is zero the ESP will use asynchronous transfers. - * If the borken flag is set we assume we shouldn't even bother - * trying to negotiate for synchronous transfer as this target - * is really stupid. If we notice the target is dropping the - * bus, and we have been allowing it to disconnect, we clear - * the disconnect flag. - */ - - -/* Manipulation of the ESP command queues. Thanks to the aha152x driver - * and its author, Juergen E. Fischer, for the methods used here. - * Note that these are per-ESP queues, not global queues like - * the aha152x driver uses. - */ -static inline void append_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC) -{ - struct scsi_cmnd *end; - - new_SC->host_scribble = (unsigned char *) NULL; - if (!*SC) - *SC = new_SC; - else { - for (end=*SC;end->host_scribble;end=(struct scsi_cmnd *)end->host_scribble) - ; - end->host_scribble = (unsigned char *) new_SC; - } -} - -static inline void prepend_SC(struct scsi_cmnd **SC, struct scsi_cmnd *new_SC) -{ - new_SC->host_scribble = (unsigned char *) *SC; - *SC = new_SC; -} - -static inline struct scsi_cmnd *remove_first_SC(struct scsi_cmnd **SC) -{ - struct scsi_cmnd *ptr; - ptr = *SC; - if (ptr) - *SC = (struct scsi_cmnd *) (*SC)->host_scribble; - return ptr; -} - -static inline struct scsi_cmnd *remove_SC(struct scsi_cmnd **SC, int target, int lun) -{ - struct scsi_cmnd *ptr, *prev; - - for (ptr = *SC, prev = NULL; - ptr && ((ptr->device->id != target) || (ptr->device->lun != lun)); - prev = ptr, ptr = (struct scsi_cmnd *) ptr->host_scribble) - ; - if (ptr) { - if (prev) - prev->host_scribble=ptr->host_scribble; - else - *SC=(struct scsi_cmnd *)ptr->host_scribble; - } - return ptr; -} - -/* Resetting various pieces of the ESP scsi driver chipset/buses. */ -static void esp_reset_dma(struct esp *esp) -{ - int can_do_burst16, can_do_burst32, can_do_burst64; - int can_do_sbus64; - u32 tmp; - - can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; - can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; - can_do_burst64 = 0; - can_do_sbus64 = 0; - if (sbus_can_dma_64bit(esp->sdev)) - can_do_sbus64 = 1; - if (sbus_can_burst64(esp->sdev)) - can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; - - /* Punt the DVMA into a known state. */ - if (esp->dma->revision != dvmahme) { - tmp = sbus_readl(esp->dregs + DMA_CSR); - sbus_writel(tmp | DMA_RST_SCSI, esp->dregs + DMA_CSR); - sbus_writel(tmp & ~DMA_RST_SCSI, esp->dregs + DMA_CSR); - } - switch (esp->dma->revision) { - case dvmahme: - /* This is the HME DVMA gate array. */ - - sbus_writel(DMA_RESET_FAS366, esp->dregs + DMA_CSR); - sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR); - - esp->prev_hme_dmacsr = (DMA_PARITY_OFF|DMA_2CLKS|DMA_SCSI_DISAB|DMA_INT_ENAB); - esp->prev_hme_dmacsr &= ~(DMA_ENABLE|DMA_ST_WRITE|DMA_BRST_SZ); - - if (can_do_burst64) - esp->prev_hme_dmacsr |= DMA_BRST64; - else if (can_do_burst32) - esp->prev_hme_dmacsr |= DMA_BRST32; - - if (can_do_sbus64) { - esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; - sbus_set_sbus64(esp->sdev, esp->bursts); - } - - /* This chip is horrible. */ - while (sbus_readl(esp->dregs + DMA_CSR) & DMA_PEND_READ) - udelay(1); - - sbus_writel(0, esp->dregs + DMA_CSR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - - /* This is necessary to avoid having the SCSI channel - * engine lock up on us. - */ - sbus_writel(0, esp->dregs + DMA_ADDR); - - break; - case dvmarev2: - /* This is the gate array found in the sun4m - * NCR SBUS I/O subsystem. - */ - if (esp->erev != esp100) { - tmp = sbus_readl(esp->dregs + DMA_CSR); - sbus_writel(tmp | DMA_3CLKS, esp->dregs + DMA_CSR); - } - break; - case dvmarev3: - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp &= ~DMA_3CLKS; - tmp |= DMA_2CLKS; - if (can_do_burst32) { - tmp &= ~DMA_BRST_SZ; - tmp |= DMA_BRST32; - } - sbus_writel(tmp, esp->dregs + DMA_CSR); - break; - case dvmaesc1: - /* This is the DMA unit found on SCSI/Ether cards. */ - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp |= DMA_ADD_ENABLE; - tmp &= ~DMA_BCNT_ENAB; - if (!can_do_burst32 && can_do_burst16) { - tmp |= DMA_ESC_BURST; - } else { - tmp &= ~(DMA_ESC_BURST); - } - sbus_writel(tmp, esp->dregs + DMA_CSR); - break; - default: - break; - }; - ESP_INTSON(esp->dregs); -} - -/* Reset the ESP chip, _not_ the SCSI bus. */ -static void __init esp_reset_esp(struct esp *esp) -{ - u8 family_code, version; - int i; - - /* Now reset the ESP chip */ - esp_cmd(esp, ESP_CMD_RC); - esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); - esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); - - /* Reload the configuration registers */ - sbus_writeb(esp->cfact, esp->eregs + ESP_CFACT); - esp->prev_stp = 0; - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - esp->prev_soff = 0; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->neg_defp, esp->eregs + ESP_TIMEO); - - /* This is the only point at which it is reliable to read - * the ID-code for a fast ESP chip variants. - */ - esp->max_period = ((35 * esp->ccycle) / 1000); - if (esp->erev == fast) { - version = sbus_readb(esp->eregs + ESP_UID); - family_code = (version & 0xf8) >> 3; - if (family_code == 0x02) - esp->erev = fas236; - else if (family_code == 0x0a) - esp->erev = fashme; /* Version is usually '5'. */ - else - esp->erev = fas100a; - ESPMISC(("esp%d: FAST chip is %s (family=%d, version=%d)\n", - esp->esp_id, - (esp->erev == fas236) ? "fas236" : - ((esp->erev == fas100a) ? "fas100a" : - "fasHME"), family_code, (version & 7))); - - esp->min_period = ((4 * esp->ccycle) / 1000); - } else { - esp->min_period = ((5 * esp->ccycle) / 1000); - } - esp->max_period = (esp->max_period + 3)>>2; - esp->min_period = (esp->min_period + 3)>>2; - - sbus_writeb(esp->config1, esp->eregs + ESP_CFG1); - switch (esp->erev) { - case esp100: - /* nothing to do */ - break; - case esp100a: - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - break; - case esp236: - /* Slow 236 */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - break; - case fashme: - esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); - /* fallthrough... */ - case fas236: - /* Fast 236 or HME */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - for (i = 0; i < 16; i++) { - if (esp->erev == fashme) { - u8 cfg3; - - cfg3 = ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; - if (esp->scsi_id >= 8) - cfg3 |= ESP_CONFIG3_IDBIT3; - esp->config3[i] |= cfg3; - } else { - esp->config3[i] |= ESP_CONFIG3_FCLK; - } - } - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - if (esp->erev == fashme) { - esp->radelay = 80; - } else { - if (esp->diff) - esp->radelay = 0; - else - esp->radelay = 96; - } - break; - case fas100a: - /* Fast 100a */ - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - for (i = 0; i < 16; i++) - esp->config3[i] |= ESP_CONFIG3_FCLOCK; - esp->prev_cfg3 = esp->config3[0]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - esp->radelay = 32; - break; - default: - panic("esp: what could it be... I wonder..."); - break; - }; - - /* Eat any bitrot in the chip */ - sbus_readb(esp->eregs + ESP_INTRPT); - udelay(100); -} - -/* This places the ESP into a known state at boot time. */ -static void __init esp_bootup_reset(struct esp *esp) -{ - u8 tmp; - - /* Reset the DMA */ - esp_reset_dma(esp); - - /* Reset the ESP */ - esp_reset_esp(esp); - - /* Reset the SCSI bus, but tell ESP not to generate an irq */ - tmp = sbus_readb(esp->eregs + ESP_CFG1); - tmp |= ESP_CONFIG1_SRRDISAB; - sbus_writeb(tmp, esp->eregs + ESP_CFG1); - - esp_cmd(esp, ESP_CMD_RS); - udelay(400); - - sbus_writeb(esp->config1, esp->eregs + ESP_CFG1); - - /* Eat any bitrot in the chip and we are done... */ - sbus_readb(esp->eregs + ESP_INTRPT); -} - -static int __init esp_find_dvma(struct esp *esp, struct sbus_dev *dma_sdev) -{ - struct sbus_dev *sdev = esp->sdev; - struct sbus_dma *dma; - - if (dma_sdev != NULL) { - for_each_dvma(dma) { - if (dma->sdev == dma_sdev) - break; - } - } else { - for_each_dvma(dma) { - /* If allocated already, can't use it. */ - if (dma->allocated) - continue; - - if (dma->sdev == NULL) - break; - - /* If bus + slot are the same and it has the - * correct OBP name, it's ours. - */ - if (sdev->bus == dma->sdev->bus && - sdev->slot == dma->sdev->slot && - (!strcmp(dma->sdev->prom_name, "dma") || - !strcmp(dma->sdev->prom_name, "espdma"))) - break; - } - } - - /* If we don't know how to handle the dvma, - * do not use this device. - */ - if (dma == NULL) { - printk("Cannot find dvma for ESP%d's SCSI\n", esp->esp_id); - return -1; - } - if (dma->allocated) { - printk("esp%d: can't use my espdma\n", esp->esp_id); - return -1; - } - dma->allocated = 1; - esp->dma = dma; - esp->dregs = dma->regs; - - return 0; -} - -static int __init esp_map_regs(struct esp *esp, int hme) -{ - struct sbus_dev *sdev = esp->sdev; - struct resource *res; - - /* On HME, two reg sets exist, first is DVMA, - * second is ESP registers. - */ - if (hme) - res = &sdev->resource[1]; - else - res = &sdev->resource[0]; - - esp->eregs = sbus_ioremap(res, 0, ESP_REG_SIZE, "ESP Registers"); - - if (esp->eregs == 0) - return -1; - return 0; -} - -static int __init esp_map_cmdarea(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - - esp->esp_command = sbus_alloc_consistent(sdev, 16, - &esp->esp_command_dvma); - if (esp->esp_command == NULL || - esp->esp_command_dvma == 0) - return -1; - return 0; -} - -static int __init esp_register_irq(struct esp *esp) -{ - esp->ehost->irq = esp->irq = esp->sdev->irqs[0]; - - /* We used to try various overly-clever things to - * reduce the interrupt processing overhead on - * sun4c/sun4m when multiple ESP's shared the - * same IRQ. It was too complex and messy to - * sanely maintain. - */ - if (request_irq(esp->ehost->irq, esp_intr, - IRQF_SHARED, "ESP SCSI", esp)) { - printk("esp%d: Cannot acquire irq line\n", - esp->esp_id); - return -1; - } - - printk("esp%d: IRQ %d ", esp->esp_id, - esp->ehost->irq); - - return 0; -} - -static void __init esp_get_scsi_id(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - struct device_node *dp = sdev->ofdev.node; - - esp->scsi_id = of_getintprop_default(dp, - "initiator-id", - -1); - if (esp->scsi_id == -1) - esp->scsi_id = of_getintprop_default(dp, - "scsi-initiator-id", - -1); - if (esp->scsi_id == -1) - esp->scsi_id = (sdev->bus == NULL) ? 7 : - of_getintprop_default(sdev->bus->ofdev.node, - "scsi-initiator-id", - 7); - esp->ehost->this_id = esp->scsi_id; - esp->scsi_id_mask = (1 << esp->scsi_id); - -} - -static void __init esp_get_clock_params(struct esp *esp) -{ - struct sbus_dev *sdev = esp->sdev; - int prom_node = esp->prom_node; - int sbus_prom_node; - unsigned int fmhz; - u8 ccf; - - if (sdev != NULL && sdev->bus != NULL) - sbus_prom_node = sdev->bus->prom_node; - else - sbus_prom_node = 0; - - /* This is getting messy but it has to be done - * correctly or else you get weird behavior all - * over the place. We are trying to basically - * figure out three pieces of information. - * - * a) Clock Conversion Factor - * - * This is a representation of the input - * crystal clock frequency going into the - * ESP on this machine. Any operation whose - * timing is longer than 400ns depends on this - * value being correct. For example, you'll - * get blips for arbitration/selection during - * high load or with multiple targets if this - * is not set correctly. - * - * b) Selection Time-Out - * - * The ESP isn't very bright and will arbitrate - * for the bus and try to select a target - * forever if you let it. This value tells - * the ESP when it has taken too long to - * negotiate and that it should interrupt - * the CPU so we can see what happened. - * The value is computed as follows (from - * NCR/Symbios chip docs). - * - * (Time Out Period) * (Input Clock) - * STO = ---------------------------------- - * (8192) * (Clock Conversion Factor) - * - * You usually want the time out period to be - * around 250ms, I think we'll set it a little - * bit higher to account for fully loaded SCSI - * bus's and slow devices that don't respond so - * quickly to selection attempts. (yeah, I know - * this is out of spec. but there is a lot of - * buggy pieces of firmware out there so bite me) - * - * c) Imperical constants for synchronous offset - * and transfer period register values - * - * This entails the smallest and largest sync - * period we could ever handle on this ESP. - */ - - fmhz = prom_getintdefault(prom_node, "clock-frequency", -1); - if (fmhz == -1) - fmhz = (!sbus_prom_node) ? 0 : - prom_getintdefault(sbus_prom_node, "clock-frequency", -1); - - if (fmhz <= (5000000)) - ccf = 0; - else - ccf = (((5000000 - 1) + (fmhz))/(5000000)); - - if (!ccf || ccf > 8) { - /* If we can't find anything reasonable, - * just assume 20MHZ. This is the clock - * frequency of the older sun4c's where I've - * been unable to find the clock-frequency - * PROM property. All other machines provide - * useful values it seems. - */ - ccf = ESP_CCF_F4; - fmhz = (20000000); - } - - if (ccf == (ESP_CCF_F7 + 1)) - esp->cfact = ESP_CCF_F0; - else if (ccf == ESP_CCF_NEVER) - esp->cfact = ESP_CCF_F2; - else - esp->cfact = ccf; - esp->raw_cfact = ccf; - - esp->cfreq = fmhz; - esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz); - esp->ctick = ESP_TICK(ccf, esp->ccycle); - esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); - esp->sync_defp = SYNC_DEFP_SLOW; - - printk("SCSI ID %d Clk %dMHz CCYC=%d CCF=%d TOut %d ", - esp->scsi_id, (fmhz / 1000000), - (int)esp->ccycle, (int)ccf, (int) esp->neg_defp); -} - -static void __init esp_get_bursts(struct esp *esp, struct sbus_dev *dma) -{ - struct sbus_dev *sdev = esp->sdev; - u8 bursts; - - bursts = prom_getintdefault(esp->prom_node, "burst-sizes", 0xff); - - if (dma) { - u8 tmp = prom_getintdefault(dma->prom_node, - "burst-sizes", 0xff); - if (tmp != 0xff) - bursts &= tmp; - } - - if (sdev->bus) { - u8 tmp = prom_getintdefault(sdev->bus->prom_node, - "burst-sizes", 0xff); - if (tmp != 0xff) - bursts &= tmp; - } - - if (bursts == 0xff || - (bursts & DMA_BURST16) == 0 || - (bursts & DMA_BURST32) == 0) - bursts = (DMA_BURST32 - 1); - - esp->bursts = bursts; -} - -static void __init esp_get_revision(struct esp *esp) -{ - u8 tmp; - - esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); - esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - - tmp = sbus_readb(esp->eregs + ESP_CFG2); - tmp &= ~ESP_CONFIG2_MAGIC; - if (tmp != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { - /* If what we write to cfg2 does not come back, cfg2 - * is not implemented, therefore this must be a plain - * esp100. - */ - esp->erev = esp100; - printk("NCR53C90(esp100)\n"); - } else { - esp->config2 = 0; - esp->prev_cfg3 = esp->config3[0] = 5; - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - sbus_writeb(0, esp->eregs + ESP_CFG3); - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - tmp = sbus_readb(esp->eregs + ESP_CFG3); - if (tmp != 5) { - /* The cfg2 register is implemented, however - * cfg3 is not, must be esp100a. - */ - esp->erev = esp100a; - printk("NCR53C90A(esp100a)\n"); - } else { - int target; - - for (target = 0; target < 16; target++) - esp->config3[target] = 0; - esp->prev_cfg3 = 0; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - /* All of cfg{1,2,3} implemented, must be one of - * the fas variants, figure out which one. - */ - if (esp->raw_cfact > ESP_CCF_F5) { - esp->erev = fast; - esp->sync_defp = SYNC_DEFP_FAST; - printk("NCR53C9XF(espfast)\n"); - } else { - esp->erev = esp236; - printk("NCR53C9x(esp236)\n"); - } - esp->config2 = 0; - sbus_writeb(esp->config2, esp->eregs + ESP_CFG2); - } - } -} - -static void __init esp_init_swstate(struct esp *esp) -{ - int i; - - /* Command queues... */ - esp->current_SC = NULL; - esp->disconnected_SC = NULL; - esp->issue_SC = NULL; - - /* Target and current command state... */ - esp->targets_present = 0; - esp->resetting_bus = 0; - esp->snip = 0; - - init_waitqueue_head(&esp->reset_queue); - - /* Debugging... */ - for(i = 0; i < 32; i++) - esp->espcmdlog[i] = 0; - esp->espcmdent = 0; - - /* MSG phase state... */ - for(i = 0; i < 16; i++) { - esp->cur_msgout[i] = 0; - esp->cur_msgin[i] = 0; - } - esp->prevmsgout = esp->prevmsgin = 0; - esp->msgout_len = esp->msgin_len = 0; - - /* Clear the one behind caches to hold unmatchable values. */ - esp->prev_soff = esp->prev_stp = esp->prev_cfg3 = 0xff; - esp->prev_hme_dmacsr = 0xffffffff; -} - -static int __init detect_one_esp(struct scsi_host_template *tpnt, - struct device *dev, - struct sbus_dev *esp_dev, - struct sbus_dev *espdma, - struct sbus_bus *sbus, - int hme) -{ - static int instance; - struct Scsi_Host *esp_host = scsi_host_alloc(tpnt, sizeof(struct esp)); - struct esp *esp; - - if (!esp_host) - return -ENOMEM; - - if (hme) - esp_host->max_id = 16; - esp = (struct esp *) esp_host->hostdata; - esp->ehost = esp_host; - esp->sdev = esp_dev; - esp->esp_id = instance; - esp->prom_node = esp_dev->prom_node; - prom_getstring(esp->prom_node, "name", esp->prom_name, - sizeof(esp->prom_name)); - - if (esp_find_dvma(esp, espdma) < 0) - goto fail_unlink; - if (esp_map_regs(esp, hme) < 0) { - printk("ESP registers unmappable"); - goto fail_dvma_release; - } - if (esp_map_cmdarea(esp) < 0) { - printk("ESP DVMA transport area unmappable"); - goto fail_unmap_regs; - } - if (esp_register_irq(esp) < 0) - goto fail_unmap_cmdarea; - - esp_get_scsi_id(esp); - - esp->diff = prom_getbool(esp->prom_node, "differential"); - if (esp->diff) - printk("Differential "); - - esp_get_clock_params(esp); - esp_get_bursts(esp, espdma); - esp_get_revision(esp); - esp_init_swstate(esp); - - esp_bootup_reset(esp); - - if (scsi_add_host(esp_host, dev)) - goto fail_free_irq; - - dev_set_drvdata(&esp_dev->ofdev.dev, esp); - - scsi_scan_host(esp_host); - instance++; - - return 0; - -fail_free_irq: - free_irq(esp->ehost->irq, esp); - -fail_unmap_cmdarea: - sbus_free_consistent(esp->sdev, 16, - (void *) esp->esp_command, - esp->esp_command_dvma); - -fail_unmap_regs: - sbus_iounmap(esp->eregs, ESP_REG_SIZE); - -fail_dvma_release: - esp->dma->allocated = 0; - -fail_unlink: - scsi_host_put(esp_host); - return -1; -} - -/* Detecting ESP chips on the machine. This is the simple and easy - * version. - */ -static int __devexit esp_remove_common(struct esp *esp) -{ - unsigned int irq = esp->ehost->irq; - - scsi_remove_host(esp->ehost); - - ESP_INTSOFF(esp->dregs); -#if 0 - esp_reset_dma(esp); - esp_reset_esp(esp); -#endif - - free_irq(irq, esp); - sbus_free_consistent(esp->sdev, 16, - (void *) esp->esp_command, esp->esp_command_dvma); - sbus_iounmap(esp->eregs, ESP_REG_SIZE); - esp->dma->allocated = 0; - - scsi_host_put(esp->ehost); - - return 0; -} - - -#ifdef CONFIG_SUN4 - -#include <asm/sun4paddr.h> - -static struct sbus_dev sun4_esp_dev; - -static int __init esp_sun4_probe(struct scsi_host_template *tpnt) -{ - if (sun4_esp_physaddr) { - memset(&sun4_esp_dev, 0, sizeof(sun4_esp_dev)); - sun4_esp_dev.reg_addrs[0].phys_addr = sun4_esp_physaddr; - sun4_esp_dev.irqs[0] = 4; - sun4_esp_dev.resource[0].start = sun4_esp_physaddr; - sun4_esp_dev.resource[0].end = - sun4_esp_physaddr + ESP_REG_SIZE - 1; - sun4_esp_dev.resource[0].flags = IORESOURCE_IO; - - return detect_one_esp(tpnt, NULL, - &sun4_esp_dev, NULL, NULL, 0); - } - return 0; -} - -static int __devexit esp_sun4_remove(void) -{ - struct of_device *dev = &sun4_esp_dev.ofdev; - struct esp *esp = dev_get_drvdata(&dev->dev); - - return esp_remove_common(esp); -} - -#else /* !CONFIG_SUN4 */ - -static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match) -{ - struct sbus_dev *sdev = to_sbus_device(&dev->dev); - struct device_node *dp = dev->node; - struct sbus_dev *dma_sdev = NULL; - int hme = 0; - - if (dp->parent && - (!strcmp(dp->parent->name, "espdma") || - !strcmp(dp->parent->name, "dma"))) - dma_sdev = sdev->parent; - else if (!strcmp(dp->name, "SUNW,fas")) { - dma_sdev = sdev; - hme = 1; - } - - return detect_one_esp(match->data, &dev->dev, - sdev, dma_sdev, sdev->bus, hme); -} - -static int __devexit esp_sbus_remove(struct of_device *dev) -{ - struct esp *esp = dev_get_drvdata(&dev->dev); - - return esp_remove_common(esp); -} - -#endif /* !CONFIG_SUN4 */ - -/* The info function will return whatever useful - * information the developer sees fit. If not provided, then - * the name field will be used instead. - */ -static const char *esp_info(struct Scsi_Host *host) -{ - struct esp *esp; - - esp = (struct esp *) host->hostdata; - switch (esp->erev) { - case esp100: - return "Sparc ESP100 (NCR53C90)"; - case esp100a: - return "Sparc ESP100A (NCR53C90A)"; - case esp236: - return "Sparc ESP236"; - case fas236: - return "Sparc ESP236-FAST"; - case fashme: - return "Sparc ESP366-HME"; - case fas100a: - return "Sparc ESP100A-FAST"; - default: - return "Bogon ESP revision"; - }; -} - -/* From Wolfgang Stanglmeier's NCR scsi driver. */ -struct info_str -{ - char *buffer; - int length; - int offset; - int pos; -}; - -static void copy_mem_info(struct info_str *info, char *data, int len) -{ - if (info->pos + len > info->length) - len = info->length - info->pos; - - if (info->pos + len < info->offset) { - info->pos += len; - return; - } - if (info->pos < info->offset) { - data += (info->offset - info->pos); - len -= (info->offset - info->pos); - } - - if (len > 0) { - memcpy(info->buffer + info->pos, data, len); - info->pos += len; - } -} - -static int copy_info(struct info_str *info, char *fmt, ...) -{ - va_list args; - char buf[81]; - int len; - - va_start(args, fmt); - len = vsprintf(buf, fmt, args); - va_end(args); - - copy_mem_info(info, buf, len); - return len; -} - -static int esp_host_info(struct esp *esp, char *ptr, off_t offset, int len) -{ - struct scsi_device *sdev; - struct info_str info; - int i; - - info.buffer = ptr; - info.length = len; - info.offset = offset; - info.pos = 0; - - copy_info(&info, "Sparc ESP Host Adapter:\n"); - copy_info(&info, "\tPROM node\t\t%08x\n", (unsigned int) esp->prom_node); - copy_info(&info, "\tPROM name\t\t%s\n", esp->prom_name); - copy_info(&info, "\tESP Model\t\t"); - switch (esp->erev) { - case esp100: - copy_info(&info, "ESP100\n"); - break; - case esp100a: - copy_info(&info, "ESP100A\n"); - break; - case esp236: - copy_info(&info, "ESP236\n"); - break; - case fas236: - copy_info(&info, "FAS236\n"); - break; - case fas100a: - copy_info(&info, "FAS100A\n"); - break; - case fast: - copy_info(&info, "FAST\n"); - break; - case fashme: - copy_info(&info, "Happy Meal FAS\n"); - break; - case espunknown: - default: - copy_info(&info, "Unknown!\n"); - break; - }; - copy_info(&info, "\tDMA Revision\t\t"); - switch (esp->dma->revision) { - case dvmarev0: - copy_info(&info, "Rev 0\n"); - break; - case dvmaesc1: - copy_info(&info, "ESC Rev 1\n"); - break; - case dvmarev1: - copy_info(&info, "Rev 1\n"); - break; - case dvmarev2: - copy_info(&info, "Rev 2\n"); - break; - case dvmarev3: - copy_info(&info, "Rev 3\n"); - break; - case dvmarevplus: - copy_info(&info, "Rev 1+\n"); - break; - case dvmahme: - copy_info(&info, "Rev HME/FAS\n"); - break; - default: - copy_info(&info, "Unknown!\n"); - break; - }; - copy_info(&info, "\tLive Targets\t\t[ "); - for (i = 0; i < 15; i++) { - if (esp->targets_present & (1 << i)) - copy_info(&info, "%d ", i); - } - copy_info(&info, "]\n\n"); - - /* Now describe the state of each existing target. */ - copy_info(&info, "Target #\tconfig3\t\tSync Capabilities\tDisconnect\tWide\n"); - - shost_for_each_device(sdev, esp->ehost) { - struct esp_device *esp_dev = sdev->hostdata; - uint id = sdev->id; - - if (!(esp->targets_present & (1 << id))) - continue; - - copy_info(&info, "%d\t\t", id); - copy_info(&info, "%08lx\t", esp->config3[id]); - copy_info(&info, "[%02lx,%02lx]\t\t\t", - esp_dev->sync_max_offset, - esp_dev->sync_min_period); - copy_info(&info, "%s\t\t", - esp_dev->disconnect ? "yes" : "no"); - copy_info(&info, "%s\n", - (esp->config3[id] & ESP_CONFIG3_EWIDE) ? "yes" : "no"); - } - return info.pos > info.offset? info.pos - info.offset : 0; -} - -/* ESP proc filesystem code. */ -static int esp_proc_info(struct Scsi_Host *host, char *buffer, char **start, off_t offset, - int length, int inout) -{ - struct esp *esp = (struct esp *) host->hostdata; - - if (inout) - return -EINVAL; /* not yet */ - - if (start) - *start = buffer; - - return esp_host_info(esp, buffer, offset, length); -} - -static void esp_get_dmabufs(struct esp *esp, struct scsi_cmnd *sp) -{ - if (sp->use_sg == 0) { - sp->SCp.this_residual = sp->request_bufflen; - sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; - sp->SCp.buffers_residual = 0; - if (sp->request_bufflen) { - sp->SCp.have_data_in = sbus_map_single(esp->sdev, sp->SCp.buffer, - sp->SCp.this_residual, - sp->sc_data_direction); - sp->SCp.ptr = (char *) ((unsigned long)sp->SCp.have_data_in); - } else { - sp->SCp.ptr = NULL; - } - } else { - sp->SCp.buffer = (struct scatterlist *) sp->request_buffer; - sp->SCp.buffers_residual = sbus_map_sg(esp->sdev, - sp->SCp.buffer, - sp->use_sg, - sp->sc_data_direction); - sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer); - sp->SCp.ptr = (char *) ((unsigned long)sg_dma_address(sp->SCp.buffer)); - } -} - -static void esp_release_dmabufs(struct esp *esp, struct scsi_cmnd *sp) -{ - if (sp->use_sg) { - sbus_unmap_sg(esp->sdev, sp->request_buffer, sp->use_sg, - sp->sc_data_direction); - } else if (sp->request_bufflen) { - sbus_unmap_single(esp->sdev, - sp->SCp.have_data_in, - sp->request_bufflen, - sp->sc_data_direction); - } -} - -static void esp_restore_pointers(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; - - sp->SCp.ptr = ep->saved_ptr; - sp->SCp.buffer = ep->saved_buffer; - sp->SCp.this_residual = ep->saved_this_residual; - sp->SCp.buffers_residual = ep->saved_buffers_residual; -} - -static void esp_save_pointers(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_pointers *ep = &esp->data_pointers[sp->device->id]; - - ep->saved_ptr = sp->SCp.ptr; - ep->saved_buffer = sp->SCp.buffer; - ep->saved_this_residual = sp->SCp.this_residual; - ep->saved_buffers_residual = sp->SCp.buffers_residual; -} - -/* Some rules: - * - * 1) Never ever panic while something is live on the bus. - * If there is to be any chance of syncing the disks this - * rule is to be obeyed. - * - * 2) Any target that causes a foul condition will no longer - * have synchronous transfers done to it, no questions - * asked. - * - * 3) Keep register accesses to a minimum. Think about some - * day when we have Xbus machines this is running on and - * the ESP chip is on the other end of the machine on a - * different board from the cpu where this is running. - */ - -/* Fire off a command. We assume the bus is free and that the only - * case where we could see an interrupt is where we have disconnected - * commands active and they are trying to reselect us. - */ -static inline void esp_check_cmd(struct esp *esp, struct scsi_cmnd *sp) -{ - switch (sp->cmd_len) { - case 6: - case 10: - case 12: - esp->esp_slowcmd = 0; - break; - - default: - esp->esp_slowcmd = 1; - esp->esp_scmdleft = sp->cmd_len; - esp->esp_scmdp = &sp->cmnd[0]; - break; - }; -} - -static inline void build_sync_nego_msg(struct esp *esp, int period, int offset) -{ - esp->cur_msgout[0] = EXTENDED_MESSAGE; - esp->cur_msgout[1] = 3; - esp->cur_msgout[2] = EXTENDED_SDTR; - esp->cur_msgout[3] = period; - esp->cur_msgout[4] = offset; - esp->msgout_len = 5; -} - -/* SIZE is in bits, currently HME only supports 16 bit wide transfers. */ -static inline void build_wide_nego_msg(struct esp *esp, int size) -{ - esp->cur_msgout[0] = EXTENDED_MESSAGE; - esp->cur_msgout[1] = 2; - esp->cur_msgout[2] = EXTENDED_WDTR; - switch (size) { - case 32: - esp->cur_msgout[3] = 2; - break; - case 16: - esp->cur_msgout[3] = 1; - break; - case 8: - default: - esp->cur_msgout[3] = 0; - break; - }; - - esp->msgout_len = 4; -} - -static void esp_exec_cmd(struct esp *esp) -{ - struct scsi_cmnd *SCptr; - struct scsi_device *SDptr; - struct esp_device *esp_dev; - volatile u8 *cmdp = esp->esp_command; - u8 the_esp_command; - int lun, target; - int i; - - /* Hold off if we have disconnected commands and - * an IRQ is showing... - */ - if (esp->disconnected_SC && ESP_IRQ_P(esp->dregs)) - return; - - /* Grab first member of the issue queue. */ - SCptr = esp->current_SC = remove_first_SC(&esp->issue_SC); - - /* Safe to panic here because current_SC is null. */ - if (!SCptr) - panic("esp: esp_exec_cmd and issue queue is NULL"); - - SDptr = SCptr->device; - esp_dev = SDptr->hostdata; - lun = SCptr->device->lun; - target = SCptr->device->id; - - esp->snip = 0; - esp->msgout_len = 0; - - /* Send it out whole, or piece by piece? The ESP - * only knows how to automatically send out 6, 10, - * and 12 byte commands. I used to think that the - * Linux SCSI code would never throw anything other - * than that to us, but then again there is the - * SCSI generic driver which can send us anything. - */ - esp_check_cmd(esp, SCptr); - - /* If arbitration/selection is successful, the ESP will leave - * ATN asserted, causing the target to go into message out - * phase. The ESP will feed the target the identify and then - * the target can only legally go to one of command, - * datain/out, status, or message in phase, or stay in message - * out phase (should we be trying to send a sync negotiation - * message after the identify). It is not allowed to drop - * BSY, but some buggy targets do and we check for this - * condition in the selection complete code. Most of the time - * we'll make the command bytes available to the ESP and it - * will not interrupt us until it finishes command phase, we - * cannot do this for command sizes the ESP does not - * understand and in this case we'll get interrupted right - * when the target goes into command phase. - * - * It is absolutely _illegal_ in the presence of SCSI-2 devices - * to use the ESP select w/o ATN command. When SCSI-2 devices are - * present on the bus we _must_ always go straight to message out - * phase with an identify message for the target. Being that - * selection attempts in SCSI-1 w/o ATN was an option, doing SCSI-2 - * selections should not confuse SCSI-1 we hope. - */ - - if (esp_dev->sync) { - /* this targets sync is known */ -#ifndef __sparc_v9__ -do_sync_known: -#endif - if (esp_dev->disconnect) - *cmdp++ = IDENTIFY(1, lun); - else - *cmdp++ = IDENTIFY(0, lun); - - if (esp->esp_slowcmd) { - the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_stop); - } else { - the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_norm); - } - } else if (!(esp->targets_present & (1<<target)) || !(esp_dev->disconnect)) { - /* After the bootup SCSI code sends both the - * TEST_UNIT_READY and INQUIRY commands we want - * to at least attempt allowing the device to - * disconnect. - */ - ESPMISC(("esp: Selecting device for first time. target=%d " - "lun=%d\n", target, SCptr->device->lun)); - if (!SDptr->borken && !esp_dev->disconnect) - esp_dev->disconnect = 1; - - *cmdp++ = IDENTIFY(0, lun); - esp->prevmsgout = NOP; - esp_advance_phase(SCptr, in_slct_norm); - the_esp_command = (ESP_CMD_SELA | ESP_CMD_DMA); - - /* Take no chances... */ - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - } else { - /* Sorry, I have had way too many problems with - * various CDROM devices on ESP. -DaveM - */ - int cdrom_hwbug_wkaround = 0; - -#ifndef __sparc_v9__ - /* Never allow disconnects or synchronous transfers on - * SparcStation1 and SparcStation1+. Allowing those - * to be enabled seems to lockup the machine completely. - */ - if ((idprom->id_machtype == (SM_SUN4C | SM_4C_SS1)) || - (idprom->id_machtype == (SM_SUN4C | SM_4C_SS1PLUS))) { - /* But we are nice and allow tapes and removable - * disks (but not CDROMs) to disconnect. - */ - if(SDptr->type == TYPE_TAPE || - (SDptr->type != TYPE_ROM && SDptr->removable)) - esp_dev->disconnect = 1; - else - esp_dev->disconnect = 0; - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 1; - esp->snip = 0; - goto do_sync_known; - } -#endif /* !(__sparc_v9__) */ - - /* We've talked to this guy before, - * but never negotiated. Let's try, - * need to attempt WIDE first, before - * sync nego, as per SCSI 2 standard. - */ - if (esp->erev == fashme && !esp_dev->wide) { - if (!SDptr->borken && - SDptr->type != TYPE_ROM && - SDptr->removable == 0) { - build_wide_nego_msg(esp, 16); - esp_dev->wide = 1; - esp->wnip = 1; - goto after_nego_msg_built; - } else { - esp_dev->wide = 1; - /* Fall through and try sync. */ - } - } - - if (!SDptr->borken) { - if ((SDptr->type == TYPE_ROM)) { - /* Nice try sucker... */ - ESPMISC(("esp%d: Disabling sync for buggy " - "CDROM.\n", esp->esp_id)); - cdrom_hwbug_wkaround = 1; - build_sync_nego_msg(esp, 0, 0); - } else if (SDptr->removable != 0) { - ESPMISC(("esp%d: Not negotiating sync/wide but " - "allowing disconnect for removable media.\n", - esp->esp_id)); - build_sync_nego_msg(esp, 0, 0); - } else { - build_sync_nego_msg(esp, esp->sync_defp, 15); - } - } else { - build_sync_nego_msg(esp, 0, 0); - } - esp_dev->sync = 1; - esp->snip = 1; - -after_nego_msg_built: - /* A fix for broken SCSI1 targets, when they disconnect - * they lock up the bus and confuse ESP. So disallow - * disconnects for SCSI1 targets for now until we - * find a better fix. - * - * Addendum: This is funny, I figured out what was going - * on. The blotzed SCSI1 target would disconnect, - * one of the other SCSI2 targets or both would be - * disconnected as well. The SCSI1 target would - * stay disconnected long enough that we start - * up a command on one of the SCSI2 targets. As - * the ESP is arbitrating for the bus the SCSI1 - * target begins to arbitrate as well to reselect - * the ESP. The SCSI1 target refuses to drop it's - * ID bit on the data bus even though the ESP is - * at ID 7 and is the obvious winner for any - * arbitration. The ESP is a poor sport and refuses - * to lose arbitration, it will continue indefinitely - * trying to arbitrate for the bus and can only be - * stopped via a chip reset or SCSI bus reset. - * Therefore _no_ disconnects for SCSI1 targets - * thank you very much. ;-) - */ - if(((SDptr->scsi_level < 3) && - (SDptr->type != TYPE_TAPE) && - SDptr->removable == 0) || - cdrom_hwbug_wkaround || SDptr->borken) { - ESPMISC((KERN_INFO "esp%d: Disabling DISCONNECT for target %d " - "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun)); - esp_dev->disconnect = 0; - *cmdp++ = IDENTIFY(0, lun); - } else { - *cmdp++ = IDENTIFY(1, lun); - } - - /* ESP fifo is only so big... - * Make this look like a slow command. - */ - esp->esp_slowcmd = 1; - esp->esp_scmdleft = SCptr->cmd_len; - esp->esp_scmdp = &SCptr->cmnd[0]; - - the_esp_command = (ESP_CMD_SELAS | ESP_CMD_DMA); - esp_advance_phase(SCptr, in_slct_msg); - } - - if (!esp->esp_slowcmd) - for (i = 0; i < SCptr->cmd_len; i++) - *cmdp++ = SCptr->cmnd[i]; - - /* HME sucks... */ - if (esp->erev == fashme) - sbus_writeb((target & 0xf) | (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT), - esp->eregs + ESP_BUSID); - else - sbus_writeb(target & 7, esp->eregs + ESP_BUSID); - if (esp->prev_soff != esp_dev->sync_max_offset || - esp->prev_stp != esp_dev->sync_min_period || - (esp->erev > esp100a && - esp->prev_cfg3 != esp->config3[target])) { - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev > esp100a) { - esp->prev_cfg3 = esp->config3[target]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - i = (cmdp - esp->esp_command); - - if (esp->erev == fashme) { - esp_cmd(esp, ESP_CMD_FLUSH); /* Grrr! */ - - /* Set up the DMA and HME counters */ - sbus_writeb(i, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - sbus_writeb(0, esp->eregs + FAS_RLO); - sbus_writeb(0, esp->eregs + FAS_RHI); - esp_cmd(esp, the_esp_command); - - /* Talk about touchy hardware... */ - esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | - (DMA_SCSI_DISAB | DMA_ENABLE)) & - ~(DMA_ST_WRITE)); - sbus_writel(16, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - } else { - u32 tmp; - - /* Set up the DMA and ESP counters */ - sbus_writeb(i, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp &= ~DMA_ST_WRITE; - tmp |= DMA_ENABLE; - sbus_writel(tmp, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) { - if (i) /* Workaround ESC gate array SBUS rerun bug. */ - sbus_writel(PAGE_SIZE, esp->dregs + DMA_COUNT); - } - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - - /* Tell ESP to "go". */ - esp_cmd(esp, the_esp_command); - } -} - -/* Queue a SCSI command delivered from the mid-level Linux SCSI code. */ -static int esp_queue(struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)) -{ - struct esp *esp; - - /* Set up func ptr and initial driver cmd-phase. */ - SCpnt->scsi_done = done; - SCpnt->SCp.phase = not_issued; - - /* We use the scratch area. */ - ESPQUEUE(("esp_queue: target=%d lun=%d ", SCpnt->device->id, SCpnt->device->lun)); - ESPDISC(("N<%02x,%02x>", SCpnt->device->id, SCpnt->device->lun)); - - esp = (struct esp *) SCpnt->device->host->hostdata; - esp_get_dmabufs(esp, SCpnt); - esp_save_pointers(esp, SCpnt); /* FIXME for tag queueing */ - - SCpnt->SCp.Status = CHECK_CONDITION; - SCpnt->SCp.Message = 0xff; - SCpnt->SCp.sent_command = 0; - - /* Place into our queue. */ - if (SCpnt->cmnd[0] == REQUEST_SENSE) { - ESPQUEUE(("RQSENSE\n")); - prepend_SC(&esp->issue_SC, SCpnt); - } else { - ESPQUEUE(("\n")); - append_SC(&esp->issue_SC, SCpnt); - } - - /* Run it now if we can. */ - if (!esp->current_SC && !esp->resetting_bus) - esp_exec_cmd(esp); - - return 0; -} - -/* Dump driver state. */ -static void esp_dump_cmd(struct scsi_cmnd *SCptr) -{ - ESPLOG(("[tgt<%02x> lun<%02x> " - "pphase<%s> cphase<%s>]", - SCptr->device->id, SCptr->device->lun, - phase_string(SCptr->SCp.sent_command), - phase_string(SCptr->SCp.phase))); -} - -static void esp_dump_state(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; -#ifdef DEBUG_ESP_CMDS - int i; -#endif - - ESPLOG(("esp%d: dumping state\n", esp->esp_id)); - ESPLOG(("esp%d: dma -- cond_reg<%08x> addr<%08x>\n", - esp->esp_id, - sbus_readl(esp->dregs + DMA_CSR), - sbus_readl(esp->dregs + DMA_ADDR))); - ESPLOG(("esp%d: SW [sreg<%02x> sstep<%02x> ireg<%02x>]\n", - esp->esp_id, esp->sreg, esp->seqreg, esp->ireg)); - ESPLOG(("esp%d: HW reread [sreg<%02x> sstep<%02x> ireg<%02x>]\n", - esp->esp_id, - sbus_readb(esp->eregs + ESP_STATUS), - sbus_readb(esp->eregs + ESP_SSTEP), - sbus_readb(esp->eregs + ESP_INTRPT))); -#ifdef DEBUG_ESP_CMDS - printk("esp%d: last ESP cmds [", esp->esp_id); - i = (esp->espcmdent - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - i = (i - 1) & 31; - printk("<"); esp_print_cmd(esp->espcmdlog[i]); printk(">"); - printk("]\n"); -#endif /* (DEBUG_ESP_CMDS) */ - - if (SCptr) { - ESPLOG(("esp%d: current command ", esp->esp_id)); - esp_dump_cmd(SCptr); - } - ESPLOG(("\n")); - SCptr = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected ", esp->esp_id)); - while (SCptr) { - esp_dump_cmd(SCptr); - SCptr = (struct scsi_cmnd *) SCptr->host_scribble; - } - ESPLOG(("\n")); -} - -/* Abort a command. The host_lock is acquired by caller. */ -static int esp_abort(struct scsi_cmnd *SCptr) -{ - struct esp *esp = (struct esp *) SCptr->device->host->hostdata; - int don; - - ESPLOG(("esp%d: Aborting command\n", esp->esp_id)); - esp_dump_state(esp); - - /* Wheee, if this is the current command on the bus, the - * best we can do is assert ATN and wait for msgout phase. - * This should even fix a hung SCSI bus when we lose state - * in the driver and timeout because the eventual phase change - * will cause the ESP to (eventually) give an interrupt. - */ - if (esp->current_SC == SCptr) { - esp->cur_msgout[0] = ABORT; - esp->msgout_len = 1; - esp->msgout_ctr = 0; - esp_cmd(esp, ESP_CMD_SATN); - return SUCCESS; - } - - /* If it is still in the issue queue then we can safely - * call the completion routine and report abort success. - */ - don = (sbus_readl(esp->dregs + DMA_CSR) & DMA_INT_ENAB); - if (don) { - ESP_INTSOFF(esp->dregs); - } - if (esp->issue_SC) { - struct scsi_cmnd **prev, *this; - for (prev = (&esp->issue_SC), this = esp->issue_SC; - this != NULL; - prev = (struct scsi_cmnd **) &(this->host_scribble), - this = (struct scsi_cmnd *) this->host_scribble) { - - if (this == SCptr) { - *prev = (struct scsi_cmnd *) this->host_scribble; - this->host_scribble = NULL; - - esp_release_dmabufs(esp, this); - this->result = DID_ABORT << 16; - this->scsi_done(this); - - if (don) - ESP_INTSON(esp->dregs); - - return SUCCESS; - } - } - } - - /* Yuck, the command to abort is disconnected, it is not - * worth trying to abort it now if something else is live - * on the bus at this time. So, we let the SCSI code wait - * a little bit and try again later. - */ - if (esp->current_SC) { - if (don) - ESP_INTSON(esp->dregs); - return FAILED; - } - - /* It's disconnected, we have to reconnect to re-establish - * the nexus and tell the device to abort. However, we really - * cannot 'reconnect' per se. Don't try to be fancy, just - * indicate failure, which causes our caller to reset the whole - * bus. - */ - - if (don) - ESP_INTSON(esp->dregs); - - return FAILED; -} - -/* We've sent ESP_CMD_RS to the ESP, the interrupt had just - * arrived indicating the end of the SCSI bus reset. Our job - * is to clean out the command queues and begin re-execution - * of SCSI commands once more. - */ -static int esp_finish_reset(struct esp *esp) -{ - struct scsi_cmnd *sp = esp->current_SC; - - /* Clean up currently executing command, if any. */ - if (sp != NULL) { - esp->current_SC = NULL; - - esp_release_dmabufs(esp, sp); - sp->result = (DID_RESET << 16); - - sp->scsi_done(sp); - } - - /* Clean up disconnected queue, they have been invalidated - * by the bus reset. - */ - if (esp->disconnected_SC) { - while ((sp = remove_first_SC(&esp->disconnected_SC)) != NULL) { - esp_release_dmabufs(esp, sp); - sp->result = (DID_RESET << 16); - - sp->scsi_done(sp); - } - } - - /* SCSI bus reset is complete. */ - esp->resetting_bus = 0; - wake_up(&esp->reset_queue); - - /* Ok, now it is safe to get commands going once more. */ - if (esp->issue_SC) - esp_exec_cmd(esp); - - return do_intr_end; -} - -static int esp_do_resetbus(struct esp *esp) -{ - ESPLOG(("esp%d: Resetting scsi bus\n", esp->esp_id)); - esp->resetting_bus = 1; - esp_cmd(esp, ESP_CMD_RS); - - return do_intr_end; -} - -/* Reset ESP chip, reset hanging bus, then kill active and - * disconnected commands for targets without soft reset. - * - * The host_lock is acquired by caller. - */ -static int esp_reset(struct scsi_cmnd *SCptr) -{ - struct esp *esp = (struct esp *) SCptr->device->host->hostdata; - - spin_lock_irq(esp->ehost->host_lock); - (void) esp_do_resetbus(esp); - spin_unlock_irq(esp->ehost->host_lock); - - wait_event(esp->reset_queue, (esp->resetting_bus == 0)); - - return SUCCESS; -} - -/* Internal ESP done function. */ -static void esp_done(struct esp *esp, int error) -{ - struct scsi_cmnd *done_SC = esp->current_SC; - - esp->current_SC = NULL; - - esp_release_dmabufs(esp, done_SC); - done_SC->result = error; - - done_SC->scsi_done(done_SC); - - /* Bus is free, issue any commands in the queue. */ - if (esp->issue_SC && !esp->current_SC) - esp_exec_cmd(esp); - -} - -/* Wheee, ESP interrupt engine. */ - -/* Forward declarations. */ -static int esp_do_phase_determine(struct esp *esp); -static int esp_do_data_finale(struct esp *esp); -static int esp_select_complete(struct esp *esp); -static int esp_do_status(struct esp *esp); -static int esp_do_msgin(struct esp *esp); -static int esp_do_msgindone(struct esp *esp); -static int esp_do_msgout(struct esp *esp); -static int esp_do_cmdbegin(struct esp *esp); - -#define sreg_datainp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DIP) -#define sreg_dataoutp(__sreg) (((__sreg) & ESP_STAT_PMASK) == ESP_DOP) - -/* Read any bytes found in the FAS366 fifo, storing them into - * the ESP driver software state structure. - */ -static void hme_fifo_read(struct esp *esp) -{ - u8 count = 0; - u8 status = esp->sreg; - - /* Cannot safely frob the fifo for these following cases, but - * we must always read the fifo when the reselect interrupt - * is pending. - */ - if (((esp->ireg & ESP_INTR_RSEL) == 0) && - (sreg_datainp(status) || - sreg_dataoutp(status) || - (esp->current_SC && - esp->current_SC->SCp.phase == in_data_done))) { - ESPHME(("<wkaround_skipped>")); - } else { - unsigned long fcnt = sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES; - - /* The HME stores bytes in multiples of 2 in the fifo. */ - ESPHME(("hme_fifo[fcnt=%d", (int)fcnt)); - while (fcnt) { - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - ESPHME(("<%02x,%02x>", esp->hme_fifo_workaround_buffer[count-2], esp->hme_fifo_workaround_buffer[count-1])); - fcnt--; - } - if (sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_F1BYTE) { - ESPHME(("<poke_byte>")); - sbus_writeb(0, esp->eregs + ESP_FDATA); - esp->hme_fifo_workaround_buffer[count++] = - sbus_readb(esp->eregs + ESP_FDATA); - ESPHME(("<%02x,0x00>", esp->hme_fifo_workaround_buffer[count-1])); - ESPHME(("CMD_FLUSH")); - esp_cmd(esp, ESP_CMD_FLUSH); - } else { - ESPHME(("no_xtra_byte")); - } - } - ESPHME(("wkarnd_cnt=%d]", (int)count)); - esp->hme_fifo_workaround_count = count; -} - -static inline void hme_fifo_push(struct esp *esp, u8 *bytes, u8 count) -{ - esp_cmd(esp, ESP_CMD_FLUSH); - while (count) { - u8 tmp = *bytes++; - sbus_writeb(tmp, esp->eregs + ESP_FDATA); - sbus_writeb(0, esp->eregs + ESP_FDATA); - count--; - } -} - -/* We try to avoid some interrupts by jumping ahead and see if the ESP - * has gotten far enough yet. Hence the following. - */ -static inline int skipahead1(struct esp *esp, struct scsi_cmnd *scp, - int prev_phase, int new_phase) -{ - if (scp->SCp.sent_command != prev_phase) - return 0; - if (ESP_IRQ_P(esp->dregs)) { - /* Yes, we are able to save an interrupt. */ - if (esp->erev == fashme) - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR)); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) - hme_fifo_read(esp); - } - if (!(esp->ireg & ESP_INTR_SR)) - return 0; - else - return do_reset_complete; - } - /* Ho hum, target is taking forever... */ - scp->SCp.sent_command = new_phase; /* so we don't recurse... */ - return do_intr_end; -} - -static inline int skipahead2(struct esp *esp, struct scsi_cmnd *scp, - int prev_phase1, int prev_phase2, int new_phase) -{ - if (scp->SCp.sent_command != prev_phase1 && - scp->SCp.sent_command != prev_phase2) - return 0; - if (ESP_IRQ_P(esp->dregs)) { - /* Yes, we are able to save an interrupt. */ - if (esp->erev == fashme) - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->sreg = (sbus_readb(esp->eregs + ESP_STATUS) & ~(ESP_STAT_INTR)); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - * Happy Meal indeed.... - */ - ESPHME(("fifo_workaround]")); - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) - hme_fifo_read(esp); - } - if (!(esp->ireg & ESP_INTR_SR)) - return 0; - else - return do_reset_complete; - } - /* Ho hum, target is taking forever... */ - scp->SCp.sent_command = new_phase; /* so we don't recurse... */ - return do_intr_end; -} - -/* Now some dma helpers. */ -static void dma_setup(struct esp *esp, __u32 addr, int count, int write) -{ - u32 nreg = sbus_readl(esp->dregs + DMA_CSR); - - if (write) - nreg |= DMA_ST_WRITE; - else - nreg &= ~(DMA_ST_WRITE); - nreg |= DMA_ENABLE; - sbus_writel(nreg, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) { - /* This ESC gate array sucks! */ - __u32 src = addr; - __u32 dest = src + count; - - if (dest & (PAGE_SIZE - 1)) - count = PAGE_ALIGN(count); - sbus_writel(count, esp->dregs + DMA_COUNT); - } - sbus_writel(addr, esp->dregs + DMA_ADDR); -} - -static void dma_drain(struct esp *esp) -{ - u32 tmp; - - if (esp->dma->revision == dvmahme) - return; - if ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_FIFO_ISDRAIN) { - switch (esp->dma->revision) { - default: - tmp |= DMA_FIFO_STDRAIN; - sbus_writel(tmp, esp->dregs + DMA_CSR); - - case dvmarev3: - case dvmaesc1: - while (sbus_readl(esp->dregs + DMA_CSR) & DMA_FIFO_ISDRAIN) - udelay(1); - }; - } -} - -static void dma_invalidate(struct esp *esp) -{ - u32 tmp; - - if (esp->dma->revision == dvmahme) { - sbus_writel(DMA_RST_SCSI, esp->dregs + DMA_CSR); - - esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | - (DMA_PARITY_OFF | DMA_2CLKS | - DMA_SCSI_DISAB | DMA_INT_ENAB)) & - ~(DMA_ST_WRITE | DMA_ENABLE)); - - sbus_writel(0, esp->dregs + DMA_CSR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - - /* This is necessary to avoid having the SCSI channel - * engine lock up on us. - */ - sbus_writel(0, esp->dregs + DMA_ADDR); - } else { - while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ) - udelay(1); - - tmp &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); - tmp |= DMA_FIFO_INV; - sbus_writel(tmp, esp->dregs + DMA_CSR); - tmp &= ~DMA_FIFO_INV; - sbus_writel(tmp, esp->dregs + DMA_CSR); - } -} - -static inline void dma_flashclear(struct esp *esp) -{ - dma_drain(esp); - dma_invalidate(esp); -} - -static int dma_can_transfer(struct esp *esp, struct scsi_cmnd *sp) -{ - __u32 base, end, sz; - - if (esp->dma->revision == dvmarev3) { - sz = sp->SCp.this_residual; - if (sz > 0x1000000) - sz = 0x1000000; - } else { - base = ((__u32)((unsigned long)sp->SCp.ptr)); - base &= (0x1000000 - 1); - end = (base + sp->SCp.this_residual); - if (end > 0x1000000) - end = 0x1000000; - sz = (end - base); - } - return sz; -} - -/* Misc. esp helper macros. */ -#define esp_setcount(__eregs, __cnt, __hme) \ - sbus_writeb(((__cnt)&0xff), (__eregs) + ESP_TCLOW); \ - sbus_writeb((((__cnt)>>8)&0xff), (__eregs) + ESP_TCMED); \ - if (__hme) { \ - sbus_writeb((((__cnt)>>16)&0xff), (__eregs) + FAS_RLO); \ - sbus_writeb(0, (__eregs) + FAS_RHI); \ - } - -#define esp_getcount(__eregs, __hme) \ - ((sbus_readb((__eregs) + ESP_TCLOW)&0xff) | \ - ((sbus_readb((__eregs) + ESP_TCMED)&0xff) << 8) | \ - ((__hme) ? sbus_readb((__eregs) + FAS_RLO) << 16 : 0)) - -#define fcount(__esp) \ - (((__esp)->erev == fashme) ? \ - (__esp)->hme_fifo_workaround_count : \ - sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_FBYTES) - -#define fnzero(__esp) \ - (((__esp)->erev == fashme) ? 0 : \ - sbus_readb(((__esp)->eregs) + ESP_FFLAGS) & ESP_FF_ONOTZERO) - -/* XXX speculative nops unnecessary when continuing amidst a data phase - * XXX even on esp100!!! another case of flooding the bus with I/O reg - * XXX writes... - */ -#define esp_maybe_nop(__esp) \ - if ((__esp)->erev == esp100) \ - esp_cmd((__esp), ESP_CMD_NULL) - -#define sreg_to_dataphase(__sreg) \ - ((((__sreg) & ESP_STAT_PMASK) == ESP_DOP) ? in_dataout : in_datain) - -/* The ESP100 when in synchronous data phase, can mistake a long final - * REQ pulse from the target as an extra byte, it places whatever is on - * the data lines into the fifo. For now, we will assume when this - * happens that the target is a bit quirky and we don't want to - * be talking synchronously to it anyways. Regardless, we need to - * tell the ESP to eat the extraneous byte so that we can proceed - * to the next phase. - */ -static int esp100_sync_hwbug(struct esp *esp, struct scsi_cmnd *sp, int fifocnt) -{ - /* Do not touch this piece of code. */ - if ((!(esp->erev == esp100)) || - (!(sreg_datainp((esp->sreg = sbus_readb(esp->eregs + ESP_STATUS))) && - !fifocnt) && - !(sreg_dataoutp(esp->sreg) && !fnzero(esp)))) { - if (sp->SCp.phase == in_dataout) - esp_cmd(esp, ESP_CMD_FLUSH); - return 0; - } else { - /* Async mode for this guy. */ - build_sync_nego_msg(esp, 0, 0); - - /* Ack the bogus byte, but set ATN first. */ - esp_cmd(esp, ESP_CMD_SATN); - esp_cmd(esp, ESP_CMD_MOK); - return 1; - } -} - -/* This closes the window during a selection with a reselect pending, because - * we use DMA for the selection process the FIFO should hold the correct - * contents if we get reselected during this process. So we just need to - * ack the possible illegal cmd interrupt pending on the esp100. - */ -static inline int esp100_reconnect_hwbug(struct esp *esp) -{ - u8 tmp; - - if (esp->erev != esp100) - return 0; - tmp = sbus_readb(esp->eregs + ESP_INTRPT); - if (tmp & ESP_INTR_SR) - return 1; - return 0; -} - -/* This verifies the BUSID bits during a reselection so that we know which - * target is talking to us. - */ -static inline int reconnect_target(struct esp *esp) -{ - int it, me = esp->scsi_id_mask, targ = 0; - - if (2 != fcount(esp)) - return -1; - if (esp->erev == fashme) { - /* HME does not latch it's own BUS ID bits during - * a reselection. Also the target number is given - * as an unsigned char, not as a sole bit number - * like the other ESP's do. - * Happy Meal indeed.... - */ - targ = esp->hme_fifo_workaround_buffer[0]; - } else { - it = sbus_readb(esp->eregs + ESP_FDATA); - if (!(it & me)) - return -1; - it &= ~me; - if (it & (it - 1)) - return -1; - while (!(it & 1)) - targ++, it >>= 1; - } - return targ; -} - -/* This verifies the identify from the target so that we know which lun is - * being reconnected. - */ -static inline int reconnect_lun(struct esp *esp) -{ - int lun; - - if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) - return -1; - if (esp->erev == fashme) - lun = esp->hme_fifo_workaround_buffer[1]; - else - lun = sbus_readb(esp->eregs + ESP_FDATA); - - /* Yes, you read this correctly. We report lun of zero - * if we see parity error. ESP reports parity error for - * the lun byte, and this is the only way to hope to recover - * because the target is connected. - */ - if (esp->sreg & ESP_STAT_PERR) - return 0; - - /* Check for illegal bits being set in the lun. */ - if ((lun & 0x40) || !(lun & 0x80)) - return -1; - - return lun & 7; -} - -/* This puts the driver in a state where it can revitalize a command that - * is being continued due to reselection. - */ -static inline void esp_connect(struct esp *esp, struct scsi_cmnd *sp) -{ - struct esp_device *esp_dev = sp->device->hostdata; - - if (esp->prev_soff != esp_dev->sync_max_offset || - esp->prev_stp != esp_dev->sync_min_period || - (esp->erev > esp100a && - esp->prev_cfg3 != esp->config3[sp->device->id])) { - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev > esp100a) { - esp->prev_cfg3 = esp->config3[sp->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - esp->current_SC = sp; -} - -/* This will place the current working command back into the issue queue - * if we are to receive a reselection amidst a selection attempt. - */ -static inline void esp_reconnect(struct esp *esp, struct scsi_cmnd *sp) -{ - if (!esp->disconnected_SC) - ESPLOG(("esp%d: Weird, being reselected but disconnected " - "command queue is empty.\n", esp->esp_id)); - esp->snip = 0; - esp->current_SC = NULL; - sp->SCp.phase = not_issued; - append_SC(&esp->issue_SC, sp); -} - -/* Begin message in phase. */ -static int esp_do_msgin(struct esp *esp) -{ - /* Must be very careful with the fifo on the HME */ - if ((esp->erev != fashme) || - !(sbus_readb(esp->eregs + ESP_STATUS2) & ESP_STAT2_FEMPTY)) - esp_cmd(esp, ESP_CMD_FLUSH); - esp_maybe_nop(esp); - esp_cmd(esp, ESP_CMD_TI); - esp->msgin_len = 1; - esp->msgin_ctr = 0; - esp_advance_phase(esp->current_SC, in_msgindone); - return do_work_bus; -} - -/* This uses various DMA csr fields and the fifo flags count value to - * determine how many bytes were successfully sent/received by the ESP. - */ -static inline int esp_bytes_sent(struct esp *esp, int fifo_count) -{ - int rval = sbus_readl(esp->dregs + DMA_ADDR) - esp->esp_command_dvma; - - if (esp->dma->revision == dvmarev1) - rval -= (4 - ((sbus_readl(esp->dregs + DMA_CSR) & DMA_READ_AHEAD)>>11)); - return rval - fifo_count; -} - -static inline void advance_sg(struct scsi_cmnd *sp) -{ - ++sp->SCp.buffer; - --sp->SCp.buffers_residual; - sp->SCp.this_residual = sg_dma_len(sp->SCp.buffer); - sp->SCp.ptr = (char *)((unsigned long)sg_dma_address(sp->SCp.buffer)); -} - -/* Please note that the way I've coded these routines is that I _always_ - * check for a disconnect during any and all information transfer - * phases. The SCSI standard states that the target _can_ cause a BUS - * FREE condition by dropping all MSG/CD/IO/BSY signals. Also note - * that during information transfer phases the target controls every - * change in phase, the only thing the initiator can do is "ask" for - * a message out phase by driving ATN true. The target can, and sometimes - * will, completely ignore this request so we cannot assume anything when - * we try to force a message out phase to abort/reset a target. Most of - * the time the target will eventually be nice and go to message out, so - * we may have to hold on to our state about what we want to tell the target - * for some period of time. - */ - -/* I think I have things working here correctly. Even partial transfers - * within a buffer or sub-buffer should not upset us at all no matter - * how bad the target and/or ESP fucks things up. - */ -static int esp_do_data(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int thisphase, hmuch; - - ESPDATA(("esp_do_data: ")); - esp_maybe_nop(esp); - thisphase = sreg_to_dataphase(esp->sreg); - esp_advance_phase(SCptr, thisphase); - ESPDATA(("newphase<%s> ", (thisphase == in_datain) ? "DATAIN" : "DATAOUT")); - hmuch = dma_can_transfer(esp, SCptr); - if (hmuch > (64 * 1024) && (esp->erev != fashme)) - hmuch = (64 * 1024); - ESPDATA(("hmuch<%d> ", hmuch)); - esp->current_transfer_size = hmuch; - - if (esp->erev == fashme) { - u32 tmp = esp->prev_hme_dmacsr; - - /* Always set the ESP count registers first. */ - esp_setcount(esp->eregs, hmuch, 1); - - /* Get the DMA csr computed. */ - tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - if (thisphase == in_datain) - tmp |= DMA_ST_WRITE; - else - tmp &= ~(DMA_ST_WRITE); - esp->prev_hme_dmacsr = tmp; - - ESPDATA(("DMA|TI --> do_intr_end\n")); - if (thisphase == in_datain) { - sbus_writel(hmuch, esp->dregs + DMA_COUNT); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } else { - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - sbus_writel(hmuch, esp->dregs + DMA_COUNT); - } - sbus_writel((__u32)((unsigned long)SCptr->SCp.ptr), esp->dregs+DMA_ADDR); - sbus_writel(esp->prev_hme_dmacsr, esp->dregs + DMA_CSR); - } else { - esp_setcount(esp->eregs, hmuch, 0); - dma_setup(esp, ((__u32)((unsigned long)SCptr->SCp.ptr)), - hmuch, (thisphase == in_datain)); - ESPDATA(("DMA|TI --> do_intr_end\n")); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - return do_intr_end; -} - -/* See how successful the data transfer was. */ -static int esp_do_data_finale(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int bogus_data = 0, bytes_sent = 0, fifocnt, ecount = 0; - - ESPDATA(("esp_do_data_finale: ")); - - if (SCptr->SCp.phase == in_datain) { - if (esp->sreg & ESP_STAT_PERR) { - /* Yuck, parity error. The ESP asserts ATN - * so that we can go to message out phase - * immediately and inform the target that - * something bad happened. - */ - ESPLOG(("esp%d: data bad parity detected.\n", - esp->esp_id)); - esp->cur_msgout[0] = INITIATOR_ERROR; - esp->msgout_len = 1; - } - dma_drain(esp); - } - dma_invalidate(esp); - - /* This could happen for the above parity error case. */ - if (esp->ireg != ESP_INTR_BSERV) { - /* Please go to msgout phase, please please please... */ - ESPLOG(("esp%d: !BSERV after data, probably to msgout\n", - esp->esp_id)); - return esp_do_phase_determine(esp); - } - - /* Check for partial transfers and other horrible events. - * Note, here we read the real fifo flags register even - * on HME broken adapters because we skip the HME fifo - * workaround code in esp_handle() if we are doing data - * phase things. We don't want to fuck directly with - * the fifo like that, especially if doing synchronous - * transfers! Also, will need to double the count on - * HME if we are doing wide transfers, as the HME fifo - * will move and count 16-bit quantities during wide data. - * SMCC _and_ Qlogic can both bite me. - */ - fifocnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES); - if (esp->erev != fashme) - ecount = esp_getcount(esp->eregs, 0); - bytes_sent = esp->current_transfer_size; - - ESPDATA(("trans_sz(%d), ", bytes_sent)); - if (esp->erev == fashme) { - if (!(esp->sreg & ESP_STAT_TCNT)) { - ecount = esp_getcount(esp->eregs, 1); - bytes_sent -= ecount; - } - - /* Always subtract any cruft remaining in the FIFO. */ - if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE) - fifocnt <<= 1; - if (SCptr->SCp.phase == in_dataout) - bytes_sent -= fifocnt; - - /* I have an IBM disk which exhibits the following - * behavior during writes to it. It disconnects in - * the middle of a partial transfer, the current sglist - * buffer is 1024 bytes, the disk stops data transfer - * at 512 bytes. - * - * However the FAS366 reports that 32 more bytes were - * transferred than really were. This is precisely - * the size of a fully loaded FIFO in wide scsi mode. - * The FIFO state recorded indicates that it is empty. - * - * I have no idea if this is a bug in the FAS366 chip - * or a bug in the firmware on this IBM disk. In any - * event the following seems to be a good workaround. -DaveM - */ - if (bytes_sent != esp->current_transfer_size && - SCptr->SCp.phase == in_dataout) { - int mask = (64 - 1); - - if ((esp->prev_cfg3 & ESP_CONFIG3_EWIDE) == 0) - mask >>= 1; - - if (bytes_sent & mask) - bytes_sent -= (bytes_sent & mask); - } - } else { - if (!(esp->sreg & ESP_STAT_TCNT)) - bytes_sent -= ecount; - if (SCptr->SCp.phase == in_dataout) - bytes_sent -= fifocnt; - } - - ESPDATA(("bytes_sent(%d), ", bytes_sent)); - - /* If we were in synchronous mode, check for peculiarities. */ - if (esp->erev == fashme) { - if (esp_dev->sync_max_offset) { - if (SCptr->SCp.phase == in_dataout) - esp_cmd(esp, ESP_CMD_FLUSH); - } else { - esp_cmd(esp, ESP_CMD_FLUSH); - } - } else { - if (esp_dev->sync_max_offset) - bogus_data = esp100_sync_hwbug(esp, SCptr, fifocnt); - else - esp_cmd(esp, ESP_CMD_FLUSH); - } - - /* Until we are sure of what has happened, we are certainly - * in the dark. - */ - esp_advance_phase(SCptr, in_the_dark); - - if (bytes_sent < 0) { - /* I've seen this happen due to lost state in this - * driver. No idea why it happened, but allowing - * this value to be negative caused things to - * lock up. This allows greater chance of recovery. - * In fact every time I've seen this, it has been - * a driver bug without question. - */ - ESPLOG(("esp%d: yieee, bytes_sent < 0!\n", esp->esp_id)); - ESPLOG(("esp%d: csz=%d fifocount=%d ecount=%d\n", - esp->esp_id, - esp->current_transfer_size, fifocnt, ecount)); - ESPLOG(("esp%d: use_sg=%d ptr=%p this_residual=%d\n", - esp->esp_id, - SCptr->use_sg, SCptr->SCp.ptr, SCptr->SCp.this_residual)); - ESPLOG(("esp%d: Forcing async for target %d\n", esp->esp_id, - SCptr->device->id)); - SCptr->device->borken = 1; - esp_dev->sync = 0; - bytes_sent = 0; - } - - /* Update the state of our transfer. */ - SCptr->SCp.ptr += bytes_sent; - SCptr->SCp.this_residual -= bytes_sent; - if (SCptr->SCp.this_residual < 0) { - /* shit */ - ESPLOG(("esp%d: Data transfer overrun.\n", esp->esp_id)); - SCptr->SCp.this_residual = 0; - } - - /* Maybe continue. */ - if (!bogus_data) { - ESPDATA(("!bogus_data, ")); - - /* NO MATTER WHAT, we advance the scatterlist, - * if the target should decide to disconnect - * in between scatter chunks (which is common) - * we could die horribly! I used to have the sg - * advance occur only if we are going back into - * (or are staying in) a data phase, you can - * imagine the hell I went through trying to - * figure this out. - */ - if (SCptr->use_sg && !SCptr->SCp.this_residual) - advance_sg(SCptr); - if (sreg_datainp(esp->sreg) || sreg_dataoutp(esp->sreg)) { - ESPDATA(("to more data\n")); - return esp_do_data(esp); - } - ESPDATA(("to new phase\n")); - return esp_do_phase_determine(esp); - } - /* Bogus data, just wait for next interrupt. */ - ESPLOG(("esp%d: bogus_data during end of data phase\n", - esp->esp_id)); - return do_intr_end; -} - -/* We received a non-good status return at the end of - * running a SCSI command. This is used to decide if - * we should clear our synchronous transfer state for - * such a device when that happens. - * - * The idea is that when spinning up a disk or rewinding - * a tape, we don't want to go into a loop re-negotiating - * synchronous capabilities over and over. - */ -static int esp_should_clear_sync(struct scsi_cmnd *sp) -{ - u8 cmd = sp->cmnd[0]; - - /* These cases are for spinning up a disk and - * waiting for that spinup to complete. - */ - if (cmd == START_STOP) - return 0; - - if (cmd == TEST_UNIT_READY) - return 0; - - /* One more special case for SCSI tape drives, - * this is what is used to probe the device for - * completion of a rewind or tape load operation. - */ - if (sp->device->type == TYPE_TAPE) { - if (cmd == MODE_SENSE) - return 0; - } - - return 1; -} - -/* Either a command is completing or a target is dropping off the bus - * to continue the command in the background so we can do other work. - */ -static int esp_do_freebus(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int rval; - - rval = skipahead2(esp, SCptr, in_status, in_msgindone, in_freeing); - if (rval) - return rval; - if (esp->ireg != ESP_INTR_DC) { - ESPLOG(("esp%d: Target will not disconnect\n", esp->esp_id)); - return do_reset_bus; /* target will not drop BSY... */ - } - esp->msgout_len = 0; - esp->prevmsgout = NOP; - if (esp->prevmsgin == COMMAND_COMPLETE) { - /* Normal end of nexus. */ - if (esp->disconnected_SC || (esp->erev == fashme)) - esp_cmd(esp, ESP_CMD_ESEL); - - if (SCptr->SCp.Status != GOOD && - SCptr->SCp.Status != CONDITION_GOOD && - ((1<<SCptr->device->id) & esp->targets_present) && - esp_dev->sync && - esp_dev->sync_max_offset) { - /* SCSI standard says that the synchronous capabilities - * should be renegotiated at this point. Most likely - * we are about to request sense from this target - * in which case we want to avoid using sync - * transfers until we are sure of the current target - * state. - */ - ESPMISC(("esp: Status <%d> for target %d lun %d\n", - SCptr->SCp.Status, SCptr->device->id, SCptr->device->lun)); - - /* But don't do this when spinning up a disk at - * boot time while we poll for completion as it - * fills up the console with messages. Also, tapes - * can report not ready many times right after - * loading up a tape. - */ - if (esp_should_clear_sync(SCptr) != 0) - esp_dev->sync = 0; - } - ESPDISC(("F<%02x,%02x>", SCptr->device->id, SCptr->device->lun)); - esp_done(esp, ((SCptr->SCp.Status & 0xff) | - ((SCptr->SCp.Message & 0xff)<<8) | - (DID_OK << 16))); - } else if (esp->prevmsgin == DISCONNECT) { - /* Normal disconnect. */ - esp_cmd(esp, ESP_CMD_ESEL); - ESPDISC(("D<%02x,%02x>", SCptr->device->id, SCptr->device->lun)); - append_SC(&esp->disconnected_SC, SCptr); - esp->current_SC = NULL; - if (esp->issue_SC) - esp_exec_cmd(esp); - } else { - /* Driver bug, we do not expect a disconnect here - * and should not have advanced the state engine - * to in_freeing. - */ - ESPLOG(("esp%d: last msg not disc and not cmd cmplt.\n", - esp->esp_id)); - return do_reset_bus; - } - return do_intr_end; -} - -/* When a reselect occurs, and we cannot find the command to - * reconnect to in our queues, we do this. - */ -static int esp_bad_reconnect(struct esp *esp) -{ - struct scsi_cmnd *sp; - - ESPLOG(("esp%d: Eieeee, reconnecting unknown command!\n", - esp->esp_id)); - ESPLOG(("QUEUE DUMP\n")); - sp = esp->issue_SC; - ESPLOG(("esp%d: issue_SC[", esp->esp_id)); - while (sp) { - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - sp = (struct scsi_cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - sp = esp->current_SC; - ESPLOG(("esp%d: current_SC[", esp->esp_id)); - if (sp) - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - else - ESPLOG(("<NULL>")); - ESPLOG(("]\n")); - sp = esp->disconnected_SC; - ESPLOG(("esp%d: disconnected_SC[", esp->esp_id)); - while (sp) { - ESPLOG(("<%02x,%02x>", sp->device->id, sp->device->lun)); - sp = (struct scsi_cmnd *) sp->host_scribble; - } - ESPLOG(("]\n")); - return do_reset_bus; -} - -/* Do the needy when a target tries to reconnect to us. */ -static int esp_do_reconnect(struct esp *esp) -{ - int lun, target; - struct scsi_cmnd *SCptr; - - /* Check for all bogus conditions first. */ - target = reconnect_target(esp); - if (target < 0) { - ESPDISC(("bad bus bits\n")); - return do_reset_bus; - } - lun = reconnect_lun(esp); - if (lun < 0) { - ESPDISC(("target=%2x, bad identify msg\n", target)); - return do_reset_bus; - } - - /* Things look ok... */ - ESPDISC(("R<%02x,%02x>", target, lun)); - - /* Must not flush FIFO or DVMA on HME. */ - if (esp->erev != fashme) { - esp_cmd(esp, ESP_CMD_FLUSH); - if (esp100_reconnect_hwbug(esp)) - return do_reset_bus; - esp_cmd(esp, ESP_CMD_NULL); - } - - SCptr = remove_SC(&esp->disconnected_SC, (u8) target, (u8) lun); - if (!SCptr) - return esp_bad_reconnect(esp); - - esp_connect(esp, SCptr); - esp_cmd(esp, ESP_CMD_MOK); - - if (esp->erev == fashme) - sbus_writeb(((SCptr->device->id & 0xf) | - (ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT)), - esp->eregs + ESP_BUSID); - - /* Reconnect implies a restore pointers operation. */ - esp_restore_pointers(esp, SCptr); - - esp->snip = 0; - esp_advance_phase(SCptr, in_the_dark); - return do_intr_end; -} - -/* End of NEXUS (hopefully), pick up status + message byte then leave if - * all goes well. - */ -static int esp_do_status(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int intr, rval; - - rval = skipahead1(esp, SCptr, in_the_dark, in_status); - if (rval) - return rval; - intr = esp->ireg; - ESPSTAT(("esp_do_status: ")); - if (intr != ESP_INTR_DC) { - int message_out = 0; /* for parity problems */ - - /* Ack the message. */ - ESPSTAT(("ack msg, ")); - esp_cmd(esp, ESP_CMD_MOK); - - if (esp->erev != fashme) { - dma_flashclear(esp); - - /* Wait till the first bits settle. */ - while (esp->esp_command[0] == 0xff) - udelay(1); - } else { - esp->esp_command[0] = esp->hme_fifo_workaround_buffer[0]; - esp->esp_command[1] = esp->hme_fifo_workaround_buffer[1]; - } - - ESPSTAT(("got something, ")); - /* ESP chimes in with one of - * - * 1) function done interrupt: - * both status and message in bytes - * are available - * - * 2) bus service interrupt: - * only status byte was acquired - * - * 3) Anything else: - * can't happen, but we test for it - * anyways - * - * ALSO: If bad parity was detected on either - * the status _or_ the message byte then - * the ESP has asserted ATN on the bus - * and we must therefore wait for the - * next phase change. - */ - if (intr & ESP_INTR_FDONE) { - /* We got it all, hallejulia. */ - ESPSTAT(("got both, ")); - SCptr->SCp.Status = esp->esp_command[0]; - SCptr->SCp.Message = esp->esp_command[1]; - esp->prevmsgin = SCptr->SCp.Message; - esp->cur_msgin[0] = SCptr->SCp.Message; - if (esp->sreg & ESP_STAT_PERR) { - /* There was bad parity for the - * message byte, the status byte - * was ok. - */ - message_out = MSG_PARITY_ERROR; - } - } else if (intr == ESP_INTR_BSERV) { - /* Only got status byte. */ - ESPLOG(("esp%d: got status only, ", esp->esp_id)); - if (!(esp->sreg & ESP_STAT_PERR)) { - SCptr->SCp.Status = esp->esp_command[0]; - SCptr->SCp.Message = 0xff; - } else { - /* The status byte had bad parity. - * we leave the scsi_pointer Status - * field alone as we set it to a default - * of CHECK_CONDITION in esp_queue. - */ - message_out = INITIATOR_ERROR; - } - } else { - /* This shouldn't happen ever. */ - ESPSTAT(("got bolixed\n")); - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - - if (!message_out) { - ESPSTAT(("status=%2x msg=%2x, ", SCptr->SCp.Status, - SCptr->SCp.Message)); - if (SCptr->SCp.Message == COMMAND_COMPLETE) { - ESPSTAT(("and was COMMAND_COMPLETE\n")); - esp_advance_phase(SCptr, in_freeing); - return esp_do_freebus(esp); - } else { - ESPLOG(("esp%d: and _not_ COMMAND_COMPLETE\n", - esp->esp_id)); - esp->msgin_len = esp->msgin_ctr = 1; - esp_advance_phase(SCptr, in_msgindone); - return esp_do_msgindone(esp); - } - } else { - /* With luck we'll be able to let the target - * know that bad parity happened, it will know - * which byte caused the problems and send it - * again. For the case where the status byte - * receives bad parity, I do not believe most - * targets recover very well. We'll see. - */ - ESPLOG(("esp%d: bad parity somewhere mout=%2x\n", - esp->esp_id, message_out)); - esp->cur_msgout[0] = message_out; - esp->msgout_len = esp->msgout_ctr = 1; - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - } else { - /* If we disconnect now, all hell breaks loose. */ - ESPLOG(("esp%d: whoops, disconnect\n", esp->esp_id)); - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } -} - -static int esp_enter_status(struct esp *esp) -{ - u8 thecmd = ESP_CMD_ICCSEQ; - - esp_cmd(esp, ESP_CMD_FLUSH); - if (esp->erev != fashme) { - u32 tmp; - - esp->esp_command[0] = esp->esp_command[1] = 0xff; - sbus_writeb(2, esp->eregs + ESP_TCLOW); - sbus_writeb(0, esp->eregs + ESP_TCMED); - tmp = sbus_readl(esp->dregs + DMA_CSR); - tmp |= (DMA_ST_WRITE | DMA_ENABLE); - sbus_writel(tmp, esp->dregs + DMA_CSR); - if (esp->dma->revision == dvmaesc1) - sbus_writel(0x100, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - thecmd |= ESP_CMD_DMA; - } - esp_cmd(esp, thecmd); - esp_advance_phase(esp->current_SC, in_status); - - return esp_do_status(esp); -} - -static int esp_disconnect_amidst_phases(struct esp *esp) -{ - struct scsi_cmnd *sp = esp->current_SC; - struct esp_device *esp_dev = sp->device->hostdata; - - /* This means real problems if we see this - * here. Unless we were actually trying - * to force the device to abort/reset. - */ - ESPLOG(("esp%d Disconnect amidst phases, ", esp->esp_id)); - ESPLOG(("pphase<%s> cphase<%s>, ", - phase_string(sp->SCp.phase), - phase_string(sp->SCp.sent_command))); - - if (esp->disconnected_SC != NULL || (esp->erev == fashme)) - esp_cmd(esp, ESP_CMD_ESEL); - - switch (esp->cur_msgout[0]) { - default: - /* We didn't expect this to happen at all. */ - ESPLOG(("device is bolixed\n")); - esp_advance_phase(sp, in_tgterror); - esp_done(esp, (DID_ERROR << 16)); - break; - - case BUS_DEVICE_RESET: - ESPLOG(("device reset successful\n")); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 0; - esp_advance_phase(sp, in_resetdev); - esp_done(esp, (DID_RESET << 16)); - break; - - case ABORT: - ESPLOG(("device abort successful\n")); - esp_advance_phase(sp, in_abortone); - esp_done(esp, (DID_ABORT << 16)); - break; - - }; - return do_intr_end; -} - -static int esp_enter_msgout(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_msgout); - return esp_do_msgout(esp); -} - -static int esp_enter_msgin(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_msgin); - return esp_do_msgin(esp); -} - -static int esp_enter_cmd(struct esp *esp) -{ - esp_advance_phase(esp->current_SC, in_cmdbegin); - return esp_do_cmdbegin(esp); -} - -static int esp_enter_badphase(struct esp *esp) -{ - ESPLOG(("esp%d: Bizarre bus phase %2x.\n", esp->esp_id, - esp->sreg & ESP_STAT_PMASK)); - return do_reset_bus; -} - -typedef int (*espfunc_t)(struct esp *); - -static espfunc_t phase_vector[] = { - esp_do_data, /* ESP_DOP */ - esp_do_data, /* ESP_DIP */ - esp_enter_cmd, /* ESP_CMDP */ - esp_enter_status, /* ESP_STATP */ - esp_enter_badphase, /* ESP_STAT_PMSG */ - esp_enter_badphase, /* ESP_STAT_PMSG | ESP_STAT_PIO */ - esp_enter_msgout, /* ESP_MOP */ - esp_enter_msgin, /* ESP_MIP */ -}; - -/* The target has control of the bus and we have to see where it has - * taken us. - */ -static int esp_do_phase_determine(struct esp *esp) -{ - if ((esp->ireg & ESP_INTR_DC) != 0) - return esp_disconnect_amidst_phases(esp); - return phase_vector[esp->sreg & ESP_STAT_PMASK](esp); -} - -/* First interrupt after exec'ing a cmd comes here. */ -static int esp_select_complete(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - int cmd_bytes_sent, fcnt; - - if (esp->erev != fashme) - esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS); - - if (esp->erev == fashme) - fcnt = esp->hme_fifo_workaround_count; - else - fcnt = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES); - - cmd_bytes_sent = esp_bytes_sent(esp, fcnt); - dma_invalidate(esp); - - /* Let's check to see if a reselect happened - * while we we're trying to select. This must - * be checked first. - */ - if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) { - esp_reconnect(esp, SCptr); - return esp_do_reconnect(esp); - } - - /* Looks like things worked, we should see a bus service & - * a function complete interrupt at this point. Note we - * are doing a direct comparison because we don't want to - * be fooled into thinking selection was successful if - * ESP_INTR_DC is set, see below. - */ - if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { - /* target speaks... */ - esp->targets_present |= (1<<SCptr->device->id); - - /* What if the target ignores the sdtr? */ - if (esp->snip) - esp_dev->sync = 1; - - /* See how far, if at all, we got in getting - * the information out to the target. - */ - switch (esp->seqreg) { - default: - - case ESP_STEP_ASEL: - /* Arbitration won, target selected, but - * we are in some phase which is not command - * phase nor is it message out phase. - * - * XXX We've confused the target, obviously. - * XXX So clear it's state, but we also end - * XXX up clearing everyone elses. That isn't - * XXX so nice. I'd like to just reset this - * XXX target, but if I cannot even get it's - * XXX attention and finish selection to talk - * XXX to it, there is not much more I can do. - * XXX If we have a loaded bus we're going to - * XXX spend the next second or so renegotiating - * XXX for synchronous transfers. - */ - ESPLOG(("esp%d: STEP_ASEL for tgt %d\n", - esp->esp_id, SCptr->device->id)); - - case ESP_STEP_SID: - /* Arbitration won, target selected, went - * to message out phase, sent one message - * byte, then we stopped. ATN is asserted - * on the SCSI bus and the target is still - * there hanging on. This is a legal - * sequence step if we gave the ESP a select - * and stop command. - * - * XXX See above, I could set the borken flag - * XXX in the device struct and retry the - * XXX command. But would that help for - * XXX tagged capable targets? - */ - - case ESP_STEP_NCMD: - /* Arbitration won, target selected, maybe - * sent the one message byte in message out - * phase, but we did not go to command phase - * in the end. Actually, we could have sent - * only some of the message bytes if we tried - * to send out the entire identify and tag - * message using ESP_CMD_SA3. - */ - cmd_bytes_sent = 0; - break; - - case ESP_STEP_PPC: - /* No, not the powerPC pinhead. Arbitration - * won, all message bytes sent if we went to - * message out phase, went to command phase - * but only part of the command was sent. - * - * XXX I've seen this, but usually in conjunction - * XXX with a gross error which appears to have - * XXX occurred between the time I told the - * XXX ESP to arbitrate and when I got the - * XXX interrupt. Could I have misloaded the - * XXX command bytes into the fifo? Actually, - * XXX I most likely missed a phase, and therefore - * XXX went into never never land and didn't even - * XXX know it. That was the old driver though. - * XXX What is even more peculiar is that the ESP - * XXX showed the proper function complete and - * XXX bus service bits in the interrupt register. - */ - - case ESP_STEP_FINI4: - case ESP_STEP_FINI5: - case ESP_STEP_FINI6: - case ESP_STEP_FINI7: - /* Account for the identify message */ - if (SCptr->SCp.phase == in_slct_norm) - cmd_bytes_sent -= 1; - }; - - if (esp->erev != fashme) - esp_cmd(esp, ESP_CMD_NULL); - - /* Be careful, we could really get fucked during synchronous - * data transfers if we try to flush the fifo now. - */ - if ((esp->erev != fashme) && /* not a Happy Meal and... */ - !fcnt && /* Fifo is empty and... */ - /* either we are not doing synchronous transfers or... */ - (!esp_dev->sync_max_offset || - /* We are not going into data in phase. */ - ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP))) - esp_cmd(esp, ESP_CMD_FLUSH); /* flush is safe */ - - /* See how far we got if this is not a slow command. */ - if (!esp->esp_slowcmd) { - if (cmd_bytes_sent < 0) - cmd_bytes_sent = 0; - if (cmd_bytes_sent != SCptr->cmd_len) { - /* Crapola, mark it as a slowcmd - * so that we have some chance of - * keeping the command alive with - * good luck. - * - * XXX Actually, if we didn't send it all - * XXX this means either we didn't set things - * XXX up properly (driver bug) or the target - * XXX or the ESP detected parity on one of - * XXX the command bytes. This makes much - * XXX more sense, and therefore this code - * XXX should be changed to send out a - * XXX parity error message or if the status - * XXX register shows no parity error then - * XXX just expect the target to bring the - * XXX bus into message in phase so that it - * XXX can send us the parity error message. - * XXX SCSI sucks... - */ - esp->esp_slowcmd = 1; - esp->esp_scmdp = &(SCptr->cmnd[cmd_bytes_sent]); - esp->esp_scmdleft = (SCptr->cmd_len - cmd_bytes_sent); - } - } - - /* Now figure out where we went. */ - esp_advance_phase(SCptr, in_the_dark); - return esp_do_phase_determine(esp); - } - - /* Did the target even make it? */ - if (esp->ireg == ESP_INTR_DC) { - /* wheee... nobody there or they didn't like - * what we told it to do, clean up. - */ - - /* If anyone is off the bus, but working on - * a command in the background for us, tell - * the ESP to listen for them. - */ - if (esp->disconnected_SC) - esp_cmd(esp, ESP_CMD_ESEL); - - if (((1<<SCptr->device->id) & esp->targets_present) && - esp->seqreg != 0 && - (esp->cur_msgout[0] == EXTENDED_MESSAGE) && - (SCptr->SCp.phase == in_slct_msg || - SCptr->SCp.phase == in_slct_stop)) { - /* shit */ - esp->snip = 0; - ESPLOG(("esp%d: Failed synchronous negotiation for target %d " - "lun %d\n", esp->esp_id, SCptr->device->id, SCptr->device->lun)); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp_dev->sync = 1; /* so we don't negotiate again */ - - /* Run the command again, this time though we - * won't try to negotiate for synchronous transfers. - * - * XXX I'd like to do something like send an - * XXX INITIATOR_ERROR or ABORT message to the - * XXX target to tell it, "Sorry I confused you, - * XXX please come back and I will be nicer next - * XXX time". But that requires having the target - * XXX on the bus, and it has dropped BSY on us. - */ - esp->current_SC = NULL; - esp_advance_phase(SCptr, not_issued); - prepend_SC(&esp->issue_SC, SCptr); - esp_exec_cmd(esp); - return do_intr_end; - } - - /* Ok, this is normal, this is what we see during boot - * or whenever when we are scanning the bus for targets. - * But first make sure that is really what is happening. - */ - if (((1<<SCptr->device->id) & esp->targets_present)) { - ESPLOG(("esp%d: Warning, live target %d not responding to " - "selection.\n", esp->esp_id, SCptr->device->id)); - - /* This _CAN_ happen. The SCSI standard states that - * the target is to _not_ respond to selection if - * _it_ detects bad parity on the bus for any reason. - * Therefore, we assume that if we've talked successfully - * to this target before, bad parity is the problem. - */ - esp_done(esp, (DID_PARITY << 16)); - } else { - /* Else, there really isn't anyone there. */ - ESPMISC(("esp: selection failure, maybe nobody there?\n")); - ESPMISC(("esp: target %d lun %d\n", - SCptr->device->id, SCptr->device->lun)); - esp_done(esp, (DID_BAD_TARGET << 16)); - } - return do_intr_end; - } - - ESPLOG(("esp%d: Selection failure.\n", esp->esp_id)); - printk("esp%d: Currently -- ", esp->esp_id); - esp_print_ireg(esp->ireg); printk(" "); - esp_print_statreg(esp->sreg); printk(" "); - esp_print_seqreg(esp->seqreg); printk("\n"); - printk("esp%d: New -- ", esp->esp_id); - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->seqreg = sbus_readb(esp->eregs + ESP_SSTEP); - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); - esp_print_ireg(esp->ireg); printk(" "); - esp_print_statreg(esp->sreg); printk(" "); - esp_print_seqreg(esp->seqreg); printk("\n"); - ESPLOG(("esp%d: resetting bus\n", esp->esp_id)); - return do_reset_bus; /* ugh... */ -} - -/* Continue reading bytes for msgin phase. */ -static int esp_do_msgincont(struct esp *esp) -{ - if (esp->ireg & ESP_INTR_BSERV) { - /* in the right phase too? */ - if ((esp->sreg & ESP_STAT_PMASK) == ESP_MIP) { - /* phew... */ - esp_cmd(esp, ESP_CMD_TI); - esp_advance_phase(esp->current_SC, in_msgindone); - return do_intr_end; - } - - /* We changed phase but ESP shows bus service, - * in this case it is most likely that we, the - * hacker who has been up for 20hrs straight - * staring at the screen, drowned in coffee - * smelling like retched cigarette ashes - * have miscoded something..... so, try to - * recover as best we can. - */ - ESPLOG(("esp%d: message in mis-carriage.\n", esp->esp_id)); - } - esp_advance_phase(esp->current_SC, in_the_dark); - return do_phase_determine; -} - -static int check_singlebyte_msg(struct esp *esp) -{ - esp->prevmsgin = esp->cur_msgin[0]; - if (esp->cur_msgin[0] & 0x80) { - /* wheee... */ - ESPLOG(("esp%d: target sends identify amidst phases\n", - esp->esp_id)); - esp_advance_phase(esp->current_SC, in_the_dark); - return 0; - } else if (((esp->cur_msgin[0] & 0xf0) == 0x20) || - (esp->cur_msgin[0] == EXTENDED_MESSAGE)) { - esp->msgin_len = 2; - esp_advance_phase(esp->current_SC, in_msgincont); - return 0; - } - esp_advance_phase(esp->current_SC, in_the_dark); - switch (esp->cur_msgin[0]) { - default: - /* We don't want to hear about it. */ - ESPLOG(("esp%d: msg %02x which we don't know about\n", esp->esp_id, - esp->cur_msgin[0])); - return MESSAGE_REJECT; - - case NOP: - ESPLOG(("esp%d: target %d sends a nop\n", esp->esp_id, - esp->current_SC->device->id)); - return 0; - - case RESTORE_POINTERS: - /* In this case we might also have to backup the - * "slow command" pointer. It is rare to get such - * a save/restore pointer sequence so early in the - * bus transition sequences, but cover it. - */ - if (esp->esp_slowcmd) { - esp->esp_scmdleft = esp->current_SC->cmd_len; - esp->esp_scmdp = &esp->current_SC->cmnd[0]; - } - esp_restore_pointers(esp, esp->current_SC); - return 0; - - case SAVE_POINTERS: - esp_save_pointers(esp, esp->current_SC); - return 0; - - case COMMAND_COMPLETE: - case DISCONNECT: - /* Freeing the bus, let it go. */ - esp->current_SC->SCp.phase = in_freeing; - return 0; - - case MESSAGE_REJECT: - ESPMISC(("msg reject, ")); - if (esp->prevmsgout == EXTENDED_MESSAGE) { - struct esp_device *esp_dev = esp->current_SC->device->hostdata; - - /* Doesn't look like this target can - * do synchronous or WIDE transfers. - */ - ESPSDTR(("got reject, was trying nego, clearing sync/WIDE\n")); - esp_dev->sync = 1; - esp_dev->wide = 1; - esp_dev->sync_min_period = 0; - esp_dev->sync_max_offset = 0; - return 0; - } else { - ESPMISC(("not sync nego, sending ABORT\n")); - return ABORT; - } - }; -} - -/* Target negotiates for synchronous transfers before we do, this - * is legal although very strange. What is even funnier is that - * the SCSI2 standard specifically recommends against targets doing - * this because so many initiators cannot cope with this occurring. - */ -static int target_with_ants_in_pants(struct esp *esp, - struct scsi_cmnd *SCptr, - struct esp_device *esp_dev) -{ - if (esp_dev->sync || SCptr->device->borken) { - /* sorry, no can do */ - ESPSDTR(("forcing to async, ")); - build_sync_nego_msg(esp, 0, 0); - esp_dev->sync = 1; - esp->snip = 1; - ESPLOG(("esp%d: hoping for msgout\n", esp->esp_id)); - esp_advance_phase(SCptr, in_the_dark); - return EXTENDED_MESSAGE; - } - - /* Ok, we'll check them out... */ - return 0; -} - -static void sync_report(struct esp *esp) -{ - int msg3, msg4; - char *type; - - msg3 = esp->cur_msgin[3]; - msg4 = esp->cur_msgin[4]; - if (msg4) { - int hz = 1000000000 / (msg3 * 4); - int integer = hz / 1000000; - int fraction = (hz - (integer * 1000000)) / 10000; - if ((esp->erev == fashme) && - (esp->config3[esp->current_SC->device->id] & ESP_CONFIG3_EWIDE)) { - type = "FAST-WIDE"; - integer <<= 1; - fraction <<= 1; - } else if ((msg3 * 4) < 200) { - type = "FAST"; - } else { - type = "synchronous"; - } - - /* Do not transform this back into one big printk - * again, it triggers a bug in our sparc64-gcc272 - * sibling call optimization. -DaveM - */ - ESPLOG((KERN_INFO "esp%d: target %d ", - esp->esp_id, esp->current_SC->device->id)); - ESPLOG(("[period %dns offset %d %d.%02dMHz ", - (int) msg3 * 4, (int) msg4, - integer, fraction)); - ESPLOG(("%s SCSI%s]\n", type, - (((msg3 * 4) < 200) ? "-II" : ""))); - } else { - ESPLOG((KERN_INFO "esp%d: target %d asynchronous\n", - esp->esp_id, esp->current_SC->device->id)); - } -} - -static int check_multibyte_msg(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - struct esp_device *esp_dev = SCptr->device->hostdata; - u8 regval = 0; - int message_out = 0; - - ESPSDTR(("chk multibyte msg: ")); - if (esp->cur_msgin[2] == EXTENDED_SDTR) { - int period = esp->cur_msgin[3]; - int offset = esp->cur_msgin[4]; - - ESPSDTR(("is sync nego response, ")); - if (!esp->snip) { - int rval; - - /* Target negotiates first! */ - ESPSDTR(("target jumps the gun, ")); - message_out = EXTENDED_MESSAGE; /* we must respond */ - rval = target_with_ants_in_pants(esp, SCptr, esp_dev); - if (rval) - return rval; - } - - ESPSDTR(("examining sdtr, ")); - - /* Offset cannot be larger than ESP fifo size. */ - if (offset > 15) { - ESPSDTR(("offset too big %2x, ", offset)); - offset = 15; - ESPSDTR(("sending back new offset\n")); - build_sync_nego_msg(esp, period, offset); - return EXTENDED_MESSAGE; - } - - if (offset && period > esp->max_period) { - /* Yeee, async for this slow device. */ - ESPSDTR(("period too long %2x, ", period)); - build_sync_nego_msg(esp, 0, 0); - ESPSDTR(("hoping for msgout\n")); - esp_advance_phase(esp->current_SC, in_the_dark); - return EXTENDED_MESSAGE; - } else if (offset && period < esp->min_period) { - ESPSDTR(("period too short %2x, ", period)); - period = esp->min_period; - if (esp->erev > esp236) - regval = 4; - else - regval = 5; - } else if (offset) { - int tmp; - - ESPSDTR(("period is ok, ")); - tmp = esp->ccycle / 1000; - regval = (((period << 2) + tmp - 1) / tmp); - if (regval && ((esp->erev == fas100a || - esp->erev == fas236 || - esp->erev == fashme))) { - if (period >= 50) - regval--; - } - } - - if (offset) { - u8 bit; - - esp_dev->sync_min_period = (regval & 0x1f); - esp_dev->sync_max_offset = (offset | esp->radelay); - if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) { - if ((esp->erev == fas100a) || (esp->erev == fashme)) - bit = ESP_CONFIG3_FAST; - else - bit = ESP_CONFIG3_FSCSI; - if (period < 50) { - /* On FAS366, if using fast-20 synchronous transfers - * we need to make sure the REQ/ACK assert/deassert - * control bits are clear. - */ - if (esp->erev == fashme) - esp_dev->sync_max_offset &= ~esp->radelay; - esp->config3[SCptr->device->id] |= bit; - } else { - esp->config3[SCptr->device->id] &= ~bit; - } - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - esp->prev_soff = esp_dev->sync_max_offset; - esp->prev_stp = esp_dev->sync_min_period; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - ESPSDTR(("soff=%2x stp=%2x cfg3=%2x\n", - esp_dev->sync_max_offset, - esp_dev->sync_min_period, - esp->config3[SCptr->device->id])); - - esp->snip = 0; - } else if (esp_dev->sync_max_offset) { - u8 bit; - - /* back to async mode */ - ESPSDTR(("unaccaptable sync nego, forcing async\n")); - esp_dev->sync_max_offset = 0; - esp_dev->sync_min_period = 0; - esp->prev_soff = 0; - esp->prev_stp = 0; - sbus_writeb(esp->prev_soff, esp->eregs + ESP_SOFF); - sbus_writeb(esp->prev_stp, esp->eregs + ESP_STP); - if (esp->erev == fas100a || esp->erev == fas236 || esp->erev == fashme) { - if ((esp->erev == fas100a) || (esp->erev == fashme)) - bit = ESP_CONFIG3_FAST; - else - bit = ESP_CONFIG3_FSCSI; - esp->config3[SCptr->device->id] &= ~bit; - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - } - } - - sync_report(esp); - - ESPSDTR(("chk multibyte msg: sync is known, ")); - esp_dev->sync = 1; - - if (message_out) { - ESPLOG(("esp%d: sending sdtr back, hoping for msgout\n", - esp->esp_id)); - build_sync_nego_msg(esp, period, offset); - esp_advance_phase(SCptr, in_the_dark); - return EXTENDED_MESSAGE; - } - - ESPSDTR(("returning zero\n")); - esp_advance_phase(SCptr, in_the_dark); /* ...or else! */ - return 0; - } else if (esp->cur_msgin[2] == EXTENDED_WDTR) { - int size = 8 << esp->cur_msgin[3]; - - esp->wnip = 0; - if (esp->erev != fashme) { - ESPLOG(("esp%d: AIEEE wide msg received and not HME.\n", - esp->esp_id)); - message_out = MESSAGE_REJECT; - } else if (size > 16) { - ESPLOG(("esp%d: AIEEE wide transfer for %d size " - "not supported.\n", esp->esp_id, size)); - message_out = MESSAGE_REJECT; - } else { - /* Things look good; let's see what we got. */ - if (size == 16) { - /* Set config 3 register for this target. */ - esp->config3[SCptr->device->id] |= ESP_CONFIG3_EWIDE; - } else { - /* Just make sure it was one byte sized. */ - if (size != 8) { - ESPLOG(("esp%d: Aieee, wide nego of %d size.\n", - esp->esp_id, size)); - message_out = MESSAGE_REJECT; - goto finish; - } - /* Pure paranoia. */ - esp->config3[SCptr->device->id] &= ~(ESP_CONFIG3_EWIDE); - } - esp->prev_cfg3 = esp->config3[SCptr->device->id]; - sbus_writeb(esp->prev_cfg3, esp->eregs + ESP_CFG3); - - /* Regardless, next try for sync transfers. */ - build_sync_nego_msg(esp, esp->sync_defp, 15); - esp_dev->sync = 1; - esp->snip = 1; - message_out = EXTENDED_MESSAGE; - } - } else if (esp->cur_msgin[2] == EXTENDED_MODIFY_DATA_POINTER) { - ESPLOG(("esp%d: rejecting modify data ptr msg\n", esp->esp_id)); - message_out = MESSAGE_REJECT; - } -finish: - esp_advance_phase(SCptr, in_the_dark); - return message_out; -} - -static int esp_do_msgindone(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - int message_out = 0, it = 0, rval; - - rval = skipahead1(esp, SCptr, in_msgin, in_msgindone); - if (rval) - return rval; - if (SCptr->SCp.sent_command != in_status) { - if (!(esp->ireg & ESP_INTR_DC)) { - if (esp->msgin_len && (esp->sreg & ESP_STAT_PERR)) { - message_out = MSG_PARITY_ERROR; - esp_cmd(esp, ESP_CMD_FLUSH); - } else if (esp->erev != fashme && - (it = (sbus_readb(esp->eregs + ESP_FFLAGS) & ESP_FF_FBYTES)) != 1) { - /* We certainly dropped the ball somewhere. */ - message_out = INITIATOR_ERROR; - esp_cmd(esp, ESP_CMD_FLUSH); - } else if (!esp->msgin_len) { - if (esp->erev == fashme) - it = esp->hme_fifo_workaround_buffer[0]; - else - it = sbus_readb(esp->eregs + ESP_FDATA); - esp_advance_phase(SCptr, in_msgincont); - } else { - /* it is ok and we want it */ - if (esp->erev == fashme) - it = esp->cur_msgin[esp->msgin_ctr] = - esp->hme_fifo_workaround_buffer[0]; - else - it = esp->cur_msgin[esp->msgin_ctr] = - sbus_readb(esp->eregs + ESP_FDATA); - esp->msgin_ctr++; - } - } else { - esp_advance_phase(SCptr, in_the_dark); - return do_work_bus; - } - } else { - it = esp->cur_msgin[0]; - } - if (!message_out && esp->msgin_len) { - if (esp->msgin_ctr < esp->msgin_len) { - esp_advance_phase(SCptr, in_msgincont); - } else if (esp->msgin_len == 1) { - message_out = check_singlebyte_msg(esp); - } else if (esp->msgin_len == 2) { - if (esp->cur_msgin[0] == EXTENDED_MESSAGE) { - if ((it + 2) >= 15) { - message_out = MESSAGE_REJECT; - } else { - esp->msgin_len = (it + 2); - esp_advance_phase(SCptr, in_msgincont); - } - } else { - message_out = MESSAGE_REJECT; /* foo on you */ - } - } else { - message_out = check_multibyte_msg(esp); - } - } - if (message_out < 0) { - return -message_out; - } else if (message_out) { - if (((message_out != 1) && - ((message_out < 0x20) || (message_out & 0x80)))) - esp->msgout_len = 1; - esp->cur_msgout[0] = message_out; - esp_cmd(esp, ESP_CMD_SATN); - esp_advance_phase(SCptr, in_the_dark); - esp->msgin_len = 0; - } - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->sreg &= ~(ESP_STAT_INTR); - if ((esp->sreg & (ESP_STAT_PMSG|ESP_STAT_PCD)) == (ESP_STAT_PMSG|ESP_STAT_PCD)) - esp_cmd(esp, ESP_CMD_MOK); - if ((SCptr->SCp.sent_command == in_msgindone) && - (SCptr->SCp.phase == in_freeing)) - return esp_do_freebus(esp); - return do_intr_end; -} - -static int esp_do_cmdbegin(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - - esp_advance_phase(SCptr, in_cmdend); - if (esp->erev == fashme) { - u32 tmp = sbus_readl(esp->dregs + DMA_CSR); - int i; - - for (i = 0; i < esp->esp_scmdleft; i++) - esp->esp_command[i] = *esp->esp_scmdp++; - esp->esp_scmdleft = 0; - esp_cmd(esp, ESP_CMD_FLUSH); - esp_setcount(esp->eregs, i, 1); - esp_cmd(esp, (ESP_CMD_DMA | ESP_CMD_TI)); - tmp |= (DMA_SCSI_DISAB | DMA_ENABLE); - tmp &= ~(DMA_ST_WRITE); - sbus_writel(i, esp->dregs + DMA_COUNT); - sbus_writel(esp->esp_command_dvma, esp->dregs + DMA_ADDR); - sbus_writel(tmp, esp->dregs + DMA_CSR); - } else { - u8 tmp; - - esp_cmd(esp, ESP_CMD_FLUSH); - tmp = *esp->esp_scmdp++; - esp->esp_scmdleft--; - sbus_writeb(tmp, esp->eregs + ESP_FDATA); - esp_cmd(esp, ESP_CMD_TI); - } - return do_intr_end; -} - -static int esp_do_cmddone(struct esp *esp) -{ - if (esp->erev == fashme) - dma_invalidate(esp); - else - esp_cmd(esp, ESP_CMD_NULL); - - if (esp->ireg & ESP_INTR_BSERV) { - esp_advance_phase(esp->current_SC, in_the_dark); - return esp_do_phase_determine(esp); - } - - ESPLOG(("esp%d: in do_cmddone() but didn't get BSERV interrupt.\n", - esp->esp_id)); - return do_reset_bus; -} - -static int esp_do_msgout(struct esp *esp) -{ - esp_cmd(esp, ESP_CMD_FLUSH); - switch (esp->msgout_len) { - case 1: - if (esp->erev == fashme) - hme_fifo_push(esp, &esp->cur_msgout[0], 1); - else - sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA); - - esp_cmd(esp, ESP_CMD_TI); - break; - - case 2: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 2); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 2, 0); - esp_setcount(esp->eregs, 2, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - case 4: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - esp->esp_command[2] = esp->cur_msgout[2]; - esp->esp_command[3] = esp->cur_msgout[3]; - esp->snip = 1; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 4); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 4, 0); - esp_setcount(esp->eregs, 4, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - case 5: - esp->esp_command[0] = esp->cur_msgout[0]; - esp->esp_command[1] = esp->cur_msgout[1]; - esp->esp_command[2] = esp->cur_msgout[2]; - esp->esp_command[3] = esp->cur_msgout[3]; - esp->esp_command[4] = esp->cur_msgout[4]; - esp->snip = 1; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 5); - esp_cmd(esp, ESP_CMD_TI); - } else { - dma_setup(esp, esp->esp_command_dvma, 5, 0); - esp_setcount(esp->eregs, 5, 0); - esp_cmd(esp, ESP_CMD_DMA | ESP_CMD_TI); - } - break; - - default: - /* whoops */ - ESPMISC(("bogus msgout sending NOP\n")); - esp->cur_msgout[0] = NOP; - - if (esp->erev == fashme) { - hme_fifo_push(esp, &esp->cur_msgout[0], 1); - } else { - sbus_writeb(esp->cur_msgout[0], esp->eregs + ESP_FDATA); - } - - esp->msgout_len = 1; - esp_cmd(esp, ESP_CMD_TI); - break; - }; - - esp_advance_phase(esp->current_SC, in_msgoutdone); - return do_intr_end; -} - -static int esp_do_msgoutdone(struct esp *esp) -{ - if (esp->msgout_len > 1) { - /* XXX HME/FAS ATN deassert workaround required, - * XXX no DMA flushing, only possible ESP_CMD_FLUSH - * XXX to kill the fifo. - */ - if (esp->erev != fashme) { - u32 tmp; - - while ((tmp = sbus_readl(esp->dregs + DMA_CSR)) & DMA_PEND_READ) - udelay(1); - tmp &= ~DMA_ENABLE; - sbus_writel(tmp, esp->dregs + DMA_CSR); - dma_invalidate(esp); - } else { - esp_cmd(esp, ESP_CMD_FLUSH); - } - } - if (!(esp->ireg & ESP_INTR_DC)) { - if (esp->erev != fashme) - esp_cmd(esp, ESP_CMD_NULL); - switch (esp->sreg & ESP_STAT_PMASK) { - case ESP_MOP: - /* whoops, parity error */ - ESPLOG(("esp%d: still in msgout, parity error assumed\n", - esp->esp_id)); - if (esp->msgout_len > 1) - esp_cmd(esp, ESP_CMD_SATN); - esp_advance_phase(esp->current_SC, in_msgout); - return do_work_bus; - - case ESP_DIP: - break; - - default: - /* Happy Meal fifo is touchy... */ - if ((esp->erev != fashme) && - !fcount(esp) && - !(((struct esp_device *)esp->current_SC->device->hostdata)->sync_max_offset)) - esp_cmd(esp, ESP_CMD_FLUSH); - break; - - }; - } else { - ESPLOG(("esp%d: disconnect, resetting bus\n", esp->esp_id)); - return do_reset_bus; - } - - /* If we sent out a synchronous negotiation message, update - * our state. - */ - if (esp->cur_msgout[2] == EXTENDED_MESSAGE && - esp->cur_msgout[4] == EXTENDED_SDTR) { - esp->snip = 1; /* anal retentiveness... */ - } - - esp->prevmsgout = esp->cur_msgout[0]; - esp->msgout_len = 0; - esp_advance_phase(esp->current_SC, in_the_dark); - return esp_do_phase_determine(esp); -} - -static int esp_bus_unexpected(struct esp *esp) -{ - ESPLOG(("esp%d: command in weird state %2x\n", - esp->esp_id, esp->current_SC->SCp.phase)); - return do_reset_bus; -} - -static espfunc_t bus_vector[] = { - esp_do_data_finale, - esp_do_data_finale, - esp_bus_unexpected, - esp_do_msgin, - esp_do_msgincont, - esp_do_msgindone, - esp_do_msgout, - esp_do_msgoutdone, - esp_do_cmdbegin, - esp_do_cmddone, - esp_do_status, - esp_do_freebus, - esp_do_phase_determine, - esp_bus_unexpected, - esp_bus_unexpected, - esp_bus_unexpected, -}; - -/* This is the second tier in our dual-level SCSI state machine. */ -static int esp_work_bus(struct esp *esp) -{ - struct scsi_cmnd *SCptr = esp->current_SC; - unsigned int phase; - - ESPBUS(("esp_work_bus: ")); - if (!SCptr) { - ESPBUS(("reconnect\n")); - return esp_do_reconnect(esp); - } - phase = SCptr->SCp.phase; - if ((phase & 0xf0) == in_phases_mask) - return bus_vector[(phase & 0x0f)](esp); - else if ((phase & 0xf0) == in_slct_mask) - return esp_select_complete(esp); - else - return esp_bus_unexpected(esp); -} - -static espfunc_t isvc_vector[] = { - NULL, - esp_do_phase_determine, - esp_do_resetbus, - esp_finish_reset, - esp_work_bus -}; - -/* Main interrupt handler for an esp adapter. */ -static void esp_handle(struct esp *esp) -{ - struct scsi_cmnd *SCptr; - int what_next = do_intr_end; - - SCptr = esp->current_SC; - - /* Check for errors. */ - esp->sreg = sbus_readb(esp->eregs + ESP_STATUS); - esp->sreg &= (~ESP_STAT_INTR); - if (esp->erev == fashme) { - esp->sreg2 = sbus_readb(esp->eregs + ESP_STATUS2); - esp->seqreg = (sbus_readb(esp->eregs + ESP_SSTEP) & ESP_STEP_VBITS); - } - - if (esp->sreg & (ESP_STAT_SPAM)) { - /* Gross error, could be due to one of: - * - * - top of fifo overwritten, could be because - * we tried to do a synchronous transfer with - * an offset greater than ESP fifo size - * - * - top of command register overwritten - * - * - DMA setup to go in one direction, SCSI - * bus points in the other, whoops - * - * - weird phase change during asynchronous - * data phase while we are initiator - */ - ESPLOG(("esp%d: Gross error sreg=%2x\n", esp->esp_id, esp->sreg)); - - /* If a command is live on the bus we cannot safely - * reset the bus, so we'll just let the pieces fall - * where they may. Here we are hoping that the - * target will be able to cleanly go away soon - * so we can safely reset things. - */ - if (!SCptr) { - ESPLOG(("esp%d: No current cmd during gross error, " - "resetting bus\n", esp->esp_id)); - what_next = do_reset_bus; - goto state_machine; - } - } - - if (sbus_readl(esp->dregs + DMA_CSR) & DMA_HNDL_ERROR) { - /* A DMA gate array error. Here we must - * be seeing one of two things. Either the - * virtual to physical address translation - * on the SBUS could not occur, else the - * translation it did get pointed to a bogus - * page. Ho hum... - */ - ESPLOG(("esp%d: DMA error %08x\n", esp->esp_id, - sbus_readl(esp->dregs + DMA_CSR))); - - /* DMA gate array itself must be reset to clear the - * error condition. - */ - esp_reset_dma(esp); - - what_next = do_reset_bus; - goto state_machine; - } - - esp->ireg = sbus_readb(esp->eregs + ESP_INTRPT); /* Unlatch intr reg */ - - if (esp->erev == fashme) { - /* This chip is really losing. */ - ESPHME(("HME[")); - - ESPHME(("sreg2=%02x,", esp->sreg2)); - /* Must latch fifo before reading the interrupt - * register else garbage ends up in the FIFO - * which confuses the driver utterly. - */ - if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || - (esp->sreg2 & ESP_STAT2_F1BYTE)) { - ESPHME(("fifo_workaround]")); - hme_fifo_read(esp); - } else { - ESPHME(("no_fifo_workaround]")); - } - } - - /* No current cmd is only valid at this point when there are - * commands off the bus or we are trying a reset. - */ - if (!SCptr && !esp->disconnected_SC && !(esp->ireg & ESP_INTR_SR)) { - /* Panic is safe, since current_SC is null. */ - ESPLOG(("esp%d: no command in esp_handle()\n", esp->esp_id)); - panic("esp_handle: current_SC == penguin within interrupt!"); - } - - if (esp->ireg & (ESP_INTR_IC)) { - /* Illegal command fed to ESP. Outside of obvious - * software bugs that could cause this, there is - * a condition with esp100 where we can confuse the - * ESP into an erroneous illegal command interrupt - * because it does not scrape the FIFO properly - * for reselection. See esp100_reconnect_hwbug() - * to see how we try very hard to avoid this. - */ - ESPLOG(("esp%d: invalid command\n", esp->esp_id)); - - esp_dump_state(esp); - - if (SCptr != NULL) { - /* Devices with very buggy firmware can drop BSY - * during a scatter list interrupt when using sync - * mode transfers. We continue the transfer as - * expected, the target drops the bus, the ESP - * gets confused, and we get a illegal command - * interrupt because the bus is in the disconnected - * state now and ESP_CMD_TI is only allowed when - * a nexus is alive on the bus. - */ - ESPLOG(("esp%d: Forcing async and disabling disconnect for " - "target %d\n", esp->esp_id, SCptr->device->id)); - SCptr->device->borken = 1; /* foo on you */ - } - - what_next = do_reset_bus; - } else if (!(esp->ireg & ~(ESP_INTR_FDONE | ESP_INTR_BSERV | ESP_INTR_DC))) { - if (SCptr) { - unsigned int phase = SCptr->SCp.phase; - - if (phase & in_phases_mask) { - what_next = esp_work_bus(esp); - } else if (phase & in_slct_mask) { - what_next = esp_select_complete(esp); - } else { - ESPLOG(("esp%d: interrupt for no good reason...\n", - esp->esp_id)); - what_next = do_intr_end; - } - } else { - ESPLOG(("esp%d: BSERV or FDONE or DC while SCptr==NULL\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } else if (esp->ireg & ESP_INTR_SR) { - ESPLOG(("esp%d: SCSI bus reset interrupt\n", esp->esp_id)); - what_next = do_reset_complete; - } else if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN)) { - ESPLOG(("esp%d: AIEEE we have been selected by another initiator!\n", - esp->esp_id)); - what_next = do_reset_bus; - } else if (esp->ireg & ESP_INTR_RSEL) { - if (SCptr == NULL) { - /* This is ok. */ - what_next = esp_do_reconnect(esp); - } else if (SCptr->SCp.phase & in_slct_mask) { - /* Only selection code knows how to clean - * up properly. - */ - ESPDISC(("Reselected during selection attempt\n")); - what_next = esp_select_complete(esp); - } else { - ESPLOG(("esp%d: Reselected while bus is busy\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } - - /* This is tier-one in our dual level SCSI state machine. */ -state_machine: - while (what_next != do_intr_end) { - if (what_next >= do_phase_determine && - what_next < do_intr_end) { - what_next = isvc_vector[what_next](esp); - } else { - /* state is completely lost ;-( */ - ESPLOG(("esp%d: interrupt engine loses state, resetting bus\n", - esp->esp_id)); - what_next = do_reset_bus; - } - } -} - -/* Service only the ESP described by dev_id. */ -static irqreturn_t esp_intr(int irq, void *dev_id) -{ - struct esp *esp = dev_id; - unsigned long flags; - - spin_lock_irqsave(esp->ehost->host_lock, flags); - if (ESP_IRQ_P(esp->dregs)) { - ESP_INTSOFF(esp->dregs); - - ESPIRQ(("I[%d:%d](", smp_processor_id(), esp->esp_id)); - esp_handle(esp); - ESPIRQ((")")); - - ESP_INTSON(esp->dregs); - } - spin_unlock_irqrestore(esp->ehost->host_lock, flags); - - return IRQ_HANDLED; -} - -static int esp_slave_alloc(struct scsi_device *SDptr) -{ - struct esp_device *esp_dev = - kmalloc(sizeof(struct esp_device), GFP_ATOMIC); - - if (!esp_dev) - return -ENOMEM; - memset(esp_dev, 0, sizeof(struct esp_device)); - SDptr->hostdata = esp_dev; - return 0; -} - -static void esp_slave_destroy(struct scsi_device *SDptr) -{ - struct esp *esp = (struct esp *) SDptr->host->hostdata; - - esp->targets_present &= ~(1 << SDptr->id); - kfree(SDptr->hostdata); - SDptr->hostdata = NULL; -} - -static struct scsi_host_template esp_template = { - .module = THIS_MODULE, - .name = "esp", - .info = esp_info, - .slave_alloc = esp_slave_alloc, - .slave_destroy = esp_slave_destroy, - .queuecommand = esp_queue, - .eh_abort_handler = esp_abort, - .eh_bus_reset_handler = esp_reset, - .can_queue = 7, - .this_id = 7, - .sg_tablesize = SG_ALL, - .cmd_per_lun = 1, - .use_clustering = ENABLE_CLUSTERING, - .proc_name = "esp", - .proc_info = esp_proc_info, -}; - -#ifndef CONFIG_SUN4 -static struct of_device_id esp_match[] = { - { - .name = "SUNW,esp", - .data = &esp_template, - }, - { - .name = "SUNW,fas", - .data = &esp_template, - }, - { - .name = "esp", - .data = &esp_template, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, esp_match); - -static struct of_platform_driver esp_sbus_driver = { - .name = "esp", - .match_table = esp_match, - .probe = esp_sbus_probe, - .remove = __devexit_p(esp_sbus_remove), -}; -#endif - -static int __init esp_init(void) -{ -#ifdef CONFIG_SUN4 - return esp_sun4_probe(&esp_template); -#else - return of_register_driver(&esp_sbus_driver, &sbus_bus_type); -#endif -} - -static void __exit esp_exit(void) -{ -#ifdef CONFIG_SUN4 - esp_sun4_remove(); -#else - of_unregister_driver(&esp_sbus_driver); -#endif -} - -MODULE_DESCRIPTION("ESP Sun SCSI driver"); -MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); - -module_init(esp_init); -module_exit(esp_exit); diff --git a/drivers/scsi/esp.h b/drivers/scsi/esp.h deleted file mode 100644 index a98cda9..0000000 --- a/drivers/scsi/esp.h +++ /dev/null @@ -1,406 +0,0 @@ -/* $Id: esp.h,v 1.29 2001/12/11 04:55:47 davem Exp $ - * esp.h: Defines and structures for the Sparc ESP (Enhanced SCSI - * Processor) driver under Linux. - * - * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -#ifndef _SPARC_ESP_H -#define _SPARC_ESP_H - -/* For dvma controller register definitions. */ -#include <asm/dma.h> - -/* The ESP SCSI controllers have their register sets in three - * "classes": - * - * 1) Registers which are both read and write. - * 2) Registers which are read only. - * 3) Registers which are write only. - * - * Yet, they all live within the same IO space. - */ - -/* All the ESP registers are one byte each and are accessed longwords - * apart with a big-endian ordering to the bytes. - */ - /* Access Description Offset */ -#define ESP_TCLOW 0x00UL /* rw Low bits of the transfer count 0x00 */ -#define ESP_TCMED 0x04UL /* rw Mid bits of the transfer count 0x04 */ -#define ESP_FDATA 0x08UL /* rw FIFO data bits 0x08 */ -#define ESP_CMD 0x0cUL /* rw SCSI command bits 0x0c */ -#define ESP_STATUS 0x10UL /* ro ESP status register 0x10 */ -#define ESP_BUSID ESP_STATUS /* wo Bus ID for select/reselect 0x10 */ -#define ESP_INTRPT 0x14UL /* ro Kind of interrupt 0x14 */ -#define ESP_TIMEO ESP_INTRPT /* wo Timeout value for select/resel 0x14 */ -#define ESP_SSTEP 0x18UL /* ro Sequence step register 0x18 */ -#define ESP_STP ESP_SSTEP /* wo Transfer period per sync 0x18 */ -#define ESP_FFLAGS 0x1cUL /* ro Bits of current FIFO info 0x1c */ -#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */ -#define ESP_CFG1 0x20UL /* rw First configuration register 0x20 */ -#define ESP_CFACT 0x24UL /* wo Clock conversion factor 0x24 */ -#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */ -#define ESP_CTEST 0x28UL /* wo Chip test register 0x28 */ -#define ESP_CFG2 0x2cUL /* rw Second configuration register 0x2c */ -#define ESP_CFG3 0x30UL /* rw Third configuration register 0x30 */ -#define ESP_TCHI 0x38UL /* rw High bits of transfer count 0x38 */ -#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ -#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ -#define ESP_FGRND 0x3cUL /* rw Data base for fifo 0x3c */ -#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */ -#define ESP_REG_SIZE 0x40UL - -/* Various revisions of the ESP board. */ -enum esp_rev { - esp100 = 0x00, /* NCR53C90 - very broken */ - esp100a = 0x01, /* NCR53C90A */ - esp236 = 0x02, - fas236 = 0x03, - fas100a = 0x04, - fast = 0x05, - fashme = 0x06, - espunknown = 0x07 -}; - -/* We allocate one of these for each scsi device and attach it to - * SDptr->hostdata for use in the driver - */ -struct esp_device { - unsigned char sync_min_period; - unsigned char sync_max_offset; - unsigned sync:1; - unsigned wide:1; - unsigned disconnect:1; -}; - -struct scsi_cmnd; - -/* We get one of these for each ESP probed. */ -struct esp { - void __iomem *eregs; /* ESP controller registers */ - void __iomem *dregs; /* DMA controller registers */ - struct sbus_dma *dma; /* DMA controller sw state */ - struct Scsi_Host *ehost; /* Backpointer to SCSI Host */ - struct sbus_dev *sdev; /* Pointer to SBus entry */ - - /* ESP Configuration Registers */ - u8 config1; /* Copy of the 1st config register */ - u8 config2; /* Copy of the 2nd config register */ - u8 config3[16]; /* Copy of the 3rd config register */ - - /* The current command we are sending to the ESP chip. This esp_command - * ptr needs to be mapped in DVMA area so we can send commands and read - * from the ESP fifo without burning precious CPU cycles. Programmed I/O - * sucks when we have the DVMA to do it for us. The ESP is stupid and will - * only send out 6, 10, and 12 byte SCSI commands, others we need to send - * one byte at a time. esp_slowcmd being set says that we are doing one - * of the command types ESP doesn't understand, esp_scmdp keeps track of - * which byte we are sending, esp_scmdleft says how many bytes to go. - */ - volatile u8 *esp_command; /* Location of command (CPU view) */ - __u32 esp_command_dvma;/* Location of command (DVMA view) */ - unsigned char esp_clen; /* Length of this command */ - unsigned char esp_slowcmd; - unsigned char *esp_scmdp; - unsigned char esp_scmdleft; - - /* The following are used to determine the cause of an IRQ. Upon every - * IRQ entry we synchronize these with the hardware registers. - */ - u8 ireg; /* Copy of ESP interrupt register */ - u8 sreg; /* Copy of ESP status register */ - u8 seqreg; /* Copy of ESP sequence step register */ - u8 sreg2; /* Copy of HME status2 register */ - - /* To save register writes to the ESP, which can be expensive, we - * keep track of the previous value that various registers had for - * the last target we connected to. If they are the same for the - * current target, we skip the register writes as they are not needed. - */ - u8 prev_soff, prev_stp; - u8 prev_cfg3, __cache_pad; - - /* We also keep a cache of the previous FAS/HME DMA CSR register value. */ - u32 prev_hme_dmacsr; - - /* The HME is the biggest piece of shit I have ever seen. */ - u8 hme_fifo_workaround_buffer[16 * 2]; - u8 hme_fifo_workaround_count; - - /* For each target we keep track of save/restore data - * pointer information. This needs to be updated majorly - * when we add support for tagged queueing. -DaveM - */ - struct esp_pointers { - char *saved_ptr; - struct scatterlist *saved_buffer; - int saved_this_residual; - int saved_buffers_residual; - } data_pointers[16] /*XXX [MAX_TAGS_PER_TARGET]*/; - - /* Clock periods, frequencies, synchronization, etc. */ - unsigned int cfreq; /* Clock frequency in HZ */ - unsigned int cfact; /* Clock conversion factor */ - unsigned int raw_cfact; /* Raw copy from probing */ - unsigned int ccycle; /* One ESP clock cycle */ - unsigned int ctick; /* One ESP clock time */ - unsigned int radelay; /* FAST chip req/ack delay */ - unsigned int neg_defp; /* Default negotiation period */ - unsigned int sync_defp; /* Default sync transfer period */ - unsigned int max_period; /* longest our period can be */ - unsigned int min_period; /* shortest period we can withstand */ - - struct esp *next; /* Next ESP we probed or NULL */ - char prom_name[64]; /* Name of ESP device from prom */ - int prom_node; /* Prom node where ESP found */ - int esp_id; /* Unique per-ESP ID number */ - - /* For slow to medium speed input clock rates we shoot for 5mb/s, - * but for high input clock rates we try to do 10mb/s although I - * don't think a transfer can even run that fast with an ESP even - * with DMA2 scatter gather pipelining. - */ -#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */ -#define SYNC_DEFP_FAST 0x19 /* 10mb/s */ - - unsigned int snip; /* Sync. negotiation in progress */ - unsigned int wnip; /* WIDE negotiation in progress */ - unsigned int targets_present;/* targets spoken to before */ - - int current_transfer_size; /* Set at beginning of data dma */ - - u8 espcmdlog[32]; /* Log of current esp cmds sent. */ - u8 espcmdent; /* Current entry in esp cmd log. */ - - /* Misc. info about this ESP */ - enum esp_rev erev; /* ESP revision */ - int irq; /* SBus IRQ for this ESP */ - int scsi_id; /* Who am I as initiator? */ - int scsi_id_mask; /* Bitmask of 'me'. */ - int diff; /* Differential SCSI bus? */ - int bursts; /* Burst sizes our DVMA supports */ - - /* Our command queues, only one cmd lives in the current_SC queue. */ - struct scsi_cmnd *issue_SC; /* Commands to be issued */ - struct scsi_cmnd *current_SC; /* Who is currently working the bus */ - struct scsi_cmnd *disconnected_SC;/* Commands disconnected from the bus */ - - /* Message goo */ - u8 cur_msgout[16]; - u8 cur_msgin[16]; - u8 prevmsgout, prevmsgin; - u8 msgout_len, msgin_len; - u8 msgout_ctr, msgin_ctr; - - /* States that we cannot keep in the per cmd structure because they - * cannot be assosciated with any specific command. - */ - u8 resetting_bus; - wait_queue_head_t reset_queue; -}; - -/* Bitfield meanings for the above registers. */ - -/* ESP config reg 1, read-write, found on all ESP chips */ -#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ -#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ -#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ -#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ -#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ -#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ - -/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ -#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ -#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ -#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ -#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tmode only) */ -#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ -#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ -#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ -#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ -#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,esp216) */ -#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (esp236) */ -#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ -#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ -#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ - -/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ -#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ -#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ -#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ -#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ -#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ -#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ -#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ -#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ -#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ -#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ -#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ -#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ -#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ -#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ -#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ -#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ - -/* ESP command register read-write */ -/* Group 1 commands: These may be sent at any point in time to the ESP - * chip. None of them can generate interrupts 'cept - * the "SCSI bus reset" command if you have not disabled - * SCSI reset interrupts in the config1 ESP register. - */ -#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ -#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ -#define ESP_CMD_RC 0x02 /* Chip reset */ -#define ESP_CMD_RS 0x03 /* SCSI bus reset */ - -/* Group 2 commands: ESP must be an initiator and connected to a target - * for these commands to work. - */ -#define ESP_CMD_TI 0x10 /* Transfer Information */ -#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ -#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ -#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ -#define ESP_CMD_SATN 0x1a /* Set ATN */ -#define ESP_CMD_RATN 0x1b /* De-assert ATN */ - -/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected - * to a target as the initiator for these commands to work. - */ -#define ESP_CMD_SMSG 0x20 /* Send message */ -#define ESP_CMD_SSTAT 0x21 /* Send status */ -#define ESP_CMD_SDATA 0x22 /* Send data */ -#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ -#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ -#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ -#define ESP_CMD_DCNCT 0x27 /* Disconnect */ -#define ESP_CMD_RMSG 0x28 /* Receive Message */ -#define ESP_CMD_RCMD 0x29 /* Receive Command */ -#define ESP_CMD_RDATA 0x2a /* Receive Data */ -#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ - -/* Group 4 commands: The ESP must be in the disconnected state and must - * not be connected to any targets as initiator for - * these commands to work. - */ -#define ESP_CMD_RSEL 0x40 /* Reselect */ -#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ -#define ESP_CMD_SELA 0x42 /* Select w/ATN */ -#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ -#define ESP_CMD_ESEL 0x44 /* Enable selection */ -#define ESP_CMD_DSEL 0x45 /* Disable selections */ -#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ -#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ - -/* This bit enables the ESP's DMA on the SBus */ -#define ESP_CMD_DMA 0x80 /* Do DMA? */ - - -/* ESP status register read-only */ -#define ESP_STAT_PIO 0x01 /* IO phase bit */ -#define ESP_STAT_PCD 0x02 /* CD phase bit */ -#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ -#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ -#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ -#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ -#define ESP_STAT_PERR 0x20 /* Parity error */ -#define ESP_STAT_SPAM 0x40 /* Real bad error */ -/* This indicates the 'interrupt pending' condition on esp236, it is a reserved - * bit on other revs of the ESP. - */ -#define ESP_STAT_INTR 0x80 /* Interrupt */ - -/* HME only: status 2 register */ -#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ -#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ -#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ -#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ -#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ -#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ -#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ -#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ - -/* The status register can be masked with ESP_STAT_PMASK and compared - * with the following values to determine the current phase the ESP - * (at least thinks it) is in. For our purposes we also add our own - * software 'done' bit for our phase management engine. - */ -#define ESP_DOP (0) /* Data Out */ -#define ESP_DIP (ESP_STAT_PIO) /* Data In */ -#define ESP_CMDP (ESP_STAT_PCD) /* Command */ -#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ -#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ -#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ - -/* ESP interrupt register read-only */ -#define ESP_INTR_S 0x01 /* Select w/o ATN */ -#define ESP_INTR_SATN 0x02 /* Select w/ATN */ -#define ESP_INTR_RSEL 0x04 /* Reselected */ -#define ESP_INTR_FDONE 0x08 /* Function done */ -#define ESP_INTR_BSERV 0x10 /* Bus service */ -#define ESP_INTR_DC 0x20 /* Disconnect */ -#define ESP_INTR_IC 0x40 /* Illegal command given */ -#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ - -/* Interrupt status macros */ -#define ESP_SRESET_IRQ(esp) ((esp)->intreg & (ESP_INTR_SR)) -#define ESP_ILLCMD_IRQ(esp) ((esp)->intreg & (ESP_INTR_IC)) -#define ESP_SELECT_WITH_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_SATN)) -#define ESP_SELECT_WITHOUT_ATN_IRQ(esp) ((esp)->intreg & (ESP_INTR_S)) -#define ESP_SELECTION_IRQ(esp) ((ESP_SELECT_WITH_ATN_IRQ(esp)) || \ - (ESP_SELECT_WITHOUT_ATN_IRQ(esp))) -#define ESP_RESELECTION_IRQ(esp) ((esp)->intreg & (ESP_INTR_RSEL)) - -/* ESP sequence step register read-only */ -#define ESP_STEP_VBITS 0x07 /* Valid bits */ -#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ -#define ESP_STEP_SID 0x01 /* One msg byte sent */ -#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ -#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd - * bytes to be lost - */ -#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ - -/* Ho hum, some ESP's set the step register to this as well... */ -#define ESP_STEP_FINI5 0x05 -#define ESP_STEP_FINI6 0x06 -#define ESP_STEP_FINI7 0x07 - -/* ESP chip-test register read-write */ -#define ESP_TEST_TARG 0x01 /* Target test mode */ -#define ESP_TEST_INI 0x02 /* Initiator test mode */ -#define ESP_TEST_TS 0x04 /* Tristate test mode */ - -/* ESP unique ID register read-only, found on fas236+fas100a only */ -#define ESP_UID_F100A 0x00 /* ESP FAS100A */ -#define ESP_UID_F236 0x02 /* ESP FAS236 */ -#define ESP_UID_REV 0x07 /* ESP revision */ -#define ESP_UID_FAM 0xf8 /* ESP family */ - -/* ESP fifo flags register read-only */ -/* Note that the following implies a 16 byte FIFO on the ESP. */ -#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ -#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ -#define ESP_FF_SSTEP 0xe0 /* Sequence step */ - -/* ESP clock conversion factor register write-only */ -#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ -#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ -#define ESP_CCF_F2 0x02 /* 10MHz */ -#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ -#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ -#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ -#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ -#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ - -/* HME only... */ -#define ESP_BUSID_RESELID 0x10 -#define ESP_BUSID_CTR32BIT 0x40 - -#define ESP_BUS_TIMEOUT 275 /* In milli-seconds */ -#define ESP_TIMEO_CONST 8192 -#define ESP_NEG_DEFP(mhz, cfact) \ - ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) -#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) -#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) - -#endif /* !(_SPARC_ESP_H) */ diff --git a/drivers/scsi/esp_scsi.c b/drivers/scsi/esp_scsi.c new file mode 100644 index 0000000..99ce033 --- /dev/null +++ b/drivers/scsi/esp_scsi.c @@ -0,0 +1,2711 @@ +/* esp_scsi.c: ESP SCSI driver. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/completion.h> +#include <linux/kallsyms.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/irqreturn.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_cmnd.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_tcq.h> +#include <scsi/scsi_dbg.h> +#include <scsi/scsi_transport_spi.h> + +#include "esp_scsi.h" + +#define DRV_MODULE_NAME "esp" +#define PFX DRV_MODULE_NAME ": " +#define DRV_VERSION "2.000" +#define DRV_MODULE_RELDATE "April 19, 2007" + +/* SCSI bus reset settle time in seconds. */ +static int esp_bus_reset_settle = 3; + +static u32 esp_debug; +#define ESP_DEBUG_INTR 0x00000001 +#define ESP_DEBUG_SCSICMD 0x00000002 +#define ESP_DEBUG_RESET 0x00000004 +#define ESP_DEBUG_MSGIN 0x00000008 +#define ESP_DEBUG_MSGOUT 0x00000010 +#define ESP_DEBUG_CMDDONE 0x00000020 +#define ESP_DEBUG_DISCONNECT 0x00000040 +#define ESP_DEBUG_DATASTART 0x00000080 +#define ESP_DEBUG_DATADONE 0x00000100 +#define ESP_DEBUG_RECONNECT 0x00000200 +#define ESP_DEBUG_AUTOSENSE 0x00000400 + +#define esp_log_intr(f, a...) \ +do { if (esp_debug & ESP_DEBUG_INTR) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_reset(f, a...) \ +do { if (esp_debug & ESP_DEBUG_RESET) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_msgin(f, a...) \ +do { if (esp_debug & ESP_DEBUG_MSGIN) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_msgout(f, a...) \ +do { if (esp_debug & ESP_DEBUG_MSGOUT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_cmddone(f, a...) \ +do { if (esp_debug & ESP_DEBUG_CMDDONE) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_disconnect(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DISCONNECT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_datastart(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DATASTART) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_datadone(f, a...) \ +do { if (esp_debug & ESP_DEBUG_DATADONE) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_reconnect(f, a...) \ +do { if (esp_debug & ESP_DEBUG_RECONNECT) \ + printk(f, ## a); \ +} while (0) + +#define esp_log_autosense(f, a...) \ +do { if (esp_debug & ESP_DEBUG_AUTOSENSE) \ + printk(f, ## a); \ +} while (0) + +#define esp_read8(REG) esp->ops->esp_read8(esp, REG) +#define esp_write8(VAL,REG) esp->ops->esp_write8(esp, VAL, REG) + +static void esp_log_fill_regs(struct esp *esp, + struct esp_event_ent *p) +{ + p->sreg = esp->sreg; + p->seqreg = esp->seqreg; + p->sreg2 = esp->sreg2; + p->ireg = esp->ireg; + p->select_state = esp->select_state; + p->event = esp->event; +} + +void scsi_esp_cmd(struct esp *esp, u8 val) +{ + struct esp_event_ent *p; + int idx = esp->esp_event_cur; + + p = &esp->esp_event_log[idx]; + p->type = ESP_EVENT_TYPE_CMD; + p->val = val; + esp_log_fill_regs(esp, p); + + esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + + esp_write8(val, ESP_CMD); +} +EXPORT_SYMBOL(scsi_esp_cmd); + +static void esp_event(struct esp *esp, u8 val) +{ + struct esp_event_ent *p; + int idx = esp->esp_event_cur; + + p = &esp->esp_event_log[idx]; + p->type = ESP_EVENT_TYPE_EVENT; + p->val = val; + esp_log_fill_regs(esp, p); + + esp->esp_event_cur = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + + esp->event = val; +} + +static void esp_dump_cmd_log(struct esp *esp) +{ + int idx = esp->esp_event_cur; + int stop = idx; + + printk(KERN_INFO PFX "esp%d: Dumping command log\n", + esp->host->unique_id); + do { + struct esp_event_ent *p = &esp->esp_event_log[idx]; + + printk(KERN_INFO PFX "esp%d: ent[%d] %s ", + esp->host->unique_id, idx, + p->type == ESP_EVENT_TYPE_CMD ? "CMD" : "EVENT"); + + printk("val[%02x] sreg[%02x] seqreg[%02x] " + "sreg2[%02x] ireg[%02x] ss[%02x] event[%02x]\n", + p->val, p->sreg, p->seqreg, + p->sreg2, p->ireg, p->select_state, p->event); + + idx = (idx + 1) & (ESP_EVENT_LOG_SZ - 1); + } while (idx != stop); +} + +static void esp_flush_fifo(struct esp *esp) +{ + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP236) { + int lim = 1000; + + while (esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: ESP_FF_BYTES " + "will not clear!\n", + esp->host->unique_id); + break; + } + udelay(1); + } + } +} + +static void hme_read_fifo(struct esp *esp) +{ + int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + int idx = 0; + + while (fcnt--) { + esp->fifo[idx++] = esp_read8(ESP_FDATA); + esp->fifo[idx++] = esp_read8(ESP_FDATA); + } + if (esp->sreg2 & ESP_STAT2_F1BYTE) { + esp_write8(0, ESP_FDATA); + esp->fifo[idx++] = esp_read8(ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } + esp->fifo_cnt = idx; +} + +static void esp_set_all_config3(struct esp *esp, u8 val) +{ + int i; + + for (i = 0; i < ESP_MAX_TARGET; i++) + esp->target[i].esp_config3 = val; +} + +/* Reset the ESP chip, _not_ the SCSI bus. */ +static void esp_reset_esp(struct esp *esp) +{ + u8 family_code, version; + + /* Now reset the ESP chip */ + scsi_esp_cmd(esp, ESP_CMD_RC); + scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); + scsi_esp_cmd(esp, ESP_CMD_NULL | ESP_CMD_DMA); + + /* Reload the configuration registers */ + esp_write8(esp->cfact, ESP_CFACT); + + esp->prev_stp = 0; + esp_write8(esp->prev_stp, ESP_STP); + + esp->prev_soff = 0; + esp_write8(esp->prev_soff, ESP_SOFF); + + esp_write8(esp->neg_defp, ESP_TIMEO); + + /* This is the only point at which it is reliable to read + * the ID-code for a fast ESP chip variants. + */ + esp->max_period = ((35 * esp->ccycle) / 1000); + if (esp->rev == FAST) { + version = esp_read8(ESP_UID); + family_code = (version & 0xf8) >> 3; + if (family_code == 0x02) + esp->rev = FAS236; + else if (family_code == 0x0a) + esp->rev = FASHME; /* Version is usually '5'. */ + else + esp->rev = FAS100A; + esp->min_period = ((4 * esp->ccycle) / 1000); + } else { + esp->min_period = ((5 * esp->ccycle) / 1000); + } + esp->max_period = (esp->max_period + 3)>>2; + esp->min_period = (esp->min_period + 3)>>2; + + esp_write8(esp->config1, ESP_CFG1); + switch (esp->rev) { + case ESP100: + /* nothing to do */ + break; + + case ESP100A: + esp_write8(esp->config2, ESP_CFG2); + break; + + case ESP236: + /* Slow 236 */ + esp_write8(esp->config2, ESP_CFG2); + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + break; + + case FASHME: + esp->config2 |= (ESP_CONFIG2_HME32 | ESP_CONFIG2_HMEFENAB); + /* fallthrough... */ + + case FAS236: + /* Fast 236 or HME */ + esp_write8(esp->config2, ESP_CFG2); + if (esp->rev == FASHME) { + u8 cfg3 = esp->target[0].esp_config3; + + cfg3 |= ESP_CONFIG3_FCLOCK | ESP_CONFIG3_OBPUSH; + if (esp->scsi_id >= 8) + cfg3 |= ESP_CONFIG3_IDBIT3; + esp_set_all_config3(esp, cfg3); + } else { + u32 cfg3 = esp->target[0].esp_config3; + + cfg3 |= ESP_CONFIG3_FCLK; + esp_set_all_config3(esp, cfg3); + } + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + if (esp->rev == FASHME) { + esp->radelay = 80; + } else { + if (esp->flags & ESP_FLAG_DIFFERENTIAL) + esp->radelay = 0; + else + esp->radelay = 96; + } + break; + + case FAS100A: + /* Fast 100a */ + esp_write8(esp->config2, ESP_CFG2); + esp_set_all_config3(esp, + (esp->target[0].esp_config3 | + ESP_CONFIG3_FCLOCK)); + esp->prev_cfg3 = esp->target[0].esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + esp->radelay = 32; + break; + + default: + break; + } + + /* Eat any bitrot in the chip */ + esp_read8(ESP_INTRPT); + udelay(100); +} + +static void esp_map_dma(struct esp *esp, struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + struct scatterlist *sg = cmd->request_buffer; + int dir = cmd->sc_data_direction; + int total, i; + + if (dir == DMA_NONE) + return; + + BUG_ON(cmd->use_sg == 0); + + spriv->u.num_sg = esp->ops->map_sg(esp, sg, + cmd->use_sg, dir); + spriv->cur_residue = sg_dma_len(sg); + spriv->cur_sg = sg; + + total = 0; + for (i = 0; i < spriv->u.num_sg; i++) + total += sg_dma_len(&sg[i]); + spriv->tot_residue = total; +} + +static dma_addr_t esp_cur_dma_addr(struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + return ent->sense_dma + + (ent->sense_ptr - cmd->sense_buffer); + } + + return sg_dma_address(p->cur_sg) + + (sg_dma_len(p->cur_sg) - + p->cur_residue); +} + +static unsigned int esp_cur_dma_len(struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + return SCSI_SENSE_BUFFERSIZE - + (ent->sense_ptr - cmd->sense_buffer); + } + return p->cur_residue; +} + +static void esp_advance_dma(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd, unsigned int len) +{ + struct esp_cmd_priv *p = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->sense_ptr += len; + return; + } + + p->cur_residue -= len; + p->tot_residue -= len; + if (p->cur_residue < 0 || p->tot_residue < 0) { + printk(KERN_ERR PFX "esp%d: Data transfer overflow.\n", + esp->host->unique_id); + printk(KERN_ERR PFX "esp%d: cur_residue[%d] tot_residue[%d] " + "len[%u]\n", + esp->host->unique_id, + p->cur_residue, p->tot_residue, len); + p->cur_residue = 0; + p->tot_residue = 0; + } + if (!p->cur_residue && p->tot_residue) { + p->cur_sg++; + p->cur_residue = sg_dma_len(p->cur_sg); + } +} + +static void esp_unmap_dma(struct esp *esp, struct scsi_cmnd *cmd) +{ + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + int dir = cmd->sc_data_direction; + + if (dir == DMA_NONE) + return; + + esp->ops->unmap_sg(esp, cmd->request_buffer, + spriv->u.num_sg, dir); +} + +static void esp_save_pointers(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->saved_sense_ptr = ent->sense_ptr; + return; + } + ent->saved_cur_residue = spriv->cur_residue; + ent->saved_cur_sg = spriv->cur_sg; + ent->saved_tot_residue = spriv->tot_residue; +} + +static void esp_restore_pointers(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct esp_cmd_priv *spriv = ESP_CMD_PRIV(cmd); + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->sense_ptr = ent->saved_sense_ptr; + return; + } + spriv->cur_residue = ent->saved_cur_residue; + spriv->cur_sg = ent->saved_cur_sg; + spriv->tot_residue = ent->saved_tot_residue; +} + +static void esp_check_command_len(struct esp *esp, struct scsi_cmnd *cmd) +{ + if (cmd->cmd_len == 6 || + cmd->cmd_len == 10 || + cmd->cmd_len == 12) { + esp->flags &= ~ESP_FLAG_DOING_SLOWCMD; + } else { + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } +} + +static void esp_write_tgt_config3(struct esp *esp, int tgt) +{ + if (esp->rev > ESP100A) { + u8 val = esp->target[tgt].esp_config3; + + if (val != esp->prev_cfg3) { + esp->prev_cfg3 = val; + esp_write8(val, ESP_CFG3); + } + } +} + +static void esp_write_tgt_sync(struct esp *esp, int tgt) +{ + u8 off = esp->target[tgt].esp_offset; + u8 per = esp->target[tgt].esp_period; + + if (off != esp->prev_soff) { + esp->prev_soff = off; + esp_write8(off, ESP_SOFF); + } + if (per != esp->prev_stp) { + esp->prev_stp = per; + esp_write8(per, ESP_STP); + } +} + +static u32 esp_dma_length_limit(struct esp *esp, u32 dma_addr, u32 dma_len) +{ + if (esp->rev == FASHME) { + /* Arbitrary segment boundaries, 24-bit counts. */ + if (dma_len > (1U << 24)) + dma_len = (1U << 24); + } else { + u32 base, end; + + /* ESP chip limits other variants by 16-bits of transfer + * count. Actually on FAS100A and FAS236 we could get + * 24-bits of transfer count by enabling ESP_CONFIG2_FENAB + * in the ESP_CFG2 register but that causes other unwanted + * changes so we don't use it currently. + */ + if (dma_len > (1U << 16)) + dma_len = (1U << 16); + + /* All of the DMA variants hooked up to these chips + * cannot handle crossing a 24-bit address boundary. + */ + base = dma_addr & ((1U << 24) - 1U); + end = base + dma_len; + if (end > (1U << 24)) + end = (1U <<24); + dma_len = end - base; + } + return dma_len; +} + +static int esp_need_to_nego_wide(struct esp_target_data *tp) +{ + struct scsi_target *target = tp->starget; + + return spi_width(target) != tp->nego_goal_width; +} + +static int esp_need_to_nego_sync(struct esp_target_data *tp) +{ + struct scsi_target *target = tp->starget; + + /* When offset is zero, period is "don't care". */ + if (!spi_offset(target) && !tp->nego_goal_offset) + return 0; + + if (spi_offset(target) == tp->nego_goal_offset && + spi_period(target) == tp->nego_goal_period) + return 0; + + return 1; +} + +static int esp_alloc_lun_tag(struct esp_cmd_entry *ent, + struct esp_lun_data *lp) +{ + if (!ent->tag[0]) { + /* Non-tagged, slot already taken? */ + if (lp->non_tagged_cmd) + return -EBUSY; + + if (lp->hold) { + /* We are being held by active tagged + * commands. + */ + if (lp->num_tagged) + return -EBUSY; + + /* Tagged commands completed, we can unplug + * the queue and run this untagged command. + */ + lp->hold = 0; + } else if (lp->num_tagged) { + /* Plug the queue until num_tagged decreases + * to zero in esp_free_lun_tag. + */ + lp->hold = 1; + return -EBUSY; + } + + lp->non_tagged_cmd = ent; + return 0; + } else { + /* Tagged command, see if blocked by a + * non-tagged one. + */ + if (lp->non_tagged_cmd || lp->hold) + return -EBUSY; + } + + BUG_ON(lp->tagged_cmds[ent->tag[1]]); + + lp->tagged_cmds[ent->tag[1]] = ent; + lp->num_tagged++; + + return 0; +} + +static void esp_free_lun_tag(struct esp_cmd_entry *ent, + struct esp_lun_data *lp) +{ + if (ent->tag[0]) { + BUG_ON(lp->tagged_cmds[ent->tag[1]] != ent); + lp->tagged_cmds[ent->tag[1]] = NULL; + lp->num_tagged--; + } else { + BUG_ON(lp->non_tagged_cmd != ent); + lp->non_tagged_cmd = NULL; + } +} + +/* When a contingent allegiance conditon is created, we force feed a + * REQUEST_SENSE command to the device to fetch the sense data. I + * tried many other schemes, relying on the scsi error handling layer + * to send out the REQUEST_SENSE automatically, but this was difficult + * to get right especially in the presence of applications like smartd + * which use SG_IO to send out their own REQUEST_SENSE commands. + */ +static void esp_autosense(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + struct scsi_device *dev = cmd->device; + int tgt, lun; + u8 *p, val; + + tgt = dev->id; + lun = dev->lun; + + + if (!ent->sense_ptr) { + esp_log_autosense("esp%d: Doing auto-sense for " + "tgt[%d] lun[%d]\n", + esp->host->unique_id, tgt, lun); + + ent->sense_ptr = cmd->sense_buffer; + ent->sense_dma = esp->ops->map_single(esp, + ent->sense_ptr, + SCSI_SENSE_BUFFERSIZE, + DMA_FROM_DEVICE); + } + ent->saved_sense_ptr = ent->sense_ptr; + + esp->active_cmd = ent; + + p = esp->command_block; + esp->msg_out_len = 0; + + *p++ = IDENTIFY(0, lun); + *p++ = REQUEST_SENSE; + *p++ = ((dev->scsi_level <= SCSI_2) ? + (lun << 5) : 0); + *p++ = 0; + *p++ = 0; + *p++ = SCSI_SENSE_BUFFERSIZE; + *p++ = 0; + + esp->select_state = ESP_SELECT_BASIC; + + val = tgt; + if (esp->rev == FASHME) + val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT; + esp_write8(val, ESP_BUSID); + + esp_write_tgt_sync(esp, tgt); + esp_write_tgt_config3(esp, tgt); + + val = (p - esp->command_block); + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + val, 16, 0, ESP_CMD_DMA | ESP_CMD_SELA); +} + +static struct esp_cmd_entry *find_and_prep_issuable_command(struct esp *esp) +{ + struct esp_cmd_entry *ent; + + list_for_each_entry(ent, &esp->queued_cmds, list) { + struct scsi_cmnd *cmd = ent->cmd; + struct scsi_device *dev = cmd->device; + struct esp_lun_data *lp = dev->hostdata; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + ent->tag[0] = 0; + ent->tag[1] = 0; + return ent; + } + + if (!scsi_populate_tag_msg(cmd, &ent->tag[0])) { + ent->tag[0] = 0; + ent->tag[1] = 0; + } + + if (esp_alloc_lun_tag(ent, lp) < 0) + continue; + + return ent; + } + + return NULL; +} + +static void esp_maybe_execute_command(struct esp *esp) +{ + struct esp_target_data *tp; + struct esp_lun_data *lp; + struct scsi_device *dev; + struct scsi_cmnd *cmd; + struct esp_cmd_entry *ent; + int tgt, lun, i; + u32 val, start_cmd; + u8 *p; + + if (esp->active_cmd || + (esp->flags & ESP_FLAG_RESETTING)) + return; + + ent = find_and_prep_issuable_command(esp); + if (!ent) + return; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp_autosense(esp, ent); + return; + } + + cmd = ent->cmd; + dev = cmd->device; + tgt = dev->id; + lun = dev->lun; + tp = &esp->target[tgt]; + lp = dev->hostdata; + + list_del(&ent->list); + list_add(&ent->list, &esp->active_cmds); + + esp->active_cmd = ent; + + esp_map_dma(esp, cmd); + esp_save_pointers(esp, ent); + + esp_check_command_len(esp, cmd); + + p = esp->command_block; + + esp->msg_out_len = 0; + if (tp->flags & ESP_TGT_CHECK_NEGO) { + /* Need to negotiate. If the target is broken + * go for synchronous transfers and non-wide. + */ + if (tp->flags & ESP_TGT_BROKEN) { + tp->flags &= ~ESP_TGT_DISCONNECT; + tp->nego_goal_period = 0; + tp->nego_goal_offset = 0; + tp->nego_goal_width = 0; + tp->nego_goal_tags = 0; + } + + /* If the settings are not changing, skip this. */ + if (spi_width(tp->starget) == tp->nego_goal_width && + spi_period(tp->starget) == tp->nego_goal_period && + spi_offset(tp->starget) == tp->nego_goal_offset) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + goto build_identify; + } + + if (esp->rev == FASHME && esp_need_to_nego_wide(tp)) { + esp->msg_out_len = + spi_populate_width_msg(&esp->msg_out[0], + (tp->nego_goal_width ? + 1 : 0)); + tp->flags |= ESP_TGT_NEGO_WIDE; + } else if (esp_need_to_nego_sync(tp)) { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + } else { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + } + + /* Process it like a slow command. */ + if (tp->flags & (ESP_TGT_NEGO_WIDE | ESP_TGT_NEGO_SYNC)) + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } + +build_identify: + /* If we don't have a lun-data struct yet, we're probing + * so do not disconnect. Also, do not disconnect unless + * we have a tag on this command. + */ + if (lp && (tp->flags & ESP_TGT_DISCONNECT) && ent->tag[0]) + *p++ = IDENTIFY(1, lun); + else + *p++ = IDENTIFY(0, lun); + + if (ent->tag[0] && esp->rev == ESP100) { + /* ESP100 lacks select w/atn3 command, use select + * and stop instead. + */ + esp->flags |= ESP_FLAG_DOING_SLOWCMD; + } + + if (!(esp->flags & ESP_FLAG_DOING_SLOWCMD)) { + start_cmd = ESP_CMD_DMA | ESP_CMD_SELA; + if (ent->tag[0]) { + *p++ = ent->tag[0]; + *p++ = ent->tag[1]; + + start_cmd = ESP_CMD_DMA | ESP_CMD_SA3; + } + + for (i = 0; i < cmd->cmd_len; i++) + *p++ = cmd->cmnd[i]; + + esp->select_state = ESP_SELECT_BASIC; + } else { + esp->cmd_bytes_left = cmd->cmd_len; + esp->cmd_bytes_ptr = &cmd->cmnd[0]; + + if (ent->tag[0]) { + for (i = esp->msg_out_len - 1; + i >= 0; i--) + esp->msg_out[i + 2] = esp->msg_out[i]; + esp->msg_out[0] = ent->tag[0]; + esp->msg_out[1] = ent->tag[1]; + esp->msg_out_len += 2; + } + + start_cmd = ESP_CMD_DMA | ESP_CMD_SELAS; + esp->select_state = ESP_SELECT_MSGOUT; + } + val = tgt; + if (esp->rev == FASHME) + val |= ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT; + esp_write8(val, ESP_BUSID); + + esp_write_tgt_sync(esp, tgt); + esp_write_tgt_config3(esp, tgt); + + val = (p - esp->command_block); + + if (esp_debug & ESP_DEBUG_SCSICMD) { + printk("ESP: tgt[%d] lun[%d] scsi_cmd [ ", tgt, lun); + for (i = 0; i < cmd->cmd_len; i++) + printk("%02x ", cmd->cmnd[i]); + printk("]\n"); + } + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + val, 16, 0, start_cmd); +} + +static struct esp_cmd_entry *esp_get_ent(struct esp *esp) +{ + struct list_head *head = &esp->esp_cmd_pool; + struct esp_cmd_entry *ret; + + if (list_empty(head)) { + ret = kzalloc(sizeof(struct esp_cmd_entry), GFP_ATOMIC); + } else { + ret = list_entry(head->next, struct esp_cmd_entry, list); + list_del(&ret->list); + memset(ret, 0, sizeof(*ret)); + } + return ret; +} + +static void esp_put_ent(struct esp *esp, struct esp_cmd_entry *ent) +{ + list_add(&ent->list, &esp->esp_cmd_pool); +} + +static void esp_cmd_is_done(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd, unsigned int result) +{ + struct scsi_device *dev = cmd->device; + int tgt = dev->id; + int lun = dev->lun; + + esp->active_cmd = NULL; + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, dev->hostdata); + cmd->result = result; + + if (ent->eh_done) { + complete(ent->eh_done); + ent->eh_done = NULL; + } + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + + /* Restore the message/status bytes to what we actually + * saw originally. Also, report that we are providing + * the sense data. + */ + cmd->result = ((DRIVER_SENSE << 24) | + (DID_OK << 16) | + (COMMAND_COMPLETE << 8) | + (SAM_STAT_CHECK_CONDITION << 0)); + + ent->flags &= ~ESP_CMD_FLAG_AUTOSENSE; + if (esp_debug & ESP_DEBUG_AUTOSENSE) { + int i; + + printk("esp%d: tgt[%d] lun[%d] AUTO SENSE[ ", + esp->host->unique_id, tgt, lun); + for (i = 0; i < 18; i++) + printk("%02x ", cmd->sense_buffer[i]); + printk("]\n"); + } + } + + cmd->scsi_done(cmd); + + list_del(&ent->list); + esp_put_ent(esp, ent); + + esp_maybe_execute_command(esp); +} + +static unsigned int compose_result(unsigned int status, unsigned int message, + unsigned int driver_code) +{ + return (status | (message << 8) | (driver_code << 16)); +} + +static void esp_event_queue_full(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_device *dev = ent->cmd->device; + struct esp_lun_data *lp = dev->hostdata; + + scsi_track_queue_full(dev, lp->num_tagged - 1); +} + +static int esp_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) +{ + struct scsi_device *dev = cmd->device; + struct esp *esp = host_to_esp(dev->host); + struct esp_cmd_priv *spriv; + struct esp_cmd_entry *ent; + + ent = esp_get_ent(esp); + if (!ent) + return SCSI_MLQUEUE_HOST_BUSY; + + ent->cmd = cmd; + + cmd->scsi_done = done; + + spriv = ESP_CMD_PRIV(cmd); + spriv->u.dma_addr = ~(dma_addr_t)0x0; + + list_add_tail(&ent->list, &esp->queued_cmds); + + esp_maybe_execute_command(esp); + + return 0; +} + +static int esp_check_gross_error(struct esp *esp) +{ + if (esp->sreg & ESP_STAT_SPAM) { + /* Gross Error, could be one of: + * - top of fifo overwritten + * - top of command register overwritten + * - DMA programmed with wrong direction + * - improper phase change + */ + printk(KERN_ERR PFX "esp%d: Gross error sreg[%02x]\n", + esp->host->unique_id, esp->sreg); + /* XXX Reset the chip. XXX */ + return 1; + } + return 0; +} + +static int esp_check_spur_intr(struct esp *esp) +{ + switch (esp->rev) { + case ESP100: + case ESP100A: + /* The interrupt pending bit of the status register cannot + * be trusted on these revisions. + */ + esp->sreg &= ~ESP_STAT_INTR; + break; + + default: + if (!(esp->sreg & ESP_STAT_INTR)) { + esp->ireg = esp_read8(ESP_INTRPT); + if (esp->ireg & ESP_INTR_SR) + return 1; + + /* If the DMA is indicating interrupt pending and the + * ESP is not, the only possibility is a DMA error. + */ + if (!esp->ops->dma_error(esp)) { + printk(KERN_ERR PFX "esp%d: Spurious irq, " + "sreg=%x.\n", + esp->host->unique_id, esp->sreg); + return -1; + } + + printk(KERN_ERR PFX "esp%d: DMA error\n", + esp->host->unique_id); + + /* XXX Reset the chip. XXX */ + return -1; + } + break; + } + + return 0; +} + +static void esp_schedule_reset(struct esp *esp) +{ + esp_log_reset("ESP: esp_schedule_reset() from %p\n", + __builtin_return_address(0)); + esp->flags |= ESP_FLAG_RESETTING; + esp_event(esp, ESP_EVENT_RESET); +} + +/* In order to avoid having to add a special half-reconnected state + * into the driver we just sit here and poll through the rest of + * the reselection process to get the tag message bytes. + */ +static struct esp_cmd_entry *esp_reconnect_with_tag(struct esp *esp, + struct esp_lun_data *lp) +{ + struct esp_cmd_entry *ent; + int i; + + if (!lp->num_tagged) { + printk(KERN_ERR PFX "esp%d: Reconnect w/num_tagged==0\n", + esp->host->unique_id); + return NULL; + } + + esp_log_reconnect("ESP: reconnect tag, "); + + for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) + break; + } + if (i == ESP_QUICKIRQ_LIMIT) { + printk(KERN_ERR PFX "esp%d: Reconnect IRQ1 timeout\n", + esp->host->unique_id); + return NULL; + } + + esp->sreg = esp_read8(ESP_STATUS); + esp->ireg = esp_read8(ESP_INTRPT); + + esp_log_reconnect("IRQ(%d:%x:%x), ", + i, esp->ireg, esp->sreg); + + if (esp->ireg & ESP_INTR_DC) { + printk(KERN_ERR PFX "esp%d: Reconnect, got disconnect.\n", + esp->host->unique_id); + return NULL; + } + + if ((esp->sreg & ESP_STAT_PMASK) != ESP_MIP) { + printk(KERN_ERR PFX "esp%d: Reconnect, not MIP sreg[%02x].\n", + esp->host->unique_id, esp->sreg); + return NULL; + } + + /* DMA in the tag bytes... */ + esp->command_block[0] = 0xff; + esp->command_block[1] = 0xff; + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + 2, 2, 1, ESP_CMD_DMA | ESP_CMD_TI); + + /* ACK the msssage. */ + scsi_esp_cmd(esp, ESP_CMD_MOK); + + for (i = 0; i < ESP_RESELECT_TAG_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) { + esp->sreg = esp_read8(ESP_STATUS); + esp->ireg = esp_read8(ESP_INTRPT); + if (esp->ireg & ESP_INTR_FDONE) + break; + } + udelay(1); + } + if (i == ESP_RESELECT_TAG_LIMIT) { + printk(KERN_ERR PFX "esp%d: Reconnect IRQ2 timeout\n", + esp->host->unique_id); + return NULL; + } + esp->ops->dma_drain(esp); + esp->ops->dma_invalidate(esp); + + esp_log_reconnect("IRQ2(%d:%x:%x) tag[%x:%x]\n", + i, esp->ireg, esp->sreg, + esp->command_block[0], + esp->command_block[1]); + + if (esp->command_block[0] < SIMPLE_QUEUE_TAG || + esp->command_block[0] > ORDERED_QUEUE_TAG) { + printk(KERN_ERR PFX "esp%d: Reconnect, bad tag " + "type %02x.\n", + esp->host->unique_id, esp->command_block[0]); + return NULL; + } + + ent = lp->tagged_cmds[esp->command_block[1]]; + if (!ent) { + printk(KERN_ERR PFX "esp%d: Reconnect, no entry for " + "tag %02x.\n", + esp->host->unique_id, esp->command_block[1]); + return NULL; + } + + return ent; +} + +static int esp_reconnect(struct esp *esp) +{ + struct esp_cmd_entry *ent; + struct esp_target_data *tp; + struct esp_lun_data *lp; + struct scsi_device *dev; + int target, lun; + + BUG_ON(esp->active_cmd); + if (esp->rev == FASHME) { + /* FASHME puts the target and lun numbers directly + * into the fifo. + */ + target = esp->fifo[0]; + lun = esp->fifo[1] & 0x7; + } else { + u8 bits = esp_read8(ESP_FDATA); + + /* Older chips put the lun directly into the fifo, but + * the target is given as a sample of the arbitration + * lines on the bus at reselection time. So we should + * see the ID of the ESP and the one reconnecting target + * set in the bitmap. + */ + if (!(bits & esp->scsi_id_mask)) + goto do_reset; + bits &= ~esp->scsi_id_mask; + if (!bits || (bits & (bits - 1))) + goto do_reset; + + target = ffs(bits) - 1; + lun = (esp_read8(ESP_FDATA) & 0x7); + + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP100) { + u8 ireg = esp_read8(ESP_INTRPT); + /* This chip has a bug during reselection that can + * cause a spurious illegal-command interrupt, which + * we simply ACK here. Another possibility is a bus + * reset so we must check for that. + */ + if (ireg & ESP_INTR_SR) + goto do_reset; + } + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + + esp_write_tgt_sync(esp, target); + esp_write_tgt_config3(esp, target); + + scsi_esp_cmd(esp, ESP_CMD_MOK); + + if (esp->rev == FASHME) + esp_write8(target | ESP_BUSID_RESELID | ESP_BUSID_CTR32BIT, + ESP_BUSID); + + tp = &esp->target[target]; + dev = __scsi_device_lookup_by_target(tp->starget, lun); + if (!dev) { + printk(KERN_ERR PFX "esp%d: Reconnect, no lp " + "tgt[%u] lun[%u]\n", + esp->host->unique_id, target, lun); + goto do_reset; + } + lp = dev->hostdata; + + ent = lp->non_tagged_cmd; + if (!ent) { + ent = esp_reconnect_with_tag(esp, lp); + if (!ent) + goto do_reset; + } + + esp->active_cmd = ent; + + if (ent->flags & ESP_CMD_FLAG_ABORT) { + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + + esp_event(esp, ESP_EVENT_CHECK_PHASE); + esp_restore_pointers(esp, ent); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + +do_reset: + esp_schedule_reset(esp); + return 0; +} + +static int esp_finish_select(struct esp *esp) +{ + struct esp_cmd_entry *ent; + struct scsi_cmnd *cmd; + u8 orig_select_state; + + orig_select_state = esp->select_state; + + /* No longer selecting. */ + esp->select_state = ESP_SELECT_NONE; + + esp->seqreg = esp_read8(ESP_SSTEP) & ESP_STEP_VBITS; + ent = esp->active_cmd; + cmd = ent->cmd; + + if (esp->ops->dma_error(esp)) { + /* If we see a DMA error during or as a result of selection, + * all bets are off. + */ + esp_schedule_reset(esp); + esp_cmd_is_done(esp, ent, cmd, (DID_ERROR << 16)); + return 0; + } + + esp->ops->dma_invalidate(esp); + + if (esp->ireg == (ESP_INTR_RSEL | ESP_INTR_FDONE)) { + struct esp_target_data *tp = &esp->target[cmd->device->id]; + + /* Carefully back out of the selection attempt. Release + * resources (such as DMA mapping & TAG) and reset state (such + * as message out and command delivery variables). + */ + if (!(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) { + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, cmd->device->hostdata); + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_NEGO_WIDE); + esp->flags &= ~ESP_FLAG_DOING_SLOWCMD; + esp->cmd_bytes_ptr = NULL; + esp->cmd_bytes_left = 0; + } else { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, + DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + } + + /* Now that the state is unwound properly, put back onto + * the issue queue. This command is no longer active. + */ + list_del(&ent->list); + list_add(&ent->list, &esp->queued_cmds); + esp->active_cmd = NULL; + + /* Return value ignored by caller, it directly invokes + * esp_reconnect(). + */ + return 0; + } + + if (esp->ireg == ESP_INTR_DC) { + struct scsi_device *dev = cmd->device; + + /* Disconnect. Make sure we re-negotiate sync and + * wide parameters if this target starts responding + * again in the future. + */ + esp->target[dev->id].flags |= ESP_TGT_CHECK_NEGO; + + scsi_esp_cmd(esp, ESP_CMD_ESEL); + esp_cmd_is_done(esp, ent, cmd, (DID_BAD_TARGET << 16)); + return 1; + } + + if (esp->ireg == (ESP_INTR_FDONE | ESP_INTR_BSERV)) { + /* Selection successful. On pre-FAST chips we have + * to do a NOP and possibly clean out the FIFO. + */ + if (esp->rev <= ESP236) { + int fcnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + + scsi_esp_cmd(esp, ESP_CMD_NULL); + + if (!fcnt && + (!esp->prev_soff || + ((esp->sreg & ESP_STAT_PMASK) != ESP_DIP))) + esp_flush_fifo(esp); + } + + /* If we are doing a slow command, negotiation, etc. + * we'll do the right thing as we transition to the + * next phase. + */ + esp_event(esp, ESP_EVENT_CHECK_PHASE); + return 0; + } + + printk("ESP: Unexpected selection completion ireg[%x].\n", + esp->ireg); + esp_schedule_reset(esp); + return 0; +} + +static int esp_data_bytes_sent(struct esp *esp, struct esp_cmd_entry *ent, + struct scsi_cmnd *cmd) +{ + int fifo_cnt, ecount, bytes_sent, flush_fifo; + + fifo_cnt = esp_read8(ESP_FFLAGS) & ESP_FF_FBYTES; + if (esp->prev_cfg3 & ESP_CONFIG3_EWIDE) + fifo_cnt <<= 1; + + ecount = 0; + if (!(esp->sreg & ESP_STAT_TCNT)) { + ecount = ((unsigned int)esp_read8(ESP_TCLOW) | + (((unsigned int)esp_read8(ESP_TCMED)) << 8)); + if (esp->rev == FASHME) + ecount |= ((unsigned int)esp_read8(FAS_RLO)) << 16; + } + + bytes_sent = esp->data_dma_len; + bytes_sent -= ecount; + + if (!(ent->flags & ESP_CMD_FLAG_WRITE)) + bytes_sent -= fifo_cnt; + + flush_fifo = 0; + if (!esp->prev_soff) { + /* Synchronous data transfer, always flush fifo. */ + flush_fifo = 1; + } else { + if (esp->rev == ESP100) { + u32 fflags, phase; + + /* ESP100 has a chip bug where in the synchronous data + * phase it can mistake a final long REQ pulse from the + * target as an extra data byte. Fun. + * + * To detect this case we resample the status register + * and fifo flags. If we're still in a data phase and + * we see spurious chunks in the fifo, we return error + * to the caller which should reset and set things up + * such that we only try future transfers to this + * target in synchronous mode. + */ + esp->sreg = esp_read8(ESP_STATUS); + phase = esp->sreg & ESP_STAT_PMASK; + fflags = esp_read8(ESP_FFLAGS); + + if ((phase == ESP_DOP && + (fflags & ESP_FF_ONOTZERO)) || + (phase == ESP_DIP && + (fflags & ESP_FF_FBYTES))) + return -1; + } + if (!(ent->flags & ESP_CMD_FLAG_WRITE)) + flush_fifo = 1; + } + + if (flush_fifo) + esp_flush_fifo(esp); + + return bytes_sent; +} + +static void esp_setsync(struct esp *esp, struct esp_target_data *tp, + u8 scsi_period, u8 scsi_offset, + u8 esp_stp, u8 esp_soff) +{ + spi_period(tp->starget) = scsi_period; + spi_offset(tp->starget) = scsi_offset; + spi_width(tp->starget) = (tp->flags & ESP_TGT_WIDE) ? 1 : 0; + + if (esp_soff) { + esp_stp &= 0x1f; + esp_soff |= esp->radelay; + if (esp->rev >= FAS236) { + u8 bit = ESP_CONFIG3_FSCSI; + if (esp->rev >= FAS100A) + bit = ESP_CONFIG3_FAST; + + if (scsi_period < 50) { + if (esp->rev == FASHME) + esp_soff &= ~esp->radelay; + tp->esp_config3 |= bit; + } else { + tp->esp_config3 &= ~bit; + } + esp->prev_cfg3 = tp->esp_config3; + esp_write8(esp->prev_cfg3, ESP_CFG3); + } + } + + tp->esp_period = esp->prev_stp = esp_stp; + tp->esp_offset = esp->prev_soff = esp_soff; + + esp_write8(esp_soff, ESP_SOFF); + esp_write8(esp_stp, ESP_STP); + + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO); + + spi_display_xfer_agreement(tp->starget); +} + +static void esp_msgin_reject(struct esp *esp) +{ + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + struct esp_target_data *tp; + int tgt; + + tgt = cmd->device->id; + tp = &esp->target[tgt]; + + if (tp->flags & ESP_TGT_NEGO_WIDE) { + tp->flags &= ~(ESP_TGT_NEGO_WIDE | ESP_TGT_WIDE); + + if (!esp_need_to_nego_sync(tp)) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + scsi_esp_cmd(esp, ESP_CMD_RATN); + } else { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + return; + } + + if (tp->flags & ESP_TGT_NEGO_SYNC) { + tp->flags &= ~(ESP_TGT_NEGO_SYNC | ESP_TGT_CHECK_NEGO); + tp->esp_period = 0; + tp->esp_offset = 0; + esp_setsync(esp, tp, 0, 0, 0, 0); + scsi_esp_cmd(esp, ESP_CMD_RATN); + return; + } + + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_sdtr(struct esp *esp, struct esp_target_data *tp) +{ + u8 period = esp->msg_in[3]; + u8 offset = esp->msg_in[4]; + u8 stp; + + if (!(tp->flags & ESP_TGT_NEGO_SYNC)) + goto do_reject; + + if (offset > 15) + goto do_reject; + + if (offset) { + int rounded_up, one_clock; + + if (period > esp->max_period) { + period = offset = 0; + goto do_sdtr; + } + if (period < esp->min_period) + goto do_reject; + + one_clock = esp->ccycle / 1000; + rounded_up = (period << 2); + rounded_up = (rounded_up + one_clock - 1) / one_clock; + stp = rounded_up; + if (stp && esp->rev >= FAS236) { + if (stp >= 50) + stp--; + } + } else { + stp = 0; + } + + esp_setsync(esp, tp, period, offset, stp, offset); + return; + +do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + return; + +do_sdtr: + tp->nego_goal_period = period; + tp->nego_goal_offset = offset; + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_wdtr(struct esp *esp, struct esp_target_data *tp) +{ + int size = 8 << esp->msg_in[3]; + u8 cfg3; + + if (esp->rev != FASHME) + goto do_reject; + + if (size != 8 && size != 16) + goto do_reject; + + if (!(tp->flags & ESP_TGT_NEGO_WIDE)) + goto do_reject; + + cfg3 = tp->esp_config3; + if (size == 16) { + tp->flags |= ESP_TGT_WIDE; + cfg3 |= ESP_CONFIG3_EWIDE; + } else { + tp->flags &= ~ESP_TGT_WIDE; + cfg3 &= ~ESP_CONFIG3_EWIDE; + } + tp->esp_config3 = cfg3; + esp->prev_cfg3 = cfg3; + esp_write8(cfg3, ESP_CFG3); + + tp->flags &= ~ESP_TGT_NEGO_WIDE; + + spi_period(tp->starget) = 0; + spi_offset(tp->starget) = 0; + if (!esp_need_to_nego_sync(tp)) { + tp->flags &= ~ESP_TGT_CHECK_NEGO; + scsi_esp_cmd(esp, ESP_CMD_RATN); + } else { + esp->msg_out_len = + spi_populate_sync_msg(&esp->msg_out[0], + tp->nego_goal_period, + tp->nego_goal_offset); + tp->flags |= ESP_TGT_NEGO_SYNC; + scsi_esp_cmd(esp, ESP_CMD_SATN); + } + return; + +do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +static void esp_msgin_extended(struct esp *esp) +{ + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + struct esp_target_data *tp; + int tgt = cmd->device->id; + + tp = &esp->target[tgt]; + if (esp->msg_in[2] == EXTENDED_SDTR) { + esp_msgin_sdtr(esp, tp); + return; + } + if (esp->msg_in[2] == EXTENDED_WDTR) { + esp_msgin_wdtr(esp, tp); + return; + } + + printk("ESP: Unexpected extended msg type %x\n", + esp->msg_in[2]); + + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); +} + +/* Analyze msgin bytes received from target so far. Return non-zero + * if there are more bytes needed to complete the message. + */ +static int esp_msgin_process(struct esp *esp) +{ + u8 msg0 = esp->msg_in[0]; + int len = esp->msg_in_len; + + if (msg0 & 0x80) { + /* Identify */ + printk("ESP: Unexpected msgin identify\n"); + return 0; + } + + switch (msg0) { + case EXTENDED_MESSAGE: + if (len == 1) + return 1; + if (len < esp->msg_in[1] + 2) + return 1; + esp_msgin_extended(esp); + return 0; + + case IGNORE_WIDE_RESIDUE: { + struct esp_cmd_entry *ent; + struct esp_cmd_priv *spriv; + if (len == 1) + return 1; + + if (esp->msg_in[1] != 1) + goto do_reject; + + ent = esp->active_cmd; + spriv = ESP_CMD_PRIV(ent->cmd); + + if (spriv->cur_residue == sg_dma_len(spriv->cur_sg)) { + spriv->cur_sg--; + spriv->cur_residue = 1; + } else + spriv->cur_residue++; + spriv->tot_residue++; + return 0; + } + case NOP: + return 0; + case RESTORE_POINTERS: + esp_restore_pointers(esp, esp->active_cmd); + return 0; + case SAVE_POINTERS: + esp_save_pointers(esp, esp->active_cmd); + return 0; + + case COMMAND_COMPLETE: + case DISCONNECT: { + struct esp_cmd_entry *ent = esp->active_cmd; + + ent->message = msg0; + esp_event(esp, ESP_EVENT_FREE_BUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 0; + } + case MESSAGE_REJECT: + esp_msgin_reject(esp); + return 0; + + default: + do_reject: + esp->msg_out[0] = MESSAGE_REJECT; + esp->msg_out_len = 1; + scsi_esp_cmd(esp, ESP_CMD_SATN); + return 0; + } +} + +static int esp_process_event(struct esp *esp) +{ + int write; + +again: + write = 0; + switch (esp->event) { + case ESP_EVENT_CHECK_PHASE: + switch (esp->sreg & ESP_STAT_PMASK) { + case ESP_DOP: + esp_event(esp, ESP_EVENT_DATA_OUT); + break; + case ESP_DIP: + esp_event(esp, ESP_EVENT_DATA_IN); + break; + case ESP_STATP: + esp_flush_fifo(esp); + scsi_esp_cmd(esp, ESP_CMD_ICCSEQ); + esp_event(esp, ESP_EVENT_STATUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + + case ESP_MOP: + esp_event(esp, ESP_EVENT_MSGOUT); + break; + + case ESP_MIP: + esp_event(esp, ESP_EVENT_MSGIN); + break; + + case ESP_CMDP: + esp_event(esp, ESP_EVENT_CMD_START); + break; + + default: + printk("ESP: Unexpected phase, sreg=%02x\n", + esp->sreg); + esp_schedule_reset(esp); + return 0; + } + goto again; + break; + + case ESP_EVENT_DATA_IN: + write = 1; + /* fallthru */ + + case ESP_EVENT_DATA_OUT: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + dma_addr_t dma_addr = esp_cur_dma_addr(ent, cmd); + unsigned int dma_len = esp_cur_dma_len(ent, cmd); + + if (esp->rev == ESP100) + scsi_esp_cmd(esp, ESP_CMD_NULL); + + if (write) + ent->flags |= ESP_CMD_FLAG_WRITE; + else + ent->flags &= ~ESP_CMD_FLAG_WRITE; + + dma_len = esp_dma_length_limit(esp, dma_addr, dma_len); + esp->data_dma_len = dma_len; + + if (!dma_len) { + printk(KERN_ERR PFX "esp%d: DMA length is zero!\n", + esp->host->unique_id); + printk(KERN_ERR PFX "esp%d: cur adr[%08llx] len[%08x]\n", + esp->host->unique_id, + (unsigned long long)esp_cur_dma_addr(ent, cmd), + esp_cur_dma_len(ent, cmd)); + esp_schedule_reset(esp); + return 0; + } + + esp_log_datastart("ESP: start data addr[%08llx] len[%u] " + "write(%d)\n", + (unsigned long long)dma_addr, dma_len, write); + + esp->ops->send_dma_cmd(esp, dma_addr, dma_len, dma_len, + write, ESP_CMD_DMA | ESP_CMD_TI); + esp_event(esp, ESP_EVENT_DATA_DONE); + break; + } + case ESP_EVENT_DATA_DONE: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + int bytes_sent; + + if (esp->ops->dma_error(esp)) { + printk("ESP: data done, DMA error, resetting\n"); + esp_schedule_reset(esp); + return 0; + } + + if (ent->flags & ESP_CMD_FLAG_WRITE) { + /* XXX parity errors, etc. XXX */ + + esp->ops->dma_drain(esp); + } + esp->ops->dma_invalidate(esp); + + if (esp->ireg != ESP_INTR_BSERV) { + /* We should always see exactly a bus-service + * interrupt at the end of a successful transfer. + */ + printk("ESP: data done, not BSERV, resetting\n"); + esp_schedule_reset(esp); + return 0; + } + + bytes_sent = esp_data_bytes_sent(esp, ent, cmd); + + esp_log_datadone("ESP: data done flgs[%x] sent[%d]\n", + ent->flags, bytes_sent); + + if (bytes_sent < 0) { + /* XXX force sync mode for this target XXX */ + esp_schedule_reset(esp); + return 0; + } + + esp_advance_dma(esp, ent, cmd, bytes_sent); + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + break; + } + + case ESP_EVENT_STATUS: { + struct esp_cmd_entry *ent = esp->active_cmd; + + if (esp->ireg & ESP_INTR_FDONE) { + ent->status = esp_read8(ESP_FDATA); + ent->message = esp_read8(ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_MOK); + } else if (esp->ireg == ESP_INTR_BSERV) { + ent->status = esp_read8(ESP_FDATA); + ent->message = 0xff; + esp_event(esp, ESP_EVENT_MSGIN); + return 0; + } + + if (ent->message != COMMAND_COMPLETE) { + printk("ESP: Unexpected message %x in status\n", + ent->message); + esp_schedule_reset(esp); + return 0; + } + + esp_event(esp, ESP_EVENT_FREE_BUS); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + } + case ESP_EVENT_FREE_BUS: { + struct esp_cmd_entry *ent = esp->active_cmd; + struct scsi_cmnd *cmd = ent->cmd; + + if (ent->message == COMMAND_COMPLETE || + ent->message == DISCONNECT) + scsi_esp_cmd(esp, ESP_CMD_ESEL); + + if (ent->message == COMMAND_COMPLETE) { + esp_log_cmddone("ESP: Command done status[%x] " + "message[%x]\n", + ent->status, ent->message); + if (ent->status == SAM_STAT_TASK_SET_FULL) + esp_event_queue_full(esp, ent); + + if (ent->status == SAM_STAT_CHECK_CONDITION && + !(ent->flags & ESP_CMD_FLAG_AUTOSENSE)) { + ent->flags |= ESP_CMD_FLAG_AUTOSENSE; + esp_autosense(esp, ent); + } else { + esp_cmd_is_done(esp, ent, cmd, + compose_result(ent->status, + ent->message, + DID_OK)); + } + } else if (ent->message == DISCONNECT) { + esp_log_disconnect("ESP: Disconnecting tgt[%d] " + "tag[%x:%x]\n", + cmd->device->id, + ent->tag[0], ent->tag[1]); + + esp->active_cmd = NULL; + esp_maybe_execute_command(esp); + } else { + printk("ESP: Unexpected message %x in freebus\n", + ent->message); + esp_schedule_reset(esp); + return 0; + } + if (esp->active_cmd) + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + } + case ESP_EVENT_MSGOUT: { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + + if (esp_debug & ESP_DEBUG_MSGOUT) { + int i; + printk("ESP: Sending message [ "); + for (i = 0; i < esp->msg_out_len; i++) + printk("%02x ", esp->msg_out[i]); + printk("]\n"); + } + + if (esp->rev == FASHME) { + int i; + + /* Always use the fifo. */ + for (i = 0; i < esp->msg_out_len; i++) { + esp_write8(esp->msg_out[i], ESP_FDATA); + esp_write8(0, ESP_FDATA); + } + scsi_esp_cmd(esp, ESP_CMD_TI); + } else { + if (esp->msg_out_len == 1) { + esp_write8(esp->msg_out[0], ESP_FDATA); + scsi_esp_cmd(esp, ESP_CMD_TI); + } else { + /* Use DMA. */ + memcpy(esp->command_block, + esp->msg_out, + esp->msg_out_len); + + esp->ops->send_dma_cmd(esp, + esp->command_block_dma, + esp->msg_out_len, + esp->msg_out_len, + 0, + ESP_CMD_DMA|ESP_CMD_TI); + } + } + esp_event(esp, ESP_EVENT_MSGOUT_DONE); + break; + } + case ESP_EVENT_MSGOUT_DONE: + if (esp->rev == FASHME) { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } else { + if (esp->msg_out_len > 1) + esp->ops->dma_invalidate(esp); + } + + if (!(esp->ireg & ESP_INTR_DC)) { + if (esp->rev != FASHME) + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + case ESP_EVENT_MSGIN: + if (esp->ireg & ESP_INTR_BSERV) { + if (esp->rev == FASHME) { + if (!(esp_read8(ESP_STATUS2) & + ESP_STAT2_FEMPTY)) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + } else { + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + if (esp->rev == ESP100) + scsi_esp_cmd(esp, ESP_CMD_NULL); + } + scsi_esp_cmd(esp, ESP_CMD_TI); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + return 1; + } + if (esp->ireg & ESP_INTR_FDONE) { + u8 val; + + if (esp->rev == FASHME) + val = esp->fifo[0]; + else + val = esp_read8(ESP_FDATA); + esp->msg_in[esp->msg_in_len++] = val; + + esp_log_msgin("ESP: Got msgin byte %x\n", val); + + if (!esp_msgin_process(esp)) + esp->msg_in_len = 0; + + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + + scsi_esp_cmd(esp, ESP_CMD_MOK); + + if (esp->event != ESP_EVENT_FREE_BUS) + esp_event(esp, ESP_EVENT_CHECK_PHASE); + } else { + printk("ESP: MSGIN neither BSERV not FDON, resetting"); + esp_schedule_reset(esp); + return 0; + } + break; + case ESP_EVENT_CMD_START: + memcpy(esp->command_block, esp->cmd_bytes_ptr, + esp->cmd_bytes_left); + if (esp->rev == FASHME) + scsi_esp_cmd(esp, ESP_CMD_FLUSH); + esp->ops->send_dma_cmd(esp, esp->command_block_dma, + esp->cmd_bytes_left, 16, 0, + ESP_CMD_DMA | ESP_CMD_TI); + esp_event(esp, ESP_EVENT_CMD_DONE); + esp->flags |= ESP_FLAG_QUICKIRQ_CHECK; + break; + case ESP_EVENT_CMD_DONE: + esp->ops->dma_invalidate(esp); + if (esp->ireg & ESP_INTR_BSERV) { + esp_event(esp, ESP_EVENT_CHECK_PHASE); + goto again; + } + esp_schedule_reset(esp); + return 0; + break; + + case ESP_EVENT_RESET: + scsi_esp_cmd(esp, ESP_CMD_RS); + break; + + default: + printk("ESP: Unexpected event %x, resetting\n", + esp->event); + esp_schedule_reset(esp); + return 0; + break; + } + return 1; +} + +static void esp_reset_cleanup_one(struct esp *esp, struct esp_cmd_entry *ent) +{ + struct scsi_cmnd *cmd = ent->cmd; + + esp_unmap_dma(esp, cmd); + esp_free_lun_tag(ent, cmd->device->hostdata); + cmd->result = DID_RESET << 16; + + if (ent->flags & ESP_CMD_FLAG_AUTOSENSE) { + esp->ops->unmap_single(esp, ent->sense_dma, + SCSI_SENSE_BUFFERSIZE, DMA_FROM_DEVICE); + ent->sense_ptr = NULL; + } + + cmd->scsi_done(cmd); + list_del(&ent->list); + esp_put_ent(esp, ent); +} + +static void esp_clear_hold(struct scsi_device *dev, void *data) +{ + struct esp_lun_data *lp = dev->hostdata; + + BUG_ON(lp->num_tagged); + lp->hold = 0; +} + +static void esp_reset_cleanup(struct esp *esp) +{ + struct esp_cmd_entry *ent, *tmp; + int i; + + list_for_each_entry_safe(ent, tmp, &esp->queued_cmds, list) { + struct scsi_cmnd *cmd = ent->cmd; + + list_del(&ent->list); + cmd->result = DID_RESET << 16; + cmd->scsi_done(cmd); + esp_put_ent(esp, ent); + } + + list_for_each_entry_safe(ent, tmp, &esp->active_cmds, list) { + if (ent == esp->active_cmd) + esp->active_cmd = NULL; + esp_reset_cleanup_one(esp, ent); + } + + BUG_ON(esp->active_cmd != NULL); + + /* Force renegotiation of sync/wide transfers. */ + for (i = 0; i < ESP_MAX_TARGET; i++) { + struct esp_target_data *tp = &esp->target[i]; + + tp->esp_period = 0; + tp->esp_offset = 0; + tp->esp_config3 &= ~(ESP_CONFIG3_EWIDE | + ESP_CONFIG3_FSCSI | + ESP_CONFIG3_FAST); + tp->flags &= ~ESP_TGT_WIDE; + tp->flags |= ESP_TGT_CHECK_NEGO; + + if (tp->starget) + starget_for_each_device(tp->starget, NULL, + esp_clear_hold); + } +} + +/* Runs under host->lock */ +static void __esp_interrupt(struct esp *esp) +{ + int finish_reset, intr_done; + u8 phase; + + esp->sreg = esp_read8(ESP_STATUS); + + if (esp->flags & ESP_FLAG_RESETTING) { + finish_reset = 1; + } else { + if (esp_check_gross_error(esp)) + return; + + finish_reset = esp_check_spur_intr(esp); + if (finish_reset < 0) + return; + } + + esp->ireg = esp_read8(ESP_INTRPT); + + if (esp->ireg & ESP_INTR_SR) + finish_reset = 1; + + if (finish_reset) { + esp_reset_cleanup(esp); + if (esp->eh_reset) { + complete(esp->eh_reset); + esp->eh_reset = NULL; + } + return; + } + + phase = (esp->sreg & ESP_STAT_PMASK); + if (esp->rev == FASHME) { + if (((phase != ESP_DIP && phase != ESP_DOP) && + esp->select_state == ESP_SELECT_NONE && + esp->event != ESP_EVENT_STATUS && + esp->event != ESP_EVENT_DATA_DONE) || + (esp->ireg & ESP_INTR_RSEL)) { + esp->sreg2 = esp_read8(ESP_STATUS2); + if (!(esp->sreg2 & ESP_STAT2_FEMPTY) || + (esp->sreg2 & ESP_STAT2_F1BYTE)) + hme_read_fifo(esp); + } + } + + esp_log_intr("ESP: intr sreg[%02x] seqreg[%02x] " + "sreg2[%02x] ireg[%02x]\n", + esp->sreg, esp->seqreg, esp->sreg2, esp->ireg); + + intr_done = 0; + + if (esp->ireg & (ESP_INTR_S | ESP_INTR_SATN | ESP_INTR_IC)) { + printk("ESP: unexpected IREG %02x\n", esp->ireg); + if (esp->ireg & ESP_INTR_IC) + esp_dump_cmd_log(esp); + + esp_schedule_reset(esp); + } else { + if (!(esp->ireg & ESP_INTR_RSEL)) { + /* Some combination of FDONE, BSERV, DC. */ + if (esp->select_state != ESP_SELECT_NONE) + intr_done = esp_finish_select(esp); + } else if (esp->ireg & ESP_INTR_RSEL) { + if (esp->active_cmd) + (void) esp_finish_select(esp); + intr_done = esp_reconnect(esp); + } + } + while (!intr_done) + intr_done = esp_process_event(esp); +} + +irqreturn_t scsi_esp_intr(int irq, void *dev_id) +{ + struct esp *esp = dev_id; + unsigned long flags; + irqreturn_t ret; + + spin_lock_irqsave(esp->host->host_lock, flags); + ret = IRQ_NONE; + if (esp->ops->irq_pending(esp)) { + ret = IRQ_HANDLED; + for (;;) { + int i; + + __esp_interrupt(esp); + if (!(esp->flags & ESP_FLAG_QUICKIRQ_CHECK)) + break; + esp->flags &= ~ESP_FLAG_QUICKIRQ_CHECK; + + for (i = 0; i < ESP_QUICKIRQ_LIMIT; i++) { + if (esp->ops->irq_pending(esp)) + break; + } + if (i == ESP_QUICKIRQ_LIMIT) + break; + } + } + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return ret; +} +EXPORT_SYMBOL(scsi_esp_intr); + +static void __devinit esp_get_revision(struct esp *esp) +{ + u8 val; + + esp->config1 = (ESP_CONFIG1_PENABLE | (esp->scsi_id & 7)); + esp->config2 = (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY); + esp_write8(esp->config2, ESP_CFG2); + + val = esp_read8(ESP_CFG2); + val &= ~ESP_CONFIG2_MAGIC; + if (val != (ESP_CONFIG2_SCSI2ENAB | ESP_CONFIG2_REGPARITY)) { + /* If what we write to cfg2 does not come back, cfg2 is not + * implemented, therefore this must be a plain esp100. + */ + esp->rev = ESP100; + } else { + esp->config2 = 0; + esp_set_all_config3(esp, 5); + esp->prev_cfg3 = 5; + esp_write8(esp->config2, ESP_CFG2); + esp_write8(0, ESP_CFG3); + esp_write8(esp->prev_cfg3, ESP_CFG3); + + val = esp_read8(ESP_CFG3); + if (val != 5) { + /* The cfg2 register is implemented, however + * cfg3 is not, must be esp100a. + */ + esp->rev = ESP100A; + } else { + esp_set_all_config3(esp, 0); + esp->prev_cfg3 = 0; + esp_write8(esp->prev_cfg3, ESP_CFG3); + + /* All of cfg{1,2,3} implemented, must be one of + * the fas variants, figure out which one. + */ + if (esp->cfact == 0 || esp->cfact > ESP_CCF_F5) { + esp->rev = FAST; + esp->sync_defp = SYNC_DEFP_FAST; + } else { + esp->rev = ESP236; + } + esp->config2 = 0; + esp_write8(esp->config2, ESP_CFG2); + } + } +} + +static void __devinit esp_init_swstate(struct esp *esp) +{ + int i; + + INIT_LIST_HEAD(&esp->queued_cmds); + INIT_LIST_HEAD(&esp->active_cmds); + INIT_LIST_HEAD(&esp->esp_cmd_pool); + + /* Start with a clear state, domain validation (via ->slave_configure, + * spi_dv_device()) will attempt to enable SYNC, WIDE, and tagged + * commands. + */ + for (i = 0 ; i < ESP_MAX_TARGET; i++) { + esp->target[i].flags = 0; + esp->target[i].nego_goal_period = 0; + esp->target[i].nego_goal_offset = 0; + esp->target[i].nego_goal_width = 0; + esp->target[i].nego_goal_tags = 0; + } +} + +/* This places the ESP into a known state at boot time. */ +static void __devinit esp_bootup_reset(struct esp *esp) +{ + u8 val; + + /* Reset the DMA */ + esp->ops->reset_dma(esp); + + /* Reset the ESP */ + esp_reset_esp(esp); + + /* Reset the SCSI bus, but tell ESP not to generate an irq */ + val = esp_read8(ESP_CFG1); + val |= ESP_CONFIG1_SRRDISAB; + esp_write8(val, ESP_CFG1); + + scsi_esp_cmd(esp, ESP_CMD_RS); + udelay(400); + + esp_write8(esp->config1, ESP_CFG1); + + /* Eat any bitrot in the chip and we are done... */ + esp_read8(ESP_INTRPT); +} + +static void __devinit esp_set_clock_params(struct esp *esp) +{ + int fmhz; + u8 ccf; + + /* This is getting messy but it has to be done correctly or else + * you get weird behavior all over the place. We are trying to + * basically figure out three pieces of information. + * + * a) Clock Conversion Factor + * + * This is a representation of the input crystal clock frequency + * going into the ESP on this machine. Any operation whose timing + * is longer than 400ns depends on this value being correct. For + * example, you'll get blips for arbitration/selection during high + * load or with multiple targets if this is not set correctly. + * + * b) Selection Time-Out + * + * The ESP isn't very bright and will arbitrate for the bus and try + * to select a target forever if you let it. This value tells the + * ESP when it has taken too long to negotiate and that it should + * interrupt the CPU so we can see what happened. The value is + * computed as follows (from NCR/Symbios chip docs). + * + * (Time Out Period) * (Input Clock) + * STO = ---------------------------------- + * (8192) * (Clock Conversion Factor) + * + * We use a time out period of 250ms (ESP_BUS_TIMEOUT). + * + * c) Imperical constants for synchronous offset and transfer period + * register values + * + * This entails the smallest and largest sync period we could ever + * handle on this ESP. + */ + fmhz = esp->cfreq; + + ccf = ((fmhz / 1000000) + 4) / 5; + if (ccf == 1) + ccf = 2; + + /* If we can't find anything reasonable, just assume 20MHZ. + * This is the clock frequency of the older sun4c's where I've + * been unable to find the clock-frequency PROM property. All + * other machines provide useful values it seems. + */ + if (fmhz <= 5000000 || ccf < 1 || ccf > 8) { + fmhz = 20000000; + ccf = 4; + } + + esp->cfact = (ccf == 8 ? 0 : ccf); + esp->cfreq = fmhz; + esp->ccycle = ESP_MHZ_TO_CYCLE(fmhz); + esp->ctick = ESP_TICK(ccf, esp->ccycle); + esp->neg_defp = ESP_NEG_DEFP(fmhz, ccf); + esp->sync_defp = SYNC_DEFP_SLOW; +} + +static const char *esp_chip_names[] = { + "ESP100", + "ESP100A", + "ESP236", + "FAS236", + "FAS100A", + "FAST", + "FASHME", +}; + +static struct scsi_transport_template *esp_transport_template; + +int __devinit scsi_esp_register(struct esp *esp, struct device *dev) +{ + static int instance; + int err; + + esp->host->transportt = esp_transport_template; + esp->host->max_lun = ESP_MAX_LUN; + esp->host->cmd_per_lun = 2; + + esp_set_clock_params(esp); + + esp_get_revision(esp); + + esp_init_swstate(esp); + + esp_bootup_reset(esp); + + printk(KERN_INFO PFX "esp%u, regs[%1p:%1p] irq[%u]\n", + esp->host->unique_id, esp->regs, esp->dma_regs, + esp->host->irq); + printk(KERN_INFO PFX "esp%u is a %s, %u MHz (ccf=%u), SCSI ID %u\n", + esp->host->unique_id, esp_chip_names[esp->rev], + esp->cfreq / 1000000, esp->cfact, esp->scsi_id); + + /* Let the SCSI bus reset settle. */ + ssleep(esp_bus_reset_settle); + + err = scsi_add_host(esp->host, dev); + if (err) + return err; + + esp->host->unique_id = instance++; + + scsi_scan_host(esp->host); + + return 0; +} +EXPORT_SYMBOL(scsi_esp_register); + +void __devexit scsi_esp_unregister(struct esp *esp) +{ + scsi_remove_host(esp->host); +} +EXPORT_SYMBOL(scsi_esp_unregister); + +static int esp_slave_alloc(struct scsi_device *dev) +{ + struct esp *esp = host_to_esp(dev->host); + struct esp_target_data *tp = &esp->target[dev->id]; + struct esp_lun_data *lp; + + lp = kzalloc(sizeof(*lp), GFP_KERNEL); + if (!lp) + return -ENOMEM; + dev->hostdata = lp; + + tp->starget = dev->sdev_target; + + spi_min_period(tp->starget) = esp->min_period; + spi_max_offset(tp->starget) = 15; + + if (esp->flags & ESP_FLAG_WIDE_CAPABLE) + spi_max_width(tp->starget) = 1; + else + spi_max_width(tp->starget) = 0; + + return 0; +} + +static int esp_slave_configure(struct scsi_device *dev) +{ + struct esp *esp = host_to_esp(dev->host); + struct esp_target_data *tp = &esp->target[dev->id]; + int goal_tags, queue_depth; + + goal_tags = 0; + + if (dev->tagged_supported) { + /* XXX make this configurable somehow XXX */ + goal_tags = ESP_DEFAULT_TAGS; + + if (goal_tags > ESP_MAX_TAG) + goal_tags = ESP_MAX_TAG; + } + + queue_depth = goal_tags; + if (queue_depth < dev->host->cmd_per_lun) + queue_depth = dev->host->cmd_per_lun; + + if (goal_tags) { + scsi_set_tag_type(dev, MSG_ORDERED_TAG); + scsi_activate_tcq(dev, queue_depth); + } else { + scsi_deactivate_tcq(dev, queue_depth); + } + tp->flags |= ESP_TGT_DISCONNECT; + + if (!spi_initial_dv(dev->sdev_target)) + spi_dv_device(dev); + + return 0; +} + +static void esp_slave_destroy(struct scsi_device *dev) +{ + struct esp_lun_data *lp = dev->hostdata; + + kfree(lp); + dev->hostdata = NULL; +} + +static int esp_eh_abort_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + struct esp_cmd_entry *ent, *tmp; + struct completion eh_done; + unsigned long flags; + + /* XXX This helps a lot with debugging but might be a bit + * XXX much for the final driver. + */ + spin_lock_irqsave(esp->host->host_lock, flags); + printk(KERN_ERR PFX "esp%d: Aborting command [%p:%02x]\n", + esp->host->unique_id, cmd, cmd->cmnd[0]); + ent = esp->active_cmd; + if (ent) + printk(KERN_ERR PFX "esp%d: Current command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + list_for_each_entry(ent, &esp->queued_cmds, list) { + printk(KERN_ERR PFX "esp%d: Queued command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + } + list_for_each_entry(ent, &esp->active_cmds, list) { + printk(KERN_ERR PFX "esp%d: Active command [%p:%02x]\n", + esp->host->unique_id, ent->cmd, ent->cmd->cmnd[0]); + } + esp_dump_cmd_log(esp); + spin_unlock_irqrestore(esp->host->host_lock, flags); + + spin_lock_irqsave(esp->host->host_lock, flags); + + ent = NULL; + list_for_each_entry(tmp, &esp->queued_cmds, list) { + if (tmp->cmd == cmd) { + ent = tmp; + break; + } + } + + if (ent) { + /* Easiest case, we didn't even issue the command + * yet so it is trivial to abort. + */ + list_del(&ent->list); + + cmd->result = DID_ABORT << 16; + cmd->scsi_done(cmd); + + esp_put_ent(esp, ent); + + goto out_success; + } + + init_completion(&eh_done); + + ent = esp->active_cmd; + if (ent && ent->cmd == cmd) { + /* Command is the currently active command on + * the bus. If we already have an output message + * pending, no dice. + */ + if (esp->msg_out_len) + goto out_failure; + + /* Send out an abort, encouraging the target to + * go to MSGOUT phase by asserting ATN. + */ + esp->msg_out[0] = ABORT_TASK_SET; + esp->msg_out_len = 1; + ent->eh_done = &eh_done; + + scsi_esp_cmd(esp, ESP_CMD_SATN); + } else { + /* The command is disconnected. This is not easy to + * abort. For now we fail and let the scsi error + * handling layer go try a scsi bus reset or host + * reset. + * + * What we could do is put together a scsi command + * solely for the purpose of sending an abort message + * to the target. Coming up with all the code to + * cook up scsi commands, special case them everywhere, + * etc. is for questionable gain and it would be better + * if the generic scsi error handling layer could do at + * least some of that for us. + * + * Anyways this is an area for potential future improvement + * in this driver. + */ + goto out_failure; + } + + spin_unlock_irqrestore(esp->host->host_lock, flags); + + if (!wait_for_completion_timeout(&eh_done, 5 * HZ)) { + spin_lock_irqsave(esp->host->host_lock, flags); + ent->eh_done = NULL; + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return FAILED; + } + + return SUCCESS; + +out_success: + spin_unlock_irqrestore(esp->host->host_lock, flags); + return SUCCESS; + +out_failure: + /* XXX This might be a good location to set ESP_TGT_BROKEN + * XXX since we know which target/lun in particular is + * XXX causing trouble. + */ + spin_unlock_irqrestore(esp->host->host_lock, flags); + return FAILED; +} + +static int esp_eh_bus_reset_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + struct completion eh_reset; + unsigned long flags; + + init_completion(&eh_reset); + + spin_lock_irqsave(esp->host->host_lock, flags); + + esp->eh_reset = &eh_reset; + + /* XXX This is too simple... We should add lots of + * XXX checks here so that if we find that the chip is + * XXX very wedged we return failure immediately so + * XXX that we can perform a full chip reset. + */ + esp->flags |= ESP_FLAG_RESETTING; + scsi_esp_cmd(esp, ESP_CMD_RS); + + spin_unlock_irqrestore(esp->host->host_lock, flags); + + ssleep(esp_bus_reset_settle); + + if (!wait_for_completion_timeout(&eh_reset, 5 * HZ)) { + spin_lock_irqsave(esp->host->host_lock, flags); + esp->eh_reset = NULL; + spin_unlock_irqrestore(esp->host->host_lock, flags); + + return FAILED; + } + + return SUCCESS; +} + +/* All bets are off, reset the entire device. */ +static int esp_eh_host_reset_handler(struct scsi_cmnd *cmd) +{ + struct esp *esp = host_to_esp(cmd->device->host); + unsigned long flags; + + spin_lock_irqsave(esp->host->host_lock, flags); + esp_bootup_reset(esp); + esp_reset_cleanup(esp); + spin_unlock_irqrestore(esp->host->host_lock, flags); + + ssleep(esp_bus_reset_settle); + + return SUCCESS; +} + +static const char *esp_info(struct Scsi_Host *host) +{ + return "esp"; +} + +struct scsi_host_template scsi_esp_template = { + .module = THIS_MODULE, + .name = "esp", + .info = esp_info, + .queuecommand = esp_queuecommand, + .slave_alloc = esp_slave_alloc, + .slave_configure = esp_slave_configure, + .slave_destroy = esp_slave_destroy, + .eh_abort_handler = esp_eh_abort_handler, + .eh_bus_reset_handler = esp_eh_bus_reset_handler, + .eh_host_reset_handler = esp_eh_host_reset_handler, + .can_queue = 7, + .this_id = 7, + .sg_tablesize = SG_ALL, + .use_clustering = ENABLE_CLUSTERING, + .max_sectors = 0xffff, + .skip_settle_delay = 1, +}; +EXPORT_SYMBOL(scsi_esp_template); + +static void esp_get_signalling(struct Scsi_Host *host) +{ + struct esp *esp = host_to_esp(host); + enum spi_signal_type type; + + if (esp->flags & ESP_FLAG_DIFFERENTIAL) + type = SPI_SIGNAL_HVD; + else + type = SPI_SIGNAL_SE; + + spi_signalling(host) = type; +} + +static void esp_set_offset(struct scsi_target *target, int offset) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_offset = offset; + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static void esp_set_period(struct scsi_target *target, int period) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_period = period; + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static void esp_set_width(struct scsi_target *target, int width) +{ + struct Scsi_Host *host = dev_to_shost(target->dev.parent); + struct esp *esp = host_to_esp(host); + struct esp_target_data *tp = &esp->target[target->id]; + + tp->nego_goal_width = (width ? 1 : 0); + tp->flags |= ESP_TGT_CHECK_NEGO; +} + +static struct spi_function_template esp_transport_ops = { + .set_offset = esp_set_offset, + .show_offset = 1, + .set_period = esp_set_period, + .show_period = 1, + .set_width = esp_set_width, + .show_width = 1, + .get_signalling = esp_get_signalling, +}; + +static int __init esp_init(void) +{ + BUILD_BUG_ON(sizeof(struct scsi_pointer) < + sizeof(struct esp_cmd_priv)); + + esp_transport_template = spi_attach_transport(&esp_transport_ops); + if (!esp_transport_template) + return -ENODEV; + + return 0; +} + +static void __exit esp_exit(void) +{ + spi_release_transport(esp_transport_template); +} + +MODULE_DESCRIPTION("ESP SCSI driver core"); +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_param(esp_bus_reset_settle, int, 0); +MODULE_PARM_DESC(esp_bus_reset_settle, + "ESP scsi bus reset delay in seconds"); + +module_param(esp_debug, int, 0); +MODULE_PARM_DESC(esp_debug, +"ESP bitmapped debugging message enable value:\n" +" 0x00000001 Log interrupt events\n" +" 0x00000002 Log scsi commands\n" +" 0x00000004 Log resets\n" +" 0x00000008 Log message in events\n" +" 0x00000010 Log message out events\n" +" 0x00000020 Log command completion\n" +" 0x00000040 Log disconnects\n" +" 0x00000080 Log data start\n" +" 0x00000100 Log data done\n" +" 0x00000200 Log reconnects\n" +" 0x00000400 Log auto-sense data\n" +); + +module_init(esp_init); +module_exit(esp_exit); diff --git a/drivers/scsi/esp_scsi.h b/drivers/scsi/esp_scsi.h new file mode 100644 index 0000000..8d4a669 --- /dev/null +++ b/drivers/scsi/esp_scsi.h @@ -0,0 +1,560 @@ +/* esp_scsi.h: Defines and structures for the ESP drier. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#ifndef _ESP_SCSI_H +#define _ESP_SCSI_H + + /* Access Description Offset */ +#define ESP_TCLOW 0x00UL /* rw Low bits transfer count 0x00 */ +#define ESP_TCMED 0x01UL /* rw Mid bits transfer count 0x04 */ +#define ESP_FDATA 0x02UL /* rw FIFO data bits 0x08 */ +#define ESP_CMD 0x03UL /* rw SCSI command bits 0x0c */ +#define ESP_STATUS 0x04UL /* ro ESP status register 0x10 */ +#define ESP_BUSID ESP_STATUS /* wo BusID for sel/resel 0x10 */ +#define ESP_INTRPT 0x05UL /* ro Kind of interrupt 0x14 */ +#define ESP_TIMEO ESP_INTRPT /* wo Timeout for sel/resel 0x14 */ +#define ESP_SSTEP 0x06UL /* ro Sequence step register 0x18 */ +#define ESP_STP ESP_SSTEP /* wo Transfer period/sync 0x18 */ +#define ESP_FFLAGS 0x07UL /* ro Bits current FIFO info 0x1c */ +#define ESP_SOFF ESP_FFLAGS /* wo Sync offset 0x1c */ +#define ESP_CFG1 0x08UL /* rw First cfg register 0x20 */ +#define ESP_CFACT 0x09UL /* wo Clock conv factor 0x24 */ +#define ESP_STATUS2 ESP_CFACT /* ro HME status2 register 0x24 */ +#define ESP_CTEST 0x0aUL /* wo Chip test register 0x28 */ +#define ESP_CFG2 0x0bUL /* rw Second cfg register 0x2c */ +#define ESP_CFG3 0x0cUL /* rw Third cfg register 0x30 */ +#define ESP_TCHI 0x0eUL /* rw High bits transf count 0x38 */ +#define ESP_UID ESP_TCHI /* ro Unique ID code 0x38 */ +#define FAS_RLO ESP_TCHI /* rw HME extended counter 0x38 */ +#define ESP_FGRND 0x0fUL /* rw Data base for fifo 0x3c */ +#define FAS_RHI ESP_FGRND /* rw HME extended counter 0x3c */ + +#define SBUS_ESP_REG_SIZE 0x40UL + +/* Bitfield meanings for the above registers. */ + +/* ESP config reg 1, read-write, found on all ESP chips */ +#define ESP_CONFIG1_ID 0x07 /* My BUS ID bits */ +#define ESP_CONFIG1_CHTEST 0x08 /* Enable ESP chip tests */ +#define ESP_CONFIG1_PENABLE 0x10 /* Enable parity checks */ +#define ESP_CONFIG1_PARTEST 0x20 /* Parity test mode enabled? */ +#define ESP_CONFIG1_SRRDISAB 0x40 /* Disable SCSI reset reports */ +#define ESP_CONFIG1_SLCABLE 0x80 /* Enable slow cable mode */ + +/* ESP config reg 2, read-write, found only on esp100a+esp200+esp236 chips */ +#define ESP_CONFIG2_DMAPARITY 0x01 /* enable DMA Parity (200,236) */ +#define ESP_CONFIG2_REGPARITY 0x02 /* enable reg Parity (200,236) */ +#define ESP_CONFIG2_BADPARITY 0x04 /* Bad parity target abort */ +#define ESP_CONFIG2_SCSI2ENAB 0x08 /* Enable SCSI-2 features (tgtmode) */ +#define ESP_CONFIG2_HI 0x10 /* High Impedance DREQ ??? */ +#define ESP_CONFIG2_HMEFENAB 0x10 /* HME features enable */ +#define ESP_CONFIG2_BCM 0x20 /* Enable byte-ctrl (236) */ +#define ESP_CONFIG2_DISPINT 0x20 /* Disable pause irq (hme) */ +#define ESP_CONFIG2_FENAB 0x40 /* Enable features (fas100,216) */ +#define ESP_CONFIG2_SPL 0x40 /* Enable status-phase latch (236) */ +#define ESP_CONFIG2_MKDONE 0x40 /* HME magic feature */ +#define ESP_CONFIG2_HME32 0x80 /* HME 32 extended */ +#define ESP_CONFIG2_MAGIC 0xe0 /* Invalid bits... */ + +/* ESP config register 3 read-write, found only esp236+fas236+fas100a+hme chips */ +#define ESP_CONFIG3_FCLOCK 0x01 /* FAST SCSI clock rate (esp100a/hme) */ +#define ESP_CONFIG3_TEM 0x01 /* Enable thresh-8 mode (esp/fas236) */ +#define ESP_CONFIG3_FAST 0x02 /* Enable FAST SCSI (esp100a/hme) */ +#define ESP_CONFIG3_ADMA 0x02 /* Enable alternate-dma (esp/fas236) */ +#define ESP_CONFIG3_TENB 0x04 /* group2 SCSI2 support (esp100a/hme) */ +#define ESP_CONFIG3_SRB 0x04 /* Save residual byte (esp/fas236) */ +#define ESP_CONFIG3_TMS 0x08 /* Three-byte msg's ok (esp100a/hme) */ +#define ESP_CONFIG3_FCLK 0x08 /* Fast SCSI clock rate (esp/fas236) */ +#define ESP_CONFIG3_IDMSG 0x10 /* ID message checking (esp100a/hme) */ +#define ESP_CONFIG3_FSCSI 0x10 /* Enable FAST SCSI (esp/fas236) */ +#define ESP_CONFIG3_GTM 0x20 /* group2 SCSI2 support (esp/fas236) */ +#define ESP_CONFIG3_IDBIT3 0x20 /* Bit 3 of HME SCSI-ID (hme) */ +#define ESP_CONFIG3_TBMS 0x40 /* Three-byte msg's ok (esp/fas236) */ +#define ESP_CONFIG3_EWIDE 0x40 /* Enable Wide-SCSI (hme) */ +#define ESP_CONFIG3_IMS 0x80 /* ID msg chk'ng (esp/fas236) */ +#define ESP_CONFIG3_OBPUSH 0x80 /* Push odd-byte to dma (hme) */ + +/* ESP command register read-write */ +/* Group 1 commands: These may be sent at any point in time to the ESP + * chip. None of them can generate interrupts 'cept + * the "SCSI bus reset" command if you have not disabled + * SCSI reset interrupts in the config1 ESP register. + */ +#define ESP_CMD_NULL 0x00 /* Null command, ie. a nop */ +#define ESP_CMD_FLUSH 0x01 /* FIFO Flush */ +#define ESP_CMD_RC 0x02 /* Chip reset */ +#define ESP_CMD_RS 0x03 /* SCSI bus reset */ + +/* Group 2 commands: ESP must be an initiator and connected to a target + * for these commands to work. + */ +#define ESP_CMD_TI 0x10 /* Transfer Information */ +#define ESP_CMD_ICCSEQ 0x11 /* Initiator cmd complete sequence */ +#define ESP_CMD_MOK 0x12 /* Message okie-dokie */ +#define ESP_CMD_TPAD 0x18 /* Transfer Pad */ +#define ESP_CMD_SATN 0x1a /* Set ATN */ +#define ESP_CMD_RATN 0x1b /* De-assert ATN */ + +/* Group 3 commands: ESP must be in the MSGOUT or MSGIN state and be connected + * to a target as the initiator for these commands to work. + */ +#define ESP_CMD_SMSG 0x20 /* Send message */ +#define ESP_CMD_SSTAT 0x21 /* Send status */ +#define ESP_CMD_SDATA 0x22 /* Send data */ +#define ESP_CMD_DSEQ 0x23 /* Discontinue Sequence */ +#define ESP_CMD_TSEQ 0x24 /* Terminate Sequence */ +#define ESP_CMD_TCCSEQ 0x25 /* Target cmd cmplt sequence */ +#define ESP_CMD_DCNCT 0x27 /* Disconnect */ +#define ESP_CMD_RMSG 0x28 /* Receive Message */ +#define ESP_CMD_RCMD 0x29 /* Receive Command */ +#define ESP_CMD_RDATA 0x2a /* Receive Data */ +#define ESP_CMD_RCSEQ 0x2b /* Receive cmd sequence */ + +/* Group 4 commands: The ESP must be in the disconnected state and must + * not be connected to any targets as initiator for + * these commands to work. + */ +#define ESP_CMD_RSEL 0x40 /* Reselect */ +#define ESP_CMD_SEL 0x41 /* Select w/o ATN */ +#define ESP_CMD_SELA 0x42 /* Select w/ATN */ +#define ESP_CMD_SELAS 0x43 /* Select w/ATN & STOP */ +#define ESP_CMD_ESEL 0x44 /* Enable selection */ +#define ESP_CMD_DSEL 0x45 /* Disable selections */ +#define ESP_CMD_SA3 0x46 /* Select w/ATN3 */ +#define ESP_CMD_RSEL3 0x47 /* Reselect3 */ + +/* This bit enables the ESP's DMA on the SBus */ +#define ESP_CMD_DMA 0x80 /* Do DMA? */ + +/* ESP status register read-only */ +#define ESP_STAT_PIO 0x01 /* IO phase bit */ +#define ESP_STAT_PCD 0x02 /* CD phase bit */ +#define ESP_STAT_PMSG 0x04 /* MSG phase bit */ +#define ESP_STAT_PMASK 0x07 /* Mask of phase bits */ +#define ESP_STAT_TDONE 0x08 /* Transfer Completed */ +#define ESP_STAT_TCNT 0x10 /* Transfer Counter Is Zero */ +#define ESP_STAT_PERR 0x20 /* Parity error */ +#define ESP_STAT_SPAM 0x40 /* Real bad error */ +/* This indicates the 'interrupt pending' condition on esp236, it is a reserved + * bit on other revs of the ESP. + */ +#define ESP_STAT_INTR 0x80 /* Interrupt */ + +/* The status register can be masked with ESP_STAT_PMASK and compared + * with the following values to determine the current phase the ESP + * (at least thinks it) is in. For our purposes we also add our own + * software 'done' bit for our phase management engine. + */ +#define ESP_DOP (0) /* Data Out */ +#define ESP_DIP (ESP_STAT_PIO) /* Data In */ +#define ESP_CMDP (ESP_STAT_PCD) /* Command */ +#define ESP_STATP (ESP_STAT_PCD|ESP_STAT_PIO) /* Status */ +#define ESP_MOP (ESP_STAT_PMSG|ESP_STAT_PCD) /* Message Out */ +#define ESP_MIP (ESP_STAT_PMSG|ESP_STAT_PCD|ESP_STAT_PIO) /* Message In */ + +/* HME only: status 2 register */ +#define ESP_STAT2_SCHBIT 0x01 /* Upper bits 3-7 of sstep enabled */ +#define ESP_STAT2_FFLAGS 0x02 /* The fifo flags are now latched */ +#define ESP_STAT2_XCNT 0x04 /* The transfer counter is latched */ +#define ESP_STAT2_CREGA 0x08 /* The command reg is active now */ +#define ESP_STAT2_WIDE 0x10 /* Interface on this adapter is wide */ +#define ESP_STAT2_F1BYTE 0x20 /* There is one byte at top of fifo */ +#define ESP_STAT2_FMSB 0x40 /* Next byte in fifo is most significant */ +#define ESP_STAT2_FEMPTY 0x80 /* FIFO is empty */ + +/* ESP interrupt register read-only */ +#define ESP_INTR_S 0x01 /* Select w/o ATN */ +#define ESP_INTR_SATN 0x02 /* Select w/ATN */ +#define ESP_INTR_RSEL 0x04 /* Reselected */ +#define ESP_INTR_FDONE 0x08 /* Function done */ +#define ESP_INTR_BSERV 0x10 /* Bus service */ +#define ESP_INTR_DC 0x20 /* Disconnect */ +#define ESP_INTR_IC 0x40 /* Illegal command given */ +#define ESP_INTR_SR 0x80 /* SCSI bus reset detected */ + +/* ESP sequence step register read-only */ +#define ESP_STEP_VBITS 0x07 /* Valid bits */ +#define ESP_STEP_ASEL 0x00 /* Selection&Arbitrate cmplt */ +#define ESP_STEP_SID 0x01 /* One msg byte sent */ +#define ESP_STEP_NCMD 0x02 /* Was not in command phase */ +#define ESP_STEP_PPC 0x03 /* Early phase chg caused cmnd + * bytes to be lost + */ +#define ESP_STEP_FINI4 0x04 /* Command was sent ok */ + +/* Ho hum, some ESP's set the step register to this as well... */ +#define ESP_STEP_FINI5 0x05 +#define ESP_STEP_FINI6 0x06 +#define ESP_STEP_FINI7 0x07 + +/* ESP chip-test register read-write */ +#define ESP_TEST_TARG 0x01 /* Target test mode */ +#define ESP_TEST_INI 0x02 /* Initiator test mode */ +#define ESP_TEST_TS 0x04 /* Tristate test mode */ + +/* ESP unique ID register read-only, found on fas236+fas100a only */ +#define ESP_UID_F100A 0x00 /* ESP FAS100A */ +#define ESP_UID_F236 0x02 /* ESP FAS236 */ +#define ESP_UID_REV 0x07 /* ESP revision */ +#define ESP_UID_FAM 0xf8 /* ESP family */ + +/* ESP fifo flags register read-only */ +/* Note that the following implies a 16 byte FIFO on the ESP. */ +#define ESP_FF_FBYTES 0x1f /* Num bytes in FIFO */ +#define ESP_FF_ONOTZERO 0x20 /* offset ctr not zero (esp100) */ +#define ESP_FF_SSTEP 0xe0 /* Sequence step */ + +/* ESP clock conversion factor register write-only */ +#define ESP_CCF_F0 0x00 /* 35.01MHz - 40MHz */ +#define ESP_CCF_NEVER 0x01 /* Set it to this and die */ +#define ESP_CCF_F2 0x02 /* 10MHz */ +#define ESP_CCF_F3 0x03 /* 10.01MHz - 15MHz */ +#define ESP_CCF_F4 0x04 /* 15.01MHz - 20MHz */ +#define ESP_CCF_F5 0x05 /* 20.01MHz - 25MHz */ +#define ESP_CCF_F6 0x06 /* 25.01MHz - 30MHz */ +#define ESP_CCF_F7 0x07 /* 30.01MHz - 35MHz */ + +/* HME only... */ +#define ESP_BUSID_RESELID 0x10 +#define ESP_BUSID_CTR32BIT 0x40 + +#define ESP_BUS_TIMEOUT 250 /* In milli-seconds */ +#define ESP_TIMEO_CONST 8192 +#define ESP_NEG_DEFP(mhz, cfact) \ + ((ESP_BUS_TIMEOUT * ((mhz) / 1000)) / (8192 * (cfact))) +#define ESP_MHZ_TO_CYCLE(mhertz) ((1000000000) / ((mhertz) / 1000)) +#define ESP_TICK(ccf, cycle) ((7682 * (ccf) * (cycle) / 1000)) + +/* For slow to medium speed input clock rates we shoot for 5mb/s, but for high + * input clock rates we try to do 10mb/s although I don't think a transfer can + * even run that fast with an ESP even with DMA2 scatter gather pipelining. + */ +#define SYNC_DEFP_SLOW 0x32 /* 5mb/s */ +#define SYNC_DEFP_FAST 0x19 /* 10mb/s */ + +struct esp_cmd_priv { + union { + dma_addr_t dma_addr; + int num_sg; + } u; + + unsigned int cur_residue; + struct scatterlist *cur_sg; + unsigned int tot_residue; +}; +#define ESP_CMD_PRIV(CMD) ((struct esp_cmd_priv *)(&(CMD)->SCp)) + +enum esp_rev { + ESP100 = 0x00, /* NCR53C90 - very broken */ + ESP100A = 0x01, /* NCR53C90A */ + ESP236 = 0x02, + FAS236 = 0x03, + FAS100A = 0x04, + FAST = 0x05, + FASHME = 0x06, +}; + +struct esp_cmd_entry { + struct list_head list; + + struct scsi_cmnd *cmd; + + unsigned int saved_cur_residue; + struct scatterlist *saved_cur_sg; + unsigned int saved_tot_residue; + + u8 flags; +#define ESP_CMD_FLAG_WRITE 0x01 /* DMA is a write */ +#define ESP_CMD_FLAG_ABORT 0x02 /* being aborted */ +#define ESP_CMD_FLAG_AUTOSENSE 0x04 /* Doing automatic REQUEST_SENSE */ + + u8 tag[2]; + + u8 status; + u8 message; + + unsigned char *sense_ptr; + unsigned char *saved_sense_ptr; + dma_addr_t sense_dma; + + struct completion *eh_done; +}; + +/* XXX make this configurable somehow XXX */ +#define ESP_DEFAULT_TAGS 16 + +#define ESP_MAX_TARGET 16 +#define ESP_MAX_LUN 8 +#define ESP_MAX_TAG 256 + +struct esp_lun_data { + struct esp_cmd_entry *non_tagged_cmd; + int num_tagged; + int hold; + struct esp_cmd_entry *tagged_cmds[ESP_MAX_TAG]; +}; + +struct esp_target_data { + /* These are the ESP_STP, ESP_SOFF, and ESP_CFG3 register values which + * match the currently negotiated settings for this target. The SCSI + * protocol values are maintained in spi_{offset,period,wide}(starget). + */ + u8 esp_period; + u8 esp_offset; + u8 esp_config3; + + u8 flags; +#define ESP_TGT_WIDE 0x01 +#define ESP_TGT_DISCONNECT 0x02 +#define ESP_TGT_NEGO_WIDE 0x04 +#define ESP_TGT_NEGO_SYNC 0x08 +#define ESP_TGT_CHECK_NEGO 0x40 +#define ESP_TGT_BROKEN 0x80 + + /* When ESP_TGT_CHECK_NEGO is set, on the next scsi command to this + * device we will try to negotiate the following parameters. + */ + u8 nego_goal_period; + u8 nego_goal_offset; + u8 nego_goal_width; + u8 nego_goal_tags; + + struct scsi_target *starget; +}; + +struct esp_event_ent { + u8 type; +#define ESP_EVENT_TYPE_EVENT 0x01 +#define ESP_EVENT_TYPE_CMD 0x02 + u8 val; + + u8 sreg; + u8 seqreg; + u8 sreg2; + u8 ireg; + u8 select_state; + u8 event; + u8 __pad; +}; + +struct esp; +struct esp_driver_ops { + /* Read and write the ESP 8-bit registers. On some + * applications of the ESP chip the registers are at 4-byte + * instead of 1-byte intervals. + */ + void (*esp_write8)(struct esp *esp, u8 val, unsigned long reg); + u8 (*esp_read8)(struct esp *esp, unsigned long reg); + + /* Map and unmap DMA memory. Eventually the driver will be + * converted to the generic DMA API as soon as SBUS is able to + * cope with that. At such time we can remove this. + */ + dma_addr_t (*map_single)(struct esp *esp, void *buf, + size_t sz, int dir); + int (*map_sg)(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir); + void (*unmap_single)(struct esp *esp, dma_addr_t addr, + size_t sz, int dir); + void (*unmap_sg)(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir); + + /* Return non-zero if there is an IRQ pending. Usually this + * status bit lives in the DMA controller sitting in front of + * the ESP. This has to be accurate or else the ESP interrupt + * handler will not run. + */ + int (*irq_pending)(struct esp *esp); + + /* Reset the DMA engine entirely. On return, ESP interrupts + * should be enabled. Often the interrupt enabling is + * controlled in the DMA engine. + */ + void (*reset_dma)(struct esp *esp); + + /* Drain any pending DMA in the DMA engine after a transfer. + * This is for writes to memory. + */ + void (*dma_drain)(struct esp *esp); + + /* Invalidate the DMA engine after a DMA transfer. */ + void (*dma_invalidate)(struct esp *esp); + + /* Setup an ESP command that will use a DMA transfer. + * The 'esp_count' specifies what transfer length should be + * programmed into the ESP transfer counter registers, whereas + * the 'dma_count' is the length that should be programmed into + * the DMA controller. Usually they are the same. If 'write' + * is non-zero, this transfer is a write into memory. 'cmd' + * holds the ESP command that should be issued by calling + * scsi_esp_cmd() at the appropriate time while programming + * the DMA hardware. + */ + void (*send_dma_cmd)(struct esp *esp, u32 dma_addr, u32 esp_count, + u32 dma_count, int write, u8 cmd); + + /* Return non-zero if the DMA engine is reporting an error + * currently. + */ + int (*dma_error)(struct esp *esp); +}; + +#define ESP_MAX_MSG_SZ 8 +#define ESP_EVENT_LOG_SZ 32 + +#define ESP_QUICKIRQ_LIMIT 100 +#define ESP_RESELECT_TAG_LIMIT 2500 + +struct esp { + void __iomem *regs; + void __iomem *dma_regs; + + const struct esp_driver_ops *ops; + + struct Scsi_Host *host; + void *dev; + + struct esp_cmd_entry *active_cmd; + + struct list_head queued_cmds; + struct list_head active_cmds; + + u8 *command_block; + dma_addr_t command_block_dma; + + unsigned int data_dma_len; + + /* The following are used to determine the cause of an IRQ. Upon every + * IRQ entry we synchronize these with the hardware registers. + */ + u8 sreg; + u8 seqreg; + u8 sreg2; + u8 ireg; + + u32 prev_hme_dmacsr; + u8 prev_soff; + u8 prev_stp; + u8 prev_cfg3; + u8 __pad; + + struct list_head esp_cmd_pool; + + struct esp_target_data target[ESP_MAX_TARGET]; + + int fifo_cnt; + u8 fifo[16]; + + struct esp_event_ent esp_event_log[ESP_EVENT_LOG_SZ]; + int esp_event_cur; + + u8 msg_out[ESP_MAX_MSG_SZ]; + int msg_out_len; + + u8 msg_in[ESP_MAX_MSG_SZ]; + int msg_in_len; + + u8 bursts; + u8 config1; + u8 config2; + + u8 scsi_id; + u32 scsi_id_mask; + + enum esp_rev rev; + + u32 flags; +#define ESP_FLAG_DIFFERENTIAL 0x00000001 +#define ESP_FLAG_RESETTING 0x00000002 +#define ESP_FLAG_DOING_SLOWCMD 0x00000004 +#define ESP_FLAG_WIDE_CAPABLE 0x00000008 +#define ESP_FLAG_QUICKIRQ_CHECK 0x00000010 + + u8 select_state; +#define ESP_SELECT_NONE 0x00 /* Not selecting */ +#define ESP_SELECT_BASIC 0x01 /* Select w/o MSGOUT phase */ +#define ESP_SELECT_MSGOUT 0x02 /* Select with MSGOUT */ + + /* When we are not selecting, we are expecting an event. */ + u8 event; +#define ESP_EVENT_NONE 0x00 +#define ESP_EVENT_CMD_START 0x01 +#define ESP_EVENT_CMD_DONE 0x02 +#define ESP_EVENT_DATA_IN 0x03 +#define ESP_EVENT_DATA_OUT 0x04 +#define ESP_EVENT_DATA_DONE 0x05 +#define ESP_EVENT_MSGIN 0x06 +#define ESP_EVENT_MSGIN_MORE 0x07 +#define ESP_EVENT_MSGIN_DONE 0x08 +#define ESP_EVENT_MSGOUT 0x09 +#define ESP_EVENT_MSGOUT_DONE 0x0a +#define ESP_EVENT_STATUS 0x0b +#define ESP_EVENT_FREE_BUS 0x0c +#define ESP_EVENT_CHECK_PHASE 0x0d +#define ESP_EVENT_RESET 0x10 + + /* Probed in esp_get_clock_params() */ + u32 cfact; + u32 cfreq; + u32 ccycle; + u32 ctick; + u32 neg_defp; + u32 sync_defp; + + /* Computed in esp_reset_esp() */ + u32 max_period; + u32 min_period; + u32 radelay; + + /* Slow command state. */ + u8 *cmd_bytes_ptr; + int cmd_bytes_left; + + struct completion *eh_reset; + + struct sbus_dma *dma; +}; + +#define host_to_esp(host) ((struct esp *)(host)->hostdata) + +/* A front-end driver for the ESP chip should do the following in + * it's device probe routine: + * 1) Allocate the host and private area using scsi_host_alloc() + * with size 'sizeof(struct esp)'. The first argument to + * scsi_host_alloc() should be &scsi_esp_template. + * 2) Set host->max_id as appropriate. + * 3) Set esp->host to the scsi_host itself, and esp->dev + * to the device object pointer. + * 4) Hook up esp->ops to the front-end implementation. + * 5) If the ESP chip supports wide transfers, set ESP_FLAG_WIDE_CAPABLE + * in esp->flags. + * 6) Map the DMA and ESP chip registers. + * 7) DMA map the ESP command block, store the DMA address + * in esp->command_block_dma. + * 8) Register the scsi_esp_intr() interrupt handler. + * 9) Probe for and provide the following chip properties: + * esp->scsi_id (assign to esp->host->this_id too) + * esp->scsi_id_mask + * If ESP bus is differential, set ESP_FLAG_DIFFERENTIAL + * esp->cfreq + * DMA burst bit mask in esp->bursts, if necessary + * 10) Perform any actions necessary before the ESP device can + * be programmed for the first time. On some configs, for + * example, the DMA engine has to be reset before ESP can + * be programmed. + * 11) If necessary, call dev_set_drvdata() as needed. + * 12) Call scsi_esp_register() with prepared 'esp' structure + * and a device pointer if possible. + * 13) Check scsi_esp_register() return value, release all resources + * if an error was returned. + */ +extern struct scsi_host_template scsi_esp_template; +extern int scsi_esp_register(struct esp *, struct device *); + +extern void scsi_esp_unregister(struct esp *); +extern irqreturn_t scsi_esp_intr(int, void *); +extern void scsi_esp_cmd(struct esp *, u8); + +#endif /* !(_ESP_SCSI_H) */ diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index 8c81cec..60446b8 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -3091,6 +3091,7 @@ static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b) cmdp->u.raw64.direction = gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN; memcpy(cmdp->u.raw64.cmd,scp->cmnd,16); + cmdp->u.raw64.sg_ranz = 0; } else { cmdp->u.raw.reserved = 0; cmdp->u.raw.mdisc_time = 0; @@ -3107,6 +3108,7 @@ static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b) cmdp->u.raw.direction = gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN; memcpy(cmdp->u.raw.cmd,scp->cmnd,12); + cmdp->u.raw.sg_ranz = 0; } if (scp->use_sg) { diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 38c3a29..bd8e7f3 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -435,7 +435,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) struct class_device *cdev; struct Scsi_Host *shost = ERR_PTR(-ENXIO), *p; - down_read(&class->subsys.rwsem); + down(&class->sem); list_for_each_entry(cdev, &class->children, node) { p = class_to_shost(cdev); if (p->host_no == hostnum) { @@ -443,7 +443,7 @@ struct Scsi_Host *scsi_host_lookup(unsigned short hostnum) break; } } - up_read(&class->subsys.rwsem); + up(&class->sem); return shost; } diff --git a/drivers/scsi/ibmvscsi/ibmvstgt.c b/drivers/scsi/ibmvscsi/ibmvstgt.c index 4368ca0..a39a478 100644 --- a/drivers/scsi/ibmvscsi/ibmvstgt.c +++ b/drivers/scsi/ibmvscsi/ibmvstgt.c @@ -897,9 +897,9 @@ static int get_system_info(void) { struct device_node *rootdn; const char *id, *model, *name; - unsigned int *num; + const unsigned int *num; - rootdn = find_path_device("/"); + rootdn = of_find_node_by_path("/"); if (!rootdn) return -ENOENT; @@ -912,10 +912,11 @@ static int get_system_info(void) if (name) strncpy(partition_name, name, sizeof(partition_name)); - num = (unsigned int *) get_property(rootdn, "ibm,partition-no", NULL); + num = get_property(rootdn, "ibm,partition-no", NULL); if (num) partition_number = *num; + of_node_put(rootdn); return 0; } diff --git a/drivers/scsi/ibmvscsi/rpa_vscsi.c b/drivers/scsi/ibmvscsi/rpa_vscsi.c index 227c0f2..0a533f3 100644 --- a/drivers/scsi/ibmvscsi/rpa_vscsi.c +++ b/drivers/scsi/ibmvscsi/rpa_vscsi.c @@ -157,7 +157,7 @@ static void gather_partition_info(void) const unsigned int *p_number_ptr; /* Retrieve information about this partition */ - rootdn = find_path_device("/"); + rootdn = of_find_node_by_path("/"); if (!rootdn) { return; } @@ -169,6 +169,7 @@ static void gather_partition_info(void) p_number_ptr = get_property(rootdn, "ibm,partition-no", NULL); if (p_number_ptr) partition_number = *p_number_ptr; + of_node_put(rootdn); } static void set_adapter_info(struct ibmvscsi_host_data *hostdata) diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c index 8f6b5bf..2b5b8a9 100644 --- a/drivers/scsi/ide-scsi.c +++ b/drivers/scsi/ide-scsi.c @@ -801,15 +801,10 @@ static int idescsi_ide_open(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; struct ide_scsi_obj *scsi; - ide_drive_t *drive; if (!(scsi = ide_scsi_get(disk))) return -ENXIO; - drive = scsi->drive; - - drive->usage++; - return 0; } @@ -817,9 +812,6 @@ static int idescsi_ide_release(struct inode *inode, struct file *filp) { struct gendisk *disk = inode->i_bdev->bd_disk; struct ide_scsi_obj *scsi = ide_scsi_g(disk); - ide_drive_t *drive = scsi->drive; - - drive->usage--; ide_scsi_put(scsi); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 95045e3..e9bd299 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -3770,7 +3770,8 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg, * Return value: * 0 on success / non-zero on failure **/ -static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes) +static int ipr_sata_reset(struct ata_port *ap, unsigned int *classes, + unsigned long deadline) { struct ipr_sata_port *sata_port = ap->private_data; struct ipr_ioa_cfg *ioa_cfg = sata_port->ioa_cfg; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index afca45c..057fd7e0 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -518,6 +518,10 @@ lpfc_handle_eratt(struct lpfc_hba * phba) struct lpfc_sli *psli = &phba->sli; struct lpfc_sli_ring *pring; uint32_t event_data; + /* If the pci channel is offline, ignore possible errors, + * since we cannot communicate with the pci card anyway. */ + if (pci_channel_offline(phba->pcidev)) + return; if (phba->work_hs & HS_FFER6 || phba->work_hs & HS_FFER5) { @@ -1797,6 +1801,91 @@ lpfc_pci_remove_one(struct pci_dev *pdev) pci_set_drvdata(pdev, NULL); } +/** + * lpfc_io_error_detected - called when PCI error is detected + * @pdev: Pointer to PCI device + * @state: The current pci conneection state + * + * This function is called after a PCI bus error affecting + * this device has been detected. + */ +static pci_ers_result_t lpfc_io_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; + struct lpfc_sli *psli = &phba->sli; + struct lpfc_sli_ring *pring; + + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + + pci_disable_device(pdev); + /* + * There may be I/Os dropped by the firmware. + * Error iocb (I/O) on txcmplq and let the SCSI layer + * retry it after re-establishing link. + */ + pring = &psli->ring[psli->fcp_ring]; + lpfc_sli_abort_iocb_ring(phba, pring); + + /* Request a slot reset. */ + return PCI_ERS_RESULT_NEED_RESET; +} + +/** + * lpfc_io_slot_reset - called after the pci bus has been reset. + * @pdev: Pointer to PCI device + * + * Restart the card from scratch, as if from a cold-boot. + */ +static pci_ers_result_t lpfc_io_slot_reset(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; + struct lpfc_sli *psli = &phba->sli; + int bars = pci_select_bars(pdev, IORESOURCE_MEM); + + dev_printk(KERN_INFO, &pdev->dev, "recovering from a slot reset.\n"); + if (pci_enable_device_bars(pdev, bars)) { + printk(KERN_ERR "lpfc: Cannot re-enable " + "PCI device after reset.\n"); + return PCI_ERS_RESULT_DISCONNECT; + } + + pci_set_master(pdev); + + /* Re-establishing Link */ + spin_lock_irq(phba->host->host_lock); + phba->fc_flag |= FC_ESTABLISH_LINK; + psli->sli_flag &= ~LPFC_SLI2_ACTIVE; + spin_unlock_irq(phba->host->host_lock); + + + /* Take device offline; this will perform cleanup */ + lpfc_offline(phba); + lpfc_sli_brdrestart(phba); + + return PCI_ERS_RESULT_RECOVERED; +} + +/** + * lpfc_io_resume - called when traffic can start flowing again. + * @pdev: Pointer to PCI device + * + * This callback is called when the error recovery driver tells us that + * its OK to resume normal operation. + */ +static void lpfc_io_resume(struct pci_dev *pdev) +{ + struct Scsi_Host *host = pci_get_drvdata(pdev); + struct lpfc_hba *phba = (struct lpfc_hba *)host->hostdata; + + if (lpfc_online(phba) == 0) { + mod_timer(&phba->fc_estabtmo, jiffies + HZ * 60); + } +} + static struct pci_device_id lpfc_id_table[] = { {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_VIPER, PCI_ANY_ID, PCI_ANY_ID, }, @@ -1857,11 +1946,18 @@ static struct pci_device_id lpfc_id_table[] = { MODULE_DEVICE_TABLE(pci, lpfc_id_table); +static struct pci_error_handlers lpfc_err_handler = { + .error_detected = lpfc_io_error_detected, + .slot_reset = lpfc_io_slot_reset, + .resume = lpfc_io_resume, +}; + static struct pci_driver lpfc_driver = { .name = LPFC_DRIVER_NAME, .id_table = lpfc_id_table, .probe = lpfc_pci_probe_one, .remove = __devexit_p(lpfc_pci_remove_one), + .err_handler = &lpfc_err_handler, }; static int __init diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index a4128e1..9fb6960 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -2104,6 +2104,10 @@ lpfc_sli_issue_mbox(struct lpfc_hba * phba, LPFC_MBOXQ_t * pmbox, uint32_t flag) volatile uint32_t word0, ldata; void __iomem *to_slim; + /* If the PCI channel is in offline state, do not post mbox. */ + if (unlikely(pci_channel_offline(phba->pcidev))) + return MBX_NOT_FINISHED; + psli = &phba->sli; spin_lock_irqsave(phba->host->host_lock, drvr_flag); @@ -2407,6 +2411,10 @@ lpfc_sli_issue_iocb(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, struct lpfc_iocbq *nextiocb; IOCB_t *iocb; + /* If the PCI channel is in offline state, do not post iocbs. */ + if (unlikely(pci_channel_offline(phba->pcidev))) + return IOCB_ERROR; + /* * We should never get an IOCB if we are in a < LINK_DOWN state */ @@ -3154,6 +3162,10 @@ lpfc_intr_handler(int irq, void *dev_id) if (unlikely(!phba)) return IRQ_NONE; + /* If the pci channel is offline, ignore all the interrupts. */ + if (unlikely(pci_channel_offline(phba->pcidev))) + return IRQ_NONE; + phba->sli.slistat.sli_intr++; /* diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 808a1b8..0aa3304 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -5072,7 +5072,7 @@ static int __init megaraid_init(void) "megaraid: failed to create megaraid root\n"); } #endif - error = pci_module_init(&megaraid_pci_driver); + error = pci_register_driver(&megaraid_pci_driver); if (error) { #ifdef CONFIG_PROC_FS remove_proc_entry("megaraid", &proc_root); diff --git a/drivers/scsi/megaraid/megaraid_sas.c b/drivers/scsi/megaraid/megaraid_sas.c index 15e24fc..7a81267 100644 --- a/drivers/scsi/megaraid/megaraid_sas.c +++ b/drivers/scsi/megaraid/megaraid_sas.c @@ -10,11 +10,13 @@ * 2 of the License, or (at your option) any later version. * * FILE : megaraid_sas.c - * Version : v00.00.03.05 + * Version : v00.00.03.10-rc1 * * Authors: - * Sreenivas Bagalkote <Sreenivas.Bagalkote@lsi.com> - * Sumant Patro <Sumant.Patro@lsi.com> + * (email-id : megaraidlinux@lsi.com) + * Sreenivas Bagalkote + * Sumant Patro + * Bo Yang * * List of supported controllers * @@ -35,6 +37,7 @@ #include <asm/uaccess.h> #include <linux/fs.h> #include <linux/compat.h> +#include <linux/blkdev.h> #include <linux/mutex.h> #include <scsi/scsi.h> @@ -841,6 +844,11 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) instance = (struct megasas_instance *) scmd->device->host->hostdata; + + /* Don't process if we have already declared adapter dead */ + if (instance->hw_crit_error) + return SCSI_MLQUEUE_HOST_BUSY; + scmd->scsi_done = done; scmd->result = 0; @@ -850,6 +858,18 @@ megasas_queue_command(struct scsi_cmnd *scmd, void (*done) (struct scsi_cmnd *)) goto out_done; } + switch (scmd->cmnd[0]) { + case SYNCHRONIZE_CACHE: + /* + * FW takes care of flush cache on its own + * No need to send it down + */ + scmd->result = DID_OK << 16; + goto out_done; + default: + break; + } + cmd = megasas_get_cmd(instance); if (!cmd) return SCSI_MLQUEUE_HOST_BUSY; @@ -1010,6 +1030,49 @@ static int megasas_reset_bus_host(struct scsi_cmnd *scmd) } /** + * megasas_bios_param - Returns disk geometry for a disk + * @sdev: device handle + * @bdev: block device + * @capacity: drive capacity + * @geom: geometry parameters + */ +static int +megasas_bios_param(struct scsi_device *sdev, struct block_device *bdev, + sector_t capacity, int geom[]) +{ + int heads; + int sectors; + sector_t cylinders; + unsigned long tmp; + /* Default heads (64) & sectors (32) */ + heads = 64; + sectors = 32; + + tmp = heads * sectors; + cylinders = capacity; + + sector_div(cylinders, tmp); + + /* + * Handle extended translation size for logical drives > 1Gb + */ + + if (capacity >= 0x200000) { + heads = 255; + sectors = 63; + tmp = heads*sectors; + cylinders = capacity; + sector_div(cylinders, tmp); + } + + geom[0] = heads; + geom[1] = sectors; + geom[2] = cylinders; + + return 0; +} + +/** * megasas_service_aen - Processes an event notification * @instance: Adapter soft state * @cmd: AEN command completed by the ISR @@ -1049,6 +1112,7 @@ static struct scsi_host_template megasas_template = { .eh_device_reset_handler = megasas_reset_device, .eh_bus_reset_handler = megasas_reset_bus_host, .eh_host_reset_handler = megasas_reset_bus_host, + .bios_param = megasas_bios_param, .use_clustering = ENABLE_CLUSTERING, }; @@ -1282,11 +1346,13 @@ megasas_deplete_reply_queue(struct megasas_instance *instance, u8 alt_status) if(instance->instancet->clear_intr(instance->reg_set)) return IRQ_NONE; + if (instance->hw_crit_error) + goto out_done; /* * Schedule the tasklet for cmd completion */ tasklet_schedule(&instance->isr_tasklet); - +out_done: return IRQ_HANDLED; } @@ -1741,6 +1807,10 @@ static void megasas_complete_cmd_dpc(unsigned long instance_addr) struct megasas_cmd *cmd; struct megasas_instance *instance = (struct megasas_instance *)instance_addr; + /* If we have already declared adapter dead, donot complete cmds */ + if (instance->hw_crit_error) + return; + producer = *instance->producer; consumer = *instance->consumer; @@ -2655,9 +2725,9 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, * For each user buffer, create a mirror buffer and copy in */ for (i = 0; i < ioc->sge_count; i++) { - kbuff_arr[i] = pci_alloc_consistent(instance->pdev, + kbuff_arr[i] = dma_alloc_coherent(&instance->pdev->dev, ioc->sgl[i].iov_len, - &buf_handle); + &buf_handle, GFP_KERNEL); if (!kbuff_arr[i]) { printk(KERN_DEBUG "megasas: Failed to alloc " "kernel SGL buffer for IOCTL \n"); @@ -2684,8 +2754,8 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, } if (ioc->sense_len) { - sense = pci_alloc_consistent(instance->pdev, ioc->sense_len, - &sense_handle); + sense = dma_alloc_coherent(&instance->pdev->dev, ioc->sense_len, + &sense_handle, GFP_KERNEL); if (!sense) { error = -ENOMEM; goto out; @@ -2744,12 +2814,12 @@ megasas_mgmt_fw_ioctl(struct megasas_instance *instance, out: if (sense) { - pci_free_consistent(instance->pdev, ioc->sense_len, + dma_free_coherent(&instance->pdev->dev, ioc->sense_len, sense, sense_handle); } for (i = 0; i < ioc->sge_count && kbuff_arr[i]; i++) { - pci_free_consistent(instance->pdev, + dma_free_coherent(&instance->pdev->dev, kern_sge32[i].length, kbuff_arr[i], kern_sge32[i].phys_addr); } diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index cacb3ad..e862992 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -18,9 +18,9 @@ /* * MegaRAID SAS Driver meta data */ -#define MEGASAS_VERSION "00.00.03.05" -#define MEGASAS_RELDATE "Oct 02, 2006" -#define MEGASAS_EXT_VERSION "Mon Oct 02 11:21:32 PDT 2006" +#define MEGASAS_VERSION "00.00.03.10-rc1" +#define MEGASAS_RELDATE "Feb 14, 2007" +#define MEGASAS_EXT_VERSION "Wed Feb 14 10:14:25 PST 2007" /* * Device IDs diff --git a/drivers/scsi/osst.c b/drivers/scsi/osst.c index 9668b73..a967fad 100644 --- a/drivers/scsi/osst.c +++ b/drivers/scsi/osst.c @@ -5574,14 +5574,14 @@ static ssize_t osst_version_show(struct device_driver *ddd, char *buf) static DRIVER_ATTR(version, S_IRUGO, osst_version_show, NULL); -static int osst_create_driverfs_files(struct device_driver *driverfs) +static int osst_create_sysfs_files(struct device_driver *sysfs) { - return driver_create_file(driverfs, &driver_attr_version); + return driver_create_file(sysfs, &driver_attr_version); } -static void osst_remove_driverfs_files(struct device_driver *driverfs) +static void osst_remove_sysfs_files(struct device_driver *sysfs) { - driver_remove_file(driverfs, &driver_attr_version); + driver_remove_file(sysfs, &driver_attr_version); } /* @@ -5953,7 +5953,7 @@ static int __init init_osst(void) if (err) goto err_out_chrdev; - err = osst_create_driverfs_files(&osst_template.gendrv); + err = osst_create_sysfs_files(&osst_template.gendrv); if (err) goto err_out_scsidrv; @@ -5973,7 +5973,7 @@ static void __exit exit_osst (void) int i; struct osst_tape * STp; - osst_remove_driverfs_files(&osst_template.gendrv); + osst_remove_sysfs_files(&osst_template.gendrv); scsi_unregister_driver(&osst_template.gendrv); unregister_chrdev(OSST_MAJOR, "osst"); osst_sysfs_cleanup(); diff --git a/drivers/scsi/qlogicpti.c b/drivers/scsi/qlogicpti.c index 9b827ce..c4195ea 100644 --- a/drivers/scsi/qlogicpti.c +++ b/drivers/scsi/qlogicpti.c @@ -1281,7 +1281,7 @@ static struct scsi_cmnd *qlogicpti_intr_handler(struct qlogicpti *qpti) (struct scatterlist *)Cmnd->request_buffer, Cmnd->use_sg, Cmnd->sc_data_direction); - } else { + } else if (Cmnd->request_bufflen) { sbus_unmap_single(qpti->sdev, (__u32)((unsigned long)Cmnd->SCp.ptr), Cmnd->request_bufflen, @@ -1403,7 +1403,7 @@ static int __devinit qpti_sbus_probe(struct of_device *dev, const struct of_devi struct scsi_host_template *tpnt = match->data; struct Scsi_Host *host; struct qlogicpti *qpti; - char *fcode; + const char *fcode; /* Sometimes Antares cards come up not completely * setup, and we get a report of a zero IRQ. diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index b8edcf5..918bb60 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -716,7 +716,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, */ if (copy_sense) { if (!SCSI_SENSE_VALID(scmd)) { - memcpy(scmd->sense_buffer, scmd->request_buffer, + memcpy(scmd->sense_buffer, page_address(sgl.page), sizeof(scmd->sense_buffer)); } __free_page(sgl.page); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9f7482d..05d79af 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -31,7 +31,7 @@ #define SG_MEMPOOL_NR ARRAY_SIZE(scsi_sg_pools) -#define SG_MEMPOOL_SIZE 32 +#define SG_MEMPOOL_SIZE 2 struct scsi_host_sg_pool { size_t size; diff --git a/drivers/scsi/scsi_netlink.c b/drivers/scsi/scsi_netlink.c index 1b59b27..4bf9aa547 100644 --- a/drivers/scsi/scsi_netlink.c +++ b/drivers/scsi/scsi_netlink.c @@ -50,7 +50,7 @@ scsi_nl_rcv_msg(struct sk_buff *skb) while (skb->len >= NLMSG_SPACE(0)) { err = 0; - nlh = (struct nlmsghdr *) skb->data; + nlh = nlmsg_hdr(skb); if ((nlh->nlmsg_len < (sizeof(*nlh) + sizeof(*hdr))) || (skb->len < nlh->nlmsg_len)) { printk(KERN_WARNING "%s: discarding partial skb\n", @@ -168,7 +168,8 @@ scsi_netlink_init(void) } scsi_nl_sock = netlink_kernel_create(NETLINK_SCSITRANSPORT, - SCSI_NL_GRP_CNT, scsi_nl_rcv, THIS_MODULE); + SCSI_NL_GRP_CNT, scsi_nl_rcv, NULL, + THIS_MODULE); if (!scsi_nl_sock) { printk(KERN_ERR "%s: register of recieve handler failed\n", __FUNCTION__); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 69d6e9b..bb6f051 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -179,9 +179,8 @@ static int proc_print_scsidevice(struct device *dev, void *data) seq_printf(s, "\n"); seq_printf(s, " Type: %s ", scsi_device_type(sdev->type)); - seq_printf(s, " ANSI" - " SCSI revision: %02x", (sdev->scsi_level - 1) ? - sdev->scsi_level - 1 : 1); + seq_printf(s, " ANSI SCSI revision: %02x", + sdev->scsi_level - (sdev->scsi_level > 1)); if (sdev->scsi_level == 2) seq_printf(s, " CCS\n"); else diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index a43b9ec..0949145 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -54,7 +54,7 @@ #define SCSI_TIMEOUT (2*HZ) /* - * Prefix values for the SCSI id's (stored in driverfs name field) + * Prefix values for the SCSI id's (stored in sysfs name field) */ #define SCSI_UID_SER_NUM 'S' #define SCSI_UID_UNKNOWN 'Z' @@ -385,6 +385,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, INIT_LIST_HEAD(&starget->siblings); INIT_LIST_HEAD(&starget->devices); starget->state = STARGET_RUNNING; + starget->scsi_level = SCSI_2; retry: spin_lock_irqsave(shost->host_lock, flags); @@ -654,6 +655,19 @@ static int scsi_probe_lun(struct scsi_device *sdev, unsigned char *inq_result, * short INQUIRY), an abort here prevents any further use of the * device, including spin up. * + * On the whole, the best approach seems to be to assume the first + * 36 bytes are valid no matter what the device says. That's + * better than copying < 36 bytes to the inquiry-result buffer + * and displaying garbage for the Vendor, Product, or Revision + * strings. + */ + if (sdev->inquiry_len < 36) { + printk(KERN_INFO "scsi scan: INQUIRY result too short (%d)," + " using 36\n", sdev->inquiry_len); + sdev->inquiry_len = 36; + } + + /* * Related to the above issue: * * XXX Devices (disk or all?) should be sent a TEST UNIT READY, diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 259c90c..939de0d 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -452,10 +452,22 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha } static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); +static void sdev_store_delete_callback(struct device *dev) +{ + scsi_remove_device(to_scsi_device(dev)); +} + static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - scsi_remove_device(to_scsi_device(dev)); + int rc; + + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, sdev_store_delete_callback); + if (rc) + count = rc; return count; }; static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); @@ -922,7 +934,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, "%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); - sdev->scsi_level = SCSI_2; + sdev->scsi_level = starget->scsi_level; transport_setup_device(&sdev->sdev_gendev); spin_lock_irqsave(shost->host_lock, flags); list_add_tail(&sdev->same_target_siblings, &starget->devices); diff --git a/drivers/scsi/scsi_tgt_if.c b/drivers/scsi/scsi_tgt_if.c index f2344ab..0e08817 100644 --- a/drivers/scsi/scsi_tgt_if.c +++ b/drivers/scsi/scsi_tgt_if.c @@ -33,6 +33,14 @@ #include "scsi_tgt_priv.h" +#if TGT_RING_SIZE < PAGE_SIZE +# define TGT_RING_SIZE PAGE_SIZE +#endif + +#define TGT_RING_PAGES (TGT_RING_SIZE >> PAGE_SHIFT) +#define TGT_EVENT_PER_PAGE (PAGE_SIZE / sizeof(struct tgt_event)) +#define TGT_MAX_EVENTS (TGT_EVENT_PER_PAGE * TGT_RING_PAGES) + struct tgt_ring { u32 tr_idx; unsigned long tr_pages[TGT_RING_PAGES]; diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 0d3c10f..58afdb4 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -855,7 +855,7 @@ static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR, /* * Note: in the target show function we recognize when the remote - * port is in the heirarchy and do not allow the driver to get + * port is in the hierarchy and do not allow the driver to get * involved in sysfs functions. The driver only gets involved if * it's the "old" style that doesn't use rports. */ diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index ce0d14a..aabaa05 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1081,7 +1081,7 @@ iscsi_if_rx(struct sock *sk, int len) struct nlmsghdr *nlh; struct iscsi_uevent *ev; - nlh = (struct nlmsghdr *)skb->data; + nlh = nlmsg_hdr(skb); if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len) { break; @@ -1435,7 +1435,7 @@ static __init int iscsi_transport_init(void) if (err) goto unregister_conn_class; - nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, + nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, NULL, THIS_MODULE); if (!nls) { err = -ENOBUFS; diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 6d39150e..b2ef71a 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -500,7 +500,7 @@ struct sas_phy *sas_phy_alloc(struct device *parent, int number) EXPORT_SYMBOL(sas_phy_alloc); /** - * sas_phy_add -- add a SAS PHY to the device hierachy + * sas_phy_add -- add a SAS PHY to the device hierarchy * @phy: The PHY to be added * * Publishes a SAS PHY to the rest of the system. @@ -1265,7 +1265,7 @@ struct sas_rphy *sas_expander_alloc(struct sas_port *parent, EXPORT_SYMBOL(sas_expander_alloc); /** - * sas_rphy_add -- add a SAS remote PHY to the device hierachy + * sas_rphy_add -- add a SAS remote PHY to the device hierarchy * @rphy: The remote PHY to be added * * Publishes a SAS remote PHY to the rest of the system. diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3f048bd..5a8f55f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1269,9 +1269,18 @@ repeat: /* Some devices return the total number of sectors, not the * highest sector number. Make the necessary adjustment. */ - if (sdp->fix_capacity) + if (sdp->fix_capacity) { --sdkp->capacity; + /* Some devices have version which report the correct sizes + * and others which do not. We guess size according to a heuristic + * and err on the side of lowering the capacity. */ + } else { + if (sdp->guess_capacity) + if (sdkp->capacity & 0x01) /* odd sizes are odd */ + --sdkp->capacity; + } + got_data: if (sector_size == 0) { sector_size = 512; diff --git a/drivers/scsi/sgiwd93.c b/drivers/scsi/sgiwd93.c index e81f97a..a15752b 100644 --- a/drivers/scsi/sgiwd93.c +++ b/drivers/scsi/sgiwd93.c @@ -244,9 +244,10 @@ static struct Scsi_Host * __init sgiwd93_setup_scsi( regs.SASR = wdregs + 3; regs.SCMD = wdregs + 7; - wd33c93_init(host, regs, dma_setup, dma_stop, WD33C93_FS_16_20); + wd33c93_init(host, regs, dma_setup, dma_stop, WD33C93_FS_MHZ(20)); - hdata->wh.no_sync = 0; + if (hdata->wh.no_sync == 0xff) + hdata->wh.no_sync = 0; if (request_irq(irq, sgiwd93_intr, 0, "SGI WD93", (void *) host)) { printk(KERN_WARNING "sgiwd93: Could not register irq %d " diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 3d2e023..98d8411 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -195,8 +195,8 @@ static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int); static int st_probe(struct device *); static int st_remove(struct device *); -static int do_create_driverfs_files(void); -static void do_remove_driverfs_files(void); +static int do_create_sysfs_files(void); +static void do_remove_sysfs_files(void); static int do_create_class_files(struct scsi_tape *, int, int); static struct scsi_driver st_template = { @@ -4193,7 +4193,7 @@ static int __init init_st(void) if (err) goto err_chrdev; - err = do_create_driverfs_files(); + err = do_create_sysfs_files(); if (err) goto err_scsidrv; @@ -4211,7 +4211,7 @@ err_class: static void __exit exit_st(void) { - do_remove_driverfs_files(); + do_remove_sysfs_files(); scsi_unregister_driver(&st_template.gendrv); unregister_chrdev_region(MKDEV(SCSI_TAPE_MAJOR, 0), ST_MAX_TAPE_ENTRIES); @@ -4249,43 +4249,43 @@ static ssize_t st_version_show(struct device_driver *ddd, char *buf) } static DRIVER_ATTR(version, S_IRUGO, st_version_show, NULL); -static int do_create_driverfs_files(void) +static int do_create_sysfs_files(void) { - struct device_driver *driverfs = &st_template.gendrv; + struct device_driver *sysfs = &st_template.gendrv; int err; - err = driver_create_file(driverfs, &driver_attr_try_direct_io); + err = driver_create_file(sysfs, &driver_attr_try_direct_io); if (err) return err; - err = driver_create_file(driverfs, &driver_attr_fixed_buffer_size); + err = driver_create_file(sysfs, &driver_attr_fixed_buffer_size); if (err) goto err_try_direct_io; - err = driver_create_file(driverfs, &driver_attr_max_sg_segs); + err = driver_create_file(sysfs, &driver_attr_max_sg_segs); if (err) goto err_attr_fixed_buf; - err = driver_create_file(driverfs, &driver_attr_version); + err = driver_create_file(sysfs, &driver_attr_version); if (err) goto err_attr_max_sg; return 0; err_attr_max_sg: - driver_remove_file(driverfs, &driver_attr_max_sg_segs); + driver_remove_file(sysfs, &driver_attr_max_sg_segs); err_attr_fixed_buf: - driver_remove_file(driverfs, &driver_attr_fixed_buffer_size); + driver_remove_file(sysfs, &driver_attr_fixed_buffer_size); err_try_direct_io: - driver_remove_file(driverfs, &driver_attr_try_direct_io); + driver_remove_file(sysfs, &driver_attr_try_direct_io); return err; } -static void do_remove_driverfs_files(void) +static void do_remove_sysfs_files(void) { - struct device_driver *driverfs = &st_template.gendrv; + struct device_driver *sysfs = &st_template.gendrv; - driver_remove_file(driverfs, &driver_attr_version); - driver_remove_file(driverfs, &driver_attr_max_sg_segs); - driver_remove_file(driverfs, &driver_attr_fixed_buffer_size); - driver_remove_file(driverfs, &driver_attr_try_direct_io); + driver_remove_file(sysfs, &driver_attr_version); + driver_remove_file(sysfs, &driver_attr_max_sg_segs); + driver_remove_file(sysfs, &driver_attr_fixed_buffer_size); + driver_remove_file(sysfs, &driver_attr_try_direct_io); } diff --git a/drivers/scsi/sun_esp.c b/drivers/scsi/sun_esp.c new file mode 100644 index 0000000..8c766bc --- /dev/null +++ b/drivers/scsi/sun_esp.c @@ -0,0 +1,634 @@ +/* sun_esp.c: ESP front-end for Sparc SBUS systems. + * + * Copyright (C) 2007 David S. Miller (davem@davemloft.net) + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/module.h> +#include <linux/init.h> + +#include <asm/irq.h> +#include <asm/io.h> +#include <asm/dma.h> + +#include <asm/sbus.h> + +#include <scsi/scsi_host.h> + +#include "esp_scsi.h" + +#define DRV_MODULE_NAME "sun_esp" +#define PFX DRV_MODULE_NAME ": " +#define DRV_VERSION "1.000" +#define DRV_MODULE_RELDATE "April 19, 2007" + +#define dma_read32(REG) \ + sbus_readl(esp->dma_regs + (REG)) +#define dma_write32(VAL, REG) \ + sbus_writel((VAL), esp->dma_regs + (REG)) + +static int __devinit esp_sbus_find_dma(struct esp *esp, struct sbus_dev *dma_sdev) +{ + struct sbus_dev *sdev = esp->dev; + struct sbus_dma *dma; + + if (dma_sdev != NULL) { + for_each_dvma(dma) { + if (dma->sdev == dma_sdev) + break; + } + } else { + for_each_dvma(dma) { + if (dma->sdev == NULL) + break; + + /* If bus + slot are the same and it has the + * correct OBP name, it's ours. + */ + if (sdev->bus == dma->sdev->bus && + sdev->slot == dma->sdev->slot && + (!strcmp(dma->sdev->prom_name, "dma") || + !strcmp(dma->sdev->prom_name, "espdma"))) + break; + } + } + + if (dma == NULL) { + printk(KERN_ERR PFX "[%s] Cannot find dma.\n", + sdev->ofdev.node->full_name); + return -ENODEV; + } + esp->dma = dma; + esp->dma_regs = dma->regs; + + return 0; + +} + +static int __devinit esp_sbus_map_regs(struct esp *esp, int hme) +{ + struct sbus_dev *sdev = esp->dev; + struct resource *res; + + /* On HME, two reg sets exist, first is DVMA, + * second is ESP registers. + */ + if (hme) + res = &sdev->resource[1]; + else + res = &sdev->resource[0]; + + esp->regs = sbus_ioremap(res, 0, SBUS_ESP_REG_SIZE, "ESP"); + if (!esp->regs) + return -ENOMEM; + + return 0; +} + +static int __devinit esp_sbus_map_command_block(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + + esp->command_block = sbus_alloc_consistent(sdev, 16, + &esp->command_block_dma); + if (!esp->command_block) + return -ENOMEM; + return 0; +} + +static int __devinit esp_sbus_register_irq(struct esp *esp) +{ + struct Scsi_Host *host = esp->host; + struct sbus_dev *sdev = esp->dev; + + host->irq = sdev->irqs[0]; + return request_irq(host->irq, scsi_esp_intr, IRQF_SHARED, "ESP", esp); +} + +static void __devinit esp_get_scsi_id(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + + esp->scsi_id = of_getintprop_default(dp, "initiator-id", 0xff); + if (esp->scsi_id != 0xff) + goto done; + + esp->scsi_id = of_getintprop_default(dp, "scsi-initiator-id", 0xff); + if (esp->scsi_id != 0xff) + goto done; + + if (!sdev->bus) { + /* SUN4 */ + esp->scsi_id = 7; + goto done; + } + + esp->scsi_id = of_getintprop_default(sdev->bus->ofdev.node, + "scsi-initiator-id", 7); + +done: + esp->host->this_id = esp->scsi_id; + esp->scsi_id_mask = (1 << esp->scsi_id); +} + +static void __devinit esp_get_differential(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + + if (of_find_property(dp, "differential", NULL)) + esp->flags |= ESP_FLAG_DIFFERENTIAL; + else + esp->flags &= ~ESP_FLAG_DIFFERENTIAL; +} + +static void __devinit esp_get_clock_params(struct esp *esp) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + struct device_node *bus_dp; + int fmhz; + + bus_dp = NULL; + if (sdev != NULL && sdev->bus != NULL) + bus_dp = sdev->bus->ofdev.node; + + fmhz = of_getintprop_default(dp, "clock-frequency", 0); + if (fmhz == 0) + fmhz = (!bus_dp) ? 0 : + of_getintprop_default(bus_dp, "clock-frequency", 0); + + esp->cfreq = fmhz; +} + +static void __devinit esp_get_bursts(struct esp *esp, struct sbus_dev *dma) +{ + struct sbus_dev *sdev = esp->dev; + struct device_node *dp = sdev->ofdev.node; + u8 bursts; + + bursts = of_getintprop_default(dp, "burst-sizes", 0xff); + if (dma) { + struct device_node *dma_dp = dma->ofdev.node; + u8 val = of_getintprop_default(dma_dp, "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; + } + + if (sdev->bus) { + u8 val = of_getintprop_default(sdev->bus->ofdev.node, + "burst-sizes", 0xff); + if (val != 0xff) + bursts &= val; + } + + if (bursts == 0xff || + (bursts & DMA_BURST16) == 0 || + (bursts & DMA_BURST32) == 0) + bursts = (DMA_BURST32 - 1); + + esp->bursts = bursts; +} + +static void __devinit esp_sbus_get_props(struct esp *esp, struct sbus_dev *espdma) +{ + esp_get_scsi_id(esp); + esp_get_differential(esp); + esp_get_clock_params(esp); + esp_get_bursts(esp, espdma); +} + +static void sbus_esp_write8(struct esp *esp, u8 val, unsigned long reg) +{ + sbus_writeb(val, esp->regs + (reg * 4UL)); +} + +static u8 sbus_esp_read8(struct esp *esp, unsigned long reg) +{ + return sbus_readb(esp->regs + (reg * 4UL)); +} + +static dma_addr_t sbus_esp_map_single(struct esp *esp, void *buf, + size_t sz, int dir) +{ + return sbus_map_single(esp->dev, buf, sz, dir); +} + +static int sbus_esp_map_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + return sbus_map_sg(esp->dev, sg, num_sg, dir); +} + +static void sbus_esp_unmap_single(struct esp *esp, dma_addr_t addr, + size_t sz, int dir) +{ + sbus_unmap_single(esp->dev, addr, sz, dir); +} + +static void sbus_esp_unmap_sg(struct esp *esp, struct scatterlist *sg, + int num_sg, int dir) +{ + sbus_unmap_sg(esp->dev, sg, num_sg, dir); +} + +static int sbus_esp_irq_pending(struct esp *esp) +{ + if (dma_read32(DMA_CSR) & (DMA_HNDL_INTR | DMA_HNDL_ERROR)) + return 1; + return 0; +} + +static void sbus_esp_reset_dma(struct esp *esp) +{ + int can_do_burst16, can_do_burst32, can_do_burst64; + int can_do_sbus64, lim; + u32 val; + + can_do_burst16 = (esp->bursts & DMA_BURST16) != 0; + can_do_burst32 = (esp->bursts & DMA_BURST32) != 0; + can_do_burst64 = 0; + can_do_sbus64 = 0; + if (sbus_can_dma_64bit(esp->dev)) + can_do_sbus64 = 1; + if (sbus_can_burst64(esp->sdev)) + can_do_burst64 = (esp->bursts & DMA_BURST64) != 0; + + /* Put the DVMA into a known state. */ + if (esp->dma->revision != dvmahme) { + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_RST_SCSI, DMA_CSR); + dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); + } + switch (esp->dma->revision) { + case dvmahme: + dma_write32(DMA_RESET_FAS366, DMA_CSR); + dma_write32(DMA_RST_SCSI, DMA_CSR); + + esp->prev_hme_dmacsr = (DMA_PARITY_OFF | DMA_2CLKS | + DMA_SCSI_DISAB | DMA_INT_ENAB); + + esp->prev_hme_dmacsr &= ~(DMA_ENABLE | DMA_ST_WRITE | + DMA_BRST_SZ); + + if (can_do_burst64) + esp->prev_hme_dmacsr |= DMA_BRST64; + else if (can_do_burst32) + esp->prev_hme_dmacsr |= DMA_BRST32; + + if (can_do_sbus64) { + esp->prev_hme_dmacsr |= DMA_SCSI_SBUS64; + sbus_set_sbus64(esp->dev, esp->bursts); + } + + lim = 1000; + while (dma_read32(DMA_CSR) & DMA_PEND_READ) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA_PEND_READ " + "will not clear!\n", + esp->host->unique_id); + break; + } + udelay(1); + } + + dma_write32(0, DMA_CSR); + dma_write32(esp->prev_hme_dmacsr, DMA_CSR); + + dma_write32(0, DMA_ADDR); + break; + + case dvmarev2: + if (esp->rev != ESP100) { + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_3CLKS, DMA_CSR); + } + break; + + case dvmarev3: + val = dma_read32(DMA_CSR); + val &= ~DMA_3CLKS; + val |= DMA_2CLKS; + if (can_do_burst32) { + val &= ~DMA_BRST_SZ; + val |= DMA_BRST32; + } + dma_write32(val, DMA_CSR); + break; + + case dvmaesc1: + val = dma_read32(DMA_CSR); + val |= DMA_ADD_ENABLE; + val &= ~DMA_BCNT_ENAB; + if (!can_do_burst32 && can_do_burst16) { + val |= DMA_ESC_BURST; + } else { + val &= ~(DMA_ESC_BURST); + } + dma_write32(val, DMA_CSR); + break; + + default: + break; + } + + /* Enable interrupts. */ + val = dma_read32(DMA_CSR); + dma_write32(val | DMA_INT_ENAB, DMA_CSR); +} + +static void sbus_esp_dma_drain(struct esp *esp) +{ + u32 csr; + int lim; + + if (esp->dma->revision == dvmahme) + return; + + csr = dma_read32(DMA_CSR); + if (!(csr & DMA_FIFO_ISDRAIN)) + return; + + if (esp->dma->revision != dvmarev3 && esp->dma->revision != dvmaesc1) + dma_write32(csr | DMA_FIFO_STDRAIN, DMA_CSR); + + lim = 1000; + while (dma_read32(DMA_CSR) & DMA_FIFO_ISDRAIN) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA will not drain!\n", + esp->host->unique_id); + break; + } + udelay(1); + } +} + +static void sbus_esp_dma_invalidate(struct esp *esp) +{ + if (esp->dma->revision == dvmahme) { + dma_write32(DMA_RST_SCSI, DMA_CSR); + + esp->prev_hme_dmacsr = ((esp->prev_hme_dmacsr | + (DMA_PARITY_OFF | DMA_2CLKS | + DMA_SCSI_DISAB | DMA_INT_ENAB)) & + ~(DMA_ST_WRITE | DMA_ENABLE)); + + dma_write32(0, DMA_CSR); + dma_write32(esp->prev_hme_dmacsr, DMA_CSR); + + /* This is necessary to avoid having the SCSI channel + * engine lock up on us. + */ + dma_write32(0, DMA_ADDR); + } else { + u32 val; + int lim; + + lim = 1000; + while ((val = dma_read32(DMA_CSR)) & DMA_PEND_READ) { + if (--lim == 0) { + printk(KERN_ALERT PFX "esp%d: DMA will not " + "invalidate!\n", esp->host->unique_id); + break; + } + udelay(1); + } + + val &= ~(DMA_ENABLE | DMA_ST_WRITE | DMA_BCNT_ENAB); + val |= DMA_FIFO_INV; + dma_write32(val, DMA_CSR); + val &= ~DMA_FIFO_INV; + dma_write32(val, DMA_CSR); + } +} + +static void sbus_esp_send_dma_cmd(struct esp *esp, u32 addr, u32 esp_count, + u32 dma_count, int write, u8 cmd) +{ + u32 csr; + + BUG_ON(!(cmd & ESP_CMD_DMA)); + + sbus_esp_write8(esp, (esp_count >> 0) & 0xff, ESP_TCLOW); + sbus_esp_write8(esp, (esp_count >> 8) & 0xff, ESP_TCMED); + if (esp->rev == FASHME) { + sbus_esp_write8(esp, (esp_count >> 16) & 0xff, FAS_RLO); + sbus_esp_write8(esp, 0, FAS_RHI); + + scsi_esp_cmd(esp, cmd); + + csr = esp->prev_hme_dmacsr; + csr |= DMA_SCSI_DISAB | DMA_ENABLE; + if (write) + csr |= DMA_ST_WRITE; + else + csr &= ~DMA_ST_WRITE; + esp->prev_hme_dmacsr = csr; + + dma_write32(dma_count, DMA_COUNT); + dma_write32(addr, DMA_ADDR); + dma_write32(csr, DMA_CSR); + } else { + csr = dma_read32(DMA_CSR); + csr |= DMA_ENABLE; + if (write) + csr |= DMA_ST_WRITE; + else + csr &= ~DMA_ST_WRITE; + dma_write32(csr, DMA_CSR); + if (esp->dma->revision == dvmaesc1) { + u32 end = PAGE_ALIGN(addr + dma_count + 16U); + dma_write32(end - addr, DMA_COUNT); + } + dma_write32(addr, DMA_ADDR); + + scsi_esp_cmd(esp, cmd); + } + +} + +static int sbus_esp_dma_error(struct esp *esp) +{ + u32 csr = dma_read32(DMA_CSR); + + if (csr & DMA_HNDL_ERROR) + return 1; + + return 0; +} + +static const struct esp_driver_ops sbus_esp_ops = { + .esp_write8 = sbus_esp_write8, + .esp_read8 = sbus_esp_read8, + .map_single = sbus_esp_map_single, + .map_sg = sbus_esp_map_sg, + .unmap_single = sbus_esp_unmap_single, + .unmap_sg = sbus_esp_unmap_sg, + .irq_pending = sbus_esp_irq_pending, + .reset_dma = sbus_esp_reset_dma, + .dma_drain = sbus_esp_dma_drain, + .dma_invalidate = sbus_esp_dma_invalidate, + .send_dma_cmd = sbus_esp_send_dma_cmd, + .dma_error = sbus_esp_dma_error, +}; + +static int __devinit esp_sbus_probe_one(struct device *dev, + struct sbus_dev *esp_dev, + struct sbus_dev *espdma, + struct sbus_bus *sbus, + int hme) +{ + struct scsi_host_template *tpnt = &scsi_esp_template; + struct Scsi_Host *host; + struct esp *esp; + int err; + + host = scsi_host_alloc(tpnt, sizeof(struct esp)); + + err = -ENOMEM; + if (!host) + goto fail; + + host->max_id = (hme ? 16 : 8); + esp = host_to_esp(host); + + esp->host = host; + esp->dev = esp_dev; + esp->ops = &sbus_esp_ops; + + if (hme) + esp->flags |= ESP_FLAG_WIDE_CAPABLE; + + err = esp_sbus_find_dma(esp, espdma); + if (err < 0) + goto fail_unlink; + + err = esp_sbus_map_regs(esp, hme); + if (err < 0) + goto fail_unlink; + + err = esp_sbus_map_command_block(esp); + if (err < 0) + goto fail_unmap_regs; + + err = esp_sbus_register_irq(esp); + if (err < 0) + goto fail_unmap_command_block; + + esp_sbus_get_props(esp, espdma); + + /* Before we try to touch the ESP chip, ESC1 dma can + * come up with the reset bit set, so make sure that + * is clear first. + */ + if (esp->dma->revision == dvmaesc1) { + u32 val = dma_read32(DMA_CSR); + + dma_write32(val & ~DMA_RST_SCSI, DMA_CSR); + } + + dev_set_drvdata(&esp_dev->ofdev.dev, esp); + + err = scsi_esp_register(esp, dev); + if (err) + goto fail_free_irq; + + return 0; + +fail_free_irq: + free_irq(host->irq, esp); +fail_unmap_command_block: + sbus_free_consistent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); +fail_unmap_regs: + sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); +fail_unlink: + scsi_host_put(host); +fail: + return err; +} + +static int __devinit esp_sbus_probe(struct of_device *dev, const struct of_device_id *match) +{ + struct sbus_dev *sdev = to_sbus_device(&dev->dev); + struct device_node *dp = dev->node; + struct sbus_dev *dma_sdev = NULL; + int hme = 0; + + if (dp->parent && + (!strcmp(dp->parent->name, "espdma") || + !strcmp(dp->parent->name, "dma"))) + dma_sdev = sdev->parent; + else if (!strcmp(dp->name, "SUNW,fas")) { + dma_sdev = sdev; + hme = 1; + } + + return esp_sbus_probe_one(&dev->dev, sdev, dma_sdev, + sdev->bus, hme); +} + +static int __devexit esp_sbus_remove(struct of_device *dev) +{ + struct esp *esp = dev_get_drvdata(&dev->dev); + unsigned int irq = esp->host->irq; + u32 val; + + scsi_esp_unregister(esp); + + /* Disable interrupts. */ + val = dma_read32(DMA_CSR); + dma_write32(val & ~DMA_INT_ENAB, DMA_CSR); + + free_irq(irq, esp); + sbus_free_consistent(esp->dev, 16, + esp->command_block, + esp->command_block_dma); + sbus_iounmap(esp->regs, SBUS_ESP_REG_SIZE); + + scsi_host_put(esp->host); + + return 0; +} + +static struct of_device_id esp_match[] = { + { + .name = "SUNW,esp", + }, + { + .name = "SUNW,fas", + }, + { + .name = "esp", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, esp_match); + +static struct of_platform_driver esp_sbus_driver = { + .name = "esp", + .match_table = esp_match, + .probe = esp_sbus_probe, + .remove = __devexit_p(esp_sbus_remove), +}; + +static int __init sunesp_init(void) +{ + return of_register_driver(&esp_sbus_driver, &sbus_bus_type); +} + +static void __exit sunesp_exit(void) +{ + of_unregister_driver(&esp_sbus_driver); +} + +MODULE_DESCRIPTION("Sun ESP SCSI driver"); +MODULE_AUTHOR("David S. Miller (davem@davemloft.net)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(sunesp_init); +module_exit(sunesp_exit); diff --git a/drivers/scsi/sym53c8xx_2/sym_fw1.h b/drivers/scsi/sym53c8xx_2/sym_fw1.h index 7b39f4a..7b08d6c 100644 --- a/drivers/scsi/sym53c8xx_2/sym_fw1.h +++ b/drivers/scsi/sym53c8xx_2/sym_fw1.h @@ -1020,7 +1020,7 @@ static struct SYM_FWA_SCR SYM_FWA_SCR = { * It shall be a tagged command. * Read SIMPLE+TAG. * The C code will deal with errors. - * Agressive optimization, is'nt it? :) + * Aggressive optimization, isn't it? :) */ SCR_MOVE_ABS (2) ^ SCR_MSG_IN, HADDR_1 (msgin), @@ -1044,7 +1044,7 @@ static struct SYM_FWA_SCR SYM_FWA_SCR = { RADDR_1 (dsa), /* * The SIDL still contains the TAG value. - * Agressive optimization, isn't it? :):) + * Aggressive optimization, isn't it? :):) */ SCR_REG_SFBR (sidl, SCR_SHL, 0), 0, diff --git a/drivers/scsi/sym53c8xx_2/sym_fw2.h b/drivers/scsi/sym53c8xx_2/sym_fw2.h index 851f270..6e5b952 100644 --- a/drivers/scsi/sym53c8xx_2/sym_fw2.h +++ b/drivers/scsi/sym53c8xx_2/sym_fw2.h @@ -956,7 +956,7 @@ static struct SYM_FWA_SCR SYM_FWA_SCR = { * It shall be a tagged command. * Read SIMPLE+TAG. * The C code will deal with errors. - * Agressive optimization, is'nt it? :) + * Aggressive optimization, isn't it? :) */ SCR_MOVE_ABS (2) ^ SCR_MSG_IN, HADDR_1 (msgin), @@ -968,7 +968,7 @@ static struct SYM_FWA_SCR SYM_FWA_SCR = { offsetof(struct sym_lcb, head.itlq_tbl_sa), /* * The SIDL still contains the TAG value. - * Agressive optimization, isn't it? :):) + * Aggressive optimization, isn't it? :):) */ SCR_REG_SFBR (sidl, SCR_SHL, 0), 0, diff --git a/drivers/scsi/wd33c93.c b/drivers/scsi/wd33c93.c index 8357516..fa4e08e 100644 --- a/drivers/scsi/wd33c93.c +++ b/drivers/scsi/wd33c93.c @@ -69,6 +69,11 @@ * Added support for pre -A chips, which don't have advanced features * and will generate CSR_RESEL rather than CSR_RESEL_AM. * Richard Hirst <richard@sleepie.demon.co.uk> August 2000 + * + * Added support for Burst Mode DMA and Fast SCSI. Enabled the use of + * default_sx_per for asynchronous data transfers. Added adjustment + * of transfer periods in sx_table to the actual input-clock. + * peter fuerst <post@pfrst.de> February 2007 */ #include <linux/module.h> @@ -86,9 +91,11 @@ #include "wd33c93.h" +#define optimum_sx_per(hostdata) (hostdata)->sx_table[1].period_ns + -#define WD33C93_VERSION "1.26" -#define WD33C93_DATE "22/Feb/2003" +#define WD33C93_VERSION "1.26++" +#define WD33C93_DATE "10/Feb/2007" MODULE_AUTHOR("John Shifflett"); MODULE_DESCRIPTION("Generic WD33C93 SCSI driver"); @@ -122,6 +129,13 @@ MODULE_LICENSE("GPL"); * defines in wd33c93.h * - clock:x -x = clock input in MHz for WD33c93 chip. Normal values * would be from 8 through 20. Default is 8. + * - burst:x -x = 1 to use Burst Mode (or Demand-Mode) DMA, x = 0 to use + * Single Byte DMA, which is the default. Argument is + * optional - if not present, same as "burst:1". + * - fast:x -x = 1 to enable Fast SCSI, which is only effective with + * input-clock divisor 4 (WD33C93_FS_16_20), x = 0 to disable + * it, which is the default. Argument is optional - if not + * present, same as "fast:1". * - next -No argument. Used to separate blocks of keywords when * there's more than one host adapter in the system. * @@ -148,7 +162,7 @@ MODULE_LICENSE("GPL"); */ /* Normally, no defaults are specified */ -static char *setup_args[] = { "", "", "", "", "", "", "", "", "" }; +static char *setup_args[] = { "", "", "", "", "", "", "", "", "", "" }; static char *setup_strings; module_param(setup_strings, charp, 0); @@ -298,20 +312,8 @@ read_1_byte(const wd33c93_regs regs) return x; } -static struct sx_period sx_table[] = { - {1, 0x20}, - {252, 0x20}, - {376, 0x30}, - {500, 0x40}, - {624, 0x50}, - {752, 0x60}, - {876, 0x70}, - {1000, 0x00}, - {0, 0} -}; - static int -round_period(unsigned int period) +round_period(unsigned int period, const struct sx_period *sx_table) { int x; @@ -324,17 +326,49 @@ round_period(unsigned int period) return 7; } +/* + * Calculate Synchronous Transfer Register value from SDTR code. + */ static uchar -calc_sync_xfer(unsigned int period, unsigned int offset) +calc_sync_xfer(unsigned int period, unsigned int offset, unsigned int fast, + const struct sx_period *sx_table) { + /* When doing Fast SCSI synchronous data transfers, the corresponding + * value in 'sx_table' is two times the actually used transfer period. + */ uchar result; + if (offset && fast) { + fast = STR_FSS; + period *= 2; + } else { + fast = 0; + } period *= 4; /* convert SDTR code to ns */ - result = sx_table[round_period(period)].reg_value; + result = sx_table[round_period(period,sx_table)].reg_value; result |= (offset < OPTIMUM_SX_OFF) ? offset : OPTIMUM_SX_OFF; + result |= fast; return result; } +/* + * Calculate SDTR code bytes [3],[4] from period and offset. + */ +static inline void +calc_sync_msg(unsigned int period, unsigned int offset, unsigned int fast, + uchar msg[2]) +{ + /* 'period' is a "normal"-mode value, like the ones in 'sx_table'. The + * actually used transfer period for Fast SCSI synchronous data + * transfers is half that value. + */ + period /= 4; + if (offset && fast) + period /= 2; + msg[0] = period; + msg[1] = offset; +} + int wd33c93_queuecommand(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *)) @@ -632,7 +666,7 @@ wd33c93_execute(struct Scsi_Host *instance) write_wd33c93_count(regs, cmd->SCp.this_residual); write_wd33c93(regs, WD_CONTROL, - CTRL_IDI | CTRL_EDI | CTRL_DMA); + CTRL_IDI | CTRL_EDI | hostdata->dma_mode); hostdata->dma = D_DMA_RUNNING; } } else @@ -712,6 +746,8 @@ transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd, cmd->SCp.ptr = page_address(cmd->SCp.buffer->page) + cmd->SCp.buffer->offset; } + if (!cmd->SCp.this_residual) /* avoid bogus setups */ + return; write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, hostdata->sync_xfer[cmd->device->id]); @@ -744,7 +780,7 @@ transfer_bytes(const wd33c93_regs regs, struct scsi_cmnd *cmd, #ifdef PROC_STATISTICS hostdata->dma_cnt++; #endif - write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_DMA); + write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | hostdata->dma_mode); write_wd33c93_count(regs, cmd->SCp.this_residual); if ((hostdata->level2 >= L2_DATA) || @@ -862,9 +898,6 @@ wd33c93_intr(struct Scsi_Host *instance) hostdata->outgoing_msg[0] |= 0x40; if (hostdata->sync_stat[cmd->device->id] == SS_FIRST) { -#ifdef SYNC_DEBUG - printk(" sending SDTR "); -#endif hostdata->sync_stat[cmd->device->id] = SS_WAITING; @@ -878,14 +911,20 @@ wd33c93_intr(struct Scsi_Host *instance) hostdata->outgoing_msg[2] = 3; hostdata->outgoing_msg[3] = EXTENDED_SDTR; if (hostdata->no_sync & (1 << cmd->device->id)) { - hostdata->outgoing_msg[4] = - hostdata->default_sx_per / 4; - hostdata->outgoing_msg[5] = 0; + calc_sync_msg(hostdata->default_sx_per, 0, + 0, hostdata->outgoing_msg + 4); } else { - hostdata->outgoing_msg[4] = OPTIMUM_SX_PER / 4; - hostdata->outgoing_msg[5] = OPTIMUM_SX_OFF; + calc_sync_msg(optimum_sx_per(hostdata), + OPTIMUM_SX_OFF, + hostdata->fast, + hostdata->outgoing_msg + 4); } hostdata->outgoing_len = 6; +#ifdef SYNC_DEBUG + ucp = hostdata->outgoing_msg + 1; + printk(" sending SDTR %02x03%02x%02x%02x ", + ucp[0], ucp[2], ucp[3], ucp[4]); +#endif } else hostdata->outgoing_len = 1; @@ -1001,8 +1040,13 @@ wd33c93_intr(struct Scsi_Host *instance) #ifdef SYNC_DEBUG printk("-REJ-"); #endif - if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) + if (hostdata->sync_stat[cmd->device->id] == SS_WAITING) { hostdata->sync_stat[cmd->device->id] = SS_SET; + /* we want default_sx_per, not DEFAULT_SX_PER */ + hostdata->sync_xfer[cmd->device->id] = + calc_sync_xfer(hostdata->default_sx_per + / 4, 0, 0, hostdata->sx_table); + } write_wd33c93_cmd(regs, WD_CMD_NEGATE_ACK); hostdata->state = S_CONNECTED; break; @@ -1022,7 +1066,10 @@ wd33c93_intr(struct Scsi_Host *instance) switch (ucp[2]) { /* what's the EXTENDED code? */ case EXTENDED_SDTR: - id = calc_sync_xfer(ucp[3], ucp[4]); + /* default to default async period */ + id = calc_sync_xfer(hostdata-> + default_sx_per / 4, 0, + 0, hostdata->sx_table); if (hostdata->sync_stat[cmd->device->id] != SS_WAITING) { @@ -1041,20 +1088,22 @@ wd33c93_intr(struct Scsi_Host *instance) hostdata->outgoing_msg[1] = 3; hostdata->outgoing_msg[2] = EXTENDED_SDTR; - hostdata->outgoing_msg[3] = - hostdata->default_sx_per / - 4; - hostdata->outgoing_msg[4] = 0; + calc_sync_msg(hostdata-> + default_sx_per, 0, + 0, hostdata->outgoing_msg + 3); hostdata->outgoing_len = 5; - hostdata->sync_xfer[cmd->device->id] = - calc_sync_xfer(hostdata-> - default_sx_per - / 4, 0); } else { - hostdata->sync_xfer[cmd->device->id] = id; + if (ucp[4]) /* well, sync transfer */ + id = calc_sync_xfer(ucp[3], ucp[4], + hostdata->fast, + hostdata->sx_table); + else if (ucp[3]) /* very unlikely... */ + id = calc_sync_xfer(ucp[3], ucp[4], + 0, hostdata->sx_table); } + hostdata->sync_xfer[cmd->device->id] = id; #ifdef SYNC_DEBUG - printk("sync_xfer=%02x", + printk(" sync_xfer=%02x\n", hostdata->sync_xfer[cmd->device->id]); #endif hostdata->sync_stat[cmd->device->id] = @@ -1486,7 +1535,7 @@ reset_wd33c93(struct Scsi_Host *instance) write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); write_wd33c93(regs, WD_SYNCHRONOUS_TRANSFER, calc_sync_xfer(hostdata->default_sx_per / 4, - DEFAULT_SX_OFF)); + DEFAULT_SX_OFF, 0, hostdata->sx_table)); write_wd33c93(regs, WD_COMMAND, WD_CMD_RESET); @@ -1512,6 +1561,9 @@ reset_wd33c93(struct Scsi_Host *instance) } else hostdata->chip = C_UNKNOWN_CHIP; + if (hostdata->chip != C_WD33C93B) /* Fast SCSI unavailable */ + hostdata->fast = 0; + write_wd33c93(regs, WD_TIMEOUT_PERIOD, TIMEOUT_PERIOD_VALUE); write_wd33c93(regs, WD_CONTROL, CTRL_IDI | CTRL_EDI | CTRL_POLLED); } @@ -1533,7 +1585,8 @@ wd33c93_host_reset(struct scsi_cmnd * SCpnt) for (i = 0; i < 8; i++) { hostdata->busy[i] = 0; hostdata->sync_xfer[i] = - calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); + calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, + 0, hostdata->sx_table); hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ } hostdata->input_Q = NULL; @@ -1782,6 +1835,98 @@ check_setup_args(char *key, int *flags, int *val, char *buf) return ++x; } +/* + * Calculate internal data-transfer-clock cycle from input-clock + * frequency (/MHz) and fill 'sx_table'. + * + * The original driver used to rely on a fixed sx_table, containing periods + * for (only) the lower limits of the respective input-clock-frequency ranges + * (8-10/12-15/16-20 MHz). Although it seems, that no problems ocurred with + * this setting so far, it might be desirable to adjust the transfer periods + * closer to the really attached, possibly 25% higher, input-clock, since + * - the wd33c93 may really use a significant shorter period, than it has + * negotiated (eg. thrashing the target, which expects 4/8MHz, with 5/10MHz + * instead). + * - the wd33c93 may ask the target for a lower transfer rate, than the target + * is capable of (eg. negotiating for an assumed minimum of 252ns instead of + * possible 200ns, which indeed shows up in tests as an approx. 10% lower + * transfer rate). + */ +static inline unsigned int +round_4(unsigned int x) +{ + switch (x & 3) { + case 1: --x; + break; + case 2: ++x; + case 3: ++x; + } + return x; +} + +static void +calc_sx_table(unsigned int mhz, struct sx_period sx_table[9]) +{ + unsigned int d, i; + if (mhz < 11) + d = 2; /* divisor for 8-10 MHz input-clock */ + else if (mhz < 16) + d = 3; /* divisor for 12-15 MHz input-clock */ + else + d = 4; /* divisor for 16-20 MHz input-clock */ + + d = (100000 * d) / 2 / mhz; /* 100 x DTCC / nanosec */ + + sx_table[0].period_ns = 1; + sx_table[0].reg_value = 0x20; + for (i = 1; i < 8; i++) { + sx_table[i].period_ns = round_4((i+1)*d / 100); + sx_table[i].reg_value = (i+1)*0x10; + } + sx_table[7].reg_value = 0; + sx_table[8].period_ns = 0; + sx_table[8].reg_value = 0; +} + +/* + * check and, maybe, map an init- or "clock:"- argument. + */ +static uchar +set_clk_freq(int freq, int *mhz) +{ + int x = freq; + if (WD33C93_FS_8_10 == freq) + freq = 8; + else if (WD33C93_FS_12_15 == freq) + freq = 12; + else if (WD33C93_FS_16_20 == freq) + freq = 16; + else if (freq > 7 && freq < 11) + x = WD33C93_FS_8_10; + else if (freq > 11 && freq < 16) + x = WD33C93_FS_12_15; + else if (freq > 15 && freq < 21) + x = WD33C93_FS_16_20; + else { + /* Hmm, wouldn't it be safer to assume highest freq here? */ + x = WD33C93_FS_8_10; + freq = 8; + } + *mhz = freq; + return x; +} + +/* + * to be used with the resync: fast: ... options + */ +static inline void set_resync ( struct WD33C93_hostdata *hd, int mask ) +{ + int i; + for (i = 0; i < 8; i++) + if (mask & (1 << i)) + hd->sync_stat[i] = SS_UNSET; +} + void wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, dma_setup_t setup, dma_stop_t stop, int clock_freq) @@ -1798,7 +1943,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, hostdata = (struct WD33C93_hostdata *) instance->hostdata; hostdata->regs = regs; - hostdata->clock_freq = clock_freq; + hostdata->clock_freq = set_clk_freq(clock_freq, &i); + calc_sx_table(i, hostdata->sx_table); hostdata->dma_setup = setup; hostdata->dma_stop = stop; hostdata->dma_bounce_buffer = NULL; @@ -1806,7 +1952,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, for (i = 0; i < 8; i++) { hostdata->busy[i] = 0; hostdata->sync_xfer[i] = - calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF); + calc_sync_xfer(DEFAULT_SX_PER / 4, DEFAULT_SX_OFF, + 0, hostdata->sx_table); hostdata->sync_stat[i] = SS_UNSET; /* using default sync values */ #ifdef PROC_STATISTICS hostdata->cmd_cnt[i] = 0; @@ -1828,6 +1975,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, hostdata->default_sx_per = DEFAULT_SX_PER; hostdata->no_sync = 0xff; /* sync defaults to off */ hostdata->no_dma = 0; /* default is DMA enabled */ + hostdata->fast = 0; /* default is Fast SCSI transfers disabled */ + hostdata->dma_mode = CTRL_DMA; /* default is Single Byte DMA */ #ifdef PROC_INTERFACE hostdata->proc = PR_VERSION | PR_INFO | PR_STATISTICS | @@ -1839,6 +1988,11 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, #endif #endif + if (check_setup_args("clock", &flags, &val, buf)) { + hostdata->clock_freq = set_clk_freq(val, &val); + calc_sx_table(val, hostdata->sx_table); + } + if (check_setup_args("nosync", &flags, &val, buf)) hostdata->no_sync = val; @@ -1847,7 +2001,8 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, if (check_setup_args("period", &flags, &val, buf)) hostdata->default_sx_per = - sx_table[round_period((unsigned int) val)].period_ns; + hostdata->sx_table[round_period((unsigned int) val, + hostdata->sx_table)].period_ns; if (check_setup_args("disconnect", &flags, &val, buf)) { if ((val >= DIS_NEVER) && (val <= DIS_ALWAYS)) @@ -1862,17 +2017,12 @@ wd33c93_init(struct Scsi_Host *instance, const wd33c93_regs regs, if (check_setup_args("debug", &flags, &val, buf)) hostdata->args = val & DB_MASK; - if (check_setup_args("clock", &flags, &val, buf)) { - if (val > 7 && val < 11) - val = WD33C93_FS_8_10; - else if (val > 11 && val < 16) - val = WD33C93_FS_12_15; - else if (val > 15 && val < 21) - val = WD33C93_FS_16_20; - else - val = WD33C93_FS_8_10; - hostdata->clock_freq = val; - } + if (check_setup_args("burst", &flags, &val, buf)) + hostdata->dma_mode = val ? CTRL_BURST:CTRL_DMA; + + if (WD33C93_FS_16_20 == hostdata->clock_freq /* divisor 4 */ + && check_setup_args("fast", &flags, &val, buf)) + hostdata->fast = !!val; if ((i = check_setup_args("next", &flags, &val, buf))) { while (i) @@ -1917,53 +2067,65 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off char tbuf[128]; struct WD33C93_hostdata *hd; struct scsi_cmnd *cmd; - int x, i; + int x; static int stop = 0; hd = (struct WD33C93_hostdata *) instance->hostdata; /* If 'in' is TRUE we need to _read_ the proc file. We accept the following - * keywords (same format as command-line, but only ONE per read): + * keywords (same format as command-line, but arguments are not optional): * debug * disconnect * period * resync * proc * nodma + * level2 + * burst + * fast + * nosync */ if (in) { buf[len] = '\0'; - bp = buf; + for (bp = buf; *bp; ) { + while (',' == *bp || ' ' == *bp) + ++bp; if (!strncmp(bp, "debug:", 6)) { - bp += 6; - hd->args = simple_strtoul(bp, NULL, 0) & DB_MASK; + hd->args = simple_strtoul(bp+6, &bp, 0) & DB_MASK; } else if (!strncmp(bp, "disconnect:", 11)) { - bp += 11; - x = simple_strtoul(bp, NULL, 0); + x = simple_strtoul(bp+11, &bp, 0); if (x < DIS_NEVER || x > DIS_ALWAYS) x = DIS_ADAPTIVE; hd->disconnect = x; } else if (!strncmp(bp, "period:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); + x = simple_strtoul(bp+7, &bp, 0); hd->default_sx_per = - sx_table[round_period((unsigned int) x)].period_ns; + hd->sx_table[round_period((unsigned int) x, + hd->sx_table)].period_ns; } else if (!strncmp(bp, "resync:", 7)) { - bp += 7; - x = simple_strtoul(bp, NULL, 0); - for (i = 0; i < 7; i++) - if (x & (1 << i)) - hd->sync_stat[i] = SS_UNSET; + set_resync(hd, (int)simple_strtoul(bp+7, &bp, 0)); } else if (!strncmp(bp, "proc:", 5)) { - bp += 5; - hd->proc = simple_strtoul(bp, NULL, 0); + hd->proc = simple_strtoul(bp+5, &bp, 0); } else if (!strncmp(bp, "nodma:", 6)) { - bp += 6; - hd->no_dma = simple_strtoul(bp, NULL, 0); + hd->no_dma = simple_strtoul(bp+6, &bp, 0); } else if (!strncmp(bp, "level2:", 7)) { - bp += 7; - hd->level2 = simple_strtoul(bp, NULL, 0); + hd->level2 = simple_strtoul(bp+7, &bp, 0); + } else if (!strncmp(bp, "burst:", 6)) { + hd->dma_mode = + simple_strtol(bp+6, &bp, 0) ? CTRL_BURST:CTRL_DMA; + } else if (!strncmp(bp, "fast:", 5)) { + x = !!simple_strtol(bp+5, &bp, 0); + if (x != hd->fast) + set_resync(hd, 0xff); + hd->fast = x; + } else if (!strncmp(bp, "nosync:", 7)) { + x = simple_strtoul(bp+7, &bp, 0); + set_resync(hd, x ^ hd->no_sync); + hd->no_sync = x; + } else { + break; /* unknown keyword,syntax-error,... */ + } } return len; } @@ -1977,8 +2139,9 @@ wd33c93_proc_info(struct Scsi_Host *instance, char *buf, char **start, off_t off strcat(bp, tbuf); } if (hd->proc & PR_INFO) { - sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d", - hd->clock_freq, hd->no_sync, hd->no_dma); + sprintf(tbuf, "\nclock_freq=%02x no_sync=%02x no_dma=%d" + " dma_mode=%02x fast=%d", + hd->clock_freq, hd->no_sync, hd->no_dma, hd->dma_mode, hd->fast); strcat(bp, tbuf); strcat(bp, "\nsync_xfer[] = "); for (x = 0; x < 7; x++) { diff --git a/drivers/scsi/wd33c93.h b/drivers/scsi/wd33c93.h index edcb036..61ffb86 100644 --- a/drivers/scsi/wd33c93.h +++ b/drivers/scsi/wd33c93.h @@ -155,6 +155,9 @@ #define WD33C93_FS_12_15 OWNID_FS_12 #define WD33C93_FS_16_20 OWNID_FS_16 + /* pass input-clock explicitely. accepted mhz values are 8-10,12-20 */ +#define WD33C93_FS_MHZ(mhz) (mhz) + /* Control register */ #define CTRL_HSP 0x01 #define CTRL_HA 0x02 @@ -253,6 +256,9 @@ struct WD33C93_hostdata { uchar sync_stat[8]; /* status of sync negotiation per target */ uchar no_sync; /* bitmask: don't do sync on these targets */ uchar no_dma; /* set this flag to disable DMA */ + uchar dma_mode; /* DMA Burst Mode or Single Byte DMA */ + uchar fast; /* set this flag to enable Fast SCSI */ + struct sx_period sx_table[9]; /* transfer periods for actual DTC-setting */ #ifdef PROC_INTERFACE uchar proc; /* bitmask: what's in proc output */ #ifdef PROC_STATISTICS diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 98ec861..90621c3 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1196,7 +1196,7 @@ static void serial8250_enable_ms(struct uart_port *port) } static void -receive_chars(struct uart_8250_port *up, int *status) +receive_chars(struct uart_8250_port *up, unsigned int *status) { struct tty_struct *tty = up->port.info->tty; unsigned char ch, lsr = *status; @@ -1310,7 +1310,8 @@ static unsigned int check_modem_status(struct uart_8250_port *up) { unsigned int status = serial_in(up, UART_MSR); - if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI) { + if (status & UART_MSR_ANY_DELTA && up->ier & UART_IER_MSI && + up->port.info != NULL) { if (status & UART_MSR_TERI) up->port.icount.rng++; if (status & UART_MSR_DDSR) @@ -1333,8 +1334,9 @@ static inline void serial8250_handle_port(struct uart_8250_port *up) { unsigned int status; + unsigned long flags; - spin_lock(&up->port.lock); + spin_lock_irqsave(&up->port.lock, flags); status = serial_inp(up, UART_LSR); @@ -1346,7 +1348,7 @@ serial8250_handle_port(struct uart_8250_port *up) if (status & UART_LSR_THRE) transmit_chars(up); - spin_unlock(&up->port.lock); + spin_unlock_irqrestore(&up->port.lock, flags); } /* diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c index cde5db4..301c8c0 100644 --- a/drivers/serial/8250_pnp.c +++ b/drivers/serial/8250_pnp.c @@ -340,6 +340,9 @@ static const struct pnp_device_id pnp_dev_table[] = { { "FUJ02B8", 0 }, { "FUJ02B9", 0 }, { "FUJ02BC", 0 }, + /* Fujitsu Wacom Tablet PC devices */ + { "FUJ02E5", 0 }, + { "FUJ02E6", 0 }, /* Rockwell's (PORALiNK) 33600 INT PNP */ { "WCI0003", 0 }, /* Unkown PnP modems */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index e8dd71d..ad9f321 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -262,7 +262,8 @@ config SERIAL_AMBA_PL010 select SERIAL_CORE help This selects the ARM(R) AMBA(R) PrimeCell PL010 UART. If you have - an Integrator/AP or Integrator/PP2 platform, say Y or M here. + an Integrator/AP or Integrator/PP2 platform, or if you have a + Cirrus Logic EP93xx CPU, say Y or M here. If unsure, say N. diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index df45a7a..935f48f 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c @@ -33,12 +33,13 @@ #include <linux/sysrq.h> #include <linux/tty_flip.h> #include <linux/platform_device.h> +#include <linux/atmel_pdc.h> #include <asm/io.h> #include <asm/mach/serial_at91.h> #include <asm/arch/board.h> -#include <asm/arch/at91_pdc.h> + #ifdef CONFIG_ARM #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> diff --git a/drivers/serial/cpm_uart/cpm_uart_cpm2.c b/drivers/serial/cpm_uart/cpm_uart_cpm2.c index 787a8f1..fa45599 100644 --- a/drivers/serial/cpm_uart/cpm_uart_cpm2.c +++ b/drivers/serial/cpm_uart/cpm_uart_cpm2.c @@ -285,7 +285,7 @@ void cpm_uart_freebuf(struct uart_cpm_port *pinfo) int __init cpm_uart_init_portdesc(void) { #if defined(CONFIG_SERIAL_CPM_SMC1) || defined(CONFIG_SERIAL_CPM_SMC2) - u32 addr; + u16 *addr; #endif pr_debug("CPM uart[-]:init portdesc\n"); diff --git a/drivers/serial/dz.c b/drivers/serial/dz.c index 587d87b..d31721f 100644 --- a/drivers/serial/dz.c +++ b/drivers/serial/dz.c @@ -170,8 +170,7 @@ static void dz_enable_ms(struct uart_port *port) * This routine deals with inputs from any lines. * ------------------------------------------------------------ */ -static inline void dz_receive_chars(struct dz_port *dport_in, - struct pt_regs *regs) +static inline void dz_receive_chars(struct dz_port *dport_in) { struct dz_port *dport; struct tty_struct *tty = NULL; @@ -226,7 +225,7 @@ static inline void dz_receive_chars(struct dz_port *dport_in, break; } - if (uart_handle_sysrq_char(&dport->port, ch, regs)) + if (uart_handle_sysrq_char(&dport->port, ch)) continue; if ((status & dport->port.ignore_status_mask) == 0) { @@ -332,7 +331,7 @@ static irqreturn_t dz_interrupt(int irq, void *dev) status = dz_in(dport, DZ_CSR); if ((status & (DZ_RDONE | DZ_RIE)) == (DZ_RDONE | DZ_RIE)) - dz_receive_chars(dport, regs); + dz_receive_chars(dport); if ((status & (DZ_TRDY | DZ_TIE)) == (DZ_TRDY | DZ_TIE)) dz_transmit_chars(dport); diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 41431d0..246c557 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -164,7 +164,7 @@ static void free_port_memory(struct icom_port *icom_port) } } -static int __init get_port_memory(struct icom_port *icom_port) +static int __devinit get_port_memory(struct icom_port *icom_port) { int index; unsigned long stgAddr; @@ -1380,7 +1380,7 @@ static void icom_port_active(struct icom_port *icom_port, struct icom_adapter *i 0x8024 + 2 - 2 * (icom_port->port - 2); } } -static int __init icom_load_ports(struct icom_adapter *icom_adapter) +static int __devinit icom_load_ports(struct icom_adapter *icom_adapter) { struct icom_port *icom_port; int port_num; @@ -1473,7 +1473,7 @@ static void icom_remove_adapter(struct icom_adapter *icom_adapter) } } - free_irq(icom_adapter->irq_number, (void *) icom_adapter); + free_irq(icom_adapter->pci_dev->irq, (void *) icom_adapter); iounmap(icom_adapter->base_addr); icom_free_adapter(icom_adapter); pci_release_regions(icom_adapter->pci_dev); @@ -1539,7 +1539,6 @@ static int __devinit icom_probe(struct pci_dev *dev, } icom_adapter->base_addr_pci = pci_resource_start(dev, 0); - icom_adapter->irq_number = dev->irq; icom_adapter->pci_dev = dev; icom_adapter->version = ent->driver_data; icom_adapter->subsystem_id = ent->subdevice; @@ -1570,7 +1569,7 @@ static int __devinit icom_probe(struct pci_dev *dev, icom_port = &icom_adapter->port_info[index]; if (icom_port->status == ICOM_PORT_ACTIVE) { - icom_port->uart_port.irq = icom_port->adapter->irq_number; + icom_port->uart_port.irq = icom_port->adapter->pci_dev->irq; icom_port->uart_port.type = PORT_ICOM; icom_port->uart_port.iotype = UPIO_MEM; icom_port->uart_port.membase = diff --git a/drivers/serial/icom.h b/drivers/serial/icom.h index 798f1ef..e8578d8 100644 --- a/drivers/serial/icom.h +++ b/drivers/serial/icom.h @@ -258,7 +258,6 @@ struct icom_port { struct icom_adapter { void __iomem * base_addr; unsigned long base_addr_pci; - unsigned char irq_number; struct pci_dev *pci_dev; struct icom_port port_info[4]; int index; diff --git a/drivers/serial/imx.c b/drivers/serial/imx.c index e216dcf..04cc88c 100644 --- a/drivers/serial/imx.c +++ b/drivers/serial/imx.c @@ -154,7 +154,7 @@ static inline void imx_transmit_buffer(struct imx_port *sport) { struct circ_buf *xmit = &sport->port.info->xmit; - do { + while (!(UTS((u32)sport->port.membase) & UTS_TXFULL)) { /* send xmit->buf[xmit->tail] * out the port here */ URTX0((u32)sport->port.membase) = xmit->buf[xmit->tail]; @@ -163,7 +163,7 @@ static inline void imx_transmit_buffer(struct imx_port *sport) sport->port.icount.tx++; if (uart_circ_empty(xmit)) break; - } while (!(UTS((u32)sport->port.membase) & UTS_TXFULL)); + } if (uart_circ_empty(xmit)) imx_stop_tx(&sport->port); @@ -178,8 +178,7 @@ static void imx_start_tx(struct uart_port *port) UCR1((u32)sport->port.membase) |= UCR1_TXMPTYEN; - if(UTS((u32)sport->port.membase) & UTS_TXEMPTY) - imx_transmit_buffer(sport); + imx_transmit_buffer(sport); } static irqreturn_t imx_rtsint(int irq, void *dev_id) @@ -404,7 +403,8 @@ static int imx_startup(struct uart_port *port) if (retval) goto error_out2; retval = request_irq(sport->rtsirq, imx_rtsint, - IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + (sport->rtsirq < IMX_IRQS) ? 0 : + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, DRIVER_NAME, sport); if (retval) goto error_out3; @@ -678,7 +678,7 @@ static struct imx_port imx_ports[] = { .mapbase = IMX_UART1_BASE, /* FIXME */ .irq = UART1_MINT_RX, .uartclk = 16000000, - .fifosize = 8, + .fifosize = 32, .flags = UPF_BOOT_AUTOCONF, .ops = &imx_pops, .line = 0, @@ -694,7 +694,7 @@ static struct imx_port imx_ports[] = { .mapbase = IMX_UART2_BASE, /* FIXME */ .irq = UART2_MINT_RX, .uartclk = 16000000, - .fifosize = 8, + .fifosize = 32, .flags = UPF_BOOT_AUTOCONF, .ops = &imx_pops, .line = 1, diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c index 0843096..99af084 100644 --- a/drivers/serial/mcfserial.c +++ b/drivers/serial/mcfserial.c @@ -425,15 +425,13 @@ irqreturn_t mcfrs_interrupt(int irq, void *dev_id) * ------------------------------------------------------------------- */ -static void mcfrs_offintr(void *private) +static void mcfrs_offintr(struct work_struct *work) { - struct mcf_serial *info = (struct mcf_serial *) private; - struct tty_struct *tty; + struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue); + struct tty_struct *tty = info->tty; - tty = info->tty; - if (!tty) - return; - tty_wakeup(tty); + if (tty) + tty_wakeup(tty); } @@ -497,16 +495,13 @@ static void mcfrs_timer(void) * do_serial_hangup() -> tty->hangup() -> mcfrs_hangup() * */ -static void do_serial_hangup(void *private) +static void do_serial_hangup(struct work_struct *work) { - struct mcf_serial *info = (struct mcf_serial *) private; - struct tty_struct *tty; + struct mcf_serial *info = container_of(work, struct mcf_serial, tqueue_hangup); + struct tty_struct *tty = info->tty; - tty = info->tty; - if (!tty) - return; - - tty_hangup(tty); + if (tty) + tty_hangup(tty); } static int startup(struct mcf_serial * info) @@ -857,7 +852,7 @@ static void mcfrs_throttle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("throttle %s: %d....\n", _tty_name(tty, buf), + printk("throttle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif @@ -876,7 +871,7 @@ static void mcfrs_unthrottle(struct tty_struct * tty) #ifdef SERIAL_DEBUG_THROTTLE char buf[64]; - printk("unthrottle %s: %d....\n", _tty_name(tty, buf), + printk("unthrottle %s: %d....\n", tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty)); #endif @@ -1541,8 +1536,8 @@ static void mcfrs_irqinit(struct mcf_serial *info) * External Pin Mask Setting & Enable External Pin for Interface * mrcbis@aliceposta.it */ - unsigned short *serpin_enable_mask; - serpin_enable_mask = (MCF_IPSBAR + MCF_GPIO_PAR_UART); + u16 *serpin_enable_mask; + serpin_enable_mask = (u16 *) (MCF_IPSBAR + MCF_GPIO_PAR_UART); if (info->line == 0) *serpin_enable_mask |= UART0_ENABLE_MASK; else if (info->line == 1) @@ -1551,6 +1546,13 @@ static void mcfrs_irqinit(struct mcf_serial *info) *serpin_enable_mask |= UART2_ENABLE_MASK; } #endif +#if defined(CONFIG_M528x) + /* make sure PUAPAR is set for UART0 and UART1 */ + if (info->line < 2) { + volatile unsigned char *portp = (volatile unsigned char *) (MCF_MBAR + MCF5282_GPIO_PUAPAR); + *portp |= (0x03 << (info->line * 2)); + } +#endif #elif defined(CONFIG_M520x) volatile unsigned char *icrp, *uartp; volatile unsigned long *imrp; @@ -1783,8 +1785,8 @@ mcfrs_init(void) info->event = 0; info->count = 0; info->blocked_open = 0; - INIT_WORK(&info->tqueue, mcfrs_offintr, info); - INIT_WORK(&info->tqueue_hangup, do_serial_hangup, info); + INIT_WORK(&info->tqueue, mcfrs_offintr); + INIT_WORK(&info->tqueue_hangup, do_serial_hangup); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c index 955bbd6..8d24cd5 100644 --- a/drivers/serial/mpc52xx_uart.c +++ b/drivers/serial/mpc52xx_uart.c @@ -995,8 +995,10 @@ mpc52xx_uart_of_remove(struct of_device *op) struct uart_port *port = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); - if (port) + if (port) { uart_remove_one_port(&mpc52xx_uart_driver, port); + irq_dispose_mapping(port->irq); + } return 0; } diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c index ccb8fa1..8321101 100644 --- a/drivers/serial/mux.c +++ b/drivers/serial/mux.c @@ -51,7 +51,11 @@ #define MUX_NR 256 static unsigned int port_cnt __read_mostly; -static struct uart_port mux_ports[MUX_NR]; +struct mux_port { + struct uart_port port; + int enabled; +}; +static struct mux_port mux_ports[MUX_NR]; static struct uart_driver mux_driver = { .owner = THIS_MODULE, @@ -66,7 +70,36 @@ static struct timer_list mux_timer; #define UART_PUT_CHAR(p, c) __raw_writel((c), (p)->membase + IO_DATA_REG_OFFSET) #define UART_GET_FIFO_CNT(p) __raw_readl((p)->membase + IO_DCOUNT_REG_OFFSET) -#define GET_MUX_PORTS(iodc_data) ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8 + +/** + * get_mux_port_count - Get the number of available ports on the Mux. + * @dev: The parisc device. + * + * This function is used to determine the number of ports the Mux + * supports. The IODC data reports the number of ports the Mux + * can support, but there are cases where not all the Mux ports + * are connected. This function can override the IODC and + * return the true port count. + */ +static int __init get_mux_port_count(struct parisc_device *dev) +{ + int status; + u8 iodc_data[32]; + unsigned long bytecnt; + + /* If this is the built-in Mux for the K-Class (Eole CAP/MUX), + * we only need to allocate resources for 1 port since the + * other 7 ports are not connected. + */ + if(dev->id.hversion == 0x15) + return 1; + + status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32); + BUG_ON(status != PDC_OK); + + /* Return the number of ports specified in the iodc data. */ + return ((((iodc_data)[4] & 0xf0) >> 4) * 8) + 8; +} /** * mux_tx_empty - Check if the transmitter fifo is empty. @@ -250,7 +283,7 @@ static void mux_read(struct uart_port *port) */ static int mux_startup(struct uart_port *port) { - mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); + mux_ports[port->line].enabled = 1; return 0; } @@ -262,6 +295,7 @@ static int mux_startup(struct uart_port *port) */ static void mux_shutdown(struct uart_port *port) { + mux_ports[port->line].enabled = 0; } /** @@ -319,7 +353,7 @@ static int mux_request_port(struct uart_port *port) * @port: Ptr to the uart_port. * @type: Bitmask of required configurations. * - * Perform any autoconfiguration steps for the port. This functino is + * Perform any autoconfiguration steps for the port. This function is * called if the UPF_BOOT_AUTOCONF flag is specified for the port. * [Note: This is required for now because of a bug in the Serial core. * rmk has already submitted a patch to linus, should be available for @@ -357,11 +391,11 @@ static void mux_poll(unsigned long unused) int i; for(i = 0; i < port_cnt; ++i) { - if(!mux_ports[i].info) + if(!mux_ports[i].enabled) continue; - mux_read(&mux_ports[i]); - mux_write(&mux_ports[i]); + mux_read(&mux_ports[i].port); + mux_write(&mux_ports[i].port); } mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); @@ -371,8 +405,17 @@ static void mux_poll(unsigned long unused) #ifdef CONFIG_SERIAL_MUX_CONSOLE static void mux_console_write(struct console *co, const char *s, unsigned count) { - while(count--) - pdc_iodc_putc(*s++); + /* Wait until the FIFO drains. */ + while(UART_GET_FIFO_CNT(&mux_ports[0].port)) + udelay(1); + + while(count--) { + if(*s == '\n') { + UART_PUT_CHAR(&mux_ports[0].port, '\r'); + } + UART_PUT_CHAR(&mux_ports[0].port, *s++); + } + } static int mux_console_setup(struct console *co, char *options) @@ -428,19 +471,14 @@ static struct uart_ops mux_pops = { */ static int __init mux_probe(struct parisc_device *dev) { - int i, status, ports; - u8 iodc_data[32]; - unsigned long bytecnt; - struct uart_port *port; + int i, status; - status = pdc_iodc_read(&bytecnt, dev->hpa.start, 0, iodc_data, 32); - if(status != PDC_OK) { - printk(KERN_ERR "Serial mux: Unable to read IODC.\n"); - return 1; - } + int port_count = get_mux_port_count(dev); + printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.6\n", port_count); - ports = GET_MUX_PORTS(iodc_data); - printk(KERN_INFO "Serial mux driver (%d ports) Revision: 0.3\n", ports); + dev_set_drvdata(&dev->dev, (void *)(long)port_count); + request_mem_region(dev->hpa.start + MUX_OFFSET, + port_count * MUX_LINE_OFFSET, "Mux"); if(!port_cnt) { mux_driver.cons = MUX_CONSOLE; @@ -450,13 +488,10 @@ static int __init mux_probe(struct parisc_device *dev) printk(KERN_ERR "Serial mux: Unable to register driver.\n"); return 1; } - - init_timer(&mux_timer); - mux_timer.function = mux_poll; } - for(i = 0; i < ports; ++i, ++port_cnt) { - port = &mux_ports[port_cnt]; + for(i = 0; i < port_count; ++i, ++port_cnt) { + struct uart_port *port = &mux_ports[port_cnt].port; port->iobase = 0; port->mapbase = dev->hpa.start + MUX_OFFSET + (i * MUX_LINE_OFFSET); @@ -477,27 +512,73 @@ static int __init mux_probe(struct parisc_device *dev) */ port->timeout = HZ / 50; spin_lock_init(&port->lock); + status = uart_add_one_port(&mux_driver, port); BUG_ON(status); } -#ifdef CONFIG_SERIAL_MUX_CONSOLE - register_console(&mux_console); -#endif return 0; } +static int __devexit mux_remove(struct parisc_device *dev) +{ + int i, j; + int port_count = (long)dev_get_drvdata(&dev->dev); + + /* Find Port 0 for this card in the mux_ports list. */ + for(i = 0; i < port_cnt; ++i) { + if(mux_ports[i].port.mapbase == dev->hpa.start + MUX_OFFSET) + break; + } + BUG_ON(i + port_count > port_cnt); + + /* Release the resources associated with each port on the device. */ + for(j = 0; j < port_count; ++j, ++i) { + struct uart_port *port = &mux_ports[i].port; + + uart_remove_one_port(&mux_driver, port); + if(port->membase) + iounmap(port->membase); + } + + release_mem_region(dev->hpa.start + MUX_OFFSET, port_count * MUX_LINE_OFFSET); + return 0; +} + +/* Hack. This idea was taken from the 8250_gsc.c on how to properly order + * the serial port detection in the proper order. The idea is we always + * want the builtin mux to be detected before addin mux cards, so we + * specifically probe for the builtin mux cards first. + * + * This table only contains the parisc_device_id of known builtin mux + * devices. All other mux cards will be detected by the generic mux_tbl. + */ +static struct parisc_device_id builtin_mux_tbl[] = { + { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x15, 0x0000D }, /* All K-class */ + { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, 0x44, 0x0000D }, /* E35, E45, and E55 */ + { 0, } +}; + static struct parisc_device_id mux_tbl[] = { { HPHW_A_DIRECT, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x0000D }, { 0, } }; +MODULE_DEVICE_TABLE(parisc, builtin_mux_tbl); MODULE_DEVICE_TABLE(parisc, mux_tbl); +static struct parisc_driver builtin_serial_mux_driver = { + .name = "builtin_serial_mux", + .id_table = builtin_mux_tbl, + .probe = mux_probe, + .remove = __devexit_p(mux_remove), +}; + static struct parisc_driver serial_mux_driver = { .name = "serial_mux", .id_table = mux_tbl, .probe = mux_probe, + .remove = __devexit_p(mux_remove), }; /** @@ -507,7 +588,21 @@ static struct parisc_driver serial_mux_driver = { */ static int __init mux_init(void) { - return register_parisc_driver(&serial_mux_driver); + register_parisc_driver(&builtin_serial_mux_driver); + register_parisc_driver(&serial_mux_driver); + + if(port_cnt > 0) { + /* Start the Mux timer */ + init_timer(&mux_timer); + mux_timer.function = mux_poll; + mod_timer(&mux_timer, jiffies + MUX_POLL_DELAY); + +#ifdef CONFIG_SERIAL_MUX_CONSOLE + register_console(&mux_console); +#endif + } + + return 0; } /** @@ -517,14 +612,16 @@ static int __init mux_init(void) */ static void __exit mux_exit(void) { - int i; - - for (i = 0; i < port_cnt; i++) { - uart_remove_one_port(&mux_driver, &mux_ports[i]); - if (mux_ports[i].membase) - iounmap(mux_ports[i].membase); + /* Delete the Mux timer. */ + if(port_cnt > 0) { + del_timer(&mux_timer); +#ifdef CONFIG_SERIAL_MUX_CONSOLE + unregister_console(&mux_console); +#endif } + unregister_parisc_driver(&builtin_serial_mux_driver); + unregister_parisc_driver(&serial_mux_driver); uart_unregister_driver(&mux_driver); } diff --git a/drivers/serial/pmac_zilog.c b/drivers/serial/pmac_zilog.c index 752ef07..be8d757 100644 --- a/drivers/serial/pmac_zilog.c +++ b/drivers/serial/pmac_zilog.c @@ -1467,7 +1467,8 @@ no_dma: if (ZS_IS_IRDA(uap)) uap->port_type = PMAC_SCC_IRDA; if (ZS_IS_INTMODEM(uap)) { - struct device_node* i2c_modem = find_devices("i2c-modem"); + struct device_node* i2c_modem = + of_find_node_by_name(NULL, "i2c-modem"); if (i2c_modem) { const char* mid = get_property(i2c_modem, "modem-id", NULL); @@ -1482,6 +1483,7 @@ no_dma: } printk(KERN_INFO "pmac_zilog: i2c-modem detected, id: %d\n", mid ? (*mid) : 0); + of_node_put(i2c_modem); } else { printk(KERN_INFO "pmac_zilog: serial modem detected\n"); } diff --git a/drivers/serial/serial_cs.c b/drivers/serial/serial_cs.c index c2f1012..6b76bab 100644 --- a/drivers/serial/serial_cs.c +++ b/drivers/serial/serial_cs.c @@ -248,6 +248,10 @@ static const struct serial_quirk quirks[] = { .multi = 2, }, { .manfid = MANFID_QUATECH, + .prodid = PRODID_QUATECH_DUAL_RS232_G, + .multi = 2, + }, { + .manfid = MANFID_QUATECH, .prodid = PRODID_QUATECH_QUAD_RS232, .multi = 4, }, { @@ -891,6 +895,7 @@ static struct pcmcia_device_id serial_ids[] = { PCMCIA_DEVICE_PROD_ID12("OEM ", "C288MX ", 0xb572d360, 0xd2385b7a), PCMCIA_DEVICE_PROD_ID12("PCMCIA ", "C336MX ", 0x99bcafe9, 0xaa25bcab), PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "PCMCIA Dual RS-232 Serial Port Card", 0xc4420b35, 0x92abc92f), + PCMCIA_DEVICE_PROD_ID12("Quatech Inc", "Dual RS-232 Serial Port PC Card", 0xc4420b35, 0x031a380d), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "EN2218-LAN/MODEM", 0x281f1c5d, 0x570f348e, "PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "PCMCIA", "UE2218-LAN/MODEM", 0x281f1c5d, 0x6fdcacee, "PCMLM28.cis"), PCMCIA_PFC_DEVICE_CIS_PROD_ID12(1, "Psion Dacom", "Gold Card V34 Ethernet", 0xf5f025c2, 0x338e8155, "PCMLM28.cis"), diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index f4440d3..509ace7 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -38,6 +38,8 @@ * Fix some spin_locks. * Do not call uart_add_one_port for absent ports. * 1.07 Use CONFIG_SERIAL_TXX9_NR_UARTS. Cleanup. + * 1.08 Use platform_device. + * Fix and cleanup suspend/resume/initialization codes. */ #if defined(CONFIG_SERIAL_TXX9_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) @@ -50,7 +52,7 @@ #include <linux/console.h> #include <linux/sysrq.h> #include <linux/delay.h> -#include <linux/device.h> +#include <linux/platform_device.h> #include <linux/pci.h> #include <linux/tty.h> #include <linux/tty_flip.h> @@ -60,7 +62,7 @@ #include <asm/io.h> -static char *serial_version = "1.07"; +static char *serial_version = "1.08"; static char *serial_name = "TX39/49 Serial driver"; #define PASS_LIMIT 256 @@ -94,12 +96,7 @@ static char *serial_name = "TX39/49 Serial driver"; struct uart_txx9_port { struct uart_port port; - - /* - * We provide a per-port pm hook. - */ - void (*pm)(struct uart_port *port, - unsigned int state, unsigned int old); + /* No additional info for now */ }; #define TXX9_REGION_SIZE 0x24 @@ -277,6 +274,31 @@ static void serial_txx9_enable_ms(struct uart_port *port) /* TXX9-SIO can not control DTR... */ } +static void serial_txx9_initialize(struct uart_port *port) +{ + struct uart_txx9_port *up = (struct uart_txx9_port *)port; + unsigned int tmout = 10000; + + sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); + /* TX4925 BUG WORKAROUND. Accessing SIOC register + * immediately after soft reset causes bus error. */ + mmiowb(); + udelay(1); + while ((sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) && --tmout) + udelay(1); + /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ + sio_set(up, TXX9_SIFCR, + TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); + /* initial settings */ + sio_out(up, TXX9_SILCR, + TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | + ((up->port.flags & UPF_TXX9_USE_SCLK) ? + TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); + sio_quot_set(up, uart_get_divisor(port, 9600)); + sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); + sio_out(up, TXX9_SIDICR, 0); +} + static inline void receive_chars(struct uart_txx9_port *up, unsigned int *status) { @@ -657,9 +679,8 @@ static void serial_txx9_pm(struct uart_port *port, unsigned int state, unsigned int oldstate) { - struct uart_txx9_port *up = (struct uart_txx9_port *)port; - if (up->pm) - up->pm(port, state, oldstate); + if (state == 0) + serial_txx9_initialize(port); } static int serial_txx9_request_resource(struct uart_txx9_port *up) @@ -732,7 +753,6 @@ static int serial_txx9_request_port(struct uart_port *port) static void serial_txx9_config_port(struct uart_port *port, int uflags) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; - unsigned long flags; int ret; /* @@ -749,30 +769,7 @@ static void serial_txx9_config_port(struct uart_port *port, int uflags) if (up->port.line == up->port.cons->index) return; #endif - spin_lock_irqsave(&up->port.lock, flags); - /* - * Reset the UART. - */ - sio_out(up, TXX9_SIFCR, TXX9_SIFCR_SWRST); -#ifdef CONFIG_CPU_TX49XX - /* TX4925 BUG WORKAROUND. Accessing SIOC register - * immediately after soft reset causes bus error. */ - iob(); - udelay(1); -#endif - while (sio_in(up, TXX9_SIFCR) & TXX9_SIFCR_SWRST) - ; - /* TX Int by FIFO Empty, RX Int by Receiving 1 char. */ - sio_set(up, TXX9_SIFCR, - TXX9_SIFCR_TDIL_MAX | TXX9_SIFCR_RDIL_1); - /* initial settings */ - sio_out(up, TXX9_SILCR, - TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | - ((up->port.flags & UPF_TXX9_USE_SCLK) ? - TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); - sio_quot_set(up, uart_get_divisor(port, 9600)); - sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); - spin_unlock_irqrestore(&up->port.lock, flags); + serial_txx9_initialize(port); } static int @@ -818,7 +815,8 @@ static struct uart_ops serial_txx9_pops = { static struct uart_txx9_port serial_txx9_ports[UART_NR]; -static void __init serial_txx9_register_ports(struct uart_driver *drv) +static void __init serial_txx9_register_ports(struct uart_driver *drv, + struct device *dev) { int i; @@ -827,6 +825,7 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv) up->port.line = i; up->port.ops = &serial_txx9_pops; + up->port.dev = dev; if (up->port.iobase || up->port.mapbase) uart_add_one_port(drv, &up->port); } @@ -898,7 +897,7 @@ serial_txx9_console_write(struct console *co, const char *s, unsigned int count) sio_out(up, TXX9_SIDICR, ier); } -static int serial_txx9_console_setup(struct console *co, char *options) +static int __init serial_txx9_console_setup(struct console *co, char *options) { struct uart_port *port; struct uart_txx9_port *up; @@ -919,17 +918,7 @@ static int serial_txx9_console_setup(struct console *co, char *options) if (!port->ops) return -ENODEV; - /* - * Disable UART interrupts, set DTR and RTS high - * and set speed. - */ - sio_out(up, TXX9_SIDICR, 0); - /* initial settings */ - sio_out(up, TXX9_SILCR, - TXX9_SILCR_UMODE_8BIT | TXX9_SILCR_USBL_1BIT | - ((port->flags & UPF_TXX9_USE_SCLK) ? - TXX9_SILCR_SCS_SCLK_BG : TXX9_SILCR_SCS_IMCLK_BG)); - sio_out(up, TXX9_SIFLCR, TXX9_SIFLCR_RTSTL_MAX /* 15 */); + serial_txx9_initialize(&up->port); if (options) uart_parse_options(options, &baud, &parity, &bits, &flow); @@ -981,31 +970,6 @@ int __init early_serial_txx9_setup(struct uart_port *port) return 0; } -#ifdef ENABLE_SERIAL_TXX9_PCI -#ifdef CONFIG_PM -/** - * serial_txx9_suspend_port - suspend one serial port - * @line: serial line number - * - * Suspend one serial port. - */ -static void serial_txx9_suspend_port(int line) -{ - uart_suspend_port(&serial_txx9_reg, &serial_txx9_ports[line].port); -} - -/** - * serial_txx9_resume_port - resume one serial port - * @line: serial line number - * - * Resume one serial port. - */ -static void serial_txx9_resume_port(int line) -{ - uart_resume_port(&serial_txx9_reg, &serial_txx9_ports[line].port); -} -#endif - static DEFINE_MUTEX(serial_txx9_mutex); /** @@ -1028,8 +992,18 @@ static int __devinit serial_txx9_register_port(struct uart_port *port) mutex_lock(&serial_txx9_mutex); for (i = 0; i < UART_NR; i++) { uart = &serial_txx9_ports[i]; - if (!(uart->port.iobase || uart->port.mapbase)) + if (uart_match_port(&uart->port, port)) { + uart_remove_one_port(&serial_txx9_reg, &uart->port); break; + } + } + if (i == UART_NR) { + /* Find unused port */ + for (i = 0; i < UART_NR; i++) { + uart = &serial_txx9_ports[i]; + if (!(uart->port.iobase || uart->port.mapbase)) + break; + } } if (i < UART_NR) { uart->port.iobase = port->iobase; @@ -1072,6 +1046,95 @@ static void __devexit serial_txx9_unregister_port(int line) } /* + * Register a set of serial devices attached to a platform device. + */ +static int __devinit serial_txx9_probe(struct platform_device *dev) +{ + struct uart_port *p = dev->dev.platform_data; + struct uart_port port; + int ret, i; + + memset(&port, 0, sizeof(struct uart_port)); + for (i = 0; p && p->uartclk != 0; p++, i++) { + port.iobase = p->iobase; + port.membase = p->membase; + port.irq = p->irq; + port.uartclk = p->uartclk; + port.iotype = p->iotype; + port.flags = p->flags; + port.mapbase = p->mapbase; + port.dev = &dev->dev; + ret = serial_txx9_register_port(&port); + if (ret < 0) { + dev_err(&dev->dev, "unable to register port at index %d " + "(IO%x MEM%lx IRQ%d): %d\n", i, + p->iobase, p->mapbase, p->irq, ret); + } + } + return 0; +} + +/* + * Remove serial ports registered against a platform device. + */ +static int __devexit serial_txx9_remove(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.dev == &dev->dev) + serial_txx9_unregister_port(i); + } + return 0; +} + +#ifdef CONFIG_PM +static int serial_txx9_suspend(struct platform_device *dev, pm_message_t state) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_suspend_port(&serial_txx9_reg, &up->port); + } + + return 0; +} + +static int serial_txx9_resume(struct platform_device *dev) +{ + int i; + + for (i = 0; i < UART_NR; i++) { + struct uart_txx9_port *up = &serial_txx9_ports[i]; + + if (up->port.type != PORT_UNKNOWN && up->port.dev == &dev->dev) + uart_resume_port(&serial_txx9_reg, &up->port); + } + + return 0; +} +#endif + +static struct platform_driver serial_txx9_plat_driver = { + .probe = serial_txx9_probe, + .remove = __devexit_p(serial_txx9_remove), +#ifdef CONFIG_PM + .suspend = serial_txx9_suspend, + .resume = serial_txx9_resume, +#endif + .driver = { + .name = "serial_txx9", + .owner = THIS_MODULE, + }, +}; + +#ifdef ENABLE_SERIAL_TXX9_PCI +/* * Probe one serial board. Unfortunately, there is no rhyme nor reason * to the arrangement of serial ports on a PCI card. */ @@ -1097,20 +1160,22 @@ pciserial_txx9_init_one(struct pci_dev *dev, const struct pci_device_id *ent) line = serial_txx9_register_port(&port); if (line < 0) { printk(KERN_WARNING "Couldn't register serial port %s: %d\n", pci_name(dev), line); + pci_disable_device(dev); + return line; } - pci_set_drvdata(dev, (void *)(long)line); + pci_set_drvdata(dev, &serial_txx9_ports[line]); return 0; } static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) { - int line = (int)(long)pci_get_drvdata(dev); + struct uart_txx9_port *up = pci_get_drvdata(dev); pci_set_drvdata(dev, NULL); - if (line) { - serial_txx9_unregister_port(line); + if (up) { + serial_txx9_unregister_port(up->port.line); pci_disable_device(dev); } } @@ -1118,10 +1183,10 @@ static void __devexit pciserial_txx9_remove_one(struct pci_dev *dev) #ifdef CONFIG_PM static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) { - int line = (int)(long)pci_get_drvdata(dev); + struct uart_txx9_port *up = pci_get_drvdata(dev); - if (line) - serial_txx9_suspend_port(line); + if (up) + uart_suspend_port(&serial_txx9_reg, &up->port); pci_save_state(dev); pci_set_power_state(dev, pci_choose_state(dev, state)); return 0; @@ -1129,15 +1194,12 @@ static int pciserial_txx9_suspend_one(struct pci_dev *dev, pm_message_t state) static int pciserial_txx9_resume_one(struct pci_dev *dev) { - int line = (int)(long)pci_get_drvdata(dev); + struct uart_txx9_port *up = pci_get_drvdata(dev); pci_set_power_state(dev, PCI_D0); pci_restore_state(dev); - - if (line) { - pci_enable_device(dev); - serial_txx9_resume_port(line); - } + if (up) + uart_resume_port(&serial_txx9_reg, &up->port); return 0; } #endif @@ -1161,6 +1223,8 @@ static struct pci_driver serial_txx9_pci_driver = { MODULE_DEVICE_TABLE(pci, serial_txx9_pci_tbl); #endif /* ENABLE_SERIAL_TXX9_PCI */ +static struct platform_device *serial_txx9_plat_devs; + static int __init serial_txx9_init(void) { int ret; @@ -1168,13 +1232,39 @@ static int __init serial_txx9_init(void) printk(KERN_INFO "%s version %s\n", serial_name, serial_version); ret = uart_register_driver(&serial_txx9_reg); - if (ret >= 0) { - serial_txx9_register_ports(&serial_txx9_reg); + if (ret) + goto out; + + serial_txx9_plat_devs = platform_device_alloc("serial_txx9", -1); + if (!serial_txx9_plat_devs) { + ret = -ENOMEM; + goto unreg_uart_drv; + } + + ret = platform_device_add(serial_txx9_plat_devs); + if (ret) + goto put_dev; + + serial_txx9_register_ports(&serial_txx9_reg, + &serial_txx9_plat_devs->dev); + + ret = platform_driver_register(&serial_txx9_plat_driver); + if (ret) + goto del_dev; #ifdef ENABLE_SERIAL_TXX9_PCI - ret = pci_register_driver(&serial_txx9_pci_driver); + ret = pci_register_driver(&serial_txx9_pci_driver); #endif - } + if (ret == 0) + goto out; + + del_dev: + platform_device_del(serial_txx9_plat_devs); + put_dev: + platform_device_put(serial_txx9_plat_devs); + unreg_uart_drv: + uart_unregister_driver(&serial_txx9_reg); + out: return ret; } @@ -1185,6 +1275,8 @@ static void __exit serial_txx9_exit(void) #ifdef ENABLE_SERIAL_TXX9_PCI pci_unregister_driver(&serial_txx9_pci_driver); #endif + platform_driver_unregister(&serial_txx9_plat_driver); + platform_device_unregister(serial_txx9_plat_devs); for (i = 0; i < UART_NR; i++) { struct uart_txx9_port *up = &serial_txx9_ports[i]; if (up->port.iobase || up->port.mapbase) diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index c53b696..46c40bb 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -17,6 +17,9 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. */ +#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +#define SUPPORT_SYSRQ +#endif #undef DEBUG @@ -49,11 +52,6 @@ #endif #include <asm/sci.h> - -#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - #include "sh-sci.h" struct sci_port { @@ -645,6 +643,9 @@ static inline int sci_handle_breaks(struct uart_port *port) struct tty_struct *tty = port->info->tty; struct sci_port *s = &sci_ports[port->line]; + if (uart_handle_break(port)) + return 0; + if (!s->break_flag && status & SCxSR_BRK(port)) { #if defined(CONFIG_CPU_SH3) /* Debounce break */ diff --git a/drivers/serial/sn_console.c b/drivers/serial/sn_console.c index 253ceb8..a27e9e9 100644 --- a/drivers/serial/sn_console.c +++ b/drivers/serial/sn_console.c @@ -636,25 +636,6 @@ static irqreturn_t sn_sal_interrupt(int irq, void *dev_id) } /** - * sn_sal_connect_interrupt - Request interrupt, handled by sn_sal_interrupt - * @port: Our sn_cons_port (which contains the uart port) - * - * returns the console irq if interrupt is successfully registered, else 0 - * - */ -static int sn_sal_connect_interrupt(struct sn_cons_port *port) -{ - if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, - IRQF_DISABLED | IRQF_SHARED, - "SAL console driver", port) >= 0) { - return SGI_UART_VECTOR; - } - - printk(KERN_INFO "sn_console: console proceeding in polled mode\n"); - return 0; -} - -/** * sn_sal_timer_poll - this function handles polled console mode * @data: A pointer to our sn_cons_port (which contains the uart port) * @@ -746,30 +727,31 @@ static void __init sn_sal_switch_to_asynch(struct sn_cons_port *port) * mode. We were previously in asynch/polling mode (using init_timer). * * We attempt to switch to interrupt mode here by calling - * sn_sal_connect_interrupt. If that works out, we enable receive interrupts. + * request_irq. If that works out, we enable receive interrupts. */ static void __init sn_sal_switch_to_interrupts(struct sn_cons_port *port) { - int irq; unsigned long flags; - if (!port) - return; - - DPRINTF("sn_console: switching to interrupt driven console\n"); - - spin_lock_irqsave(&port->sc_port.lock, flags); + if (port) { + DPRINTF("sn_console: switching to interrupt driven console\n"); - irq = sn_sal_connect_interrupt(port); + if (request_irq(SGI_UART_VECTOR, sn_sal_interrupt, + IRQF_DISABLED | IRQF_SHARED, + "SAL console driver", port) >= 0) { + spin_lock_irqsave(&port->sc_port.lock, flags); + port->sc_port.irq = SGI_UART_VECTOR; + port->sc_ops = &intr_ops; - if (irq) { - port->sc_port.irq = irq; - port->sc_ops = &intr_ops; - - /* turn on receive interrupts */ - ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); + /* turn on receive interrupts */ + ia64_sn_console_intr_enable(SAL_CONSOLE_INTR_RECV); + spin_unlock_irqrestore(&port->sc_port.lock, flags); + } + else { + printk(KERN_INFO + "sn_console: console proceeding in polled mode\n"); + } } - spin_unlock_irqrestore(&port->sc_port.lock, flags); } /* diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c index 96a852a..bfd4417 100644 --- a/drivers/serial/sunsu.c +++ b/drivers/serial/sunsu.c @@ -1387,8 +1387,8 @@ static enum su_type __devinit su_get_type(struct device_node *dp) struct device_node *ap = of_find_node_by_path("/aliases"); if (ap) { - char *keyb = of_get_property(ap, "keyboard", NULL); - char *ms = of_get_property(ap, "mouse", NULL); + const char *keyb = of_get_property(ap, "keyboard", NULL); + const char *ms = of_get_property(ap, "mouse", NULL); if (keyb) { if (dp == of_find_node_by_path(keyb)) diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c index 48e4f48..8efa07e 100644 --- a/drivers/spi/at25.c +++ b/drivers/spi/at25.c @@ -291,7 +291,7 @@ static int at25_probe(struct spi_device *spi) */ sr = spi_w8r8(spi, AT25_RDSR); if (sr < 0 || sr & AT25_SR_nRDY) { - dev_dbg(&at25->spi->dev, "rdsr --> %d (%02x)\n", sr, sr); + dev_dbg(&spi->dev, "rdsr --> %d (%02x)\n", sr, sr); err = -ENXIO; goto fail; } diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index c2a9fef..66e7bc9 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -23,6 +23,10 @@ #include <asm/arch/board.h> #include <asm/arch/gpio.h> +#ifdef CONFIG_ARCH_AT91 +#include <asm/arch/cpu.h> +#endif + #include "atmel_spi.h" /* @@ -421,7 +425,7 @@ static int atmel_spi_setup(struct spi_device *spi) if (ret) return ret; spi->controller_state = (void *)npcs_pin; - gpio_direction_output(npcs_pin); + gpio_direction_output(npcs_pin, !(spi->mode & SPI_CS_HIGH)); } dev_dbg(&spi->dev, @@ -491,7 +495,7 @@ static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) return 0; } -static void atmel_spi_cleanup(const struct spi_device *spi) +static void atmel_spi_cleanup(struct spi_device *spi) { if (spi->controller_state) gpio_free((unsigned int)spi->controller_data); diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c index 366af49..96f62b2 100644 --- a/drivers/spi/omap_uwire.c +++ b/drivers/spi/omap_uwire.c @@ -459,7 +459,7 @@ static int uwire_setup(struct spi_device *spi) return uwire_setup_transfer(spi, NULL); } -static void uwire_cleanup(const struct spi_device *spi) +static void uwire_cleanup(struct spi_device *spi) { kfree(spi->controller_state); } diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 2328128..6657331 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -189,8 +189,8 @@ static DECLARE_MUTEX(board_lock); * this is exported so that for example a USB or parport based adapter * driver could add devices (which it would learn about out-of-band). */ -struct spi_device *__init_or_module -spi_new_device(struct spi_master *master, struct spi_board_info *chip) +struct spi_device *spi_new_device(struct spi_master *master, + struct spi_board_info *chip) { struct spi_device *proxy; struct device *dev = master->cdev.dev; @@ -352,8 +352,7 @@ static struct class spi_master_class = { * the master's methods before calling spi_register_master(); and (after errors * adding the device) calling spi_master_put() to prevent a memory leak. */ -struct spi_master * __init_or_module -spi_alloc_master(struct device *dev, unsigned size) +struct spi_master *spi_alloc_master(struct device *dev, unsigned size) { struct spi_master *master; @@ -392,8 +391,7 @@ EXPORT_SYMBOL_GPL(spi_alloc_master); * After a successful return, the caller is responsible for calling * spi_unregister_master(). */ -int __init_or_module -spi_register_master(struct spi_master *master) +int spi_register_master(struct spi_master *master) { static atomic_t dyn_bus_id = ATOMIC_INIT((1<<16) - 1); struct device *dev = master->cdev.dev; diff --git a/drivers/spi/spi_bitbang.c b/drivers/spi/spi_bitbang.c index 24a330d..88425e1 100644 --- a/drivers/spi/spi_bitbang.c +++ b/drivers/spi/spi_bitbang.c @@ -302,10 +302,6 @@ static void bitbang_work(struct work_struct *work) setup_transfer = NULL; list_for_each_entry (t, &m->transfers, transfer_list) { - if (bitbang->shutdown) { - status = -ESHUTDOWN; - break; - } /* override or restore speed and wordsize */ if (t->speed_hz || t->bits_per_word) { @@ -410,8 +406,6 @@ int spi_bitbang_transfer(struct spi_device *spi, struct spi_message *m) m->status = -EINPROGRESS; bitbang = spi_master_get_devdata(spi->master); - if (bitbang->shutdown) - return -ESHUTDOWN; spin_lock_irqsave(&bitbang->lock, flags); if (!spi->max_speed_hz) @@ -507,28 +501,12 @@ EXPORT_SYMBOL_GPL(spi_bitbang_start); */ int spi_bitbang_stop(struct spi_bitbang *bitbang) { - unsigned limit = 500; - - spin_lock_irq(&bitbang->lock); - bitbang->shutdown = 0; - while (!list_empty(&bitbang->queue) && limit--) { - spin_unlock_irq(&bitbang->lock); + spi_unregister_master(bitbang->master); - dev_dbg(bitbang->master->cdev.dev, "wait for queue\n"); - msleep(10); - - spin_lock_irq(&bitbang->lock); - } - spin_unlock_irq(&bitbang->lock); - if (!list_empty(&bitbang->queue)) { - dev_err(bitbang->master->cdev.dev, "queue didn't empty\n"); - return -EBUSY; - } + WARN_ON(!list_empty(&bitbang->queue)); destroy_workqueue(bitbang->workqueue); - spi_unregister_master(bitbang->master); - return 0; } EXPORT_SYMBOL_GPL(spi_bitbang_stop); diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index 6ccf8a1..51daa21 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c @@ -1361,10 +1361,9 @@ err_first_setup: return status; } -static void cleanup(const struct spi_device *spi) +static void cleanup(struct spi_device *spi) { - struct chip_data *chip = spi_get_ctldata((struct spi_device *)spi); - kfree(chip); + kfree(spi_get_ctldata(spi)); } static int init_queue(struct driver_data *drv_data) diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index 651379c..b10211c 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -41,7 +41,7 @@ struct s3c24xx_spi { int len; int count; - int (*set_cs)(struct s3c2410_spi_info *spi, + void (*set_cs)(struct s3c2410_spi_info *spi, int cs, int pol); /* data buffers */ @@ -77,7 +77,7 @@ static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) switch (value) { case BITBANG_CS_INACTIVE: - hw->pdata->set_cs(hw->pdata, spi->chip_select, cspol^1); + hw->set_cs(hw->pdata, spi->chip_select, cspol^1); break; case BITBANG_CS_ACTIVE: @@ -98,7 +98,7 @@ static void s3c24xx_spi_chipsel(struct spi_device *spi, int value) /* write new configration */ writeb(spcon, hw->regs + S3C2410_SPCON); - hw->pdata->set_cs(hw->pdata, spi->chip_select, cspol); + hw->set_cs(hw->pdata, spi->chip_select, cspol); break; } diff --git a/drivers/spi/spi_s3c24xx_gpio.c b/drivers/spi/spi_s3c24xx_gpio.c index eda53ed..611ac22 100644 --- a/drivers/spi/spi_s3c24xx_gpio.c +++ b/drivers/spi/spi_s3c24xx_gpio.c @@ -73,6 +73,19 @@ static u32 s3c2410_spigpio_txrx_mode1(struct spi_device *spi, return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, bits); } +static u32 s3c2410_spigpio_txrx_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, bits); +} + +static u32 s3c2410_spigpio_txrx_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, bits); +} + + static void s3c2410_spigpio_chipselect(struct spi_device *dev, int value) { struct s3c2410_spigpio *sg = spidev_to_sg(dev); @@ -108,6 +121,8 @@ static int s3c2410_spigpio_probe(struct platform_device *dev) sp->bitbang.txrx_word[SPI_MODE_0] = s3c2410_spigpio_txrx_mode0; sp->bitbang.txrx_word[SPI_MODE_1] = s3c2410_spigpio_txrx_mode1; + sp->bitbang.txrx_word[SPI_MODE_2] = s3c2410_spigpio_txrx_mode2; + sp->bitbang.txrx_word[SPI_MODE_3] = s3c2410_spigpio_txrx_mode3; /* set state of spi pins */ s3c2410_gpio_setpin(sp->info->pin_clk, 0); diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c index 3d72aa5..3524e3fc 100644 --- a/drivers/tc/zs.c +++ b/drivers/tc/zs.c @@ -70,7 +70,6 @@ #include <asm/dec/machtype.h> #include <asm/dec/serial.h> #include <asm/dec/system.h> -#include <asm/dec/tc.h> #ifdef CONFIG_KGDB #include <asm/kgdb.h> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 825bf88..f5de58a 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -15,7 +15,6 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_U132_HCD) += host/ -obj-$(CONFIG_ETRAX_USB_HOST) += host/ obj-$(CONFIG_USB_OHCI_AT91) += host/ obj-$(CONFIG_USB_ACM) += class/ @@ -27,10 +26,7 @@ obj-$(CONFIG_USB) += storage/ obj-$(CONFIG_USB_ACECAD) += input/ obj-$(CONFIG_USB_AIPTEK) += input/ obj-$(CONFIG_USB_ATI_REMOTE) += input/ -obj-$(CONFIG_USB_HID) += input/ -obj-$(CONFIG_USB_KBD) += input/ obj-$(CONFIG_USB_KBTAB) += input/ -obj-$(CONFIG_USB_MOUSE) += input/ obj-$(CONFIG_USB_MTOUCH) += input/ obj-$(CONFIG_USB_POWERMATE) += input/ obj-$(CONFIG_USB_WACOM) += input/ @@ -51,6 +47,7 @@ obj-$(CONFIG_USB_SERIAL) += serial/ obj-$(CONFIG_USB_ADUTUX) += misc/ obj-$(CONFIG_USB_APPLEDISPLAY) += misc/ obj-$(CONFIG_USB_AUERSWALD) += misc/ +obj-$(CONFIG_USB_BERRY_CHARGE) += misc/ obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/ obj-$(CONFIG_USB_CYTHERM) += misc/ obj-$(CONFIG_USB_EMI26) += misc/ diff --git a/drivers/usb/atm/cxacru.c b/drivers/usb/atm/cxacru.c index 3dfa3e4..30b7bfb 100644 --- a/drivers/usb/atm/cxacru.c +++ b/drivers/usb/atm/cxacru.c @@ -4,6 +4,7 @@ * * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru) + * Copyright (C) 2007 Simon Arlott * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,14 +35,14 @@ #include <linux/errno.h> #include <linux/slab.h> #include <linux/init.h> -#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */ +#include <linux/device.h> #include <linux/firmware.h> #include <linux/mutex.h> #include "usbatm.h" -#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands" -#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott" +#define DRIVER_VERSION "0.3" #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver" static const char cxacru_driver_name[] = "cxacru"; @@ -64,7 +65,7 @@ static const char cxacru_driver_name[] = "cxacru"; #define SDRAM_ENA 0x1 #define CMD_TIMEOUT 2000 /* msecs */ -#define POLL_INTERVAL 5000 /* msecs */ +#define POLL_INTERVAL 1 /* secs */ /* commands for interaction with the modem through the control channel before * firmware is loaded */ @@ -146,6 +147,13 @@ enum cxacru_info_idx { CXINF_MAX = 0x1c, }; +enum cxacru_poll_state { + CXPOLL_STOPPING, + CXPOLL_STOPPED, + CXPOLL_POLLING, + CXPOLL_SHUTDOWN +}; + struct cxacru_modem_type { u32 pll_f_clk; u32 pll_b_clk; @@ -158,7 +166,12 @@ struct cxacru_data { const struct cxacru_modem_type *modem_type; int line_status; + struct mutex adsl_state_serialize; + int adsl_status; struct delayed_work poll_work; + u32 card_info[CXINF_MAX]; + struct mutex poll_state_serialize; + int poll_state; /* contol handles */ struct mutex cm_serialize; @@ -170,6 +183,275 @@ struct cxacru_data { struct completion snd_done; }; +static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm, + u8 *wdata, int wsize, u8 *rdata, int rsize); +static void cxacru_poll_status(struct work_struct *work); + +/* Card info exported through sysfs */ +#define CXACRU__ATTR_INIT(_name) \ +static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL) + +#define CXACRU_CMD_INIT(_name) \ +static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \ + cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name) + +#define CXACRU_ATTR_INIT(_value, _type, _name) \ +static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface(dev); \ + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \ + struct cxacru_data *instance = usbatm_instance->driver_data; \ + return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \ +} \ +CXACRU__ATTR_INIT(_name) + +#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name) +#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) +#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name) + +#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name) +#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) +#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name) + +static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%u\n", value); +} + +static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", value); +} + +static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf) +{ + if (unlikely(value < 0)) { + return snprintf(buf, PAGE_SIZE, "%d.%02u\n", + value / 100, -value % 100); + } else { + return snprintf(buf, PAGE_SIZE, "%d.%02u\n", + value / 100, value % 100); + } +} + +static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf) +{ + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "no\n"); + case 1: return snprintf(buf, PAGE_SIZE, "yes\n"); + default: return 0; + } +} + +static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf) +{ + switch (value) { + case 1: return snprintf(buf, PAGE_SIZE, "not connected\n"); + case 2: return snprintf(buf, PAGE_SIZE, "connected\n"); + case 3: return snprintf(buf, PAGE_SIZE, "lost\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf) +{ + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "down\n"); + case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n"); + case 2: return snprintf(buf, PAGE_SIZE, "training\n"); + case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n"); + case 4: return snprintf(buf, PAGE_SIZE, "exchange\n"); + case 5: return snprintf(buf, PAGE_SIZE, "up\n"); + case 6: return snprintf(buf, PAGE_SIZE, "waiting\n"); + case 7: return snprintf(buf, PAGE_SIZE, "initialising\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf) +{ + switch (value) { + case 0: return 0; + case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n"); + case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n"); + case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +/* + * This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since + * this data is already in atm_dev there's no point. + * + * MAC_ADDRESS_HIGH = 0x????5544 + * MAC_ADDRESS_LOW = 0x33221100 + * Where 00-55 are bytes 0-5 of the MAC. + */ +static ssize_t cxacru_sysfs_show_mac_address(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct atm_dev *atm_dev = usbatm_instance->atm_dev; + + return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n", + atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2], + atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]); +} + +static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct cxacru_data *instance = usbatm_instance->driver_data; + u32 value = instance->card_info[CXINF_LINE_STARTABLE]; + + switch (value) { + case 0: return snprintf(buf, PAGE_SIZE, "running\n"); + case 1: return snprintf(buf, PAGE_SIZE, "stopped\n"); + default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value); + } +} + +static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); + struct cxacru_data *instance = usbatm_instance->driver_data; + int ret; + int poll = -1; + char str_cmd[8]; + int len = strlen(buf); + + if (!capable(CAP_NET_ADMIN)) + return -EACCES; + + ret = sscanf(buf, "%7s", str_cmd); + if (ret != 1) + return -EINVAL; + ret = 0; + + if (mutex_lock_interruptible(&instance->adsl_state_serialize)) + return -ERESTARTSYS; + + if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) { + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0); + if (ret < 0) { + atm_err(usbatm_instance, "change adsl state:" + " CHIP_ADSL_LINE_STOP returned %d\n", ret); + + ret = -EIO; + } else { + ret = len; + poll = CXPOLL_STOPPED; + } + } + + /* Line status is only updated every second + * and the device appears to only react to + * START/STOP every second too. Wait 1.5s to + * be sure that restart will have an effect. */ + if (!strcmp(str_cmd, "restart")) + msleep(1500); + + if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) { + ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); + if (ret < 0) { + atm_err(usbatm_instance, "change adsl state:" + " CHIP_ADSL_LINE_START returned %d\n", ret); + + ret = -EIO; + } else { + ret = len; + poll = CXPOLL_POLLING; + } + } + + if (!strcmp(str_cmd, "poll")) { + ret = len; + poll = CXPOLL_POLLING; + } + + if (ret == 0) { + ret = -EINVAL; + poll = -1; + } + + if (poll == CXPOLL_POLLING) { + mutex_lock(&instance->poll_state_serialize); + switch (instance->poll_state) { + case CXPOLL_STOPPED: + /* start polling */ + instance->poll_state = CXPOLL_POLLING; + break; + + case CXPOLL_STOPPING: + /* abort stop request */ + instance->poll_state = CXPOLL_POLLING; + case CXPOLL_POLLING: + case CXPOLL_SHUTDOWN: + /* don't start polling */ + poll = -1; + } + mutex_unlock(&instance->poll_state_serialize); + } else if (poll == CXPOLL_STOPPED) { + mutex_lock(&instance->poll_state_serialize); + /* request stop */ + if (instance->poll_state == CXPOLL_POLLING) + instance->poll_state = CXPOLL_STOPPING; + mutex_unlock(&instance->poll_state_serialize); + } + + mutex_unlock(&instance->adsl_state_serialize); + + if (poll == CXPOLL_POLLING) + cxacru_poll_status(&instance->poll_work.work); + + return ret; +} + +/* + * All device attributes are included in CXACRU_ALL_FILES + * so that the same list can be used multiple times: + * INIT (define the device attributes) + * CREATE (create all the device files) + * REMOVE (remove all the device files) + * + * With the last two being defined as needed in the functions + * they are used in before calling CXACRU_ALL_FILES() + */ +#define CXACRU_ALL_FILES(_action) \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \ +CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \ +CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \ +CXACRU__ATTR_##_action( mac_address); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \ +CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \ +CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \ +CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \ +CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \ +CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \ +CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \ +CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \ +CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \ +CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \ +CXACRU_CMD_##_action( adsl_state); + +CXACRU_ALL_FILES(INIT); + /* the following three functions are stolen from drivers/usb/core/message.c */ static void cxacru_blocking_completion(struct urb *urb) { @@ -347,8 +629,6 @@ static int cxacru_card_status(struct cxacru_data *instance) return 0; } -static void cxacru_poll_status(struct work_struct *work); - static int cxacru_atm_start(struct usbatm_data *usbatm_instance, struct atm_dev *atm_dev) { @@ -357,6 +637,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, struct atm_dev *atm_dev = usbatm_instance->atm_dev; */ int ret; + int start_polling = 1; dbg("cxacru_atm_start"); @@ -369,14 +650,35 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance, } /* start ADSL */ + mutex_lock(&instance->adsl_state_serialize); ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0); if (ret < 0) { atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret); + mutex_unlock(&instance->adsl_state_serialize); return ret; } /* Start status polling */ - cxacru_poll_status(&instance->poll_work.work); + mutex_lock(&instance->poll_state_serialize); + switch (instance->poll_state) { + case CXPOLL_STOPPED: + /* start polling */ + instance->poll_state = CXPOLL_POLLING; + break; + + case CXPOLL_STOPPING: + /* abort stop request */ + instance->poll_state = CXPOLL_POLLING; + case CXPOLL_POLLING: + case CXPOLL_SHUTDOWN: + /* don't start polling */ + start_polling = 0; + } + mutex_unlock(&instance->poll_state_serialize); + mutex_unlock(&instance->adsl_state_serialize); + + if (start_polling) + cxacru_poll_status(&instance->poll_work.work); return 0; } @@ -387,14 +689,46 @@ static void cxacru_poll_status(struct work_struct *work) u32 buf[CXINF_MAX] = {}; struct usbatm_data *usbatm = instance->usbatm; struct atm_dev *atm_dev = usbatm->atm_dev; + int keep_polling = 1; int ret; ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX); if (ret < 0) { - atm_warn(usbatm, "poll status: error %d\n", ret); + if (ret != -ESHUTDOWN) + atm_warn(usbatm, "poll status: error %d\n", ret); + + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state != CXPOLL_SHUTDOWN) { + instance->poll_state = CXPOLL_STOPPED; + + if (ret != -ESHUTDOWN) + atm_warn(usbatm, "polling disabled, set adsl_state" + " to 'start' or 'poll' to resume\n"); + } + mutex_unlock(&instance->poll_state_serialize); goto reschedule; } + memcpy(instance->card_info, buf, sizeof(instance->card_info)); + + if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) { + instance->adsl_status = buf[CXINF_LINE_STARTABLE]; + + switch (instance->adsl_status) { + case 0: + atm_printk(KERN_INFO, usbatm, "ADSL state: running\n"); + break; + + case 1: + atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n"); + break; + + default: + atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status); + break; + } + } + if (instance->line_status == buf[CXINF_LINE_STATUS]) goto reschedule; @@ -449,7 +783,20 @@ static void cxacru_poll_status(struct work_struct *work) break; } reschedule: - schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL)); + + mutex_lock(&instance->poll_state_serialize); + if (instance->poll_state == CXPOLL_STOPPING && + instance->adsl_status == 1 && /* stopped */ + instance->line_status == 0) /* down */ + instance->poll_state = CXPOLL_STOPPED; + + if (instance->poll_state == CXPOLL_STOPPED) + keep_polling = 0; + mutex_unlock(&instance->poll_state_serialize); + + if (keep_polling) + schedule_delayed_work(&instance->poll_work, + round_jiffies_relative(POLL_INTERVAL*HZ)); } static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw, @@ -684,6 +1031,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, instance->usbatm = usbatm_instance; instance->modem_type = (struct cxacru_modem_type *) id->driver_info; + memset(instance->card_info, 0, sizeof(instance->card_info)); + + mutex_init(&instance->poll_state_serialize); + instance->poll_state = CXPOLL_STOPPED; + instance->line_status = -1; + instance->adsl_status = -1; + + mutex_init(&instance->adsl_state_serialize); instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL); if (!instance->rcv_buf) { @@ -710,6 +1065,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, goto fail; } + #define CXACRU_DEVICE_CREATE_FILE(_name) \ + ret = device_create_file(&intf->dev, &dev_attr_##_name); \ + if (unlikely(ret)) \ + goto fail_sysfs; + CXACRU_ALL_FILES(CREATE); + #undef CXACRU_DEVICE_CREATE_FILE + usb_fill_int_urb(instance->rcv_urb, usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD), instance->rcv_buf, PAGE_SIZE, @@ -730,6 +1092,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance, return 0; + fail_sysfs: + dbg("cxacru_bind: device_create_file failed (%d)\n", ret); + + #define CXACRU_DEVICE_REMOVE_FILE(_name) \ + device_remove_file(&intf->dev, &dev_attr_##_name); + CXACRU_ALL_FILES(REMOVE); + #undef CXACRU_DEVICE_REVOVE_FILE + fail: free_page((unsigned long) instance->snd_buf); free_page((unsigned long) instance->rcv_buf); @@ -744,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, struct usb_interface *intf) { struct cxacru_data *instance = usbatm_instance->driver_data; + int is_polling = 1; dbg("cxacru_unbind entered"); @@ -752,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, return; } - while (!cancel_delayed_work(&instance->poll_work)) - flush_scheduled_work(); + mutex_lock(&instance->poll_state_serialize); + BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN); + + /* ensure that status polling continues unless + * it has already stopped */ + if (instance->poll_state == CXPOLL_STOPPED) + is_polling = 0; + + /* stop polling from being stopped or started */ + instance->poll_state = CXPOLL_SHUTDOWN; + mutex_unlock(&instance->poll_state_serialize); + + if (is_polling) + cancel_rearming_delayed_work(&instance->poll_work); usb_kill_urb(instance->snd_urb); usb_kill_urb(instance->rcv_urb); @@ -762,6 +1145,12 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance, free_page((unsigned long) instance->snd_buf); free_page((unsigned long) instance->rcv_buf); + + #define CXACRU_DEVICE_REMOVE_FILE(_name) \ + device_remove_file(&intf->dev, &dev_attr_##_name); + CXACRU_ALL_FILES(REMOVE); + #undef CXACRU_DEVICE_REVOVE_FILE + kfree(instance); usbatm_instance->driver_data = NULL; diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index dae4ef1..4973e14 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -61,6 +61,7 @@ #include <linux/usb.h> #include <linux/firmware.h> #include <linux/ctype.h> +#include <linux/sched.h> #include <linux/kthread.h> #include <linux/version.h> #include <linux/mutex.h> diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index ec63b0e..b3f779f 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -274,6 +274,9 @@ static void usbatm_complete(struct urb *urb) (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) || urb->status != -EILSEQ )) { + if (urb->status == -ESHUTDOWN) + return; + if (printk_ratelimit()) atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n", __func__, urb, urb->status); @@ -343,7 +346,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char UDSL_ASSERT(sarb->tail + ATM_CELL_PAYLOAD <= sarb->end); } - memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); + memcpy(skb_tail_pointer(sarb), source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD); __skb_put(sarb, ATM_CELL_PAYLOAD); if (pti & 1) { @@ -370,7 +373,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char goto out; } - if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) { + if (crc32_be(~0, skb_tail_pointer(sarb) - pdu_length, pdu_length) != 0xc704dd7b) { atm_rldbg(instance, "%s: packet failed crc check (vcc: 0x%p)!\n", __func__, vcc); atomic_inc(&vcc->stats->rx_err); @@ -396,7 +399,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char goto out; /* atm_charge increments rx_drop */ } - memcpy(skb->data, sarb->tail - pdu_length, length); + skb_copy_to_linear_data(skb, + skb_tail_pointer(sarb) - pdu_length, + length); __skb_put(skb, length); vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u", @@ -484,7 +489,7 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance, ptr[4] = 0xec; ptr += ATM_CELL_HEADER; - memcpy(ptr, skb->data, data_len); + skb_copy_from_linear_data(skb, ptr, data_len); ptr += data_len; __skb_pull(skb, data_len); @@ -966,6 +971,14 @@ static int usbatm_atm_init(struct usbatm_data *instance) /* temp init ATM device, set to 128kbit */ atm_dev->link_rate = 128 * 1000 / 424; + ret = sysfs_create_link(&atm_dev->class_dev.kobj, + &instance->usb_intf->dev.kobj, "device"); + if (ret) { + atm_err(instance, "%s: sysfs_create_link failed: %d\n", + __func__, ret); + goto fail_sysfs; + } + if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) { atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret); goto fail; @@ -984,6 +997,8 @@ static int usbatm_atm_init(struct usbatm_data *instance) return 0; fail: + sysfs_remove_link(&atm_dev->class_dev.kobj, "device"); + fail_sysfs: instance->atm_dev = NULL; atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */ return ret; @@ -1316,8 +1331,10 @@ void usbatm_usb_disconnect(struct usb_interface *intf) kfree(instance->cell_buf); /* ATM finalize */ - if (instance->atm_dev) + if (instance->atm_dev) { + sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device"); atm_dev_deregister(instance->atm_dev); + } usbatm_put_instance(instance); /* taken in usbatm_usb_probe */ } diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 9819962..14de3b1 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -212,7 +212,41 @@ static int acm_write_start(struct acm *acm) } return rc; } +/* + * attributes exported through sysfs + */ +static ssize_t show_caps +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->ctrl_caps); +} +static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL); + +static ssize_t show_country_codes +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + memcpy(buf, acm->country_codes, acm->country_code_size); + return acm->country_code_size; +} +static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL); + +static ssize_t show_country_rel_date +(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct acm *acm = usb_get_intfdata(intf); + + return sprintf(buf, "%d", acm->country_rel_date); +} + +static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL); /* * Interrupt handlers for various ACM device responses */ @@ -326,10 +360,16 @@ static void acm_rx_tasklet(unsigned long _acm) struct tty_struct *tty = acm->tty; struct acm_ru *rcv; unsigned long flags; - int i = 0; + unsigned char throttled; dbg("Entering acm_rx_tasklet"); - if (!ACM_READY(acm) || acm->throttle) + if (!ACM_READY(acm)) + return; + + spin_lock_irqsave(&acm->throttle_lock, flags); + throttled = acm->throttle; + spin_unlock_irqrestore(&acm->throttle_lock, flags); + if (throttled) return; next_buffer: @@ -346,22 +386,20 @@ next_buffer: dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size); tty_buffer_request_room(tty, buf->size); - if (!acm->throttle) + spin_lock_irqsave(&acm->throttle_lock, flags); + throttled = acm->throttle; + spin_unlock_irqrestore(&acm->throttle_lock, flags); + if (!throttled) tty_insert_flip_string(tty, buf->base, buf->size); tty_flip_buffer_push(tty); - spin_lock(&acm->throttle_lock); - if (acm->throttle) { - dbg("Throtteling noticed"); - memmove(buf->base, buf->base + i, buf->size - i); - buf->size -= i; - spin_unlock(&acm->throttle_lock); + if (throttled) { + dbg("Throttling noticed"); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->filled_read_bufs); spin_unlock_irqrestore(&acm->read_lock, flags); return; } - spin_unlock(&acm->throttle_lock); spin_lock_irqsave(&acm->read_lock, flags); list_add(&buf->list, &acm->spare_read_bufs); @@ -467,7 +505,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) goto bail_out; } - if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS)) + if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) && + (acm->ctrl_caps & USB_CDC_CAP_LINE)) goto full_bailout; INIT_LIST_HEAD(&acm->spare_read_urbs); @@ -480,6 +519,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp) list_add(&(acm->rb[i].list), &acm->spare_read_bufs); } + acm->throttle = 0; + tasklet_schedule(&acm->urb_task); done: @@ -507,6 +548,7 @@ static void acm_tty_unregister(struct acm *acm) usb_free_urb(acm->writeurb); for (i = 0; i < nr; i++) usb_free_urb(acm->ru[i].urb); + kfree(acm->country_codes); kfree(acm); } @@ -754,6 +796,7 @@ static int acm_probe (struct usb_interface *intf, const struct usb_device_id *id) { struct usb_cdc_union_desc *union_header = NULL; + struct usb_cdc_country_functional_desc *cfd = NULL; char *buffer = intf->altsetting->extra; int buflen = intf->altsetting->extralen; struct usb_interface *control_interface; @@ -817,8 +860,9 @@ static int acm_probe (struct usb_interface *intf, union_header = (struct usb_cdc_union_desc *) buffer; break; - case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */ - break; /* for now we ignore it */ + case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/ + cfd = (struct usb_cdc_country_functional_desc *)buffer; + break; case USB_CDC_HEADER_TYPE: /* maybe check version */ break; /* for now we ignore it */ case USB_CDC_ACM_TYPE: @@ -976,6 +1020,34 @@ skip_normal_probe: goto alloc_fail7; } + usb_set_intfdata (intf, acm); + + i = device_create_file(&intf->dev, &dev_attr_bmCapabilities); + if (i < 0) + goto alloc_fail8; + + if (cfd) { /* export the country data */ + acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL); + if (!acm->country_codes) + goto skip_countries; + acm->country_code_size = cfd->bLength - 4; + memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4); + acm->country_rel_date = cfd->iCountryCodeRelDate; + + i = device_create_file(&intf->dev, &dev_attr_wCountryCodes); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + + i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + if (i < 0) { + kfree(acm->country_codes); + goto skip_countries; + } + } + +skip_countries: usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress), acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; @@ -999,9 +1071,10 @@ skip_normal_probe: tty_register_device(acm_tty_driver, minor, &control_interface->dev); acm_table[minor] = acm; - usb_set_intfdata (intf, acm); - return 0; + return 0; +alloc_fail8: + usb_free_urb(acm->writeurb); alloc_fail7: for (i = 0; i < num_rx_buf; i++) usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma); @@ -1020,7 +1093,7 @@ alloc_fail: static void acm_disconnect(struct usb_interface *intf) { - struct acm *acm = usb_get_intfdata (intf); + struct acm *acm = usb_get_intfdata(intf); struct usb_device *usb_dev = interface_to_usbdev(intf); int i; @@ -1034,6 +1107,11 @@ static void acm_disconnect(struct usb_interface *intf) mutex_unlock(&open_mutex); return; } + if (acm->country_codes){ + device_remove_file(&intf->dev, &dev_attr_wCountryCodes); + device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate); + } + device_remove_file(&intf->dev, &dev_attr_bmCapabilities); acm->dev = NULL; usb_set_intfdata(acm->control, NULL); usb_set_intfdata(acm->data, NULL); @@ -1092,6 +1170,10 @@ static struct usb_device_id acm_ids[] = { { USB_DEVICE(0x0ace, 0x1611), /* ZyDAS 56K USB MODEM - new version */ .driver_info = SINGLE_RX_URB, /* firmware bug */ }, + { USB_DEVICE(0x22b8, 0x7000), /* Motorola Q Phone */ + .driver_info = NO_UNION_NORMAL, /* has no union descriptor */ + }, + /* control interfaces with various AT-command sets */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_ACM_PROTO_AT_V25TER) }, diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 1bcaea3..09f7765 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -91,6 +91,9 @@ struct acm { struct urb *ctrlurb, *writeurb; /* urbs */ u8 *ctrl_buffer; /* buffers of urbs */ dma_addr_t ctrl_dma; /* dma handles of buffers */ + u8 *country_codes; /* country codes from device */ + unsigned int country_code_size; /* size of this buffer */ + unsigned int country_rel_date; /* release date of version */ struct acm_wb wb[ACM_NW]; struct acm_ru ru[ACM_NR]; struct acm_rb rb[ACM_NR]; diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 63e50a1..6584cf0 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -202,6 +202,7 @@ struct quirk_printer_struct { #define USBLP_QUIRK_BIDIR 0x1 /* reports bidir but requires unidirectional mode (no INs/reads) */ #define USBLP_QUIRK_USB_INIT 0x2 /* needs vendor USB init string */ +#define USBLP_QUIRK_BAD_CLASS 0x4 /* descriptor uses vendor-specific Class or SubClass */ static const struct quirk_printer_struct quirk_printers[] = { { 0x03f0, 0x0004, USBLP_QUIRK_BIDIR }, /* HP DeskJet 895C */ @@ -218,6 +219,7 @@ static const struct quirk_printer_struct quirk_printers[] = { { 0x0409, 0xf0be, USBLP_QUIRK_BIDIR }, /* NEC Picty920 (HP OEM) */ { 0x0409, 0xf1be, USBLP_QUIRK_BIDIR }, /* NEC Picty800 (HP OEM) */ { 0x0482, 0x0010, USBLP_QUIRK_BIDIR }, /* Kyocera Mita FS 820, by zut <kernel@zut.de> */ + { 0x04b8, 0x0202, USBLP_QUIRK_BAD_CLASS }, /* Seiko Epson Receipt Printer M129C */ { 0, 0 } }; @@ -1048,7 +1050,8 @@ static int usblp_select_alts(struct usblp *usblp) ifd = &if_alt->altsetting[i]; if (ifd->desc.bInterfaceClass != 7 || ifd->desc.bInterfaceSubClass != 1) - continue; + if (!(usblp->quirks & USBLP_QUIRK_BAD_CLASS)) + continue; if (ifd->desc.bInterfaceProtocol < USBLP_FIRST_PROTOCOL || ifd->desc.bInterfaceProtocol > USBLP_LAST_PROTOCOL) @@ -1232,6 +1235,7 @@ static struct usb_device_id usblp_ids [] = { { USB_INTERFACE_INFO(7, 1, 1) }, { USB_INTERFACE_INFO(7, 1, 2) }, { USB_INTERFACE_INFO(7, 1, 3) }, + { USB_DEVICE(0x04b8, 0x0202) }, /* Seiko Epson Receipt Printer M129C */ { } /* Terminating entry */ }; diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index 2fc0f88..f493fb1 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -31,7 +31,30 @@ config USB_DEVICEFS For the format of the various /proc/bus/usb/ files, please read <file:Documentation/usb/proc_usb_info.txt>. - Most users want to say Y here. + Usbfs files can't handle Access Control Lists (ACL), which are the + default way to grant access to USB devices for untrusted users of a + desktop system. The usbfs functionality is replaced by real + device-nodes managed by udev. These nodes live in /dev/bus/usb and + are used by libusb. + +config USB_DEVICE_CLASS + bool "USB device class-devices (DEPRECATED)" + depends on USB + default n + ---help--- + Userspace access to USB devices is granted by device-nodes exported + directly from the usbdev in sysfs. Old versions of the driver + core and udev needed additional class devices to export device nodes. + + These additional devices are difficult to handle in userspace, if + information about USB interfaces must be available. One device contains + the device node, the other device contains the interface data. Both + devices are at the same level in sysfs (siblings) and one can't access + the other. The device node created directly by the usbdev is the parent + device of the interface and therefore easily accessible from the interface + event. + + This option provides backward compatibility if needed. config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation (EXPERIMENTAL)" diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 34e9bac..b607870 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -4,7 +4,7 @@ usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \ config.o file.o buffer.o sysfs.o endpoint.o \ - devio.o notify.o generic.o + devio.o notify.o generic.o quirks.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index a47c30b..6753ca0 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -246,7 +246,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end, if (start > end) return start; - down_read(&usb_bus_type.subsys.rwsem); if (iface) { driver_name = (iface->dev.driver ? iface->dev.driver->name @@ -263,7 +262,6 @@ static char *usb_dump_interface_descriptor(char *start, char *end, desc->bInterfaceSubClass, desc->bInterfaceProtocol, driver_name); - up_read(&usb_bus_type.subsys.rwsem); return start; } @@ -604,10 +602,6 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct lock_kernel(); if (!st) { st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); - if (!st) { - unlock_kernel(); - return POLLIN; - } /* we may have dropped BKL - need to check for having lost the race */ if (file->private_data) { @@ -615,6 +609,11 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct st = file->private_data; goto lost_race; } + /* we haven't lost - check for allocation failure now */ + if (!st) { + unlock_kernel(); + return POLLIN; + } /* * need to prevent the module from being unloaded, since diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 2087766..927a181 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -57,7 +57,6 @@ #define USB_MAXBUS 64 #define USB_DEVICE_MAX USB_MAXBUS * 128 -static struct class *usb_device_class; /* Mutual exclusion for removal, open, and release */ DEFINE_MUTEX(usbfs_mutex); @@ -421,14 +420,11 @@ static int claimintf(struct dev_state *ps, unsigned int ifnum) if (test_bit(ifnum, &ps->ifclaimed)) return 0; - /* lock against other changes to driver bindings */ - down_write(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; else err = usb_driver_claim_interface(&usbfs_driver, intf, ps); - up_write(&usb_bus_type.subsys.rwsem); if (err == 0) set_bit(ifnum, &ps->ifclaimed); return err; @@ -444,8 +440,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) if (ifnum >= 8*sizeof(ps->ifclaimed)) return err; dev = ps->dev; - /* lock against other changes to driver bindings */ - down_write(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(dev, ifnum); if (!intf) err = -ENOENT; @@ -453,7 +447,6 @@ static int releaseintf(struct dev_state *ps, unsigned int ifnum) usb_driver_release_interface(&usbfs_driver, intf); err = 0; } - up_write(&usb_bus_type.subsys.rwsem); return err; } @@ -520,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig return ret; } -static struct usb_device *usbdev_lookup_minor(int minor) +static int __match_minor(struct device *dev, void *data) { - struct device *device; - struct usb_device *udev = NULL; + int minor = *((int *)data); - down(&usb_device_class->sem); - list_for_each_entry(device, &usb_device_class->devices, node) { - if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) { - udev = device->platform_data; - break; - } - } - up(&usb_device_class->sem); + if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor)) + return 1; + return 0; +} - return udev; -}; +static struct usb_device *usbdev_lookup_by_minor(int minor) +{ + struct device *dev; + + dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor); + if (!dev) + return NULL; + put_device(dev); + return container_of(dev, struct usb_device, dev); +} /* * file operations @@ -554,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file) goto out; ret = -ENOENT; - /* check if we are called from a real node or usbfs */ + /* usbdev device-node */ if (imajor(inode) == USB_DEVICE_MAJOR) - dev = usbdev_lookup_minor(iminor(inode)); + dev = usbdev_lookup_by_minor(iminor(inode)); +#ifdef CONFIG_USB_DEVICEFS + /* procfs file */ if (!dev) dev = inode->i_private; +#endif if (!dev) goto out; ret = usb_autoresume_device(dev); @@ -581,7 +580,7 @@ static int usbdev_open(struct inode *inode, struct file *file) ps->disccontext = NULL; ps->ifclaimed = 0; security_task_getsecid(current, &ps->secid); - wmb(); + smp_wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; out: @@ -813,7 +812,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) if (copy_from_user(&gd, arg, sizeof(gd))) return -EFAULT; - down_read(&usb_bus_type.subsys.rwsem); intf = usb_ifnum_to_if(ps->dev, gd.interface); if (!intf || !intf->dev.driver) ret = -ENODATA; @@ -822,7 +820,6 @@ static int proc_getdriver(struct dev_state *ps, void __user *arg) sizeof(gd.driver)); ret = (copy_to_user(arg, &gd, sizeof(gd)) ? -EFAULT : 0); } - up_read(&usb_bus_type.subsys.rwsem); return ret; } @@ -857,11 +854,11 @@ static int proc_setintf(struct dev_state *ps, void __user *arg) static int proc_setconfig(struct dev_state *ps, void __user *arg) { - unsigned int u; + int u; int status = 0; struct usb_host_config *actconfig; - if (get_user(u, (unsigned int __user *)arg)) + if (get_user(u, (int __user *)arg)) return -EFAULT; actconfig = ps->dev->actconfig; @@ -912,7 +909,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, struct async *as; struct usb_ctrlrequest *dr = NULL; unsigned int u, totlen, isofrmlen; - int ret, interval = 0, ifnum = -1; + int ret, ifnum = -1; if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_SHORT_NOT_OK| URB_NO_FSBR|URB_ZERO_PACKET)) @@ -992,7 +989,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC) return -EINVAL; - interval = 1 << min (15, ep->desc.bInterval - 1); isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb->number_of_packets; if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) return -ENOMEM; @@ -1021,10 +1017,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) return -EINVAL; - if (ps->dev->speed == USB_SPEED_HIGH) - interval = 1 << min (15, ep->desc.bInterval - 1); - else - interval = ep->desc.bInterval; if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) return -EINVAL; if (!access_ok((uurb->endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb->buffer, uurb->buffer_length)) @@ -1053,7 +1045,11 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, as->urb->setup_packet = (unsigned char*)dr; as->urb->start_frame = uurb->start_frame; as->urb->number_of_packets = uurb->number_of_packets; - as->urb->interval = interval; + if (uurb->type == USBDEVFS_URB_TYPE_ISO || + ps->dev->speed == USB_SPEED_HIGH) + as->urb->interval = 1 << min(15, ep->desc.bInterval - 1); + else + as->urb->interval = ep->desc.bInterval; as->urb->context = as; as->urb->complete = async_completed; for (totlen = u = 0; u < uurb->number_of_packets; u++) { @@ -1352,15 +1348,12 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* disconnect kernel driver from interface */ case USBDEVFS_DISCONNECT: - - down_write(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) { driver = to_usb_driver(intf->dev.driver); dev_dbg (&intf->dev, "disconnect by usbfs\n"); usb_driver_release_interface(driver, intf); } else retval = -ENODATA; - up_write(&usb_bus_type.subsys.rwsem); break; /* let kernel drivers try to (re)bind to the interface */ @@ -1372,7 +1365,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) /* talk directly to the interface's driver */ default: - down_read(&usb_bus_type.subsys.rwsem); if (intf->dev.driver) driver = to_usb_driver(intf->dev.driver); if (driver == NULL || driver->ioctl == NULL) { @@ -1382,7 +1374,6 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl) if (retval == -ENOIOCTLCMD) retval = -ENOTTY; } - up_read(&usb_bus_type.subsys.rwsem); } /* cleanup and return */ @@ -1584,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai return mask; } -const struct file_operations usbfs_device_file_operations = { +const struct file_operations usbdev_file_operations = { .llseek = usbdev_lseek, .read = usbdev_read, .poll = usbdev_poll, @@ -1593,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = { .release = usbdev_release, }; -static int usbdev_add(struct usb_device *dev) +#ifdef CONFIG_USB_DEVICE_CLASS +static struct class *usb_classdev_class; + +static int usb_classdev_add(struct usb_device *dev) { int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1); - dev->usbfs_dev = device_create(usb_device_class, &dev->dev, + dev->usb_classdev = device_create(usb_classdev_class, &dev->dev, MKDEV(USB_DEVICE_MAJOR, minor), "usbdev%d.%d", dev->bus->busnum, dev->devnum); - if (IS_ERR(dev->usbfs_dev)) - return PTR_ERR(dev->usbfs_dev); + if (IS_ERR(dev->usb_classdev)) + return PTR_ERR(dev->usb_classdev); - dev->usbfs_dev->platform_data = dev; return 0; } -static void usbdev_remove(struct usb_device *dev) +static void usb_classdev_remove(struct usb_device *dev) { - device_unregister(dev->usbfs_dev); + device_unregister(dev->usb_classdev); } -static int usbdev_notify(struct notifier_block *self, unsigned long action, - void *dev) +static int usb_classdev_notify(struct notifier_block *self, + unsigned long action, void *dev) { switch (action) { case USB_DEVICE_ADD: - if (usbdev_add(dev)) + if (usb_classdev_add(dev)) return NOTIFY_BAD; break; case USB_DEVICE_REMOVE: - usbdev_remove(dev); + usb_classdev_remove(dev); break; } return NOTIFY_OK; } static struct notifier_block usbdev_nb = { - .notifier_call = usbdev_notify, + .notifier_call = usb_classdev_notify, }; +#endif static struct cdev usb_device_cdev = { .kobj = {.name = "usb_device", }, .owner = THIS_MODULE, }; -int __init usbdev_init(void) +int __init usb_devio_init(void) { int retval; @@ -1646,38 +1640,38 @@ int __init usbdev_init(void) err("unable to register minors for usb_device"); goto out; } - cdev_init(&usb_device_cdev, &usbfs_device_file_operations); + cdev_init(&usb_device_cdev, &usbdev_file_operations); retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX); if (retval) { err("unable to get usb_device major %d", USB_DEVICE_MAJOR); goto error_cdev; } - usb_device_class = class_create(THIS_MODULE, "usb_device"); - if (IS_ERR(usb_device_class)) { +#ifdef CONFIG_USB_DEVICE_CLASS + usb_classdev_class = class_create(THIS_MODULE, "usb_device"); + if (IS_ERR(usb_classdev_class)) { err("unable to register usb_device class"); - retval = PTR_ERR(usb_device_class); - goto error_class; + retval = PTR_ERR(usb_classdev_class); + cdev_del(&usb_device_cdev); + usb_classdev_class = NULL; + goto out; } usb_register_notify(&usbdev_nb); - +#endif out: return retval; -error_class: - usb_device_class = NULL; - cdev_del(&usb_device_cdev); - error_cdev: unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); goto out; } -void usbdev_cleanup(void) +void usb_devio_cleanup(void) { +#ifdef CONFIG_USB_DEVICE_CLASS usb_unregister_notify(&usbdev_nb); - class_destroy(usb_device_class); + class_destroy(usb_classdev_class); +#endif cdev_del(&usb_device_cdev); unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX); } - diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 600d1bc..b9f7f90 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -287,9 +287,9 @@ static int usb_unbind_interface(struct device *dev) * way to bind to an interface is to return the private data from * the driver's probe() method. * - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver probe() entries don't need extra locking, - * but other call contexts may need to explicitly claim those locks. + * Callers must own the device lock, so driver probe() entries don't need + * extra locking, but other call contexts may need to explicitly claim that + * lock. */ int usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) @@ -330,9 +330,9 @@ EXPORT_SYMBOL(usb_driver_claim_interface); * also causes the driver disconnect() method to be called. * * This call is synchronous, and may not be used in an interrupt context. - * Callers must own the device lock and the driver model's usb_bus_type.subsys - * writelock. So driver disconnect() entries don't need extra locking, - * but other call contexts may need to explicitly claim those locks. + * Callers must own the device lock, so driver disconnect() entries don't + * need extra locking, but other call contexts may need to explicitly claim + * that lock. */ void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) @@ -366,19 +366,8 @@ void usb_driver_release_interface(struct usb_driver *driver, EXPORT_SYMBOL(usb_driver_release_interface); /* returns 0 if no match, 1 if match */ -int usb_match_one_id(struct usb_interface *interface, - const struct usb_device_id *id) +int usb_match_device(struct usb_device *dev, const struct usb_device_id *id) { - struct usb_host_interface *intf; - struct usb_device *dev; - - /* proc_connectinfo in devio.c may call us with id == NULL. */ - if (id == NULL) - return 0; - - intf = interface->cur_altsetting; - dev = interface_to_usbdev(interface); - if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) return 0; @@ -409,6 +398,26 @@ int usb_match_one_id(struct usb_interface *interface, (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) return 0; + return 1; +} + +/* returns 0 if no match, 1 if match */ +int usb_match_one_id(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_host_interface *intf; + struct usb_device *dev; + + /* proc_connectinfo in devio.c may call us with id == NULL. */ + if (id == NULL) + return 0; + + intf = interface->cur_altsetting; + dev = interface_to_usbdev(interface); + + if (!usb_match_device(dev, id)) + return 0; + /* The interface class, subclass, and protocol should never be * checked for a match if the device class is Vendor Specific, * unless the match record specifies the Vendor ID. */ @@ -565,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv) } #ifdef CONFIG_HOTPLUG - -/* - * This sends an uevent to userspace, typically helping to load driver - * or other modules, configure the device, and more. Drivers can provide - * a MODULE_DEVICE_TABLE to help with module loading subtasks. - * - * We're called either from khubd (the typical case) or from root hub - * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle - * delays in event delivery. Use sysfs (and DEVPATH) to make sure the - * device (and this configuration!) are still present. - */ static int usb_uevent(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { - struct usb_interface *intf; struct usb_device *usb_dev; - struct usb_host_interface *alt; int i = 0; int length = 0; @@ -591,13 +587,11 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, /* driver is often null here; dev_dbg() would oops */ pr_debug ("usb %s: uevent\n", dev->bus_id); - if (is_usb_device(dev)) { + if (is_usb_device(dev)) usb_dev = to_usb_device(dev); - alt = NULL; - } else { - intf = to_usb_interface(dev); + else { + struct usb_interface *intf = to_usb_interface(dev); usb_dev = interface_to_usbdev(intf); - alt = intf->cur_altsetting; } if (usb_dev->devnum < 0) { @@ -612,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, #ifdef CONFIG_USB_DEVICEFS /* If this is available, userspace programs can directly read * all the device descriptors we don't tell them about. Or - * even act as usermode drivers. - * - * FIXME reduce hardwired intelligence here + * act as usermode drivers. */ if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, @@ -641,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp, usb_dev->descriptor.bDeviceProtocol)) return -ENOMEM; - if (!is_usb_device(dev)) { - - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "INTERFACE=%d/%d/%d", - alt->desc.bInterfaceClass, - alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) - return -ENOMEM; + "BUSNUM=%03d", + usb_dev->bus->busnum)) + return -ENOMEM; - if (add_uevent_var(envp, num_envp, &i, + if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", - le16_to_cpu(usb_dev->descriptor.idVendor), - le16_to_cpu(usb_dev->descriptor.idProduct), - le16_to_cpu(usb_dev->descriptor.bcdDevice), - usb_dev->descriptor.bDeviceClass, - usb_dev->descriptor.bDeviceSubClass, - usb_dev->descriptor.bDeviceProtocol, - alt->desc.bInterfaceClass, - alt->desc.bInterfaceSubClass, - alt->desc.bInterfaceProtocol)) - return -ENOMEM; - } + "DEVNUM=%03d", + usb_dev->devnum)) + return -ENOMEM; envp[i] = NULL; - return 0; } #else static int usb_uevent(struct device *dev, char **envp, - int num_envp, char *buffer, int buffer_size) + int num_envp, char *buffer, int buffer_size) { return -ENODEV; } - #endif /* CONFIG_HOTPLUG */ /** @@ -743,6 +720,7 @@ EXPORT_SYMBOL_GPL(usb_deregister_device_driver); * usb_register_driver - register a USB interface driver * @new_driver: USB operations for the interface driver * @owner: module owner of this driver. + * @mod_name: module name string * * Registers a USB interface driver with the USB core. The list of * unattached interfaces will be rescanned whenever a new driver is @@ -862,8 +840,10 @@ static int usb_resume_device(struct usb_device *udev) done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) + if (status == 0) { + udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; + } return status; } @@ -952,13 +932,19 @@ static int autosuspend_check(struct usb_device *udev) { int i; struct usb_interface *intf; + unsigned long suspend_time; - /* For autosuspend, fail fast if anything is in use. - * Also fail if any interfaces require remote wakeup but it - * isn't available. */ + /* For autosuspend, fail fast if anything is in use or autosuspend + * is disabled. Also fail if any interfaces require remote wakeup + * but it isn't available. + */ udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->pm_usage_cnt > 0) return -EBUSY; + if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled) + return -EPERM; + + suspend_time = udev->last_busy + udev->autosuspend_delay; if (udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; @@ -974,6 +960,24 @@ static int autosuspend_check(struct usb_device *udev) } } } + + /* If everything is okay but the device hasn't been idle for long + * enough, queue a delayed autosuspend request. + */ + if (time_after(suspend_time, jiffies)) { + if (!timer_pending(&udev->autosuspend.timer)) { + + /* The value of jiffies may change between the + * time_after() comparison above and the subtraction + * below. That's okay; the system behaves sanely + * when a timer is registered for the present moment + * or for the past. + */ + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + suspend_time - jiffies); + } + return -EAGAIN; + } return 0; } @@ -981,7 +985,7 @@ static int autosuspend_check(struct usb_device *udev) #define autosuspend_check(udev) 0 -#endif +#endif /* CONFIG_USB_SUSPEND */ /** * usb_suspend_both - suspend a USB device and its interfaces @@ -1019,26 +1023,25 @@ static int autosuspend_check(struct usb_device *udev) * * This routine can run only in process context. */ -int usb_suspend_both(struct usb_device *udev, pm_message_t msg) +static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) { int status = 0; int i = 0; struct usb_interface *intf; struct usb_device *parent = udev->parent; - cancel_delayed_work(&udev->autosuspend); - if (udev->state == USB_STATE_NOTATTACHED) - return 0; - if (udev->state == USB_STATE_SUSPENDED) - return 0; + if (udev->state == USB_STATE_NOTATTACHED || + udev->state == USB_STATE_SUSPENDED) + goto done; udev->do_remote_wakeup = device_may_wakeup(&udev->dev); if (udev->auto_pm) { status = autosuspend_check(udev); if (status < 0) - return status; + goto done; } + cancel_delayed_work(&udev->autosuspend); /* Suspend all the interfaces and then udev itself */ if (udev->actconfig) { @@ -1063,6 +1066,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } else if (parent) usb_autosuspend_device(parent); + done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1095,7 +1099,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * * This routine can run only in process context. */ -int usb_resume_both(struct usb_device *udev) +static int usb_resume_both(struct usb_device *udev) { int status = 0; int i; @@ -1103,11 +1107,17 @@ int usb_resume_both(struct usb_device *udev) struct usb_device *parent = udev->parent; cancel_delayed_work(&udev->autosuspend); - if (udev->state == USB_STATE_NOTATTACHED) - return -ENODEV; + if (udev->state == USB_STATE_NOTATTACHED) { + status = -ENODEV; + goto done; + } /* Propagate the resume up the tree, if necessary */ if (udev->state == USB_STATE_SUSPENDED) { + if (udev->auto_pm && udev->autoresume_disabled) { + status = -EPERM; + goto done; + } if (parent) { status = usb_autoresume_device(parent); if (status == 0) { @@ -1153,6 +1163,7 @@ int usb_resume_both(struct usb_device *udev) } } + done: // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1167,20 +1178,34 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt) int status = 0; usb_pm_lock(udev); + udev->auto_pm = 1; udev->pm_usage_cnt += inc_usage_cnt; WARN_ON(udev->pm_usage_cnt < 0); if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) { - udev->auto_pm = 1; - status = usb_resume_both(udev); + if (udev->state == USB_STATE_SUSPENDED) + status = usb_resume_both(udev); if (status != 0) udev->pm_usage_cnt -= inc_usage_cnt; - } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + else if (inc_usage_cnt) + udev->last_busy = jiffies; + } else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) { + if (inc_usage_cnt) + udev->last_busy = jiffies; + status = usb_suspend_both(udev, PMSG_SUSPEND); + } usb_pm_unlock(udev); return status; } +/* usb_autosuspend_work - callback routine to autosuspend a USB device */ +void usb_autosuspend_work(struct work_struct *work) +{ + struct usb_device *udev = + container_of(work, struct usb_device, autosuspend.work); + + usb_autopm_do_device(udev, 0); +} + /** * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces * @udev: the usb_device to autosuspend @@ -1211,6 +1236,26 @@ void usb_autosuspend_device(struct usb_device *udev) } /** + * usb_try_autosuspend_device - attempt an autosuspend of a USB device and its interfaces + * @udev: the usb_device to autosuspend + * + * This routine should be called when a core subsystem thinks @udev may + * be ready to autosuspend. + * + * @udev's usage counter left unchanged. If it or any of the usage counters + * for an active interface is greater than 0, or autosuspend is not allowed + * for any other reason, no autosuspend request will be queued. + * + * This routine can run only in process context. + */ +void usb_try_autosuspend_device(struct usb_device *udev) +{ + usb_autopm_do_device(udev, 0); + // dev_dbg(&udev->dev, "%s: cnt %d\n", + // __FUNCTION__, udev->pm_usage_cnt); +} + +/** * usb_autoresume_device - immediately autoresume a USB device and its interfaces * @udev: the usb_device to autoresume * @@ -1252,15 +1297,20 @@ static int usb_autopm_do_interface(struct usb_interface *intf, if (intf->condition == USB_INTERFACE_UNBOUND) status = -ENODEV; else { + udev->auto_pm = 1; intf->pm_usage_cnt += inc_usage_cnt; if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) { - udev->auto_pm = 1; - status = usb_resume_both(udev); + if (udev->state == USB_STATE_SUSPENDED) + status = usb_resume_both(udev); if (status != 0) intf->pm_usage_cnt -= inc_usage_cnt; - } else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0) - queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + else if (inc_usage_cnt) + udev->last_busy = jiffies; + } else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) { + if (inc_usage_cnt) + udev->last_busy = jiffies; + status = usb_suspend_both(udev, PMSG_SUSPEND); + } } usb_pm_unlock(udev); return status; @@ -1319,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface); * or @intf is unbound. A typical example would be a character-device * driver when its device file is opened. * - * The routine increments @intf's usage counter. So long as the counter - * is greater than 0, autosuspend will not be allowed for @intf or its - * usb_device. When the driver is finished using @intf it should call - * usb_autopm_put_interface() to decrement the usage counter and queue - * a delayed autosuspend request (if the counter is <= 0). + * + * The routine increments @intf's usage counter. (However if the + * autoresume fails then the counter is re-decremented.) So long as the + * counter is greater than 0, autosuspend will not be allowed for @intf + * or its usb_device. When the driver is finished using @intf it should + * call usb_autopm_put_interface() to decrement the usage counter and + * queue a delayed autosuspend request (if the counter is <= 0). + * * * Note that @intf->pm_usage_cnt is owned by the interface driver. The * core will not change its value other than the increment and decrement @@ -1371,50 +1424,96 @@ int usb_autopm_set_interface(struct usb_interface *intf) } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); +#else + +void usb_autosuspend_work(struct work_struct *work) +{} + #endif /* CONFIG_USB_SUSPEND */ -static int usb_suspend(struct device *dev, pm_message_t message) +/** + * usb_external_suspend_device - external suspend of a USB device and its interfaces + * @udev: the usb_device to suspend + * @msg: Power Management message describing this state transition + * + * This routine handles external suspend requests: ones not generated + * internally by a USB driver (autosuspend) but rather coming from the user + * (via sysfs) or the PM core (system sleep). The suspend will be carried + * out regardless of @udev's usage counter or those of its interfaces, + * and regardless of whether or not remote wakeup is enabled. Of course, + * interface drivers still have the option of failing the suspend (if + * there are unsuspended children, for example). + * + * The caller must hold @udev's device lock. + */ +int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_suspend_both(udev, message); - usb_pm_unlock(udev); - } else - status = 0; + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_suspend_both(udev, msg); + usb_pm_unlock(udev); return status; } -static int usb_resume(struct device *dev) +/** + * usb_external_resume_device - external resume of a USB device and its interfaces + * @udev: the usb_device to resume + * + * This routine handles external resume requests: ones not generated + * internally by a USB driver (autoresume) but rather coming from the user + * (via sysfs), the PM core (system resume), or the device itself (remote + * wakeup). @udev's usage counter is unaffected. + * + * The caller must hold @udev's device lock. + */ +int usb_external_resume_device(struct usb_device *udev) { int status; - if (is_usb_device(dev)) { - struct usb_device *udev = to_usb_device(dev); - - usb_pm_lock(udev); - udev->auto_pm = 0; - status = usb_resume_both(udev); - usb_pm_unlock(udev); + usb_pm_lock(udev); + udev->auto_pm = 0; + status = usb_resume_both(udev); + usb_pm_unlock(udev); - /* Rebind drivers that had no suspend method? */ - } else - status = 0; + /* Now that the device is awake, we can start trying to autosuspend + * it again. */ + if (status == 0) + usb_try_autosuspend_device(udev); return status; } +static int usb_suspend(struct device *dev, pm_message_t message) +{ + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + return usb_external_suspend_device(to_usb_device(dev), message); +} + +static int usb_resume(struct device *dev) +{ + struct usb_device *udev; + + if (!is_usb_device(dev)) /* Ignore PM for interfaces */ + return 0; + udev = to_usb_device(dev); + if (udev->autoresume_disabled) + return -EPERM; + return usb_external_resume_device(udev); +} + +#else + +#define usb_suspend NULL +#define usb_resume NULL + #endif /* CONFIG_PM */ struct bus_type usb_bus_type = { .name = "usb", .match = usb_device_match, .uevent = usb_uevent, -#ifdef CONFIG_PM .suspend = usb_suspend, .resume = usb_resume, -#endif }; diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 5e628ae..e0ec704 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -229,7 +229,7 @@ static int init_endpoint_class(void) kref_init(&ep_class->kref); ep_class->class = class_create(THIS_MODULE, "usb_endpoint"); if (IS_ERR(ep_class->class)) { - result = IS_ERR(ep_class->class); + result = PTR_ERR(ep_class->class); goto class_create_error; } diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index b531a4f..9bbcb20e 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -184,7 +184,7 @@ static void generic_disconnect(struct usb_device *udev) /* if this is only an unbind, not a physical disconnect, then * unconfigure the device */ if (udev->actconfig) - usb_set_configuration(udev, 0); + usb_set_configuration(udev, -1); usb_remove_sysfs_dev_files(udev); } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index b26c19e..40cf882 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -37,6 +37,7 @@ #include <asm/irq.h> #include <asm/byteorder.h> #include <linux/platform_device.h> +#include <linux/workqueue.h> #include <linux/usb.h> @@ -544,6 +545,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) unsigned long flags; char buffer[4]; /* Any root hubs with > 31 ports? */ + if (unlikely(!hcd->rh_registered)) + return; if (!hcd->uses_new_polling && !hcd->status_urb) return; @@ -1296,14 +1299,26 @@ int hcd_bus_resume (struct usb_bus *bus) return status; } +/* Workqueue routine for root-hub remote wakeup */ +static void hcd_resume_work(struct work_struct *work) +{ + struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work); + struct usb_device *udev = hcd->self.root_hub; + + usb_lock_device(udev); + usb_mark_last_busy(udev); + usb_external_resume_device(udev); + usb_unlock_device(udev); +} + /** * usb_hcd_resume_root_hub - called by HCD to resume its root hub * @hcd: host controller for this root hub * * The USB host controller calls this function when its root hub is * suspended (with the remote wakeup feature enabled) and a remote - * wakeup request is received. It queues a request for khubd to - * resume the root hub (that is, manage its downstream ports again). + * wakeup request is received. The routine submits a workqueue request + * to resume the root hub (that is, manage its downstream ports again). */ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) { @@ -1311,7 +1326,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) - usb_resume_root_hub (hcd->self.root_hub); + queue_work(ksuspend_usb_wq, &hcd->wakeup_work); spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); @@ -1500,6 +1515,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; +#ifdef CONFIG_PM + INIT_WORK(&hcd->wakeup_work, hcd_resume_work); +#endif hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -1666,16 +1684,20 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); +#ifdef CONFIG_PM + flush_workqueue(ksuspend_usb_wq); +#endif + mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); mutex_unlock(&usb_bus_list_lock); - hcd->poll_rh = 0; - del_timer_sync(&hcd->rh_timer); - hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + if (hcd->irq >= 0) free_irq(hcd->irq, hcd); usb_deregister_bus(&hcd->self); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 2a269ca..ef50fa4 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -68,6 +68,9 @@ struct usb_hcd { struct timer_list rh_timer; /* drives root-hub polling */ struct urb *status_urb; /* the current status urb */ +#ifdef CONFIG_PM + struct work_struct wakeup_work; /* for remote wakeup */ +#endif /* * hardware info/state diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 590ec82..bde29ab 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -44,6 +44,7 @@ struct usb_hub { struct usb_hub_status hub; struct usb_port_status port; } *status; /* buffer for status reports */ + struct mutex status_mutex; /* for the status buffer */ int error; /* last reported error */ int nerrors; /* track consecutive errors */ @@ -118,8 +119,7 @@ MODULE_PARM_DESC(use_both_schemes, "first one fails"); -#ifdef DEBUG -static inline char *portspeed (int portstatus) +static inline char *portspeed(int portstatus) { if (portstatus & (1 << USB_PORT_FEAT_HIGHSPEED)) return "480 Mb/s"; @@ -128,7 +128,6 @@ static inline char *portspeed (int portstatus) else return "12 Mb/s"; } -#endif /* Note that hdev or one of its children must be locked! */ static inline struct usb_hub *hdev_to_hub(struct usb_device *hdev) @@ -535,6 +534,7 @@ static int hub_hub_status(struct usb_hub *hub, { int ret; + mutex_lock(&hub->status_mutex); ret = get_hub_status(hub->hdev, &hub->status->hub); if (ret < 0) dev_err (hub->intfdev, @@ -544,6 +544,7 @@ static int hub_hub_status(struct usb_hub *hub, *change = le16_to_cpu(hub->status->hub.wHubChange); ret = 0; } + mutex_unlock(&hub->status_mutex); return ret; } @@ -617,6 +618,7 @@ static int hub_configure(struct usb_hub *hub, ret = -ENOMEM; goto fail; } + mutex_init(&hub->status_mutex); hub->descriptor = kmalloc(sizeof(*hub->descriptor), GFP_KERNEL); if (!hub->descriptor) { @@ -1277,11 +1279,8 @@ int usb_new_device(struct usb_device *udev) { int err; - /* Lock ourself into memory in order to keep a probe sequence - * sleeping in a new thread from allowing us to be unloaded. - */ - if (!try_module_get(THIS_MODULE)) - return -EINVAL; + /* Determine quirks */ + usb_detect_quirks(udev); err = usb_get_configuration(udev); if (err < 0) { @@ -1368,11 +1367,15 @@ int usb_new_device(struct usb_device *udev) } #endif + /* export the usbdev device-node for libusb */ + udev->dev.devt = MKDEV(USB_DEVICE_MAJOR, + (((udev->bus->busnum-1) * 128) + (udev->devnum-1))); + /* Register the device. The device driver is responsible - * for adding the device files to usbfs and sysfs and for - * configuring the device. + * for adding the device files to sysfs and for configuring + * the device. */ - err = device_add (&udev->dev); + err = device_add(&udev->dev); if (err) { dev_err(&udev->dev, "can't device_add, error %d\n", err); goto fail; @@ -1383,7 +1386,6 @@ int usb_new_device(struct usb_device *udev) usb_autoresume_device(udev->parent); exit: - module_put(THIS_MODULE); return err; fail: @@ -1396,6 +1398,7 @@ static int hub_port_status(struct usb_hub *hub, int port1, { int ret; + mutex_lock(&hub->status_mutex); ret = get_port_status(hub->hdev, port1, &hub->status->port); if (ret < 4) { dev_err (hub->intfdev, @@ -1407,6 +1410,7 @@ static int hub_port_status(struct usb_hub *hub, int port1, *change = le16_to_cpu(hub->status->port.wPortChange); ret = 0; } + mutex_unlock(&hub->status_mutex); return ret; } @@ -1855,12 +1859,8 @@ static int remote_wakeup(struct usb_device *udev) usb_lock_device(udev); if (udev->state == USB_STATE_SUSPENDED) { dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-"); - status = usb_autoresume_device(udev); - - /* Give the interface drivers a chance to do something, - * then autosuspend the device again. */ - if (status == 0) - usb_autosuspend_device(udev); + usb_mark_last_busy(udev); + status = usb_external_resume_device(udev); } usb_unlock_device(udev); return status; @@ -1904,6 +1904,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; + int status = 0; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -1927,24 +1928,18 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) dev_dbg(&intf->dev, "%s\n", __FUNCTION__); + /* stop khubd and related activity */ + hub_quiesce(hub); + /* "global suspend" of the downstream HC-to-USB interface */ if (!hdev->parent) { - struct usb_bus *bus = hdev->bus; - if (bus) { - int status = hcd_bus_suspend (bus); - - if (status != 0) { - dev_dbg(&hdev->dev, "'global' suspend %d\n", - status); - return status; - } - } else - return -EOPNOTSUPP; + status = hcd_bus_suspend(hdev->bus); + if (status != 0) { + dev_dbg(&hdev->dev, "'global' suspend %d\n", status); + hub_activate(hub); + } } - - /* stop khubd and related activity */ - hub_quiesce(hub); - return 0; + return status; } static int hub_resume(struct usb_interface *intf) @@ -1989,13 +1984,6 @@ static inline int remote_wakeup(struct usb_device *udev) #define hub_resume NULL #endif -void usb_resume_root_hub(struct usb_device *hdev) -{ - struct usb_hub *hub = hdev_to_hub(hdev); - - kick_khubd(hub); -} - /* USB 2.0 spec, 7.1.7.3 / fig 7-29: * @@ -2439,7 +2427,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, if (portchange & USB_PORT_STAT_C_CONNECTION) { status = hub_port_debounce(hub, port1); - if (status < 0) { + if (status < 0 && printk_ratelimit()) { dev_err (hub_dev, "connect-debounce failed, port %d disabled\n", port1); diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 11dad22..cddfc62 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -662,7 +662,7 @@ static void usbfs_add_device(struct usb_device *dev) sprintf (name, "%03d", dev->devnum); dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG, dev->bus->usbfs_dentry, dev, - &usbfs_device_file_operations, + &usbdev_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { err ("error creating usbfs device entry"); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 8aca357..b743478 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -11,6 +11,7 @@ #include <linux/timer.h> #include <linux/ctype.h> #include <linux/device.h> +#include <linux/usb/quirks.h> #include <asm/byteorder.h> #include <asm/scatterlist.h> @@ -220,10 +221,15 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + int interval; + + if (usb_dev->speed == USB_SPEED_HIGH) + interval = 1 << min(15, ep->desc.bInterval - 1); + else + interval = ep->desc.bInterval; pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30); usb_fill_int_urb(urb, usb_dev, pipe, data, len, - usb_api_blocking_completion, NULL, - ep->desc.bInterval); + usb_api_blocking_completion, NULL, interval); } else usb_fill_bulk_urb(urb, usb_dev, pipe, data, len, usb_api_blocking_completion, NULL); @@ -406,10 +412,24 @@ int usb_sg_init ( io->urbs [i]->status = -EINPROGRESS; io->urbs [i]->actual_length = 0; + /* + * Some systems need to revert to PIO when DMA is temporarily + * unavailable. For their sakes, both transfer_buffer and + * transfer_dma are set when possible. However this can only + * work on systems without HIGHMEM, since DMA buffers located + * in high memory are not directly addressable by the CPU for + * PIO ... so when HIGHMEM is in use, transfer_buffer is NULL + * to prevent stale pointers and to help spot bugs. + */ if (dma) { - /* hc may use _only_ transfer_dma */ io->urbs [i]->transfer_dma = sg_dma_address (sg + i); len = sg_dma_len (sg + i); +#ifdef CONFIG_HIGHMEM + io->urbs[i]->transfer_buffer = NULL; +#else + io->urbs[i]->transfer_buffer = + page_address(sg[i].page) + sg[i].offset; +#endif } else { /* hc may use _only_ transfer_buffer */ io->urbs [i]->transfer_buffer = @@ -685,7 +705,10 @@ static int usb_string_sub(struct usb_device *dev, unsigned int langid, /* Try to read the string descriptor by asking for the maximum * possible number of bytes */ - rc = usb_get_string(dev, langid, index, buf, 255); + if (dev->quirks & USB_QUIRK_STRING_FETCH_255) + rc = -EIO; + else + rc = usb_get_string(dev, langid, index, buf, 255); /* If that failed try to read the descriptor length, then * ask for just that many bytes */ @@ -1296,7 +1319,7 @@ int usb_reset_configuration(struct usb_device *dev) return 0; } -static void release_interface(struct device *dev) +void usb_release_interface(struct device *dev) { struct usb_interface *intf = to_usb_interface(dev); struct usb_interface_cache *intfc = @@ -1306,6 +1329,67 @@ static void release_interface(struct device *dev) kfree(intf); } +#ifdef CONFIG_HOTPLUG +static int usb_if_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct usb_device *usb_dev; + struct usb_interface *intf; + struct usb_host_interface *alt; + int i = 0; + int length = 0; + + if (!dev) + return -ENODEV; + + /* driver is often null here; dev_dbg() would oops */ + pr_debug ("usb %s: uevent\n", dev->bus_id); + + intf = to_usb_interface(dev); + usb_dev = interface_to_usbdev(intf); + alt = intf->cur_altsetting; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "INTERFACE=%d/%d/%d", + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + if (add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X", + le16_to_cpu(usb_dev->descriptor.idVendor), + le16_to_cpu(usb_dev->descriptor.idProduct), + le16_to_cpu(usb_dev->descriptor.bcdDevice), + usb_dev->descriptor.bDeviceClass, + usb_dev->descriptor.bDeviceSubClass, + usb_dev->descriptor.bDeviceProtocol, + alt->desc.bInterfaceClass, + alt->desc.bInterfaceSubClass, + alt->desc.bInterfaceProtocol)) + return -ENOMEM; + + envp[i] = NULL; + return 0; +} + +#else + +static int usb_if_uevent(struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + return -ENODEV; +} +#endif /* CONFIG_HOTPLUG */ + +struct device_type usb_if_device_type = { + .name = "usb_interface", + .release = usb_release_interface, + .uevent = usb_if_uevent, +}; + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1316,6 +1400,14 @@ static void release_interface(struct device *dev) * use this kind of configurability; many devices only have one * configuration. * + * @configuration is the value of the configuration to be installed. + * According to the USB spec (e.g. section 9.1.1.5), configuration values + * must be non-zero; a value of zero indicates that the device in + * unconfigured. However some devices erroneously use 0 as one of their + * configuration values. To help manage such devices, this routine will + * accept @configuration = -1 as indicating the device should be put in + * an unconfigured state. + * * USB device configurations may affect Linux interoperability, * power consumption and the functionality available. For example, * the default configuration is limited to using 100mA of bus power, @@ -1332,7 +1424,7 @@ static void release_interface(struct device *dev) * * This call is synchronous. The calling context must be able to sleep, * must own the device lock, and must not hold the driver model's USB - * bus rwsem; usb device driver probe() methods cannot use this routine. + * bus mutex; usb device driver probe() methods cannot use this routine. * * Returns zero on success, or else the status code returned by the * underlying call that failed. On successful completion, each interface @@ -1347,10 +1439,15 @@ int usb_set_configuration(struct usb_device *dev, int configuration) struct usb_interface **new_interfaces = NULL; int n, nintf; - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - if (dev->config[i].desc.bConfigurationValue == configuration) { - cp = &dev->config[i]; - break; + if (configuration == -1) + configuration = 0; + else { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (dev->config[i].desc.bConfigurationValue == + configuration) { + cp = &dev->config[i]; + break; + } } } if ((!cp && configuration != 0)) @@ -1359,6 +1456,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration) /* The USB spec says configuration 0 means unconfigured. * But if a device includes a configuration numbered 0, * we will accept it as a correctly configured state. + * Use -1 if you really want to unconfigure the device. */ if (cp && configuration == 0) dev_warn(&dev->dev, "config 0 descriptor??\n"); @@ -1455,8 +1553,8 @@ free_interfaces: intf->dev.parent = &dev->dev; intf->dev.driver = NULL; intf->dev.bus = &usb_bus_type; + intf->dev.type = &usb_if_device_type; intf->dev.dma_mask = dev->dev.dma_mask; - intf->dev.release = release_interface; device_initialize (&intf->dev); mark_quiesced(intf); sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d", diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index 627a5a2..7f31a49 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -31,7 +31,7 @@ static struct usb_device_id whitelist_table [] = { { USB_DEVICE_INFO(7, 1, 3) }, #endif -#ifdef CONFIG_USB_CDCETHER +#ifdef CONFIG_USB_NET_CDCETHER /* Linux-USB CDC Ethernet gadget */ { USB_DEVICE(0x0525, 0xa4a1), }, /* Linux-USB CDC Ethernet + RNDIS gadget */ diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c new file mode 100644 index 0000000..739f520 --- /dev/null +++ b/drivers/usb/core/quirks.c @@ -0,0 +1,78 @@ +/* + * USB device quirk handling logic and table + * + * Copyright (c) 2007 Oliver Neukum + * Copyright (c) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * + * This program is free software; 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. + * + * + */ + +#include <linux/usb.h> +#include <linux/usb/quirks.h> +#include "usb.h" + +/* List of quirky USB devices. Please keep this list ordered by: + * 1) Vendor ID + * 2) Product ID + * 3) Class ID + * + * as we want specific devices to be overridden first, and only after that, any + * class specific quirks. + * + * Right now the logic aborts if it finds a valid device in the table, we might + * want to change that in the future if it turns out that a whole class of + * devices is broken... + */ +static const struct usb_device_id usb_quirk_list[] = { + /* HP 5300/5370C scanner */ + { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* Seiko Epson Corp - Perfection 1670 */ + { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Elsa MicroLink 56k (V.250) */ + { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + + { } /* terminating entry must be last */ +}; + +static void usb_autosuspend_quirk(struct usb_device *udev) +{ +#ifdef CONFIG_USB_SUSPEND + /* disable autosuspend, but allow the user to re-enable it via sysfs */ + udev->autosuspend_disabled = 1; +#endif +} + +static const struct usb_device_id *find_id(struct usb_device *udev) +{ + const struct usb_device_id *id = usb_quirk_list; + + for (; id->idVendor || id->bDeviceClass || id->bInterfaceClass || + id->driver_info; id++) { + if (usb_match_device(udev, id)) + return id; + } + return NULL; +} + +/* + * Detect any quirks the device has, and do any housekeeping for it if needed. + */ +void usb_detect_quirks(struct usb_device *udev) +{ + const struct usb_device_id *id = usb_quirk_list; + + id = find_id(udev); + if (id) + udev->quirks = (u32)(id->driver_info); + if (udev->quirks) + dev_dbg(&udev->dev, "USB quirks for this device: %x\n", + udev->quirks); + + /* do any special quirk handling here if needed */ + if (udev->quirks & USB_QUIRK_NO_AUTOSUSPEND) + usb_autosuspend_quirk(udev); +} diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 4eaa0ee..e7c9823 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -11,6 +11,7 @@ #include <linux/kernel.h> +#include <linux/string.h> #include <linux/usb.h> #include "usb.h" @@ -63,7 +64,7 @@ set_bConfigurationValue(struct device *dev, struct device_attribute *attr, struct usb_device *udev = to_usb_device(dev); int config, value; - if (sscanf(buf, "%u", &config) != 1 || config > 255) + if (sscanf(buf, "%d", &config) != 1 || config < -1 || config > 255) return -EINVAL; usb_lock_device(udev); value = usb_set_configuration(udev, config); @@ -117,6 +118,16 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf) static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL); static ssize_t +show_busnum(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "%d\n", udev->bus->busnum); +} +static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL); + +static ssize_t show_devnum(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev; @@ -148,6 +159,151 @@ show_maxchild(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(maxchild, S_IRUGO, show_maxchild, NULL); +static ssize_t +show_quirks(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev; + + udev = to_usb_device(dev); + return sprintf(buf, "0x%x\n", udev->quirks); +} +static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); + +#ifdef CONFIG_USB_SUSPEND + +static ssize_t +show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + + return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ); +} + +static ssize_t +set_autosuspend(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int value; + + if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ || + value <= - INT_MAX/HZ) + return -EINVAL; + value *= HZ; + + udev->autosuspend_delay = value; + if (value >= 0) + usb_try_autosuspend_device(udev); + else { + if (usb_autoresume_device(udev) == 0) + usb_autosuspend_device(udev); + } + return count; +} + +static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR, + show_autosuspend, set_autosuspend); + +static const char on_string[] = "on"; +static const char auto_string[] = "auto"; +static const char suspend_string[] = "suspend"; + +static ssize_t +show_level(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + const char *p = auto_string; + + if (udev->state == USB_STATE_SUSPENDED) { + if (udev->autoresume_disabled) + p = suspend_string; + } else { + if (udev->autosuspend_disabled) + p = on_string; + } + return sprintf(buf, "%s\n", p); +} + +static ssize_t +set_level(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int len = count; + char *cp; + int rc = 0; + + cp = memchr(buf, '\n', count); + if (cp) + len = cp - buf; + + usb_lock_device(udev); + + /* Setting the flags without calling usb_pm_lock is a subject to + * races, but who cares... + */ + if (len == sizeof on_string - 1 && + strncmp(buf, on_string, len) == 0) { + udev->autosuspend_disabled = 1; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof auto_string - 1 && + strncmp(buf, auto_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 0; + rc = usb_external_resume_device(udev); + + } else if (len == sizeof suspend_string - 1 && + strncmp(buf, suspend_string, len) == 0) { + udev->autosuspend_disabled = 0; + udev->autoresume_disabled = 1; + rc = usb_external_suspend_device(udev, PMSG_SUSPEND); + + } else + rc = -EINVAL; + + usb_unlock_device(udev); + return (rc < 0 ? rc : count); +} + +static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); + +static char power_group[] = "power"; + +static int add_power_attributes(struct device *dev) +{ + int rc = 0; + + if (is_usb_device(dev)) { + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_autosuspend.attr, + power_group); + if (rc == 0) + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_level.attr, + power_group); + } + return rc; +} + +static void remove_power_attributes(struct device *dev) +{ + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_level.attr, + power_group); + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_autosuspend.attr, + power_group); +} + +#else + +#define add_power_attributes(dev) 0 +#define remove_power_attributes(dev) do {} while (0) + +#endif /* CONFIG_USB_SUSPEND */ + /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ @@ -201,9 +357,11 @@ static struct attribute *dev_attrs[] = { &dev_attr_bNumConfigurations.attr, &dev_attr_bMaxPacketSize0.attr, &dev_attr_speed.attr, + &dev_attr_busnum.attr, &dev_attr_devnum.attr, &dev_attr_version.attr, &dev_attr_maxchild.attr, + &dev_attr_quirks.attr, NULL, }; static struct attribute_group dev_attr_grp = { @@ -219,6 +377,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) if (retval) return retval; + retval = add_power_attributes(dev); + if (retval) + goto error; + if (udev->manufacturer) { retval = device_create_file(dev, &dev_attr_manufacturer); if (retval) @@ -239,10 +401,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) goto error; return 0; error: - usb_remove_ep_files(&udev->ep0); - device_remove_file(dev, &dev_attr_manufacturer); - device_remove_file(dev, &dev_attr_product); - device_remove_file(dev, &dev_attr_serial); + usb_remove_sysfs_dev_files(udev); return retval; } @@ -251,14 +410,11 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) struct device *dev = &udev->dev; usb_remove_ep_files(&udev->ep0); + device_remove_file(dev, &dev_attr_manufacturer); + device_remove_file(dev, &dev_attr_product); + device_remove_file(dev, &dev_attr_serial); + remove_power_attributes(dev); sysfs_remove_group(&dev->kobj, &dev_attr_grp); - - if (udev->manufacturer) - device_remove_file(dev, &dev_attr_manufacturer); - if (udev->product) - device_remove_file(dev, &dev_attr_product); - if (udev->serial) - device_remove_file(dev, &dev_attr_serial); } /* Interface fields */ @@ -362,33 +518,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf) int usb_create_sysfs_intf_files(struct usb_interface *intf) { + struct device *dev = &intf->dev; struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *alt = intf->cur_altsetting; int retval; - retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp); + retval = sysfs_create_group(&dev->kobj, &intf_attr_grp); if (retval) - goto error; + return retval; if (alt->string == NULL) alt->string = usb_cache_string(udev, alt->desc.iInterface); if (alt->string) - retval = device_create_file(&intf->dev, &dev_attr_interface); + retval = device_create_file(dev, &dev_attr_interface); usb_create_intf_ep_files(intf, udev); return 0; -error: - if (alt->string) - device_remove_file(&intf->dev, &dev_attr_interface); - sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); - usb_remove_intf_ep_files(intf); - return retval; } void usb_remove_sysfs_intf_files(struct usb_interface *intf) { - usb_remove_intf_ep_files(intf); - sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp); + struct device *dev = &intf->dev; - if (intf->cur_altsetting->string) - device_remove_file(&intf->dev, &dev_attr_interface); + usb_remove_intf_ep_files(intf); + device_remove_file(dev, &dev_attr_interface); + sysfs_remove_group(&dev->kobj, &intf_attr_grp); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 3db721c..dfd1b5c 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -22,6 +22,7 @@ */ #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/string.h> #include <linux/bitops.h> #include <linux/slab.h> @@ -48,7 +49,18 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ -struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ +/* Workqueue for autosuspend and for remote wakeup of root hubs */ +struct workqueue_struct *ksuspend_usb_wq; + +#ifdef CONFIG_USB_SUSPEND +static int usb_autosuspend_delay = 2; /* Default delay value, + * in seconds */ +module_param_named(autosuspend, usb_autosuspend_delay, int, 0644); +MODULE_PARM_DESC(autosuspend, "default autosuspend delay"); + +#else +#define usb_autosuspend_delay 0 +#endif /** @@ -185,6 +197,11 @@ static void usb_release_dev(struct device *dev) kfree(udev); } +struct device_type usb_device_type = { + .name = "usb_device", + .release = usb_release_dev, +}; + #ifdef CONFIG_PM static int ksuspend_usb_init(void) @@ -200,27 +217,6 @@ static void ksuspend_usb_cleanup(void) destroy_workqueue(ksuspend_usb_wq); } -#ifdef CONFIG_USB_SUSPEND - -/* usb_autosuspend_work - callback routine to autosuspend a USB device */ -static void usb_autosuspend_work(struct work_struct *work) -{ - struct usb_device *udev = - container_of(work, struct usb_device, autosuspend.work); - - usb_pm_lock(udev); - udev->auto_pm = 1; - usb_suspend_both(udev, PMSG_SUSPEND); - usb_pm_unlock(udev); -} - -#else - -static void usb_autosuspend_work(struct work_struct *work) -{} - -#endif /* CONFIG_USB_SUSPEND */ - #else #define ksuspend_usb_init() 0 @@ -256,13 +252,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) device_initialize(&dev->dev); dev->dev.bus = &usb_bus_type; + dev->dev.type = &usb_device_type; dev->dev.dma_mask = bus->controller->dma_mask; - dev->dev.release = usb_release_dev; dev->state = USB_STATE_ATTACHED; - /* This magic assignment distinguishes devices from interfaces */ - dev->dev.platform_data = &usb_generic_driver; - INIT_LIST_HEAD(&dev->ep0.urb_list); dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE; dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT; @@ -306,6 +299,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) #ifdef CONFIG_PM mutex_init(&dev->pm_mutex); INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work); + dev->autosuspend_delay = usb_autosuspend_delay * HZ; #endif return dev; } @@ -890,9 +884,9 @@ static int __init usb_init(void) retval = usb_register(&usbfs_driver); if (retval) goto driver_register_failed; - retval = usbdev_init(); + retval = usb_devio_init(); if (retval) - goto usbdevice_init_failed; + goto usb_devio_init_failed; retval = usbfs_init(); if (retval) goto fs_init_failed; @@ -907,8 +901,8 @@ static int __init usb_init(void) hub_init_failed: usbfs_cleanup(); fs_init_failed: - usbdev_cleanup(); -usbdevice_init_failed: + usb_devio_cleanup(); +usb_devio_init_failed: usb_deregister(&usbfs_driver); driver_register_failed: usb_major_cleanup(); @@ -935,7 +929,7 @@ static void __exit usb_exit(void) usb_major_cleanup(); usbfs_cleanup(); usb_deregister(&usbfs_driver); - usbdev_cleanup(); + usb_devio_cleanup(); usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 17830a8..bf2eb0d 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -13,6 +13,7 @@ extern void usb_disable_interface (struct usb_device *dev, struct usb_interface *intf); extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device (struct usb_device *dev, int skip_ep0); +extern void usb_detect_quirks(struct usb_device *udev); extern int usb_get_device_descriptor(struct usb_device *dev, unsigned int size); @@ -20,7 +21,8 @@ extern char *usb_cache_string(struct usb_device *udev, int index); extern int usb_set_configuration(struct usb_device *dev, int configuration); extern void usb_kick_khubd(struct usb_device *dev); -extern void usb_resume_root_hub(struct usb_device *dev); +extern int usb_match_device(struct usb_device *dev, + const struct usb_device_id *id); extern int usb_hub_init(void); extern void usb_hub_cleanup(void); @@ -31,10 +33,12 @@ extern void usb_host_cleanup(void); #ifdef CONFIG_PM -extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg); -extern int usb_resume_both(struct usb_device *udev); +extern void usb_autosuspend_work(struct work_struct *work); extern int usb_port_suspend(struct usb_device *dev); extern int usb_port_resume(struct usb_device *dev); +extern int usb_external_suspend_device(struct usb_device *udev, + pm_message_t msg); +extern int usb_external_resume_device(struct usb_device *udev); static inline void usb_pm_lock(struct usb_device *udev) { @@ -48,11 +52,6 @@ static inline void usb_pm_unlock(struct usb_device *udev) #else -#define usb_suspend_both(udev, msg) 0 -static inline int usb_resume_both(struct usb_device *udev) -{ - return 0; -} #define usb_port_suspend(dev) 0 #define usb_port_resume(dev) 0 static inline void usb_pm_lock(struct usb_device *udev) {} @@ -62,14 +61,14 @@ static inline void usb_pm_unlock(struct usb_device *udev) {} #ifdef CONFIG_USB_SUSPEND -#define USB_AUTOSUSPEND_DELAY (HZ*2) - extern void usb_autosuspend_device(struct usb_device *udev); +extern void usb_try_autosuspend_device(struct usb_device *udev); extern int usb_autoresume_device(struct usb_device *udev); #else -#define usb_autosuspend_device(udev) do {} while (0) +#define usb_autosuspend_device(udev) do {} while (0) +#define usb_try_autosuspend_device(udev) do {} while (0) static inline int usb_autoresume_device(struct usb_device *udev) { return 0; @@ -79,15 +78,13 @@ static inline int usb_autoresume_device(struct usb_device *udev) extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; +extern struct device_type usb_device_type; +extern struct device_type usb_if_device_type; extern struct usb_device_driver usb_generic_driver; -/* Here's how we tell apart devices and interfaces. Luckily there's - * no such thing as a platform USB device, so we can steal the use - * of the platform_data field. */ - static inline int is_usb_device(const struct device *dev) { - return dev->platform_data == &usb_generic_driver; + return dev->type == &usb_device_type; } /* Do the same for device drivers and interface drivers. */ @@ -123,11 +120,11 @@ extern const char *usbcore_name; extern struct mutex usbfs_mutex; extern struct usb_driver usbfs_driver; extern const struct file_operations usbfs_devices_fops; -extern const struct file_operations usbfs_device_file_operations; +extern const struct file_operations usbdev_file_operations; extern void usbfs_conn_disc_event(void); -extern int usbdev_init(void); -extern void usbdev_cleanup(void); +extern int usb_devio_init(void); +extern void usb_devio_cleanup(void); struct dev_state { struct list_head list; /* state list */ diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 4097a86..8065f2b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -68,6 +68,27 @@ choice Many controller drivers are platform-specific; these often need board-specific hooks. +config USB_GADGET_FSL_USB2 + boolean "Freescale Highspeed USB DR Peripheral Controller" + depends on MPC834x || PPC_MPC831x + select USB_GADGET_DUALSPEED + help + Some of Freescale PowerPC processors have a High Speed + Dual-Role(DR) USB controller, which supports device mode. + + The number of programmable endpoints is different through + SOC revisions. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "fsl_usb2_udc" and force + all gadget drivers to also be dynamically linked. + +config USB_FSL_USB2 + tristate + depends on USB_GADGET_FSL_USB2 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_NET2280 boolean "NetChip 228x" depends on PCI @@ -370,6 +391,7 @@ config USB_GADGETFS config USB_FILE_STORAGE tristate "File-backed Storage Gadget" + depends on BLOCK help The File-backed Storage Gadget acts as a USB Mass Storage disk drive. As its storage repository it can use a regular diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e71e086..5db1939 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o # # USB gadget drivers diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 36b36e0..2a6e316 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -784,7 +784,7 @@ static int at91_ep_set_halt(struct usb_ep *_ep, int value) return status; } -static struct usb_ep_ops at91_ep_ops = { +static const struct usb_ep_ops at91_ep_ops = { .enable = at91_ep_enable, .disable = at91_ep_disable, .alloc_request = at91_ep_alloc_request, @@ -912,7 +912,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, 0); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 1); - else if (cpu_is_at91sam9260()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc |= AT91_UDP_TXVC_PUON; @@ -929,7 +929,7 @@ static void pullup(struct at91_udc *udc, int is_on) at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 0); - else if (cpu_is_at91sam9260()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); txvc &= ~AT91_UDP_TXVC_PUON; @@ -1651,7 +1651,7 @@ static void at91udc_shutdown(struct platform_device *dev) pullup(platform_get_drvdata(dev), 0); } -static int __devinit at91udc_probe(struct platform_device *pdev) +static int __init at91udc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct at91_udc *udc; @@ -1762,7 +1762,7 @@ fail0: return retval; } -static int __devexit at91udc_remove(struct platform_device *pdev) +static int __exit at91udc_remove(struct platform_device *pdev) { struct at91_udc *udc = platform_get_drvdata(pdev); struct resource *res; @@ -1835,9 +1835,8 @@ static int at91udc_resume(struct platform_device *pdev) #define at91udc_resume NULL #endif -static struct platform_driver at91_udc = { - .probe = at91udc_probe, - .remove = __devexit_p(at91udc_remove), +static struct platform_driver at91_udc_driver = { + .remove = __exit_p(at91udc_remove), .shutdown = at91udc_shutdown, .suspend = at91udc_suspend, .resume = at91udc_resume, @@ -1847,15 +1846,15 @@ static struct platform_driver at91_udc = { }, }; -static int __devinit udc_init_module(void) +static int __init udc_init_module(void) { - return platform_driver_register(&at91_udc); + return platform_driver_probe(&at91_udc_driver, at91udc_probe); } module_init(udc_init_module); -static void __devexit udc_exit_module(void) +static void __exit udc_exit_module(void) { - platform_driver_unregister(&at91_udc); + platform_driver_unregister(&at91_udc_driver); } module_exit(udc_exit_module); diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c index 04e6b85..1dd8b57 100644 --- a/drivers/usb/gadget/ether.c +++ b/drivers/usb/gadget/ether.c @@ -282,6 +282,9 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define DEV_CONFIG_CDC +#endif /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -1735,7 +1738,8 @@ enomem: defer_kevent (dev, WORK_RX_MEMORY); if (retval) { DEBUG (dev, "rx submit --> %d\n", retval); - dev_kfree_skb_any (skb); + if (skb) + dev_kfree_skb_any(skb); spin_lock(&dev->req_lock); list_add (&req->list, &dev->rx_reqs); spin_unlock(&dev->req_lock); @@ -1766,7 +1770,6 @@ static void rx_complete (struct usb_ep *ep, struct usb_request *req) break; } - skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c new file mode 100644 index 0000000..157054e --- /dev/null +++ b/drivers/usb/gadget/fsl_usb2_udc.c @@ -0,0 +1,2500 @@ +/* + * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. + * + * Author: Li Yang <leoli@freescale.com> + * Jiang Bo <tanya.jiang@freescale.com> + * + * Description: + * Freescale high-speed USB SOC DR module device controller driver. + * This can be found on MPC8349E/MPC8313E cpus. + * The driver is previously named as mpc_udc. Based on bare board + * code from Dave Liu and Shlomi Gridish. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#undef VERBOSE + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/init.h> +#include <linux/timer.h> +#include <linux/list.h> +#include <linux/interrupt.h> +#include <linux/proc_fs.h> +#include <linux/mm.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb_gadget.h> +#include <linux/usb/otg.h> +#include <linux/dma-mapping.h> +#include <linux/platform_device.h> +#include <linux/fsl_devices.h> +#include <linux/dmapool.h> + +#include <asm/byteorder.h> +#include <asm/io.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/unaligned.h> +#include <asm/dma.h> +#include <asm/cacheflush.h> + +#include "fsl_usb2_udc.h" + +#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver" +#define DRIVER_AUTHOR "Li Yang/Jiang Bo" +#define DRIVER_VERSION "Apr 20, 2007" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +static const char driver_name[] = "fsl-usb2-udc"; +static const char driver_desc[] = DRIVER_DESC; + +volatile static struct usb_dr_device *dr_regs = NULL; +volatile static struct usb_sys_interface *usb_sys_regs = NULL; + +/* it is initialized in probe() */ +static struct fsl_udc *udc_controller = NULL; + +static const struct usb_endpoint_descriptor +fsl_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, +}; + +static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state); +static int fsl_udc_resume(struct platform_device *pdev); +static void fsl_ep_fifo_flush(struct usb_ep *_ep); + +#ifdef CONFIG_PPC32 +#define fsl_readl(addr) in_le32(addr) +#define fsl_writel(addr, val32) out_le32(val32, addr) +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(addr, val32) writel(addr, val32) +#endif + +/******************************************************************** + * Internal Used Function +********************************************************************/ +/*----------------------------------------------------------------- + * done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + *--------------------------------------------------------------*/ +static void done(struct fsl_ep *ep, struct fsl_req *req, int status) +{ + struct fsl_udc *udc = NULL; + unsigned char stopped = ep->stopped; + struct ep_td_struct *curr_td, *next_td; + int j; + + udc = (struct fsl_udc *)ep->udc; + /* Removed the req from fsl_ep->queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free dtd for the request */ + next_td = req->head; + for (j = 0; j < req->dtd_count; j++) { + curr_td = next_td; + if (j != req->dtd_count - 1) { + next_td = curr_td->next_td_virt; + } + dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); + } + + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + if (status && (status != -ESHUTDOWN)) + VDBG("complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + ep->stopped = 1; + + spin_unlock(&ep->udc->lock); + /* complete() is from gadget layer, + * eg fsg->bulk_in_complete() */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/*----------------------------------------------------------------- + * nuke(): delete all requests related to this ep + * called with spinlock held + *--------------------------------------------------------------*/ +static void nuke(struct fsl_ep *ep, int status) +{ + ep->stopped = 1; + + /* Flush fifo */ + fsl_ep_fifo_flush(&ep->ep); + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct fsl_req *req = NULL; + + req = list_entry(ep->queue.next, struct fsl_req, queue); + done(ep, req, status); + } +} + +/*------------------------------------------------------------------ + Internal Hardware related function + ------------------------------------------------------------------*/ + +static int dr_controller_setup(struct fsl_udc *udc) +{ + unsigned int tmp = 0, portctrl = 0, ctrl = 0; + unsigned long timeout; +#define FSL_UDC_RESET_TIMEOUT 1000 + + /* before here, make sure dr_regs has been initialized */ + if (!udc) + return -EINVAL; + + /* Stop and reset the usb controller */ + tmp = fsl_readl(&dr_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + fsl_writel(tmp, &dr_regs->usbcmd); + + tmp = fsl_readl(&dr_regs->usbcmd); + tmp |= USB_CMD_CTRL_RESET; + fsl_writel(tmp, &dr_regs->usbcmd); + + /* Wait for reset to complete */ + timeout = jiffies + FSL_UDC_RESET_TIMEOUT; + while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) { + if (time_after(jiffies, timeout)) { + ERR("udc reset timeout! \n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + /* Set the controller as device mode */ + tmp = fsl_readl(&dr_regs->usbmode); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; + fsl_writel(tmp, &dr_regs->usbmode); + + /* Clear the setup status */ + fsl_writel(0, &dr_regs->usbsts); + + tmp = udc->ep_qh_dma; + tmp &= USB_EP_LIST_ADDRESS_MASK; + fsl_writel(tmp, &dr_regs->endpointlistaddr); + + VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", + (int)udc->ep_qh, (int)tmp, + fsl_readl(&dr_regs->endpointlistaddr)); + + /* Config PHY interface */ + portctrl = fsl_readl(&dr_regs->portsc1); + portctrl &= ~PORTSCX_PHY_TYPE_SEL; + switch (udc->phy_mode) { + case FSL_USB2_PHY_ULPI: + portctrl |= PORTSCX_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI: + case FSL_USB2_PHY_UTMI_WIDE: + portctrl |= PORTSCX_PTS_UTMI; + break; + case FSL_USB2_PHY_SERIAL: + portctrl |= PORTSCX_PTS_FSLS; + break; + default: + return -EINVAL; + } + fsl_writel(portctrl, &dr_regs->portsc1); + + /* Config control enable i/o output, cpu endian register */ + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); + +#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) + /* Turn on cache snooping hardware, since some PowerPC platforms + * wholly rely on hardware to deal with cache coherent. */ + + /* Setup Snooping for all the 4GB space */ + tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop1); + tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop2); +#endif + + return 0; +} + +/* Enable DR irq and set controller to run state */ +static void dr_controller_run(struct fsl_udc *udc) +{ + u32 temp; + + /* Enable DR irq reg */ + temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN + | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN + | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + fsl_writel(temp, &dr_regs->usbintr); + + /* Clear stopped bit */ + udc->stopped = 0; + + /* Set the controller as device mode */ + temp = fsl_readl(&dr_regs->usbmode); + temp |= USB_MODE_CTRL_MODE_DEVICE; + fsl_writel(temp, &dr_regs->usbmode); + + /* Set controller to Run */ + temp = fsl_readl(&dr_regs->usbcmd); + temp |= USB_CMD_RUN_STOP; + fsl_writel(temp, &dr_regs->usbcmd); + + return; +} + +static void dr_controller_stop(struct fsl_udc *udc) +{ + unsigned int tmp; + + /* disable all INTR */ + fsl_writel(0, &dr_regs->usbintr); + + /* Set stopped bit for isr */ + udc->stopped = 1; + + /* disable IO output */ +/* usb_sys_regs->control = 0; */ + + /* set controller to Stop */ + tmp = fsl_readl(&dr_regs->usbcmd); + tmp &= ~USB_CMD_RUN_STOP; + fsl_writel(tmp, &dr_regs->usbcmd); + + return; +} + +void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (dir) { + if (ep_num) + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num) + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_RX_EP_TYPE_SHIFT); + } + + fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]); +} + +static void +dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value) +{ + u32 tmp_epctrl = 0; + + tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + + if (value) { + /* set the stall bit */ + if (dir) + tmp_epctrl |= EPCTRL_TX_EP_STALL; + else + tmp_epctrl |= EPCTRL_RX_EP_STALL; + } else { + /* clear the stall bit and reset data toggle */ + if (dir) { + tmp_epctrl &= ~EPCTRL_TX_EP_STALL; + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + tmp_epctrl &= ~EPCTRL_RX_EP_STALL; + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + } + } + fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]); +} + +/* Get stall status of a specific ep + Return: 0: not stalled; 1:stalled */ +static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir) +{ + u32 epctrl; + + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (dir) + return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0; + else + return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0; +} + +/******************************************************************** + Internal Structure Build up functions +********************************************************************/ + +/*------------------------------------------------------------------ +* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH + * @zlt: Zero Length Termination Select (1: disable; 0: enable) + * @mult: Mult field + ------------------------------------------------------------------*/ +static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, + unsigned int zlt, unsigned char mult) +{ + struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir]; + unsigned int tmp = 0; + + /* set the Endpoint Capabilites in QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + break; + default: + VDBG("error ep type is %d", ep_type); + return; + } + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + p_QH->max_pkt_length = cpu_to_le32(tmp); + + return; +} + +/* Setup qh structure and ep register for ep0. */ +static void ep0_setup(struct fsl_udc *udc) +{ + /* the intialization of an ep includes: fields in QH, Regs, + * fsl_ep struct */ + struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 0, 0); + dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL); + dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL); + + return; + +} + +/*********************************************************************** + Endpoint Management Functions +***********************************************************************/ + +/*------------------------------------------------------------------------- + * when configurations are set, or when interface settings change + * for example the do_set_interface() in gadget layer, + * the driver will enable or disable the relevant endpoints + * ep0 doesn't use this routine. It is always enabled. +-------------------------------------------------------------------------*/ +static int fsl_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct fsl_udc *udc = NULL; + struct fsl_ep *ep = NULL; + unsigned short max = 0; + unsigned char mult = 0, zlt; + int retval = -EINVAL; + unsigned long flags = 0; + + ep = container_of(_ep, struct fsl_ep, ep); + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + udc = ep->udc; + + if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Disable automatic zlp generation. Driver is reponsible to indicate + * explicitly through req->req.zero. This is needed to enable multi-td + * request. */ + zlt = 1; + + /* Assume the max packet size from gadget is always correct */ + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + /* mult = 0. Execute N Transactions as demonstrated by + * the USB variable length packet protocol where N is + * computed using the Maximum Packet Length (dQH) and + * the Total Bytes field (dTD) */ + mult = 0; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Calculate transactions needed for high bandwidth iso */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x8ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto en_done; + break; + default: + goto en_done; + } + + spin_lock_irqsave(&udc->lock, flags); + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + /* Controller related setup */ + /* Init EPx Queue Head (Ep Capabilites field in QH + * according to max, zlt, mult) */ + struct_ep_qh_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK), + max, zlt, mult); + + /* Init endpoint ctrl register */ + dr_ep_setup((unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK)); + + spin_unlock_irqrestore(&udc->lock, flags); + retval = 0; + + VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name, + ep->desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) + ? "in" : "out", max); +en_done: + return retval; +} + +/*--------------------------------------------------------------------- + * @ep : the ep being unconfigured. May not be ep0 + * Any pending and uncomplete req will complete with status (-ESHUTDOWN) +*---------------------------------------------------------------------*/ +static int fsl_ep_disable(struct usb_ep *_ep) +{ + struct fsl_udc *udc = NULL; + struct fsl_ep *ep = NULL; + unsigned long flags = 0; + u32 epctrl; + int ep_num; + + ep = container_of(_ep, struct fsl_ep, ep); + if (!_ep || !ep->desc) { + VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + return -EINVAL; + } + + /* disable ep on controller */ + ep_num = ep_index(ep); + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + + udc = (struct fsl_udc *)ep->udc; + spin_lock_irqsave(&udc->lock, flags); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = 0; + ep->stopped = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + VDBG("disabled %s OK", _ep->name); + return 0; +} + +/*--------------------------------------------------------------------- + * allocate a request object used by this endpoint + * the main operation is to insert the req->queue to the eq->queue + * Returns the request, or null if one could not be allocated +*---------------------------------------------------------------------*/ +static struct usb_request * +fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct fsl_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fsl_req *req = NULL; + + req = container_of(_req, struct fsl_req, req); + + if (_req) + kfree(req); +} + +/*------------------------------------------------------------------ + * Allocate an I/O buffer +*---------------------------------------------------------------------*/ +static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, gfp_t gfp_flags) +{ + struct fsl_ep *ep; + + if (!_ep) + return NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + + return dma_alloc_coherent(ep->udc->gadget.dev.parent, + bytes, dma, gfp_flags); +} + +/*------------------------------------------------------------------ + * frees an i/o buffer +*---------------------------------------------------------------------*/ +static void fsl_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + struct fsl_ep *ep; + + if (!_ep) + return NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + + dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); +} + +/*-------------------------------------------------------------------------*/ +static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) +{ + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; + + /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr); + VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */ + + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* Add td to the end */ + struct fsl_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); + lastreq->tail->next_td_ptr = + cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + /* Read prime bit, if 1 goto done */ + if (fsl_readl(&dr_regs->endpointprime) & bitmask) + goto out; + + do { + /* Set ATDTW bit in USBCMD */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd); + + /* Read correct status bit */ + tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask; + + } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW)); + + /* Write ATDTW bit to 0 */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd); + + if (tmp_stat) + goto out; + } + + /* Write dQH next pointer and terminate bit to 0 */ + temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + dQH->next_dtd_ptr = cpu_to_le32(temp); + + /* Clear active and halt bit */ + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + dQH->size_ioc_int_sts &= temp; + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + temp = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + fsl_writel(temp, &dr_regs->endpointprime); +out: + return 0; +} + +/* Fill in the dTD structure + * @req: request that the transfer belongs to + * @length: return actually data length of the dTD + * @dma: return dma address of the dTD + * @is_last: return flag if it is the last dTD of the request + * return: pointer to the built dTD */ +static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, + dma_addr_t *dma, int *is_last) +{ + u32 swap_temp; + struct ep_td_struct *dtd; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)EP_MAX_LENGTH_TRANSFER); + + dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma); + if (dtd == NULL) + return dtd; + + dtd->td_dma = *dma; + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + if ((*is_last) == 0) + VDBG("multi-dtd request!\n"); + /* Fill in the transfer size; set active bit */ + swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + swap_temp |= DTD_IOC; + + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + mb(); + + VDBG("length = %d address= 0x%x", *length, (int)*dma); + + return dtd; +} + +/* Generate dtd chain for a request */ +static int fsl_req_to_dtd(struct fsl_req *req) +{ + unsigned count; + int is_last; + int is_first =1; + struct ep_td_struct *last_dtd = NULL, *dtd; + dma_addr_t dma; + + do { + dtd = fsl_build_dtd(req, &count, &dma, &is_last); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_virt = dtd; + } + last_dtd = dtd; + + req->dtd_count++; + } while (!is_last); + + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + + req->tail = dtd; + + return 0; +} + +/* queues (submits) an I/O request to an endpoint */ +static int +fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); + struct fsl_req *req = container_of(_req, struct fsl_req, req); + struct fsl_udc *udc; + unsigned long flags; + int is_iso = 0; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + VDBG("%s, bad params\n", __FUNCTION__); + return -EINVAL; + } + if (!_ep || (!ep->desc && ep_index(ep))) { + VDBG("%s, bad ep\n", __FUNCTION__); + return -EINVAL; + } + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) + return -EMSGSIZE; + is_iso = 1; + } + + udc = ep->udc; + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* build dtds and push them to device queue */ + if (!fsl_req_to_dtd(req)) { + fsl_queue_td(ep, req); + } else { + spin_unlock_irqrestore(&udc->lock, flags); + return -ENOMEM; + } + + /* Update ep0 state */ + if ((ep_index(ep) == 0)) + udc->ep0_state = DATA_STATE_XMIT; + + /* irq handler advances the queue */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep); + struct fsl_req *req; + unsigned long flags; + int ep_num, stopped, ret = 0; + u32 epctrl; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + stopped = ep->stopped; + + /* Stop the ep before we deal with the queue */ + ep->stopped = 1; + ep_num = ep_index(ep); + epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + fsl_ep_fifo_flush(_ep); /* flush current transfer */ + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + struct ep_queue_head *qh; + struct fsl_req *next_req; + + qh = ep->qh; + next_req = list_entry(req->queue.next, struct fsl_req, + queue); + + /* Point the QH to the first TD of next request */ + fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr); + } + + /* The request hasn't been processed, patch up the TD chain */ + } else { + struct fsl_req *prev_req; + + prev_req = list_entry(req->queue.prev, struct fsl_req, queue); + fsl_writel(fsl_readl(&req->tail->next_td_ptr), + &prev_req->tail->next_td_ptr); + + } + + done(ep, req, -ECONNRESET); + + /* Enable EP */ +out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); + if (ep_is_in(ep)) + epctrl |= EPCTRL_TX_ENABLE; + else + epctrl |= EPCTRL_RX_ENABLE; + fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return ret; +} + +/*-------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------- + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt 0--clear halt + * Returns zero, or a negative error code. +*----------------------------------------------------------------*/ +static int fsl_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct fsl_ep *ep = NULL; + unsigned long flags = 0; + int status = -EOPNOTSUPP; /* operation not supported */ + unsigned char ep_dir = 0, ep_num = 0; + struct fsl_udc *udc = NULL; + + ep = container_of(_ep, struct fsl_ep, ep); + udc = ep->udc; + if (!_ep || !ep->desc) { + status = -EINVAL; + goto out; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + status = 0; + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + ep_num = (unsigned char)(ep_index(ep)); + spin_lock_irqsave(&ep->udc->lock, flags); + dr_ep_change_stall(ep_num, ep_dir, value); + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep_index(ep) == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + } +out: + VDBG(" %s %s halt stat %d", ep->ep.name, + value ? "set" : "clear", status); + + return status; +} + +static void fsl_ep_fifo_flush(struct usb_ep *_ep) +{ + struct fsl_ep *ep; + int ep_num, ep_dir; + u32 bits; + unsigned long timeout; +#define FSL_UDC_FLUSH_TIMEOUT 1000 + + if (!_ep) { + return; + } else { + ep = container_of(_ep, struct fsl_ep, ep); + if (!ep->desc) + return; + } + ep_num = ep_index(ep); + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + + if (ep_num == 0) + bits = (1 << 16) | 1; + else if (ep_dir == USB_SEND) + bits = 1 << (16 + ep_num); + else + bits = 1 << ep_num; + + timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT; + do { + fsl_writel(bits, &dr_regs->endptflush); + + /* Wait until flush complete */ + while (fsl_readl(&dr_regs->endptflush)) { + if (time_after(jiffies, timeout)) { + ERR("ep flush timeout\n"); + return; + } + cpu_relax(); + } + /* See if we need to flush again */ + } while (fsl_readl(&dr_regs->endptstatus) & bits); +} + +static struct usb_ep_ops fsl_ep_ops = { + .enable = fsl_ep_enable, + .disable = fsl_ep_disable, + + .alloc_request = fsl_alloc_request, + .free_request = fsl_free_request, + + .alloc_buffer = fsl_alloc_buffer, + .free_buffer = fsl_free_buffer, + + .queue = fsl_ep_queue, + .dequeue = fsl_ep_dequeue, + + .set_halt = fsl_ep_set_halt, + .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ +}; + +/*------------------------------------------------------------------------- + Gadget Driver Layer Operations +-------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------- + * Get the current frame number (from DR frame_index Reg ) + *----------------------------------------------------------------------*/ +static int fsl_get_frame(struct usb_gadget *gadget) +{ + return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS); +} + +/*----------------------------------------------------------------------- + * Tries to wake up the host connected to this gadget + -----------------------------------------------------------------------*/ +static int fsl_wakeup(struct usb_gadget *gadget) +{ + struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget); + u32 portsc; + + /* Remote wakeup feature not enabled by host */ + if (!udc->remote_wakeup) + return -ENOTSUPP; + + portsc = fsl_readl(&dr_regs->portsc1); + /* not suspended? */ + if (!(portsc & PORTSCX_PORT_SUSPEND)) + return 0; + /* trigger force resume */ + portsc |= PORTSCX_PORT_FORCE_RESUME; + fsl_writel(portsc, &dr_regs->portsc1); + return 0; +} + +static int can_pullup(struct fsl_udc *udc) +{ + return udc->driver && udc->softconnect && udc->vbus_active; +} + +/* Notify controller that VBUS is powered, Called by whatever + detects VBUS sessions */ +static int fsl_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct fsl_udc *udc; + unsigned long flags; + + udc = container_of(gadget, struct fsl_udc, gadget); + spin_lock_irqsave(&udc->lock, flags); + VDBG("VBUS %s\n", is_active ? "on" : "off"); + udc->vbus_active = (is_active != 0); + if (can_pullup(udc)) + fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + else + fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + spin_unlock_irqrestore(&udc->lock, flags); + return 0; +} + +/* constrain controller's VBUS power usage + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ +#ifdef CONFIG_USB_OTG + struct fsl_udc *udc; + + udc = container_of(gadget, struct fsl_udc, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); +#endif + return -ENOTSUPP; +} + +/* Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnet + */ +static int fsl_pullup(struct usb_gadget *gadget, int is_on) +{ + struct fsl_udc *udc; + + udc = container_of(gadget, struct fsl_udc, gadget); + udc->softconnect = (is_on != 0); + if (can_pullup(udc)) + fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + else + fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), + &dr_regs->usbcmd); + + return 0; +} + +/* defined in usb_gadget.h */ +static struct usb_gadget_ops fsl_gadget_ops = { + .get_frame = fsl_get_frame, + .wakeup = fsl_wakeup, +/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */ + .vbus_session = fsl_vbus_session, + .vbus_draw = fsl_vbus_draw, + .pullup = fsl_pullup, +}; + +/* Set protocol stall on ep0, protocol stall will automatically be cleared + on new transaction */ +static void ep0stall(struct fsl_udc *udc) +{ + u32 tmp; + + /* must set tx and rx to stall at the same time */ + tmp = fsl_readl(&dr_regs->endptctrl[0]); + tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + fsl_writel(tmp, &dr_regs->endptctrl[0]); + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; +} + +/* Prime a status phase for ep0 */ +static int ep0_prime_status(struct fsl_udc *udc, int direction) +{ + struct fsl_req *req = udc->status_req; + struct fsl_ep *ep; + int status = 0; + + if (direction == EP_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + ep = &udc->eps[0]; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + if (fsl_req_to_dtd(req) == 0) + status = fsl_queue_td(ep, req); + else + return -ENOMEM; + + if (status) + ERR("Can't queue ep0 status request \n"); + list_add_tail(&req->queue, &ep->queue); + + return status; +} + +static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe) +{ + struct fsl_ep *ep = get_ep_by_pipe(udc, pipe); + + if (!ep->name) + return 0; + + nuke(ep, -ESHUTDOWN); + + return 0; +} + +/* + * ch9 Set address + */ +static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length) +{ + /* Save the new address to device struct */ + udc->device_address = (u8) value; + /* Update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + /* Status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); +} + +/* + * ch9 Get status + */ +static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) +{ + u16 tmp = 0; /* Status, cpu endian */ + + struct fsl_req *req; + struct fsl_ep *ep; + int status = 0; + + ep = &udc->eps[0]; + + if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* Get device status */ + tmp = 1 << USB_DEVICE_SELF_POWERED; + tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + /* Get interface status */ + /* We don't have interface information in udc driver */ + tmp = 0; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + /* Get endpoint status */ + struct fsl_ep *target_ep; + + target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); + + /* stall if endpoint doesn't exist */ + if (!target_ep->desc) + goto stall; + tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep)) + << USB_ENDPOINT_HALT; + } + + udc->ep0_dir = USB_DIR_IN; + /* Borrow the per device status_req */ + req = udc->status_req; + /* Fill in the reqest structure */ + *((u16 *) req->req.buf) = cpu_to_le16(tmp); + req->ep = ep; + req->req.length = 2; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + /* prime the data phase */ + if ((fsl_req_to_dtd(req) == 0)) + status = fsl_queue_td(ep, req); + else /* no mem */ + goto stall; + + if (status) { + ERR("Can't respond to getstatus request \n"); + goto stall; + } + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + return; +stall: + ep0stall(udc); +} + +static void setup_received_irq(struct fsl_udc *udc, + struct usb_ctrlrequest *setup) +{ + u16 wValue = le16_to_cpu(setup->wValue); + u16 wIndex = le16_to_cpu(setup->wIndex); + u16 wLength = le16_to_cpu(setup->wLength); + + udc_reset_ep_queue(udc, 0); + + switch (setup->bRequest) { + /* Request that need Data+Status phase from udc */ + case USB_REQ_GET_STATUS: + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength); + break; + + /* Requests that need Status phase from udc */ + case USB_REQ_SET_ADDRESS: + if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD + | USB_RECIP_DEVICE)) + break; + ch9setaddress(udc, wValue, wIndex, wLength); + break; + + /* Handled by udc, no data, status by udc */ + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + { /* status transaction */ + int rc = -EOPNOTSUPP; + + if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_ENDPOINT) { + int pipe = get_pipe_by_windex(wIndex); + struct fsl_ep *ep; + + if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + break; + ep = get_ep_by_pipe(udc, pipe); + + spin_unlock(&udc->lock); + rc = fsl_ep_set_halt(&ep->ep, + (setup->bRequest == USB_REQ_SET_FEATURE) + ? 1 : 0); + spin_lock(&udc->lock); + + } else if ((setup->bRequestType & USB_RECIP_MASK) + == USB_RECIP_DEVICE) { + /* Note: The driver has not include OTG support yet. + * This will be set when OTG support is added */ + if (!udc->gadget.is_otg) + break; + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + rc = 0; + } + if (rc == 0) { + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + } + break; + } + /* Requests handled by gadget */ + default: + if (wLength) { + /* Data phase from gadget, status phase from udc */ + udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? USB_DIR_IN : USB_DIR_OUT; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + + } else { + /* No data phase, IN status from gadget */ + udc->ep0_dir = USB_DIR_IN; + spin_unlock(&udc->lock); + if (udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = WAIT_FOR_OUT_STATUS; + } + break; + } +} + +/* Process request for Data or Status phase of ep0 + * prime status phase if needed */ +static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, + struct fsl_req *req) +{ + if (udc->usb_state == USB_STATE_ADDRESS) { + /* Set the new address */ + u32 new_address = (u32) udc->device_address; + fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS, + &dr_regs->deviceaddr); + } + + done(ep0, req, 0); + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + break; + case WAIT_FOR_OUT_STATUS: + udc->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + ERR("Unexpect ep0 packets \n"); + break; + default: + ep0stall(udc); + break; + } +} + +/* Tripwire mechanism to ensure a setup packet payload is extracted without + * being corrupted by another incoming setup packet */ +static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) +{ + u32 temp; + struct ep_queue_head *qh; + + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ + temp = fsl_readl(&dr_regs->endptsetupstat); + fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat); + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ + temp = fsl_readl(&dr_regs->usbcmd); + fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd); +} + +/* process-ep_req(): free the completed Tds for this req */ +static int process_ep_req(struct fsl_udc *udc, int pipe, + struct fsl_req *curr_req) +{ + struct ep_td_struct *curr_td; + int td_complete, actual, remaining_length, j, tmp; + int status = 0; + int errors = 0; + struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; + int direction = pipe % 2; + + curr_td = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + for (j = 0; j < curr_req->dtd_count; j++) { + remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + + if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & + DTD_ERROR_MASK)) { + if (errors & DTD_STATUS_HALTED) { + ERR("dTD error %08x QH=%d\n", errors, pipe); + /* Clear the errors and Halt condition */ + tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; + curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + status = -EPIPE; + /* FIXME: continue with next queued TD? */ + + break; + } + if (errors & DTD_STATUS_DATA_BUFF_ERR) { + VDBG("Transfer overflow"); + status = -EPROTO; + break; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + VDBG("ISO error"); + status = -EILSEQ; + break; + } else + ERR("Unknown error has occured (0x%x)!\r\n", + errors); + + } else if (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_STATUS_ACTIVE) { + VDBG("Request not complete"); + status = REQ_UNCOMPLETE; + return status; + } else if (remaining_length) { + if (direction) { + VDBG("Transmit dTD remaining length not zero"); + status = -EPROTO; + break; + } else { + td_complete++; + break; + } + } else { + td_complete++; + VDBG("dTD transmitted successful "); + } + + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *)curr_td->next_td_virt; + } + + if (status) + return status; + + curr_req->req.actual = actual; + + return 0; +} + +/* Process a DTD completion interrupt */ +static void dtd_complete_irq(struct fsl_udc *udc) +{ + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct fsl_ep *curr_ep; + struct fsl_req *curr_req, *temp_req; + + /* Clear the bits in the register */ + bit_pos = fsl_readl(&dr_regs->endptcomplete); + fsl_writel(bit_pos, &dr_regs->endptcomplete); + + if (!bit_pos) + return; + + for (i = 0; i < udc->max_ep * 2; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) + continue; + + curr_ep = get_ep_by_pipe(udc, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + WARN("Invalid EP?"); + continue; + } + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, + queue) { + status = process_ep_req(udc, i, curr_req); + + VDBG("status of process_ep_req= %d, ep = %d", + status, ep_num); + if (status == REQ_UNCOMPLETE) + break; + /* write back status to req */ + curr_req->req.status = status; + + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else + done(curr_ep, curr_req, status); + } + } +} + +/* Process a port change interrupt */ +static void port_change_irq(struct fsl_udc *udc) +{ + u32 speed; + + if (udc->bus_reset) + udc->bus_reset = 0; + + /* Bus resetting is finished */ + if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (fsl_readl(&dr_regs->portsc1) + & PORTSCX_PORT_SPEED_MASK); + switch (speed) { + case PORTSCX_PORT_SPEED_HIGH: + udc->gadget.speed = USB_SPEED_HIGH; + break; + case PORTSCX_PORT_SPEED_FULL: + udc->gadget.speed = USB_SPEED_FULL; + break; + case PORTSCX_PORT_SPEED_LOW: + udc->gadget.speed = USB_SPEED_LOW; + break; + default: + udc->gadget.speed = USB_SPEED_UNKNOWN; + break; + } + } + + /* Update USB state */ + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +/* Process suspend interrupt */ +static void suspend_irq(struct fsl_udc *udc) +{ + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver, serial.c does not support this */ + if (udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void bus_resume(struct fsl_udc *udc) +{ + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver, serial.c does not support this */ + if (udc->driver->resume) + udc->driver->resume(&udc->gadget); +} + +/* Clear up all ep queues */ +static int reset_queues(struct fsl_udc *udc) +{ + u8 pipe; + + for (pipe = 0; pipe < udc->max_pipes; pipe++) + udc_reset_ep_queue(udc, pipe); + + /* report disconnect; the driver is already quiesced */ + udc->driver->disconnect(&udc->gadget); + + return 0; +} + +/* Process reset interrupt */ +static void reset_irq(struct fsl_udc *udc) +{ + u32 temp; + unsigned long timeout; + + /* Clear the device address */ + temp = fsl_readl(&dr_regs->deviceaddr); + fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr); + + udc->device_address = 0; + + /* Clear usb state */ + udc->resume_state = 0; + udc->ep0_dir = 0; + udc->ep0_state = WAIT_FOR_SETUP; + udc->remote_wakeup = 0; /* default to 0 on reset */ + udc->gadget.b_hnp_enable = 0; + udc->gadget.a_hnp_support = 0; + udc->gadget.a_alt_hnp_support = 0; + + /* Clear all the setup token semaphores */ + temp = fsl_readl(&dr_regs->endptsetupstat); + fsl_writel(temp, &dr_regs->endptsetupstat); + + /* Clear all the endpoint complete status bits */ + temp = fsl_readl(&dr_regs->endptcomplete); + fsl_writel(temp, &dr_regs->endptcomplete); + + timeout = jiffies + 100; + while (fsl_readl(&dr_regs->endpointprime)) { + /* Wait until all endptprime bits cleared */ + if (time_after(jiffies, timeout)) { + ERR("Timeout for reset\n"); + break; + } + cpu_relax(); + } + + /* Write 1s to the flush register */ + fsl_writel(0xffffffff, &dr_regs->endptflush); + + if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { + VDBG("Bus reset"); + /* Bus is reseting */ + udc->bus_reset = 1; + /* Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + reset_queues(udc); + udc->usb_state = USB_STATE_DEFAULT; + } else { + VDBG("Controller reset"); + /* initialize usb hw reg except for regs for EP, not + * touch usbintr reg */ + dr_controller_setup(udc); + + /* Reset all internal used Queues */ + reset_queues(udc); + + ep0_setup(udc); + + /* Enable DR IRQ reg, Set Run bit, change udc state */ + dr_controller_run(udc); + udc->usb_state = USB_STATE_ATTACHED; + } +} + +/* + * USB device controller interrupt handler + */ +static irqreturn_t fsl_udc_irq(int irq, void *_udc) +{ + struct fsl_udc *udc = _udc; + u32 irq_src; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + /* Disable ISR for OTG host mode */ + if (udc->stopped) + return IRQ_NONE; + spin_lock_irqsave(&udc->lock, flags); + irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr); + /* Clear notification bits */ + fsl_writel(irq_src, &dr_regs->usbsts); + + /* VDBG("irq_src [0x%8x]", irq_src); */ + + /* Need to resume? */ + if (udc->usb_state == USB_STATE_SUSPENDED) + if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0) + bus_resume(udc); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + VDBG("Packet int"); + /* Setup package, we only support ep0 as control ep */ + if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) { + tripwire_handler(udc, 0, + (u8 *) (&udc->local_setup_buff)); + setup_received_irq(udc, &udc->local_setup_buff); + status = IRQ_HANDLED; + } + + /* completion of dtd */ + if (fsl_readl(&dr_regs->endptcomplete)) { + dtd_complete_irq(udc); + status = IRQ_HANDLED; + } + } + + /* SOF (for ISO transfer) */ + if (irq_src & USB_STS_SOF) { + status = IRQ_HANDLED; + } + + /* Port Change */ + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_irq(udc); + status = IRQ_HANDLED; + } + + /* Reset Received */ + if (irq_src & USB_STS_RESET) { + reset_irq(udc); + status = IRQ_HANDLED; + } + + /* Sleep Enable (Suspend) */ + if (irq_src & USB_STS_SUSPEND) { + suspend_irq(udc); + status = IRQ_HANDLED; + } + + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) { + VDBG("Error IRQ %x ", irq_src); + } + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/*----------------------------------------------------------------* + * Hook to gadget drivers + * Called by initialization code of gadget drivers +*----------------------------------------------------------------*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + int retval = -ENODEV; + unsigned long flags = 0; + + if (!udc_controller) + return -ENODEV; + + if (!driver || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !driver->bind || !driver->disconnect + || !driver->setup) + return -EINVAL; + + if (udc_controller->driver) + return -EBUSY; + + /* lock is needed but whether should use this lock or another */ + spin_lock_irqsave(&udc_controller->lock, flags); + + driver->driver.bus = 0; + /* hook up the driver */ + udc_controller->driver = driver; + udc_controller->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc_controller->lock, flags); + + /* bind udc driver to gadget driver */ + retval = driver->bind(&udc_controller->gadget); + if (retval) { + VDBG("bind to %s --> %d", driver->driver.name, retval); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + goto out; + } + + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + printk(KERN_INFO "%s: bind to driver %s \n", + udc_controller->gadget.name, driver->driver.name); + +out: + if (retval) + printk("retval %d \n", retval); + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +/* Disconnect from gadget driver */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct fsl_ep *loop_ep; + unsigned long flags; + + if (!udc_controller) + return -ENODEV; + + if (!driver || driver != udc_controller->driver || !driver->unbind) + return -EINVAL; + +#ifdef CONFIG_USB_OTG + if (udc_controller->transceiver) + (void)otg_set_peripheral(udc_controller->transceiver, 0); +#endif + + /* stop DR, disable intr */ + dr_controller_stop(udc_controller); + + /* in fact, no needed */ + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + + /* stand operation */ + spin_lock_irqsave(&udc_controller->lock, flags); + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc_controller->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list, + ep.ep_list) + nuke(loop_ep, -ESHUTDOWN); + spin_unlock_irqrestore(&udc_controller->lock, flags); + + /* unbind gadget and unhook driver. */ + driver->unbind(&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + + printk("unregistered gadget driver '%s'\r\n", driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*------------------------------------------------------------------------- + PROC File System Support +-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +#include <linux/seq_file.h> + +static const char proc_filename[] = "driver/fsl_usb2_udc"; + +static int fsl_proc_read(char *page, char **start, off_t off, int count, + int *eof, void *_dev) +{ + char *buf = page; + char *next = buf; + unsigned size = count; + unsigned long flags; + int t, i; + u32 tmp_reg; + struct fsl_ep *ep = NULL; + struct fsl_req *req; + + struct fsl_udc *udc = udc_controller; + if (off != 0) + return 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* ------basic driver infomation ---- */ + t = scnprintf(next, size, + DRIVER_DESC "\n" + "%s version: %s\n" + "Gadget driver: %s\n\n", + driver_name, DRIVER_VERSION, + udc->driver ? udc->driver->driver.name : "(none)"); + size -= t; + next += t; + + /* ------ DR Registers ----- */ + tmp_reg = fsl_readl(&dr_regs->usbcmd); + t = scnprintf(next, size, + "USBCMD reg:\n" + "SetupTW: %d\n" + "Run/Stop: %s\n\n", + (tmp_reg & USB_CMD_SUTW) ? 1 : 0, + (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbsts); + t = scnprintf(next, size, + "USB Status Reg:\n" + "Dr Suspend: %d" "Reset Received: %d" "System Error: %s" + "USB Error Interrupt: %s\n\n", + (tmp_reg & USB_STS_SUSPEND) ? 1 : 0, + (tmp_reg & USB_STS_RESET) ? 1 : 0, + (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal", + (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbintr); + t = scnprintf(next, size, + "USB Intrrupt Enable Reg:\n" + "Sleep Enable: %d" "SOF Received Enable: %d" + "Reset Enable: %d\n" + "System Error Enable: %d" + "Port Change Dectected Enable: %d\n" + "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n", + (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0, + (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0, + (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0, + (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0, + (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0, + (tmp_reg & USB_INTR_INT_EN) ? 1 : 0); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->frindex); + t = scnprintf(next, size, + "USB Frame Index Reg:" "Frame Number is 0x%x\n\n", + (tmp_reg & USB_FRINDEX_MASKS)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->deviceaddr); + t = scnprintf(next, size, + "USB Device Address Reg:" "Device Addr is 0x%x\n\n", + (tmp_reg & USB_DEVICE_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->endpointlistaddr); + t = scnprintf(next, size, + "USB Endpoint List Address Reg:" + "Device Addr is 0x%x\n\n", + (tmp_reg & USB_EP_LIST_ADDRESS_MASK)); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->portsc1); + t = scnprintf(next, size, + "USB Port Status&Control Reg:\n" + "Port Transceiver Type : %s" "Port Speed: %s \n" + "PHY Low Power Suspend: %s" "Port Reset: %s" + "Port Suspend Mode: %s \n" "Over-current Change: %s" + "Port Enable/Disable Change: %s\n" + "Port Enabled/Disabled: %s" + "Current Connect Status: %s\n\n", ( { + char *s; + switch (tmp_reg & PORTSCX_PTS_FSLS) { + case PORTSCX_PTS_UTMI: + s = "UTMI"; break; + case PORTSCX_PTS_ULPI: + s = "ULPI "; break; + case PORTSCX_PTS_FSLS: + s = "FS/LS Serial"; break; + default: + s = "None"; break; + } + s;} ), ( { + char *s; + switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) { + case PORTSCX_PORT_SPEED_FULL: + s = "Full Speed"; break; + case PORTSCX_PORT_SPEED_LOW: + s = "Low Speed"; break; + case PORTSCX_PORT_SPEED_HIGH: + s = "High Speed"; break; + default: + s = "Undefined"; break; + } + s; + } ), + (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ? + "Normal PHY mode" : "Low power mode", + (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" : + "Not in Reset", + (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in", + (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" : + "No", + (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" : + "Not change", + (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" : + "Not correct", + (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ? + "Attached" : "Not-Att"); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->usbmode); + t = scnprintf(next, size, + "USB Mode Reg:" "Controller Mode is : %s\n\n", ( { + char *s; + switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) { + case USB_MODE_CTRL_MODE_IDLE: + s = "Idle"; break; + case USB_MODE_CTRL_MODE_DEVICE: + s = "Device Controller"; break; + case USB_MODE_CTRL_MODE_HOST: + s = "Host Controller"; break; + default: + s = "None"; break; + } + s; + } )); + size -= t; + next += t; + + tmp_reg = fsl_readl(&dr_regs->endptsetupstat); + t = scnprintf(next, size, + "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n", + (tmp_reg & EP_SETUP_STATUS_MASK)); + size -= t; + next += t; + + for (i = 0; i < udc->max_ep / 2; i++) { + tmp_reg = fsl_readl(&dr_regs->endptctrl[i]); + t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n", + i, tmp_reg); + size -= t; + next += t; + } + tmp_reg = fsl_readl(&dr_regs->endpointprime); + t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = usb_sys_regs->snoop1; + t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg); + size -= t; + next += t; + + tmp_reg = usb_sys_regs->control; + t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", + tmp_reg); + size -= t; + next += t; + + /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ + ep = &udc->eps[0]; + t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n", + ep->ep.name, ep_maxpacket(ep), ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length 0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } + } + /* other gadget->eplist ep */ + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) { + t = scnprintf(next, size, + "\nFor %s Maxpkt is 0x%x " + "index is 0x%x\n", + ep->ep.name, ep_maxpacket(ep), + ep_index(ep)); + size -= t; + next += t; + + if (list_empty(&ep->queue)) { + t = scnprintf(next, size, + "its req queue is empty\n\n"); + size -= t; + next += t; + } else { + list_for_each_entry(req, &ep->queue, queue) { + t = scnprintf(next, size, + "req %p actual 0x%x length" + "0x%x buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); + size -= t; + next += t; + } /* end for each_entry of ep req */ + } /* end for else */ + } /* end for if(ep->queue) */ + } /* end (ep->desc) */ + + spin_unlock_irqrestore(&udc->lock, flags); + + *eof = 1; + return count - size; +} + +#define create_proc_file() create_proc_read_entry(proc_filename, \ + 0, NULL, fsl_proc_read, NULL) + +#define remove_proc_file() remove_proc_entry(proc_filename, NULL) + +#else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +#define create_proc_file() do {} while (0) +#define remove_proc_file() do {} while (0) + +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +/*-------------------------------------------------------------------------*/ + +/* Release udc structures */ +static void fsl_udc_release(struct device *dev) +{ + complete(udc_controller->done); + dma_free_coherent(dev, udc_controller->ep_qh_size, + udc_controller->ep_qh, udc_controller->ep_qh_dma); + kfree(udc_controller); +} + +/****************************************************************** + Internal structure setup functions +*******************************************************************/ +/*------------------------------------------------------------------ + * init resource for globle controller + * Return the udc handle on success or NULL on failure + ------------------------------------------------------------------*/ +static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev) +{ + struct fsl_udc *udc; + struct fsl_usb2_platform_data *pdata; + size_t size; + + udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL); + if (udc == NULL) { + ERR("malloc udc failed\n"); + return NULL; + } + + pdata = pdev->dev.platform_data; + udc->phy_mode = pdata->phy_mode; + /* max_ep_nr is bidirectional ep number, max_ep doubles the number */ + udc->max_ep = pdata->max_ep_nr * 2; + + udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL); + if (!udc->eps) { + ERR("malloc fsl_ep failed\n"); + goto cleanup; + } + + /* initialized QHs, take care of alignment */ + size = udc->max_ep * sizeof(struct ep_queue_head); + if (size < QH_ALIGNMENT) + size = QH_ALIGNMENT; + else if ((size % QH_ALIGNMENT) != 0) { + size += QH_ALIGNMENT + 1; + size &= ~(QH_ALIGNMENT - 1); + } + udc->ep_qh = dma_alloc_coherent(&pdev->dev, size, + &udc->ep_qh_dma, GFP_KERNEL); + if (!udc->ep_qh) { + ERR("malloc QHs for udc failed\n"); + kfree(udc->eps); + goto cleanup; + } + + udc->ep_qh_size = size; + + /* Initialize ep0 status request structure */ + /* FIXME: fsl_alloc_request() ignores ep argument */ + udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL), + struct fsl_req, req); + /* allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = kmalloc(8, GFP_KERNEL); + udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf); + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = 0; + udc->remote_wakeup = 0; /* default to 0 on reset */ + spin_lock_init(&udc->lock); + + return udc; + +cleanup: + kfree(udc); + return NULL; +} + +/*---------------------------------------------------------------- + * Setup the fsl_ep struct for eps + * Link fsl_ep->ep to gadget->ep_list + * ep0out is not used so do nothing here + * ep0in should be taken care + *--------------------------------------------------------------*/ +static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, + char *name, int link) +{ + struct fsl_ep *ep = &udc->eps[index]; + + ep->udc = udc; + strcpy(ep->name, name); + ep->ep.name = ep->name; + + ep->ep.ops = &fsl_ep_ops; + ep->stopped = 0; + + /* for ep0: maxP defined in desc + * for other eps, maxP is set by epautoconfig() called by gadget layer + */ + ep->ep.maxpacket = (unsigned short) ~0; + + /* the queue lists any req for this ep */ + INIT_LIST_HEAD(&ep->queue); + + /* gagdet.ep_list used for ep_autoconfig so no ep0 */ + if (link) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->gadget = &udc->gadget; + ep->qh = &udc->ep_qh[index]; + + return 0; +} + +/* Driver probe function + * all intialize operations implemented here except enabling usb_intr reg + */ +static int __init fsl_udc_probe(struct platform_device *pdev) +{ + struct resource *res; + int ret = -ENODEV; + unsigned int i; + + if (strcmp(pdev->name, driver_name)) { + VDBG("Wrong device\n"); + return -ENODEV; + } + + /* board setup should have been done in the platform code */ + + /* Initialize the udc structure including QH member and other member */ + udc_controller = struct_udc_setup(pdev); + if (!udc_controller) { + VDBG("udc_controller is NULL \n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region for %s failed \n", pdev->name); + return -EBUSY; + } + + dr_regs = ioremap(res->start, res->end - res->start + 1); + if (!dr_regs) { + ret = -ENOMEM; + goto err1; + } + + usb_sys_regs = (struct usb_sys_interface *) + ((u32)dr_regs + USB_DR_SYS_OFFSET); + + udc_controller->irq = platform_get_irq(pdev, 0); + if (!udc_controller->irq) { + ret = -ENODEV; + goto err2; + } + + ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ, + driver_name, udc_controller); + if (ret != 0) { + ERR("cannot request irq %d err %d \n", + udc_controller->irq, ret); + goto err2; + } + + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); + + /* Setup gadget structure */ + udc_controller->gadget.ops = &fsl_gadget_ops; + udc_controller->gadget.is_dualspeed = 1; + udc_controller->gadget.ep0 = &udc_controller->eps[0].ep; + INIT_LIST_HEAD(&udc_controller->gadget.ep_list); + udc_controller->gadget.speed = USB_SPEED_UNKNOWN; + udc_controller->gadget.name = driver_name; + + /* Setup gadget.dev and register with kernel */ + strcpy(udc_controller->gadget.dev.bus_id, "gadget"); + udc_controller->gadget.dev.release = fsl_udc_release; + udc_controller->gadget.dev.parent = &pdev->dev; + ret = device_register(&udc_controller->gadget.dev); + if (ret < 0) + goto err3; + + /* setup QH and epctrl for ep0 */ + ep0_setup(udc_controller); + + /* setup udc->eps[] for ep0 */ + struct_ep_setup(udc_controller, 0, "ep0", 0); + /* for ep0: the desc defined here; + * for other eps, gadget layer called ep_enable with defined desc + */ + udc_controller->eps[0].desc = &fsl_ep0_desc; + udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + + /* setup the udc->eps[] for non-control endpoints and link + * to gadget.ep_list */ + for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) { + char name[14]; + + sprintf(name, "ep%dout", i); + struct_ep_setup(udc_controller, i * 2, name, 1); + sprintf(name, "ep%din", i); + struct_ep_setup(udc_controller, i * 2 + 1, name, 1); + } + + /* use dma_pool for TD management */ + udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev, + sizeof(struct ep_td_struct), + DTD_ALIGNMENT, UDC_DMA_BOUNDARY); + if (udc_controller->td_pool == NULL) { + ret = -ENOMEM; + goto err4; + } + create_proc_file(); + return 0; + +err4: + device_unregister(&udc_controller->gadget.dev); +err3: + free_irq(udc_controller->irq, udc_controller); +err2: + iounmap(dr_regs); +err1: + release_mem_region(res->start, res->end - res->start + 1); + return ret; +} + +/* Driver removal function + * Free resources and finish pending transactions + */ +static int __exit fsl_udc_remove(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + DECLARE_COMPLETION(done); + + if (!udc_controller) + return -ENODEV; + udc_controller->done = &done; + + /* DR has been stopped in usb_gadget_unregister_driver() */ + remove_proc_file(); + + /* Free allocated memory */ + kfree(udc_controller->status_req->req.buf); + kfree(udc_controller->status_req); + kfree(udc_controller->eps); + + dma_pool_destroy(udc_controller->td_pool); + free_irq(udc_controller->irq, udc_controller); + iounmap(dr_regs); + release_mem_region(res->start, res->end - res->start + 1); + + device_unregister(&udc_controller->gadget.dev); + /* free udc --wait for the release() finished */ + wait_for_completion(&done); + + return 0; +} + +/*----------------------------------------------------------------- + * Modify Power management attributes + * Used by OTG statemachine to disable gadget temporarily + -----------------------------------------------------------------*/ +static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + dr_controller_stop(udc_controller); + return 0; +} + +/*----------------------------------------------------------------- + * Invoked on USB resume. May be called in_interrupt. + * Here we start the DR controller and enable the irq + *-----------------------------------------------------------------*/ +static int fsl_udc_resume(struct platform_device *pdev) +{ + /* Enable DR irq reg and set controller Run */ + if (udc_controller->stopped) { + dr_controller_setup(udc_controller); + dr_controller_run(udc_controller); + } + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + return 0; +} + +/*------------------------------------------------------------------------- + Register entry point for the peripheral controller driver +--------------------------------------------------------------------------*/ + +static struct platform_driver udc_driver = { + .remove = __exit_p(fsl_udc_remove), + /* these suspend and resume are not usb suspend and resume */ + .suspend = fsl_udc_suspend, + .resume = fsl_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); + return platform_driver_probe(&udc_driver, fsl_udc_probe); +} + +module_init(udc_init); + +static void __exit udc_exit(void) +{ + platform_driver_unregister(&udc_driver); + printk("%s unregistered \n", driver_desc); +} + +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h new file mode 100644 index 0000000..c6291e0 --- /dev/null +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -0,0 +1,579 @@ +/* + * Freescale USB device/endpoint management registers + */ +#ifndef __FSL_USB2_UDC_H +#define __FSL_USB2_UDC_H + +/* ### define USB registers here + */ +#define USB_MAX_CTRL_PAYLOAD 64 +#define USB_DR_SYS_OFFSET 0x400 + + /* USB DR device mode registers (Little Endian) */ +struct usb_dr_device { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[24]; + u32 configflag; /* Configure Flag Register */ + u32 portsc1; /* Port 1 Status and Control Register */ + u8 res7[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ +}; + + /* USB DR host mode registers (Little Endian) */ +struct usb_dr_host { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 periodiclistbase; /* Periodic Frame List Base Address Register */ + u32 asynclistaddr; /* Current Asynchronous List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[24]; + u32 configflag; /* Configure Flag Register */ + u32 portsc1; /* Port 1 Status and Control Register */ + u8 res7[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ +}; + + /* non-EHCI USB system interface registers (Big Endian) */ +struct usb_sys_interface { + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res[236]; + u32 control; /* General Purpose Control Register */ +}; + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_MASKS 0x3fff +/* USB CMD Register Bit Masks */ +#define USB_CMD_RUN_STOP 0x00000001 +#define USB_CMD_CTRL_RESET 0x00000002 +#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 +#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 +#define USB_CMD_INT_AA_DOORBELL 0x00000040 +#define USB_CMD_ASP 0x00000300 +#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 +#define USB_CMD_SUTW 0x00002000 +#define USB_CMD_ATDTW 0x00004000 +#define USB_CMD_ITC 0x00FF0000 + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 0x00000000 +#define USB_CMD_FRAME_SIZE_512 0x00000004 +#define USB_CMD_FRAME_SIZE_256 0x00000008 +#define USB_CMD_FRAME_SIZE_128 0x0000000C +#define USB_CMD_FRAME_SIZE_64 0x00008000 +#define USB_CMD_FRAME_SIZE_32 0x00008004 +#define USB_CMD_FRAME_SIZE_16 0x00008008 +#define USB_CMD_FRAME_SIZE_8 0x0000800C + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 0x00000000 +#define USB_CMD_ASP_01 0x00000100 +#define USB_CMD_ASP_10 0x00000200 +#define USB_CMD_ASP_11 0x00000300 +#define USB_CMD_ASP_BIT_POS 8 + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 +#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 +#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 +#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 +#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 +#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 +#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 +#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 +#define USB_CMD_ITC_BIT_POS 16 + +/* USB STS Register Bit Masks */ +#define USB_STS_INT 0x00000001 +#define USB_STS_ERR 0x00000002 +#define USB_STS_PORT_CHANGE 0x00000004 +#define USB_STS_FRM_LST_ROLL 0x00000008 +#define USB_STS_SYS_ERR 0x00000010 +#define USB_STS_IAA 0x00000020 +#define USB_STS_RESET 0x00000040 +#define USB_STS_SOF 0x00000080 +#define USB_STS_SUSPEND 0x00000100 +#define USB_STS_HC_HALTED 0x00001000 +#define USB_STS_RCL 0x00002000 +#define USB_STS_PERIODIC_SCHEDULE 0x00004000 +#define USB_STS_ASYNC_SCHEDULE 0x00008000 + +/* USB INTR Register Bit Masks */ +#define USB_INTR_INT_EN 0x00000001 +#define USB_INTR_ERR_INT_EN 0x00000002 +#define USB_INTR_PTC_DETECT_EN 0x00000004 +#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 +#define USB_INTR_SYS_ERR_EN 0x00000010 +#define USB_INTR_ASYN_ADV_EN 0x00000020 +#define USB_INTR_RESET_EN 0x00000040 +#define USB_INTR_SOF_EN 0x00000080 +#define USB_INTR_DEVICE_SUSPEND 0x00000100 + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK 0xFE000000 +#define USB_DEVICE_ADDRESS_BIT_POS 25 + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 + +/* PORTSCX Register Bit Masks */ +#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 +#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 +#define PORTSCX_PORT_ENABLE 0x00000004 +#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 +#define PORTSCX_OVER_CURRENT_ACT 0x00000010 +#define PORTSCX_OVER_CURRENT_CHG 0x00000020 +#define PORTSCX_PORT_FORCE_RESUME 0x00000040 +#define PORTSCX_PORT_SUSPEND 0x00000080 +#define PORTSCX_PORT_RESET 0x00000100 +#define PORTSCX_LINE_STATUS_BITS 0x00000C00 +#define PORTSCX_PORT_POWER 0x00001000 +#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 +#define PORTSCX_PORT_TEST_CTRL 0x000F0000 +#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 +#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 +#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 +#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 +#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000 +#define PORTSCX_PORT_SPEED_MASK 0x0C000000 +#define PORTSCX_PORT_WIDTH 0x10000000 +#define PORTSCX_PHY_TYPE_SEL 0xC0000000 + +/* bit 11-10 are line status */ +#define PORTSCX_LINE_STATUS_SE0 0x00000000 +#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 +#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 +#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 +#define PORTSCX_LINE_STATUS_BIT_POS 10 + +/* bit 15-14 are port indicator control */ +#define PORTSCX_PIC_OFF 0x00000000 +#define PORTSCX_PIC_AMBER 0x00004000 +#define PORTSCX_PIC_GREEN 0x00008000 +#define PORTSCX_PIC_UNDEF 0x0000C000 +#define PORTSCX_PIC_BIT_POS 14 + +/* bit 19-16 are port test control */ +#define PORTSCX_PTC_DISABLE 0x00000000 +#define PORTSCX_PTC_JSTATE 0x00010000 +#define PORTSCX_PTC_KSTATE 0x00020000 +#define PORTSCX_PTC_SEQNAK 0x00030000 +#define PORTSCX_PTC_PACKET 0x00040000 +#define PORTSCX_PTC_FORCE_EN 0x00050000 +#define PORTSCX_PTC_BIT_POS 16 + +/* bit 27-26 are port speed */ +#define PORTSCX_PORT_SPEED_FULL 0x00000000 +#define PORTSCX_PORT_SPEED_LOW 0x04000000 +#define PORTSCX_PORT_SPEED_HIGH 0x08000000 +#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000 +#define PORTSCX_SPEED_BIT_POS 26 + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSCX_PTW 0x10000000 +#define PORTSCX_PTW_8BIT 0x00000000 +#define PORTSCX_PTW_16BIT 0x10000000 + +/* bit 31-30 are port transceiver select */ +#define PORTSCX_PTS_UTMI 0x00000000 +#define PORTSCX_PTS_ULPI 0x80000000 +#define PORTSCX_PTS_FSLS 0xC0000000 +#define PORTSCX_PTS_BIT_POS 30 + +/* otgsc Register Bit Masks */ +#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001 +#define OTGSC_CTRL_VUSB_CHARGE 0x00000002 +#define OTGSC_CTRL_OTG_TERM 0x00000008 +#define OTGSC_CTRL_DATA_PULSING 0x00000010 +#define OTGSC_STS_USB_ID 0x00000100 +#define OTGSC_STS_A_VBUS_VALID 0x00000200 +#define OTGSC_STS_A_SESSION_VALID 0x00000400 +#define OTGSC_STS_B_SESSION_VALID 0x00000800 +#define OTGSC_STS_B_SESSION_END 0x00001000 +#define OTGSC_STS_1MS_TOGGLE 0x00002000 +#define OTGSC_STS_DATA_PULSING 0x00004000 +#define OTGSC_INTSTS_USB_ID 0x00010000 +#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000 +#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000 +#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000 +#define OTGSC_INTSTS_B_SESSION_END 0x00100000 +#define OTGSC_INTSTS_1MS 0x00200000 +#define OTGSC_INTSTS_DATA_PULSING 0x00400000 +#define OTGSC_INTR_USB_ID 0x01000000 +#define OTGSC_INTR_A_VBUS_VALID 0x02000000 +#define OTGSC_INTR_A_SESSION_VALID 0x04000000 +#define OTGSC_INTR_B_SESSION_VALID 0x08000000 +#define OTGSC_INTR_B_SESSION_END 0x10000000 +#define OTGSC_INTR_1MS_TIMER 0x20000000 +#define OTGSC_INTR_DATA_PULSING 0x40000000 + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE 0x00000000 +#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 +#define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_SETUP_LOCK_OFF 0x00000008 +#define USB_MODE_STREAM_DISABLE 0x00000010 +/* Endpoint Flush Register */ +#define EPFLUSH_TX_OFFSET 0x00010000 +#define EPFLUSH_RX_OFFSET 0x00000000 + +/* Endpoint Setup Status bit masks */ +#define EP_SETUP_STATUS_MASK 0x0000003F +#define EP_SETUP_STATUS_EP0 0x00000001 + +/* ENDPOINTCTRLx Register Bit Masks */ +#define EPCTRL_TX_ENABLE 0x00800000 +#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ +#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ +#define EPCTRL_TX_TYPE 0x000C0000 +#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ +#define EPCTRL_TX_EP_STALL 0x00010000 +#define EPCTRL_RX_ENABLE 0x00000080 +#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ +#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ +#define EPCTRL_RX_TYPE 0x0000000C +#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ +#define EPCTRL_RX_EP_STALL 0x00000001 + +/* bit 19-18 and 3-2 are endpoint type */ +#define EPCTRL_EP_TYPE_CONTROL 0 +#define EPCTRL_EP_TYPE_ISO 1 +#define EPCTRL_EP_TYPE_BULK 2 +#define EPCTRL_EP_TYPE_INTERRUPT 3 +#define EPCTRL_TX_EP_TYPE_SHIFT 18 +#define EPCTRL_RX_EP_TYPE_SHIFT 2 + +/* SNOOPn Register Bit Masks */ +#define SNOOP_ADDRESS_MASK 0xFFFFF000 +#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */ +#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */ +#define SNOOP_SIZE_8KB 0x0C +#define SNOOP_SIZE_16KB 0x0D +#define SNOOP_SIZE_32KB 0x0E +#define SNOOP_SIZE_64KB 0x0F +#define SNOOP_SIZE_128KB 0x10 +#define SNOOP_SIZE_256KB 0x11 +#define SNOOP_SIZE_512KB 0x12 +#define SNOOP_SIZE_1MB 0x13 +#define SNOOP_SIZE_2MB 0x14 +#define SNOOP_SIZE_4MB 0x15 +#define SNOOP_SIZE_8MB 0x16 +#define SNOOP_SIZE_16MB 0x17 +#define SNOOP_SIZE_32MB 0x18 +#define SNOOP_SIZE_64MB 0x19 +#define SNOOP_SIZE_128MB 0x1A +#define SNOOP_SIZE_256MB 0x1B +#define SNOOP_SIZE_512MB 0x1C +#define SNOOP_SIZE_1GB 0x1D +#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */ + +/* pri_ctrl Register Bit Masks */ +#define PRI_CTRL_PRI_LVL1 0x0000000C +#define PRI_CTRL_PRI_LVL0 0x00000003 + +/* si_ctrl Register Bit Masks */ +#define SI_CTRL_ERR_DISABLE 0x00000010 +#define SI_CTRL_IDRC_DISABLE 0x00000008 +#define SI_CTRL_RD_SAFE_EN 0x00000004 +#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002 +#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001 + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB 0x00000004 +#define USB_CTRL_ULPI_INT0EN 0x00000001 + +/* Endpoint Queue Head data struct + * Rem: all the variables of qh are LittleEndian Mode + * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr + */ +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len + and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ + u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ + u32 res1; + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; +}; + +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS 30 +#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS 0x00008000 +#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 +#define EP_QUEUE_HEAD_IOC 0x00008000 +#define EP_QUEUE_HEAD_MULTO 0x00000C00 +#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 +#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 +#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 +#define EP_QUEUE_FRINDEX_MASK 0x000007FF +#define EP_MAX_LENGTH_TRANSFER 0x4000 + +/* Endpoint Transfer Descriptor data struct */ +/* Rem: all the variables of td are LittleEndian Mode */ +struct ep_td_struct { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set + indicate invalid */ + u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; + /* 32 bytes */ + dma_addr_t td_dma; /* dma address for this td */ + /* virtual address of next td specified in next_td_ptr */ + struct ep_td_struct *next_td_virt; +}; + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE 0x00000001 +#define DTD_IOC 0x00008000 +#define DTD_STATUS_ACTIVE 0x00000080 +#define DTD_STATUS_HALTED 0x00000040 +#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 +#define DTD_STATUS_TRANSACTION_ERR 0x00000008 +#define DTD_RESERVED_FIELDS 0x80007300 +#define DTD_ADDR_MASK 0xFFFFFFE0 +#define DTD_PACKET_SIZE 0x7FFF0000 +#define DTD_LENGTH_BIT_POS 16 +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) +/* Alignment requirements; must be a power of two */ +#define DTD_ALIGNMENT 0x20 +#define QH_ALIGNMENT 2048 + +/* Controller dma boundary */ +#define UDC_DMA_BOUNDARY 0x1000 + +/* -----------------------------------------------------------------------*/ +/* ##### enum data +*/ +typedef enum { + e_ULPI, + e_UTMI_8BIT, + e_UTMI_16BIT, + e_SERIAL +} e_PhyInterface; + +/*-------------------------------------------------------------------------*/ + +/* ### driver private data + */ +struct fsl_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct fsl_ep *ep; + unsigned mapped:1; + + struct ep_td_struct *head, *tail; /* For dTD List + cpu endian Virtual addr */ + unsigned int dtd_count; +}; + +#define REQ_UNCOMPLETE 1 + +struct fsl_ep { + struct usb_ep ep; + struct list_head queue; + struct fsl_udc *udc; + struct ep_queue_head *qh; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + char name[14]; + unsigned stopped:1; +}; + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +struct fsl_udc { + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct fsl_ep *eps; + unsigned int max_ep; + unsigned int irq; + + struct usb_ctrlrequest local_setup_buff; + spinlock_t lock; + struct otg_transceiver *transceiver; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned stopped:1; + unsigned remote_wakeup:1; + + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct fsl_req *status_req; /* ep0 status request */ + struct dma_pool *td_pool; /* dma pool for DTD */ + enum fsl_usb2_phy_modes phy_mode; + + size_t ep_qh_size; /* size after alignment adjustment*/ + dma_addr_t ep_qh_dma; /* dma address of QH */ + + u32 max_pipes; /* Device max pipes */ + u32 max_use_endpts; /* Max endpointes to be used */ + u32 bus_reset; /* Device is bus reseting */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ + u32 usb_next_state; /* USB next state */ + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: can be + USB_DIR_IN or USB_DIR_OUT */ + u32 usb_sof_count; /* SOF count */ + u32 errors; /* USB ERRORs count */ + u8 device_address; /* Device USB address */ + + struct completion *done; /* to make sure release() is done */ +}; + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \ + __FUNCTION__, ## args) +#else +#define DBG(fmt, args...) do{}while(0) +#endif + +#if 0 +static void dump_msg(const char *label, const u8 * buf, unsigned int length) +{ + unsigned int start, num, i; + char line[52], *p; + + if (length >= 512) + return; + DBG("%s, length %u:\n", label, length); + start = 0; + while (length > 0) { + num = min(length, 16u); + p = line; + for (i = 0; i < num; ++i) { + if (i == 8) + *p++ = ' '; + sprintf(p, " %02x", buf[i]); + p += 3; + } + *p = 0; + printk(KERN_DEBUG "%6x: %s\n", start, line); + buf += num; + start += num; + length -= num; + } +} +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "udc: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) + +/*-------------------------------------------------------------------------*/ + +/* ### Add board specific defines here + */ + +/* + * ### pipe direction macro from device view + */ +#define USB_RECV 0 /* OUT EP */ +#define USB_SEND 1 /* IN EP */ + +/* + * ### internal used help routines. + */ +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) +#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN ):((EP)->desc->bEndpointAddress \ + & USB_DIR_IN)==USB_DIR_IN) +#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \ + &udc->eps[pipe]) +#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \ + * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) +#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP)) + +#endif diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 2e3d662..d041b91 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -99,6 +99,12 @@ #define gadget_is_imx(g) 0 #endif +#ifdef CONFIG_USB_GADGET_FSL_USB2 +#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name) +#else +#define gadget_is_fsl_usb2(g) 0 +#endif + /* Mentor high speed function controller */ #ifdef CONFIG_USB_GADGET_MUSBHSFC #define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name) @@ -177,5 +183,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x17; else if (gadget_is_husb2dev(gadget)) return 0x18; + else if (gadget_is_fsl_usb2(gadget)) + return 0x19; return -ENOENT; } diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c index 7b3a326..65c91d3 100644 --- a/drivers/usb/gadget/goku_udc.c +++ b/drivers/usb/gadget/goku_udc.c @@ -297,27 +297,6 @@ goku_free_request(struct usb_ep *_ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ -#undef USE_KMALLOC - -/* many common platforms have dma-coherent caches, which means that it's - * safe to use kmalloc() memory for all i/o buffers without using any - * cache flushing calls. (unless you're trying to share cache lines - * between dma and non-dma activities, which is a slow idea in any case.) - * - * other platforms need more care, with 2.6 having a moderately general - * solution except for the common "buffer is smaller than a page" case. - */ -#if defined(CONFIG_X86) -#define USE_KMALLOC - -#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT) -#define USE_KMALLOC - -#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) -#define USE_KMALLOC - -#endif - /* allocating buffers this way eliminates dma mapping overhead, which * on some platforms will mean eliminating a per-io buffer copy. with * some kinds of system caches, further tweaks may still be needed. @@ -334,11 +313,6 @@ goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes, return NULL; *dma = DMA_ADDR_INVALID; -#if defined(USE_KMALLOC) - retval = kmalloc(bytes, gfp_flags); - if (retval) - *dma = virt_to_phys(retval); -#else if (ep->dma) { /* the main problem with this call is that it wastes memory * on typical 1/N page allocations: it allocates 1-N pages. @@ -348,7 +322,6 @@ goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes, bytes, dma, gfp_flags); } else retval = kmalloc(bytes, gfp_flags); -#endif return retval; } @@ -356,7 +329,6 @@ static void goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) { /* free memory into the right allocator */ -#ifndef USE_KMALLOC if (dma != DMA_ADDR_INVALID) { struct goku_ep *ep; @@ -365,7 +337,6 @@ goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) return; dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma); } else -#endif kfree (buf); } diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c index 34296e7..188c74a 100644 --- a/drivers/usb/gadget/inode.c +++ b/drivers/usb/gadget/inode.c @@ -553,6 +553,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) { struct kiocb_priv *priv = iocb->private; ssize_t len, total; + void *to_copy; int i; /* we "retry" to get the right mm context for this: */ @@ -560,10 +561,11 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) /* copy stuff into user buffers */ total = priv->actual; len = 0; + to_copy = priv->buf; for (i=0; i < priv->nr_segs; i++) { ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total); - if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) { + if (copy_to_user(priv->iv[i].iov_base, to_copy, this)) { if (len == 0) len = -EFAULT; break; @@ -571,6 +573,7 @@ static ssize_t ep_aio_read_retry(struct kiocb *iocb) total -= this; len += this; + to_copy += this; if (total == 0) break; } diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index 8f9a2b6..b394e63 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -296,6 +296,15 @@ omap_free_request(struct usb_ep *ep, struct usb_request *_req) /*-------------------------------------------------------------------------*/ +/* + * dma-coherent memory allocation (for dma-capable endpoints) + * + * NOTE: the dma_*_coherent() API calls suck. Most implementations are + * (a) page-oriented, so small buffers lose big; and (b) asymmetric with + * respect to calls with irqs disabled: alloc is safe, free is not. + * We currently work around (b), but not (a). + */ + static void * omap_alloc_buffer( struct usb_ep *_ep, @@ -307,6 +316,9 @@ omap_alloc_buffer( void *retval; struct omap_ep *ep; + if (!_ep) + return NULL; + ep = container_of(_ep, struct omap_ep, ep); if (use_dma && ep->has_dma) { static int warned; @@ -326,6 +338,35 @@ omap_alloc_buffer( return retval; } +static DEFINE_SPINLOCK(buflock); +static LIST_HEAD(buffers); + +struct free_record { + struct list_head list; + struct device *dev; + unsigned bytes; + dma_addr_t dma; +}; + +static void do_free(unsigned long ignored) +{ + spin_lock_irq(&buflock); + while (!list_empty(&buffers)) { + struct free_record *buf; + + buf = list_entry(buffers.next, struct free_record, list); + list_del(&buf->list); + spin_unlock_irq(&buflock); + + dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma); + + spin_lock_irq(&buflock); + } + spin_unlock_irq(&buflock); +} + +static DECLARE_TASKLET(deferred_free, do_free, 0); + static void omap_free_buffer( struct usb_ep *_ep, void *buf, @@ -333,13 +374,29 @@ static void omap_free_buffer( unsigned bytes ) { - struct omap_ep *ep; + if (!_ep) { + WARN_ON(1); + return; + } - ep = container_of(_ep, struct omap_ep, ep); - if (use_dma && _ep && ep->has_dma) - dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma); - else - kfree (buf); + /* free memory into the right allocator */ + if (dma != DMA_ADDR_INVALID) { + struct omap_ep *ep; + struct free_record *rec = buf; + unsigned long flags; + + ep = container_of(_ep, struct omap_ep, ep); + + rec->dev = ep->udc->gadget.dev.parent; + rec->bytes = bytes; + rec->dma = dma; + + spin_lock_irqsave(&buflock, flags); + list_add_tail(&rec->list, &buffers); + tasklet_schedule(&deferred_free); + spin_unlock_irqrestore(&buflock, flags); + } else + kfree(buf); } /*-------------------------------------------------------------------------*/ @@ -1691,12 +1748,38 @@ ep0out_status_stage: udc->ep0_pending = 0; break; case USB_REQ_GET_STATUS: + /* USB_ENDPOINT_HALT status? */ + if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) + goto intf_status; + + /* ep0 never stalls */ + if (!(w_index & 0xf)) + goto zero_status; + + /* only active endpoints count */ + ep = &udc->ep[w_index & 0xf]; + if (w_index & USB_DIR_IN) + ep += 16; + if (!ep->desc) + goto do_stall; + + /* iso never stalls */ + if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) + goto zero_status; + + /* FIXME don't assume non-halted endpoints!! */ + ERR("%s status, can't report\n", ep->ep.name); + goto do_stall; + +intf_status: /* return interface status. if we were pedantic, * we'd detect non-existent interfaces, and stall. */ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) goto delegate; + +zero_status: /* return two zero bytes */ UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR; UDC_DATA_REG = 0; @@ -2068,7 +2151,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev) /*-------------------------------------------------------------------------*/ -static inline int machine_needs_vbus_session(void) +static inline int machine_without_vbus_sense(void) { return (machine_is_omap_innovator() || machine_is_omap_osk() @@ -2156,7 +2239,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) /* boards that don't have VBUS sensing can't autogate 48MHz; * can't enter deep sleep while a gadget driver is active. */ - if (machine_needs_vbus_session()) + if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 1); done: @@ -2179,7 +2262,7 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) if (udc->dc_clk != NULL) omap_udc_enable_clock(1); - if (machine_needs_vbus_session()) + if (machine_without_vbus_sense()) omap_vbus_session(&udc->gadget, 0); if (udc->transceiver) @@ -2822,7 +2905,7 @@ static int __init omap_udc_probe(struct platform_device *pdev) hmc = HMC_1510; type = "(unknown)"; - if (machine_is_omap_innovator() || machine_is_sx1()) { + if (machine_without_vbus_sense()) { /* just set up software VBUS detect, and then * later rig it so we always report VBUS. * FIXME without really sensing VBUS, we can't diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c index 27904a5..2c043a1 100644 --- a/drivers/usb/gadget/pxa2xx_udc.c +++ b/drivers/usb/gadget/pxa2xx_udc.c @@ -71,7 +71,7 @@ * by the host to interact with this device, and allocates endpoints to * the different protocol interfaces. The controller driver virtualizes * usb hardware so that the gadget drivers will be more portable. - * + * * This UDC hardware wants to implement a bit too much USB protocol, so * it constrains the sorts of USB configuration change events that work. * The errata for these chips are misleading; some "fixed" bugs from @@ -141,7 +141,7 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode"); #endif /* --------------------------------------------------------------------------- - * endpoint related parts of the api to the usb controller hardware, + * endpoint related parts of the api to the usb controller hardware, * used by gadget driver; and the inner talker-to-hardware core. * --------------------------------------------------------------------------- */ @@ -155,7 +155,7 @@ static int is_vbus_present(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_vbus) - return pxa_gpio_get(mach->gpio_vbus); + return udc_gpio_get(mach->gpio_vbus); if (mach->udc_is_connected) return mach->udc_is_connected(); return 1; @@ -167,7 +167,7 @@ static void pullup_off(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - pxa_gpio_set(mach->gpio_pullup, 0); + udc_gpio_set(mach->gpio_pullup, 0); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); } @@ -177,7 +177,7 @@ static void pullup_on(void) struct pxa2xx_udc_mach_info *mach = the_controller->mach; if (mach->gpio_pullup) - pxa_gpio_set(mach->gpio_pullup, 1); + udc_gpio_set(mach->gpio_pullup, 1); else if (mach->udc_command) mach->udc_command(PXA2XX_UDC_CMD_CONNECT); } @@ -293,7 +293,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, #ifdef USE_DMA /* for (some) bulk and ISO endpoints, try to get a DMA channel and - * bind it to the endpoint. otherwise use PIO. + * bind it to the endpoint. otherwise use PIO. */ switch (ep->bmAttributes) { case USB_ENDPOINT_XFER_ISOC: @@ -304,7 +304,7 @@ static int pxa2xx_ep_enable (struct usb_ep *_ep, if (!use_dma || !ep->reg_drcmr) break; ep->dma = pxa_request_dma ((char *)_ep->name, - (le16_to_cpu (desc->wMaxPacketSize) > 64) + (le16_to_cpu (desc->wMaxPacketSize) > 64) ? DMA_PRIO_MEDIUM /* some iso */ : DMA_PRIO_LOW, dma_nodesc_handler, ep); @@ -361,7 +361,7 @@ static int pxa2xx_ep_disable (struct usb_ep *_ep) */ /* - * pxa2xx_ep_alloc_request - allocate a request data structure + * pxa2xx_ep_alloc_request - allocate a request data structure */ static struct usb_request * pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) @@ -378,7 +378,7 @@ pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags) /* - * pxa2xx_ep_free_request - deallocate a request data structure + * pxa2xx_ep_free_request - deallocate a request data structure */ static void pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) @@ -1031,7 +1031,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* - * nuke - dequeue ALL requests + * nuke - dequeue ALL requests */ static void nuke(struct pxa2xx_ep *ep, int status) { @@ -1136,16 +1136,16 @@ static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value) ep->dev->req_pending = 0; ep->dev->ep0state = EP0_STALL; - /* and bulk/intr endpoints like dropping stalls too */ - } else { - unsigned i; - for (i = 0; i < 1000; i += 20) { - if (*ep->reg_udccs & UDCCS_BI_SST) - break; - udelay(20); - } - } - local_irq_restore(flags); + /* and bulk/intr endpoints like dropping stalls too */ + } else { + unsigned i; + for (i = 0; i < 1000; i += 20) { + if (*ep->reg_udccs & UDCCS_BI_SST) + break; + udelay(20); + } + } + local_irq_restore(flags); DBG(DBG_VERBOSE, "%s halt\n", _ep->name); return 0; @@ -1216,7 +1216,7 @@ static struct usb_ep_ops pxa2xx_ep_ops = { /* --------------------------------------------------------------------------- - * device-scoped parts of the api to the usb controller hardware + * device-scoped parts of the api to the usb controller hardware * --------------------------------------------------------------------------- */ @@ -1239,7 +1239,7 @@ static void udc_enable (struct pxa2xx_udc *); static void udc_disable(struct pxa2xx_udc *); /* We disable the UDC -- and its 48 MHz clock -- whenever it's not - * in active use. + * in active use. */ static int pullup(struct pxa2xx_udc *udc, int is_active) { @@ -1464,24 +1464,10 @@ done: #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -/* "function" sysfs attribute */ -static ssize_t -show_function (struct device *_dev, struct device_attribute *attr, char *buf) -{ - struct pxa2xx_udc *dev = dev_get_drvdata (_dev); - - if (!dev->driver - || !dev->driver->function - || strlen (dev->driver->function) > PAGE_SIZE) - return 0; - return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -} -static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); - /*-------------------------------------------------------------------------*/ /* - * udc_disable - disable USB device controller + * udc_disable - disable USB device controller */ static void udc_disable(struct pxa2xx_udc *dev) { @@ -1507,7 +1493,7 @@ static void udc_disable(struct pxa2xx_udc *dev) /* - * udc_reinit - initialize software state + * udc_reinit - initialize software state */ static void udc_reinit(struct pxa2xx_udc *dev) { @@ -1635,18 +1621,20 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver) dev->gadget.dev.driver = &driver->driver; dev->pullup = 1; - device_add (&dev->gadget.dev); + retval = device_add (&dev->gadget.dev); + if (retval) { +fail: + dev->driver = NULL; + dev->gadget.dev.driver = NULL; + return retval; + } retval = driver->bind(&dev->gadget); if (retval) { DMSG("bind to driver %s --> error %d\n", driver->driver.name, retval); device_del (&dev->gadget.dev); - - dev->driver = NULL; - dev->gadget.dev.driver = NULL; - return retval; + goto fail; } - device_create_file(dev->dev, &dev_attr_function); /* ... then enable host detection and ep0; and we're ready * for set_configuration as well as eventual disconnect. @@ -1704,7 +1692,6 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) dev->driver = NULL; device_del (&dev->gadget.dev); - device_remove_file(dev->dev, &dev_attr_function); DMSG("unregistered gadget driver '%s'\n", driver->driver.name); dump_state(dev); @@ -1755,7 +1742,7 @@ lubbock_vbus_irq(int irq, void *_dev) static irqreturn_t udc_vbus_irq(int irq, void *_dev) { struct pxa2xx_udc *dev = _dev; - int vbus = pxa_gpio_get(dev->mach->gpio_vbus); + int vbus = udc_gpio_get(dev->mach->gpio_vbus); pxa2xx_udc_vbus_session(&dev->gadget, vbus); return IRQ_HANDLED; @@ -2474,12 +2461,12 @@ static struct pxa2xx_udc memory = { #define IXP465_AD 0x00000200 /* - * probe - binds to the platform device + * probe - binds to the platform device */ static int __init pxa2xx_udc_probe(struct platform_device *pdev) { struct pxa2xx_udc *dev = &memory; - int retval, out_dma = 1, vbus_irq; + int retval, out_dma = 1, vbus_irq, irq; u32 chiprev; /* insist on Intel/ARM/XScale */ @@ -2522,7 +2509,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) return -ENODEV; } - pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB, + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -ENODEV; + + pr_debug("%s: IRQ %d%s%s%s\n", driver_name, irq, dev->has_cfr ? "" : " (!cfr)", out_dma ? "" : " (broken dma-out)", SIZE_STR DMASTR @@ -2545,15 +2536,13 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) dev->dev = &pdev->dev; dev->mach = pdev->dev.platform_data; if (dev->mach->gpio_vbus) { - vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR); - pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR) - | GPIO_IN); + udc_gpio_init_vbus(dev->mach->gpio_vbus); + vbus_irq = udc_gpio_to_irq(dev->mach->gpio_vbus); set_irq_type(vbus_irq, IRQT_BOTHEDGE); } else vbus_irq = 0; if (dev->mach->gpio_pullup) - pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR) - | GPIO_OUT | GPIO_DFLT_LOW); + udc_gpio_init_pullup(dev->mach->gpio_pullup); init_timer(&dev->timer); dev->timer.function = udc_watchdog; @@ -2572,11 +2561,11 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) dev->vbus = is_vbus_present(); /* irq setup after old hardware state is cleaned up */ - retval = request_irq(IRQ_USB, pxa2xx_udc_irq, + retval = request_irq(irq, pxa2xx_udc_irq, IRQF_DISABLED, driver_name, dev); if (retval != 0) { - printk(KERN_ERR "%s: can't get irq %i, err %d\n", - driver_name, IRQ_USB, retval); + printk(KERN_ERR "%s: can't get irq %d, err %d\n", + driver_name, irq, retval); return -EBUSY; } dev->got_irq = 1; @@ -2591,7 +2580,7 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev) printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, LUBBOCK_USB_DISC_IRQ, retval); lubbock_fail0: - free_irq(IRQ_USB, dev); + free_irq(irq, dev); return -EBUSY; } retval = request_irq(LUBBOCK_USB_IRQ, @@ -2618,7 +2607,7 @@ lubbock_fail0: if (retval != 0) { printk(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name, vbus_irq, retval); - free_irq(IRQ_USB, dev); + free_irq(irq, dev); return -EBUSY; } } @@ -2643,7 +2632,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) remove_proc_files(); if (dev->got_irq) { - free_irq(IRQ_USB, dev); + free_irq(platform_get_irq(pdev, 0), dev); dev->got_irq = 0; } #ifdef CONFIG_ARCH_LUBBOCK @@ -2670,7 +2659,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) * * For now, we punt and forcibly disconnect from the USB host when PXA * enters any suspend state. While we're disconnected, we always disable - * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. + * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states. * Boards without software pullup control shouldn't use those states. * VBUS IRQs should probably be ignored so that the PXA device just acts * "dead" to USB hosts until system resume. @@ -2703,7 +2692,6 @@ static int pxa2xx_udc_resume(struct platform_device *dev) /*-------------------------------------------------------------------------*/ static struct platform_driver udc_driver = { - .probe = pxa2xx_udc_probe, .shutdown = pxa2xx_udc_shutdown, .remove = __exit_p(pxa2xx_udc_remove), .suspend = pxa2xx_udc_suspend, @@ -2717,7 +2705,7 @@ static struct platform_driver udc_driver = { static int __init udc_init(void) { printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); - return platform_driver_register(&udc_driver); + return platform_driver_probe(&udc_driver, pxa2xx_udc_probe); } module_init(udc_init); diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h index 8e598c8..773e549 100644 --- a/drivers/usb/gadget/pxa2xx_udc.h +++ b/drivers/usb/gadget/pxa2xx_udc.h @@ -177,21 +177,6 @@ struct pxa2xx_udc { static struct pxa2xx_udc *the_controller; -static inline int pxa_gpio_get(unsigned gpio) -{ - return (GPLR(gpio) & GPIO_bit(gpio)) != 0; -} - -static inline void pxa_gpio_set(unsigned gpio, int is_on) -{ - int mask = GPIO_bit(gpio); - - if (is_on) - GPSR(gpio) = mask; - else - GPCR(gpio) = mask; -} - /*-------------------------------------------------------------------------*/ /* diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h index 4c3c725..397b149 100644 --- a/drivers/usb/gadget/rndis.h +++ b/drivers/usb/gadget/rndis.h @@ -195,7 +195,7 @@ struct rndis_packet_msg_type __le32 PerPacketInfoLength; __le32 VcHandle; __le32 Reserved; -}; +} __attribute__ ((packed)); struct rndis_config_parameter { diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c index e6c19aa..e552668 100644 --- a/drivers/usb/gadget/serial.c +++ b/drivers/usb/gadget/serial.c @@ -1699,6 +1699,7 @@ static int gs_setup_class(struct usb_gadget *gadget, memcpy(&port->port_line_coding, req->buf, ret); spin_unlock(&port->port_lock); } + ret = 0; break; case USB_CDC_REQ_GET_LINE_CODING: diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index a2e58c8..2ff396bd 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -15,4 +15,3 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o -obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 246afea..43eddae 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -322,7 +322,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { } #else -/* troubleshooting help: expose state in driverfs */ +/* troubleshooting help: expose state in sysfs */ #define speed_char(info1) ({ char tmp; \ switch (info1 & (3 << 12)) { \ diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index caac0d1..f28736a 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -31,7 +31,7 @@ #define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */ #define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */ #define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */ -#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */ -#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */ +#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */ +#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */ #define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */ #endif /* _EHCI_FSL_H */ diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 185721d..c7458f7 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -42,6 +42,9 @@ #include <asm/irq.h> #include <asm/system.h> #include <asm/unaligned.h> +#ifdef CONFIG_PPC_PS3 +#include <asm/firmware.h> +#endif /*-------------------------------------------------------------------------*/ @@ -299,6 +302,19 @@ static void ehci_watchdog (unsigned long param) spin_unlock_irqrestore (&ehci->lock, flags); } +/* On some systems, leaving remote wakeup enabled prevents system shutdown. + * The firmware seems to think that powering off is a wakeup event! + * This routine turns off remote wakeup and everything else, on all ports. + */ +static void ehci_turn_off_all_ports(struct ehci_hcd *ehci) +{ + int port = HCS_N_PORTS(ehci->hcs_params); + + while (port--) + ehci_writel(ehci, PORT_RWC_BITS, + &ehci->regs->port_status[port]); +} + /* ehci_shutdown kick in for silicon on any bus (not just pci, etc). * This forcibly disables dma and IRQs, helping kexec and other cases * where the next system software may expect clean state. @@ -310,9 +326,13 @@ ehci_shutdown (struct usb_hcd *hcd) ehci = hcd_to_ehci (hcd); (void) ehci_halt (ehci); + ehci_turn_off_all_ports(ehci); /* make BIOS/etc use companion controller during reboot */ ehci_writel(ehci, 0, &ehci->regs->configured_flag); + + /* unblock posted writes */ + ehci_readl(ehci, &ehci->regs->configured_flag); } static void ehci_port_power (struct ehci_hcd *ehci, int is_on) @@ -649,6 +669,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) */ ehci->reset_done [i] = jiffies + msecs_to_jiffies (20); ehci_dbg (ehci, "port %d remote wakeup\n", i + 1); + mod_timer(&hcd->rh_timer, ehci->reset_done[i]); } } @@ -951,15 +972,18 @@ static int __init ehci_hcd_init(void) #endif #ifdef PS3_SYSTEM_BUS_DRIVER - retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) { + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + retval = ps3_system_bus_driver_register( + &PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) { #ifdef PLATFORM_DRIVER - platform_driver_unregister(&PLATFORM_DRIVER); + platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PCI_DRIVER - pci_unregister_driver(&PCI_DRIVER); + pci_unregister_driver(&PCI_DRIVER); #endif - return retval; + return retval; + } } #endif @@ -976,7 +1000,8 @@ static void __exit ehci_hcd_cleanup(void) pci_unregister_driver(&PCI_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 0d83c6d..f4d301b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -36,6 +36,8 @@ static int ehci_bus_suspend (struct usb_hcd *hcd) int port; int mask; + ehci_dbg(ehci, "suspend root hub\n"); + if (time_before (jiffies, ehci->next_statechange)) msleep(5); @@ -134,6 +136,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd) /* restore CMD_RUN, framelist size, and irq threshold */ ehci_writel(ehci, ehci->command, &ehci->regs->command); + /* Some controller/firmware combinations need a delay during which + * they set up the port statuses. See Bugzilla #8190. */ + mdelay(8); + /* manually resume the ports we suspended during bus_suspend() */ i = HCS_N_PORTS (ehci->hcs_params); while (i--) { @@ -651,8 +657,7 @@ static int ehci_hub_control ( if (status & ~0xffff) /* only if wPortChange is interesting */ #endif dbg_port (ehci, "GetStatus", wIndex + 1, temp); - // we "know" this alignment is good, caller used kmalloc()... - *((__le32 *) buf) = cpu_to_le32 (status); + put_unaligned(cpu_to_le32 (status), (__le32 *) buf); break; case SetHubFeature: switch (wValue) { diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c deleted file mode 100644 index 32f7caf..0000000 --- a/drivers/usb/host/hc_crisv10.c +++ /dev/null @@ -1,4550 +0,0 @@ -/* - * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD) - * - * Copyright (c) 2002, 2003 Axis Communications AB. - */ - -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/slab.h> -#include <linux/errno.h> -#include <linux/unistd.h> -#include <linux/interrupt.h> -#include <linux/init.h> -#include <linux/list.h> -#include <linux/spinlock.h> - -#include <asm/uaccess.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/dma.h> -#include <asm/system.h> -#include <asm/arch/svinto.h> - -#include <linux/usb.h> -/* Ugly include because we don't live with the other host drivers. */ -#include <../drivers/usb/core/hcd.h> -#include <../drivers/usb/core/usb.h> - -#include "hc_crisv10.h" - -#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR -#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR -#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR - -static const char *usb_hcd_version = "$Revision: 1.2 $"; - -#undef KERN_DEBUG -#define KERN_DEBUG "" - - -#undef USB_DEBUG_RH -#undef USB_DEBUG_EPID -#undef USB_DEBUG_SB -#undef USB_DEBUG_DESC -#undef USB_DEBUG_URB -#undef USB_DEBUG_TRACE -#undef USB_DEBUG_BULK -#undef USB_DEBUG_CTRL -#undef USB_DEBUG_INTR -#undef USB_DEBUG_ISOC - -#ifdef USB_DEBUG_RH -#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg) -#else -#define dbg_rh(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_EPID -#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg) -#else -#define dbg_epid(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_SB -#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg) -#else -#define dbg_sb(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_CTRL -#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg) -#else -#define dbg_ctrl(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_BULK -#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg) -#else -#define dbg_bulk(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_INTR -#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg) -#else -#define dbg_intr(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_ISOC -#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg) -#else -#define dbg_isoc(format, arg...) do {} while (0) -#endif - -#ifdef USB_DEBUG_TRACE -#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__)) -#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__)) -#else -#define DBFENTER do {} while (0) -#define DBFEXIT do {} while (0) -#endif - -#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) - -/*------------------------------------------------------------------- - Virtual Root Hub - -------------------------------------------------------------------*/ - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __le16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __le16 idVendor; */ - 0x00, - 0x00, /* __le16 idProduct; */ - 0x00, - 0x00, /* __le16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x02, /* __u8 iProduct; */ - 0x01, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __le16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -static __u8 root_hub_hub_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x29, /* __u8 bDescriptorType; Hub-descriptor */ - 0x02, /* __u8 bNbrPorts; */ - 0x00, /* __u16 wHubCharacteristics; */ - 0x00, - 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ - 0x00, /* __u8 bHubContrCurrent; 0 mA */ - 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ - 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ -}; - -static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0); -static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0); - -/* We want the start timer to expire before the eot timer, because the former might start - traffic, thus making it unnecessary for the latter to time out. */ -#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */ -#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */ - -#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break -#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \ -{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);} - -#define SLAB_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) -#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) - -/* Most helpful debugging aid */ -#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) - -/* Alternative assert define which stops after a failed assert. */ -/* -#define assert(expr) \ -{ \ - if (!(expr)) { \ - err("assert failed at line %d",__LINE__); \ - while (1); \ - } \ -} -*/ - - -/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically? - To adjust it dynamically we would have to get an interrupt when we reach the end - of the rx descriptor list, or when we get close to the end, and then allocate more - descriptors. */ - -#define NBR_OF_RX_DESC 512 -#define RX_DESC_BUF_SIZE 1024 -#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE) - -/* The number of epids is, among other things, used for pre-allocating - ctrl, bulk and isoc EP descriptors (one for each epid). - Assumed to be > 1 when initiating the DMA lists. */ -#define NBR_OF_EPIDS 32 - -/* Support interrupt traffic intervals up to 128 ms. */ -#define MAX_INTR_INTERVAL 128 - -/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table - must be "invalid". By this we mean that we shouldn't care about epid attentions - for this epid, or at least handle them differently from epid attentions for "valid" - epids. This define determines which one to use (don't change it). */ -#define INVALID_EPID 31 -/* A special epid for the bulk dummys. */ -#define DUMMY_EPID 30 - -/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */ -static __u32 epid_usage_bitmask; - -/* A bitfield to keep information on in/out traffic is needed to uniquely identify - an endpoint on a device, since the most significant bit which indicates traffic - direction is lacking in the ep_id field (ETRAX epids can handle both in and - out traffic on endpoints that are otherwise identical). The USB framework, however, - relies on them to be handled separately. For example, bulk IN and OUT urbs cannot - be queued in the same list, since they would block each other. */ -static __u32 epid_out_traffic; - -/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line. - Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */ -static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32))); -static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4))); - -/* Pointers into RxDescList. */ -static volatile USB_IN_Desc_t *myNextRxDesc; -static volatile USB_IN_Desc_t *myLastRxDesc; -static volatile USB_IN_Desc_t *myPrevRxDesc; - -/* EP descriptors must be 32-bit aligned. */ -static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set, - causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which - gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the - EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors - in each frame. */ -static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4))); - -static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4))); -static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4))); - -static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4))); -static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4))); - -/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting - this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0 - results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point - it to this buffer. */ -static int zout_buffer[4] __attribute__ ((aligned (4))); - -/* Cache for allocating new EP and SB descriptors. */ -static struct kmem_cache *usb_desc_cache; - -/* Cache for the registers allocated in the top half. */ -static struct kmem_cache *top_half_reg_cache; - -/* Cache for the data allocated in the isoc descr top half. */ -static struct kmem_cache *isoc_compl_cache; - -static struct usb_bus *etrax_usb_bus; - -/* This is a circular (double-linked) list of the active urbs for each epid. - The head is never removed, and new urbs are linked onto the list as - urb_entry_t elements. Don't reference urb_list directly; use the wrapper - functions instead. Note that working with these lists might require spinlock - protection. */ -static struct list_head urb_list[NBR_OF_EPIDS]; - -/* Read about the need and usage of this lock in submit_ctrl_urb. */ -static spinlock_t urb_list_lock; - -/* Used when unlinking asynchronously. */ -static struct list_head urb_unlink_list; - -/* for returning string descriptors in UTF-16LE */ -static int ascii2utf (char *ascii, __u8 *utf, int utfmax) -{ - int retval; - - for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { - *utf++ = *ascii++ & 0x7f; - *utf++ = 0; - } - return retval; -} - -static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) -{ - char buf [30]; - - // assert (len > (2 * (sizeof (buf) + 1))); - // assert (strlen (type) <= 8); - - // language ids - if (id == 0) { - *data++ = 4; *data++ = 3; /* 4 bytes data */ - *data++ = 0; *data++ = 0; /* some language id */ - return 4; - - // serial number - } else if (id == 1) { - sprintf (buf, "%x", serial); - - // product description - } else if (id == 2) { - sprintf (buf, "USB %s Root Hub", type); - - // id 3 == vendor description - - // unsupported IDs --> "stall" - } else - return 0; - - data [0] = 2 + ascii2utf (buf, data + 2, len - 2); - data [1] = 3; - return data [0]; -} - -/* Wrappers around the list functions (include/linux/list.h). */ - -static inline int urb_list_empty(int epid) -{ - return list_empty(&urb_list[epid]); -} - -/* Returns first urb for this epid, or NULL if list is empty. */ -static inline struct urb *urb_list_first(int epid) -{ - struct urb *first_urb = 0; - - if (!urb_list_empty(epid)) { - /* Get the first urb (i.e. head->next). */ - urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list); - first_urb = urb_entry->urb; - } - return first_urb; -} - -/* Adds an urb_entry last in the list for this epid. */ -static inline void urb_list_add(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG); - assert(urb_entry); - - urb_entry->urb = urb; - list_add_tail(&urb_entry->list, &urb_list[epid]); -} - -/* Search through the list for an element that contains this urb. (The list - is expected to be short and the one we are about to delete will often be - the first in the list.) */ -static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid) -{ - struct list_head *entry; - struct list_head *tmp; - urb_entry_t *urb_entry; - - list_for_each_safe(entry, tmp, &urb_list[epid]) { - urb_entry = list_entry(entry, urb_entry_t, list); - assert(urb_entry); - assert(urb_entry->urb); - - if (urb_entry->urb == urb) { - return urb_entry; - } - } - return 0; -} - -/* Delete an urb from the list. */ -static inline void urb_list_del(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - assert(urb_entry); - - /* Delete entry and free. */ - list_del(&urb_entry->list); - kfree(urb_entry); -} - -/* Move an urb to the end of the list. */ -static inline void urb_list_move_last(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - assert(urb_entry); - - list_move_tail(&urb_entry->list, &urb_list[epid]); -} - -/* Get the next urb in the list. */ -static inline struct urb *urb_list_next(struct urb *urb, int epid) -{ - urb_entry_t *urb_entry = __urb_list_entry(urb, epid); - - assert(urb_entry); - - if (urb_entry->list.next != &urb_list[epid]) { - struct list_head *elem = urb_entry->list.next; - urb_entry = list_entry(elem, urb_entry_t, list); - return urb_entry->urb; - } else { - return NULL; - } -} - - - -/* For debug purposes only. */ -static inline void urb_list_dump(int epid) -{ - struct list_head *entry; - struct list_head *tmp; - urb_entry_t *urb_entry; - int i = 0; - - info("Dumping urb list for epid %d", epid); - - list_for_each_safe(entry, tmp, &urb_list[epid]) { - urb_entry = list_entry(entry, urb_entry_t, list); - info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb); - } -} - -static void init_rx_buffers(void); -static int etrax_rh_unlink_urb(struct urb *urb); -static void etrax_rh_send_irq(struct urb *urb); -static void etrax_rh_init_int_timer(struct urb *urb); -static void etrax_rh_int_timer_do(unsigned long ptr); - -static int etrax_usb_setup_epid(struct urb *urb); -static int etrax_usb_lookup_epid(struct urb *urb); -static int etrax_usb_allocate_epid(void); -static void etrax_usb_free_epid(int epid); - -static int etrax_remove_from_sb_list(struct urb *urb); - -static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, - unsigned mem_flags, dma_addr_t *dma); -static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma); - -static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid); -static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid); - -static int etrax_usb_submit_bulk_urb(struct urb *urb); -static int etrax_usb_submit_ctrl_urb(struct urb *urb); -static int etrax_usb_submit_intr_urb(struct urb *urb); -static int etrax_usb_submit_isoc_urb(struct urb *urb); - -static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags); -static int etrax_usb_unlink_urb(struct urb *urb, int status); -static int etrax_usb_get_frame_number(struct usb_device *usb_dev); - -static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc); -static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc); -static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc); -static void etrax_usb_hc_interrupt_bottom_half(void *data); - -static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data); - - -/* The following is a list of interrupt handlers for the host controller interrupts we use. - They are called from etrax_usb_hc_interrupt_bottom_half. */ -static void etrax_usb_hc_isoc_eof_interrupt(void); -static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced); -static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg); -static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg); -static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg); - -static int etrax_rh_submit_urb (struct urb *urb); - -/* Forward declaration needed because they are used in the rx interrupt routine. */ -static void etrax_usb_complete_urb(struct urb *urb, int status); -static void etrax_usb_complete_bulk_urb(struct urb *urb, int status); -static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status); -static void etrax_usb_complete_intr_urb(struct urb *urb, int status); -static void etrax_usb_complete_isoc_urb(struct urb *urb, int status); - -static int etrax_usb_hc_init(void); -static void etrax_usb_hc_cleanup(void); - -static struct usb_operations etrax_usb_device_operations = -{ - .get_frame_number = etrax_usb_get_frame_number, - .submit_urb = etrax_usb_submit_urb, - .unlink_urb = etrax_usb_unlink_urb, - .buffer_alloc = etrax_usb_buffer_alloc, - .buffer_free = etrax_usb_buffer_free -}; - -/* Note that these functions are always available in their "__" variants, for use in - error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/ - USB_DEBUG_URB macros. */ -static void __dump_urb(struct urb* purb) -{ - printk("\nurb :0x%08lx\n", (unsigned long)purb); - printk("dev :0x%08lx\n", (unsigned long)purb->dev); - printk("pipe :0x%08x\n", purb->pipe); - printk("status :%d\n", purb->status); - printk("transfer_flags :0x%08x\n", purb->transfer_flags); - printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer); - printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length); - printk("actual_length :%d\n", purb->actual_length); - printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet); - printk("start_frame :%d\n", purb->start_frame); - printk("number_of_packets :%d\n", purb->number_of_packets); - printk("interval :%d\n", purb->interval); - printk("error_count :%d\n", purb->error_count); - printk("context :0x%08lx\n", (unsigned long)purb->context); - printk("complete :0x%08lx\n\n", (unsigned long)purb->complete); -} - -static void __dump_in_desc(volatile USB_IN_Desc_t *in) -{ - printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in); - printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len); - printk(" command : 0x%04x\n", in->command); - printk(" next : 0x%08lx\n", in->next); - printk(" buf : 0x%08lx\n", in->buf); - printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len); - printk(" status : 0x%04x\n\n", in->status); -} - -static void __dump_sb_desc(volatile USB_SB_Desc_t *sb) -{ - char tt = (sb->command & 0x30) >> 4; - char *tt_string; - - switch (tt) { - case 0: - tt_string = "zout"; - break; - case 1: - tt_string = "in"; - break; - case 2: - tt_string = "out"; - break; - case 3: - tt_string = "setup"; - break; - default: - tt_string = "unknown (weird)"; - } - - printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); - printk(" command : 0x%04x\n", sb->command); - printk(" rem : %d\n", (sb->command & 0x3f00) >> 8); - printk(" full : %d\n", (sb->command & 0x40) >> 6); - printk(" tt : %d (%s)\n", tt, tt_string); - printk(" intr : %d\n", (sb->command & 0x8) >> 3); - printk(" eot : %d\n", (sb->command & 0x2) >> 1); - printk(" eol : %d\n", sb->command & 0x1); - printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len); - printk(" next : 0x%08lx\n", sb->next); - printk(" buf : 0x%08lx\n\n", sb->buf); -} - - -static void __dump_ep_desc(volatile USB_EP_Desc_t *ep) -{ - printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep); - printk(" command : 0x%04x\n", ep->command); - printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8); - printk(" enable : %d\n", (ep->command & 0x10) >> 4); - printk(" intr : %d\n", (ep->command & 0x8) >> 3); - printk(" eof : %d\n", (ep->command & 0x2) >> 1); - printk(" eol : %d\n", ep->command & 0x1); - printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len); - printk(" next : 0x%08lx\n", ep->next); - printk(" sub : 0x%08lx\n\n", ep->sub); -} - -static inline void __dump_ep_list(int pipe_type) -{ - volatile USB_EP_Desc_t *ep; - volatile USB_EP_Desc_t *first_ep; - volatile USB_SB_Desc_t *sb; - - switch (pipe_type) - { - case PIPE_BULK: - first_ep = &TxBulkEPList[0]; - break; - case PIPE_CONTROL: - first_ep = &TxCtrlEPList[0]; - break; - case PIPE_INTERRUPT: - first_ep = &TxIntrEPList[0]; - break; - case PIPE_ISOCHRONOUS: - first_ep = &TxIsocEPList[0]; - break; - default: - warn("Cannot dump unknown traffic type"); - return; - } - ep = first_ep; - - printk("\n\nDumping EP list...\n\n"); - - do { - __dump_ep_desc(ep); - /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */ - sb = ep->sub ? phys_to_virt(ep->sub) : 0; - while (sb) { - __dump_sb_desc(sb); - sb = sb->next ? phys_to_virt(sb->next) : 0; - } - ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next)); - - } while (ep != first_ep); -} - -static inline void __dump_ept_data(int epid) -{ - unsigned long flags; - __u32 r_usb_ept_data; - - if (epid < 0 || epid > 31) { - printk("Cannot dump ept data for invalid epid %d\n", epid); - return; - } - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid); - if (r_usb_ept_data == 0) { - /* No need for more detailed printing. */ - return; - } - printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31); - printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30); - printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28); - printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27); - printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26); - printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24); - printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22); - printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21); - printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19); - printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11); - printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7); - printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f)); -} - -static inline void __dump_ept_data_list(void) -{ - int i; - - printk("Dumping the whole R_USB_EPT_DATA list\n"); - - for (i = 0; i < 32; i++) { - __dump_ept_data(i); - } -} -#ifdef USB_DEBUG_DESC -#define dump_in_desc(...) __dump_in_desc(...) -#define dump_sb_desc(...) __dump_sb_desc(...) -#define dump_ep_desc(...) __dump_ep_desc(...) -#else -#define dump_in_desc(...) do {} while (0) -#define dump_sb_desc(...) do {} while (0) -#define dump_ep_desc(...) do {} while (0) -#endif - -#ifdef USB_DEBUG_URB -#define dump_urb(x) __dump_urb(x) -#else -#define dump_urb(x) do {} while (0) -#endif - -static void init_rx_buffers(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) { - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = 0; - RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - - /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc - for the relevant fields.) */ - prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]); - - } - - RxDescList[i].sw_len = RX_DESC_BUF_SIZE; - RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes); - RxDescList[i].next = virt_to_phys(&RxDescList[0]); - RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE)); - RxDescList[i].hw_len = 0; - RxDescList[i].status = 0; - - myNextRxDesc = &RxDescList[0]; - myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1]; - - *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc); - *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start); - - DBFEXIT; -} - -static void init_tx_bulk_ep(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxBulkEPList[i]); - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]); - - /* Initiate two EPs, disabled and with the eol flag set. No need for any - preserved epid. */ - - /* The first one has the intr flag set so we get an interrupt when the DMA - channel is about to become disabled. */ - CHECK_ALIGN(&TxBulkDummyEPList[i][0]); - TxBulkDummyEPList[i][0].hw_len = 0; - TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | - IO_STATE(USB_EP_command, eol, yes) | - IO_STATE(USB_EP_command, intr, yes)); - TxBulkDummyEPList[i][0].sub = 0; - TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]); - - /* The second one. */ - CHECK_ALIGN(&TxBulkDummyEPList[i][1]); - TxBulkDummyEPList[i][1].hw_len = 0; - TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) | - IO_STATE(USB_EP_command, eol, yes)); - TxBulkDummyEPList[i][1].sub = 0; - /* The last dummy's next pointer is the same as the current EP's next pointer. */ - TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]); - } - - /* Configure the last one. */ - CHECK_ALIGN(&TxBulkEPList[i]); - TxBulkEPList[i].hw_len = 0; - TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i)); - TxBulkEPList[i].sub = 0; - TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]); - - /* No need configuring dummy EPs for the last one as it will never be used for - bulk traffic (i == INVALD_EPID at this point). */ - - /* Set up to start on the last EP so we will enable it when inserting traffic - for the first time (imitating the situation where the DMA has stopped - because there was no more traffic). */ - *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]); - /* No point in starting the bulk channel yet. - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */ - DBFEXIT; -} - -static void init_tx_ctrl_ep(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxCtrlEPList[i]); - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]); - } - - CHECK_ALIGN(&TxCtrlEPList[i]); - TxCtrlEPList[i].hw_len = 0; - TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, i)); - - TxCtrlEPList[i].sub = 0; - TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]); - - *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]); - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - - DBFEXIT; -} - - -static void init_tx_intr_ep(void) -{ - int i; - - DBFENTER; - - /* Read comment at zout_buffer declaration for an explanation to this. */ - TxIntrSB_zout.sw_len = 1; - TxIntrSB_zout.next = 0; - TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]); - TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) { - CHECK_ALIGN(&TxIntrEPList[i]); - TxIntrEPList[i].hw_len = 0; - TxIntrEPList[i].command = - (IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]); - } - - CHECK_ALIGN(&TxIntrEPList[i]); - TxIntrEPList[i].hw_len = 0; - TxIntrEPList[i].command = - (IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, eol, yes) | - IO_STATE(USB_EP_command, enable, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout); - TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]); - - *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]); - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); - DBFEXIT; -} - -static void init_tx_isoc_ep(void) -{ - int i; - - DBFENTER; - - /* Read comment at zout_buffer declaration for an explanation to this. */ - TxIsocSB_zout.sw_len = 1; - TxIsocSB_zout.next = 0; - TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]); - TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - /* The last isochronous EP descriptor is a dummy. */ - - for (i = 0; i < (NBR_OF_EPIDS - 1); i++) { - CHECK_ALIGN(&TxIsocEPList[i]); - TxIsocEPList[i].hw_len = 0; - TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i); - TxIsocEPList[i].sub = 0; - TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]); - } - - CHECK_ALIGN(&TxIsocEPList[i]); - TxIsocEPList[i].hw_len = 0; - - /* Must enable the last EP descr to get eof interrupt. */ - TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) | - IO_STATE(USB_EP_command, eof, yes) | - IO_STATE(USB_EP_command, eol, yes) | - IO_FIELD(USB_EP_command, epid, INVALID_EPID)); - TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout); - TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]); - - *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]); - *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_unlink_intr_urb(struct urb *urb) -{ - volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */ - volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */ - volatile USB_EP_Desc_t *next_ep; /* The EP after current. */ - volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */ - - int epid; - - /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */ - - DBFENTER; - - epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid; - - first_ep = &TxIntrEPList[0]; - curr_ep = first_ep; - - - /* Note that this loop removes all EP descriptors with this epid. This assumes - that all EP descriptors belong to the one and only urb for this epid. */ - - do { - next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next); - - if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) { - - dbg_intr("Found EP to unlink for epid %d", epid); - - /* This is the one we should unlink. */ - unlink_ep = next_ep; - - /* Actually unlink the EP from the DMA list. */ - curr_ep->next = unlink_ep->next; - - /* Wait until the DMA is no longer at this descriptor. */ - while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep)); - - /* Now we are free to remove it and its SB descriptor. - Note that it is assumed here that there is only one sb in the - sb list for this ep. */ - kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub)); - kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep); - } - - curr_ep = phys_to_virt(curr_ep->next); - - } while (curr_ep != first_ep); - urb->hcpriv = NULL; -} - -void etrax_usb_do_intr_recover(int epid) -{ - USB_EP_Desc_t *first_ep, *tmp_ep; - - DBFENTER; - - first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP); - tmp_ep = first_ep; - - /* What this does is simply to walk the list of interrupt - ep descriptors and enable those that are disabled. */ - - do { - if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid && - !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) { - tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes); - } - - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); - - } while (tmp_ep != first_ep); - - - DBFEXIT; -} - -static int etrax_rh_unlink_urb (struct urb *urb) -{ - etrax_hc_t *hc; - - DBFENTER; - - hc = urb->dev->bus->hcpriv; - - if (hc->rh.urb == urb) { - hc->rh.send = 0; - del_timer(&hc->rh.rh_int_timer); - } - - DBFEXIT; - return 0; -} - -static void etrax_rh_send_irq(struct urb *urb) -{ - __u16 data = 0; - etrax_hc_t *hc = urb->dev->bus->hcpriv; - DBFENTER; - -/* - dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER); - dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING); -*/ - - data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0; - data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0; - - *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data); - /* FIXME: Why is actual_length set to 1 when data is 2 bytes? - Since only 1 byte is used, why not declare data as __u8? */ - urb->actual_length = 1; - urb->status = 0; - - if (hc->rh.send && urb->complete) { - dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1); - dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2); - - urb->complete(urb, NULL); - } - - DBFEXIT; -} - -static void etrax_rh_init_int_timer(struct urb *urb) -{ - etrax_hc_t *hc; - - DBFENTER; - - hc = urb->dev->bus->hcpriv; - hc->rh.interval = urb->interval; - init_timer(&hc->rh.rh_int_timer); - hc->rh.rh_int_timer.function = etrax_rh_int_timer_do; - hc->rh.rh_int_timer.data = (unsigned long)urb; - /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped - to 0, and the rest to the nearest lower 10 ms. */ - hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000); - add_timer(&hc->rh.rh_int_timer); - - DBFEXIT; -} - -static void etrax_rh_int_timer_do(unsigned long ptr) -{ - struct urb *urb; - etrax_hc_t *hc; - - DBFENTER; - - urb = (struct urb*)ptr; - hc = urb->dev->bus->hcpriv; - - if (hc->rh.send) { - etrax_rh_send_irq(urb); - } - - DBFEXIT; -} - -static int etrax_usb_setup_epid(struct urb *urb) -{ - int epid; - char devnum, endpoint, out_traffic, slow; - int maxlen; - unsigned long flags; - - DBFENTER; - - epid = etrax_usb_lookup_epid(urb); - if ((epid != -1)){ - /* An epid that fits this urb has been found. */ - DBFEXIT; - return epid; - } - - /* We must find and initiate a new epid for this urb. */ - epid = etrax_usb_allocate_epid(); - - if (epid == -1) { - /* Failed to allocate a new epid. */ - DBFEXIT; - return epid; - } - - /* We now have a new epid to use. Initiate it. */ - set_bit(epid, (void *)&epid_usage_bitmask); - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ - out_traffic = 1; - } else { - out_traffic = usb_pipeout(urb->pipe); - } - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) | - /* FIXME: Change any to the actual port? */ - IO_STATE(R_USB_EPT_DATA_ISO, port, any) | - IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) | - IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) | - IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum); - } else { - *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) | - IO_FIELD(R_USB_EPT_DATA, low_speed, slow) | - /* FIXME: Change any to the actual port? */ - IO_STATE(R_USB_EPT_DATA, port, any) | - IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) | - IO_FIELD(R_USB_EPT_DATA, ep, endpoint) | - IO_FIELD(R_USB_EPT_DATA, dev, devnum); - } - - restore_flags(flags); - - if (out_traffic) { - set_bit(epid, (void *)&epid_out_traffic); - } else { - clear_bit(epid, (void *)&epid_out_traffic); - } - - dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)", - epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN"); - - DBFEXIT; - return epid; -} - -static void etrax_usb_free_epid(int epid) -{ - unsigned long flags; - - DBFENTER; - - if (!test_bit(epid, (void *)&epid_usage_bitmask)) { - warn("Trying to free unused epid %d", epid); - DBFEXIT; - return; - } - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)); - /* This will, among other things, set the valid field to 0. */ - *R_USB_EPT_DATA = 0; - restore_flags(flags); - - clear_bit(epid, (void *)&epid_usage_bitmask); - - - dbg_epid("Freed epid %d", epid); - - DBFEXIT; -} - -static int etrax_usb_lookup_epid(struct urb *urb) -{ - int i; - __u32 data; - char devnum, endpoint, slow, out_traffic; - int maxlen; - unsigned long flags; - - DBFENTER; - - devnum = usb_pipedevice(urb->pipe); - endpoint = usb_pipeendpoint(urb->pipe); - slow = usb_pipeslow(urb->pipe); - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - /* We want both IN and OUT control traffic to be put on the same EP/SB list. */ - out_traffic = 1; - } else { - out_traffic = usb_pipeout(urb->pipe); - } - - /* Step through att epids. */ - for (i = 0; i < NBR_OF_EPIDS; i++) { - if (test_bit(i, (void *)&epid_usage_bitmask) && - test_bit(i, (void *)&epid_out_traffic) == out_traffic) { - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i); - nop(); - - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - data = *R_USB_EPT_DATA_ISO; - restore_flags(flags); - - if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) { - dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", - i, devnum, endpoint, out_traffic ? "OUT" : "IN"); - DBFEXIT; - return i; - } - } else { - data = *R_USB_EPT_DATA; - restore_flags(flags); - - if ((IO_MASK(R_USB_EPT_DATA, valid) & data) && - (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) && - (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) && - (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) && - (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) { - dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)", - i, devnum, endpoint, out_traffic ? "OUT" : "IN"); - DBFEXIT; - return i; - } - } - } - } - - DBFEXIT; - return -1; -} - -static int etrax_usb_allocate_epid(void) -{ - int i; - - DBFENTER; - - for (i = 0; i < NBR_OF_EPIDS; i++) { - if (!test_bit(i, (void *)&epid_usage_bitmask)) { - dbg_epid("Found free epid %d", i); - DBFEXIT; - return i; - } - } - - dbg_epid("Found no free epids"); - DBFEXIT; - return -1; -} - -static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags) -{ - etrax_hc_t *hc; - int ret = -EINVAL; - - DBFENTER; - - if (!urb->dev || !urb->dev->bus) { - return -ENODEV; - } - if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) { - info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe); - return -EMSGSIZE; - } - - if (urb->timeout) { - /* FIXME. */ - warn("urb->timeout specified, ignoring."); - } - - hc = (etrax_hc_t*)urb->dev->bus->hcpriv; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - /* This request is for the Virtual Root Hub. */ - ret = etrax_rh_submit_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - ret = etrax_usb_submit_bulk_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - ret = etrax_usb_submit_ctrl_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - int bustime; - - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - } else { - ret = etrax_usb_submit_intr_urb(urb); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { - /* Bandwidth already set. */ - ret = etrax_usb_submit_intr_urb(urb); - } - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - int bustime; - - if (urb->bandwidth == 0) { - bustime = usb_check_bandwidth(urb->dev, urb); - if (bustime < 0) { - ret = bustime; - } else { - ret = etrax_usb_submit_isoc_urb(urb); - if (ret == 0) - usb_claim_bandwidth(urb->dev, urb, bustime, 0); - } - } else { - /* Bandwidth already set. */ - ret = etrax_usb_submit_isoc_urb(urb); - } - } - - DBFEXIT; - - if (ret != 0) - printk("Submit URB error %d\n", ret); - - return ret; -} - -static int etrax_usb_unlink_urb(struct urb *urb, int status) -{ - etrax_hc_t *hc; - etrax_urb_priv_t *urb_priv; - int epid; - unsigned int flags; - - DBFENTER; - - if (!urb) { - return -EINVAL; - } - - /* Disable interrupts here since a descriptor interrupt for the isoc epid - will modify the sb list. This could possibly be done more granular, but - unlink_urb should not be used frequently anyway. - */ - - save_flags(flags); - cli(); - - if (!urb->dev || !urb->dev->bus) { - restore_flags(flags); - return -ENODEV; - } - if (!urb->hcpriv) { - /* This happens if a device driver calls unlink on an urb that - was never submitted (lazy driver) or if the urb was completed - while unlink was being called. */ - restore_flags(flags); - return 0; - } - if (urb->transfer_flags & URB_ASYNC_UNLINK) { - /* FIXME. */ - /* If URB_ASYNC_UNLINK is set: - unlink - move to a separate urb list - call complete at next sof with ECONNRESET - - If not: - wait 1 ms - unlink - call complete with ENOENT - */ - warn("URB_ASYNC_UNLINK set, ignoring."); - } - - /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking, - but that doesn't work for interrupt and isochronous traffic since they are completed - repeatedly, and urb->status is set then. That may in itself be a bug though. */ - - hc = urb->dev->bus->hcpriv; - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - epid = urb_priv->epid; - - /* Set the urb status (synchronous unlink). */ - urb->status = -ENOENT; - urb_priv->urb_state = UNLINK; - - if (usb_pipedevice(urb->pipe) == hc->rh.devnum) { - int ret; - ret = etrax_rh_unlink_urb(urb); - DBFEXIT; - restore_flags(flags); - return ret; - - } else if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb); - - if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid])); - } - /* Kicking dummy list out of the party. */ - TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb); - - if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid])); - } - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - - dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb); - - /* Separate function because it's a tad more complicated. */ - etrax_usb_unlink_intr_urb(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb); - - if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); - } - } - - /* Note that we need to remove the urb from the urb list *before* removing its SB - descriptors. (This means that the isoc eof handler might get a null urb when we - are unlinking the last urb.) */ - - if (usb_pipetype(urb->pipe) == PIPE_BULK) { - - urb_list_del(urb, epid); - TxBulkEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - - urb_list_del(urb, epid); - TxCtrlEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - - } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) { - - urb_list_del(urb, epid); - /* Sanity check (should never happen). */ - assert(urb_list_empty(epid)); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - if (usb_pipeout(urb->pipe)) { - - USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb; - - if (__urb_list_entry(urb, epid)) { - - urb_list_del(urb, epid); - iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; - prev_sb = 0; - while (iter_sb && (iter_sb != urb_priv->first_sb)) { - prev_sb = iter_sb; - iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } - - if (iter_sb == 0) { - /* Unlink of the URB currently being transmitted. */ - prev_sb = 0; - iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0; - } - - while (iter_sb && (iter_sb != urb_priv->last_sb)) { - iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } - if (iter_sb) { - next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0; - } else { - /* This should only happen if the DMA has completed - processing the SB list for this EP while interrupts - are disabled. */ - dbg_isoc("Isoc urb not found, already sent?"); - next_sb = 0; - } - if (prev_sb) { - prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0; - } else { - TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0; - } - - etrax_remove_from_sb_list(urb); - if (urb_list_empty(epid)) { - TxIsocEPList[epid].sub = 0; - dbg_isoc("Last isoc out urb epid %d", epid); - } else if (next_sb || prev_sb) { - dbg_isoc("Re-enable isoc out epid %d", epid); - - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - } else { - TxIsocEPList[epid].sub = 0; - dbg_isoc("URB list non-empty and no SB list, EP disabled"); - } - } else { - dbg_isoc("Urb 0x%p not found, completed already?", urb); - } - } else { - - urb_list_del(urb, epid); - - /* For in traffic there is only one SB descriptor for each EP even - though there may be several urbs (all urbs point at the same SB). */ - if (urb_list_empty(epid)) { - /* No more urbs, remove the SB. */ - TxIsocEPList[epid].sub = 0; - etrax_remove_from_sb_list(urb); - } else { - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - } - } - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 1); - } - /* Free the epid if urb list is empty. */ - if (urb_list_empty(epid)) { - etrax_usb_free_epid(epid); - } - restore_flags(flags); - - /* Must be done before calling completion handler. */ - kfree(urb_priv); - urb->hcpriv = 0; - - if (urb->complete) { - urb->complete(urb, NULL); - } - - DBFEXIT; - return 0; -} - -static int etrax_usb_get_frame_number(struct usb_device *usb_dev) -{ - DBFENTER; - DBFEXIT; - return (*R_USB_FM_NUMBER & 0x7ff); -} - -static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc) -{ - DBFENTER; - - /* This interrupt handler could be used when unlinking EP descriptors. */ - - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) { - USB_EP_Desc_t *ep; - - //dbg_bulk("dma8_sub0_descr (BULK) intr."); - - /* It should be safe clearing the interrupt here, since we don't expect to get a new - one until we restart the bulk channel. */ - *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do); - - /* Wait while the DMA is running (though we don't expect it to be). */ - while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd)); - - /* Advance the DMA to the next EP descriptor. */ - ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); - - //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep); - - /* ep->next is already a physical address; no need for a virt_to_phys. */ - *R_DMA_CH8_SUB0_EP = ep->next; - - /* Start the DMA bulk channel again. */ - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) { - struct urb *urb; - int epid; - etrax_urb_priv_t *urb_priv; - unsigned long int flags; - - dbg_ctrl("dma8_sub1_descr (CTRL) intr."); - *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do); - - /* The complete callback gets called so we cli. */ - save_flags(flags); - cli(); - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - if ((TxCtrlEPList[epid].sub == 0) || - (epid == DUMMY_EPID) || - (epid == INVALID_EPID)) { - /* Nothing here to see. */ - continue; - } - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - - if (urb) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_CONTROL); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) { - assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - - etrax_usb_complete_urb(urb, 0); - } - } - } - restore_flags(flags); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) { - dbg_intr("dma8_sub2_descr (INTR) intr."); - *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do); - } - if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) { - struct urb *urb; - int epid; - int epid_done; - etrax_urb_priv_t *urb_priv; - USB_SB_Desc_t *sb_desc; - - usb_isoc_complete_data_t *comp_data = NULL; - - /* One or more isoc out transfers are done. */ - dbg_isoc("dma8_sub3_descr (ISOC) intr."); - - /* For each isoc out EP search for the first sb_desc with the intr flag - set. This descriptor must be the last packet from an URB. Then - traverse the URB list for the EP until the URB with urb_priv->last_sb - matching the intr-marked sb_desc is found. All URBs before this have - been sent. - */ - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - /* Skip past epids with no SB lists, epids used for in traffic, - and special (dummy, invalid) epids. */ - if ((TxIsocEPList[epid].sub == 0) || - (test_bit(epid, (void *)&epid_out_traffic) == 0) || - (epid == DUMMY_EPID) || - (epid == INVALID_EPID)) { - /* Nothing here to see. */ - continue; - } - sb_desc = phys_to_virt(TxIsocEPList[epid].sub); - - /* Find the last descriptor of the currently active URB for this ep. - This is the first descriptor in the sub list marked for a descriptor - interrupt. */ - while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) { - sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0; - } - assert(sb_desc); - - dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p", - epid, - phys_to_virt(TxIsocEPList[epid].sub), - sb_desc); - - epid_done = 0; - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - assert(urb); - - while (urb && !epid_done) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); - - if (!usb_pipeout(urb->pipe)) { - /* descr interrupts are generated only for out pipes. */ - epid_done = 1; - continue; - } - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (sb_desc != urb_priv->last_sb) { - - /* This urb has been sent. */ - dbg_isoc("out URB 0x%p sent", urb); - - urb_priv->urb_state = TRANSFER_DONE; - - } else if ((sb_desc == urb_priv->last_sb) && - !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - - assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes)); - assert(sb_desc->next == 0); - - dbg_isoc("out URB 0x%p last in list, epid disabled", urb); - TxIsocEPList[epid].sub = 0; - TxIsocEPList[epid].hw_len = 0; - urb_priv->urb_state = TRANSFER_DONE; - - epid_done = 1; - - } else { - epid_done = 1; - } - if (!epid_done) { - urb = urb_list_next(urb, epid); - } - } - - } - - *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do); - - comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC); - assert(comp_data != NULL); - - INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data); - schedule_work(&comp_data->usb_bh); - } - - DBFEXIT; - return IRQ_HANDLED; -} - -static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data) -{ - usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data; - - struct urb *urb; - int epid; - int epid_done; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - dbg_isoc("dma8_sub3_descr (ISOC) bottom half."); - - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - unsigned long flags; - - save_flags(flags); - cli(); - - epid_done = 0; - - /* The descriptor interrupt handler has marked all transmitted isoch. out - URBs with TRANSFER_DONE. Now we traverse all epids and for all that - have isoch. out traffic traverse its URB list and complete the - transmitted URB. - */ - - while (!epid_done) { - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - if (urb == 0) { - epid_done = 1; - continue; - } - - if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) { - epid_done = 1; - continue; - } - - if (!usb_pipeout(urb->pipe)) { - /* descr interrupts are generated only for out pipes. */ - epid_done = 1; - continue; - } - - dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == TRANSFER_DONE) { - int i; - struct usb_iso_packet_descriptor *packet; - - /* This urb has been sent. */ - dbg_isoc("Completing isoc out URB 0x%p", urb); - - for (i = 0; i < urb->number_of_packets; i++) { - packet = &urb->iso_frame_desc[i]; - packet->status = 0; - packet->actual_length = packet->length; - } - - etrax_usb_complete_isoc_urb(urb, 0); - - if (urb_list_empty(epid)) { - etrax_usb_free_epid(epid); - epid_done = 1; - } - } else { - epid_done = 1; - } - } - restore_flags(flags); - - } - kmem_cache_free(isoc_compl_cache, comp_data); - - DBFEXIT; -} - - - -static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc) -{ - struct urb *urb; - etrax_urb_priv_t *urb_priv; - int epid = 0; - unsigned long flags; - - /* Isoc diagnostics. */ - static int curr_fm = 0; - static int prev_fm = 0; - - DBFENTER; - - /* Clear this interrupt. */ - *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do); - - /* Note that this while loop assumes that all packets span only - one rx descriptor. */ - - /* The reason we cli here is that we call the driver's callback functions. */ - save_flags(flags); - cli(); - - while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) { - - epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status); - urb = urb_list_first(epid); - - //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb); - - if (!urb) { - err("No urb for epid %d in rx interrupt", epid); - __dump_ept_data(epid); - goto skip_out; - } - - /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since - ctrl pipes are not. */ - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) { - __u32 r_usb_ept_data; - int no_error = 0; - - assert(test_bit(epid, (void *)&epid_usage_bitmask)); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - r_usb_ept_data = *R_USB_EPT_DATA_ISO; - - if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) && - (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) && - (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) { - /* Not an error, just a failure to receive an expected iso - in packet in this frame. This is not documented - in the designers reference. - */ - no_error++; - } else { - warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data); - } - } else { - r_usb_ept_data = *R_USB_EPT_DATA; - warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data); - } - - if (!no_error){ - warn("error in rx desc->status, epid %d, first urb = 0x%lx", - epid, (unsigned long)urb); - __dump_in_desc(myNextRxDesc); - - warn("R_USB_STATUS = 0x%x", *R_USB_STATUS); - - /* Check that ept was disabled when error occurred. */ - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_CONTROL: - assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_INTERRUPT: - assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - case PIPE_ISOCHRONOUS: - assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))); - break; - default: - warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p", - usb_pipetype(urb->pipe), - urb); - } - etrax_usb_complete_urb(urb, -EPROTO); - goto skip_out; - } - } - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if ((usb_pipetype(urb->pipe) == PIPE_BULK) || - (usb_pipetype(urb->pipe) == PIPE_CONTROL) || - (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { - /* We get nodata for empty data transactions, and the rx descriptor's - hw_len field is not valid in that case. No data to copy in other - words. */ - } else { - /* Make sure the data fits in the buffer. */ - assert(urb_priv->rx_offset + myNextRxDesc->hw_len - <= urb->transfer_buffer_length); - - memcpy(urb->transfer_buffer + urb_priv->rx_offset, - phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len); - urb_priv->rx_offset += myNextRxDesc->hw_len; - } - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) { - if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) && - ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) == - IO_STATE(USB_EP_command, enable, yes))) { - /* The EP is still enabled, so the OUT packet used to ack - the in data is probably not processed yet. If the EP - sub pointer has not moved beyond urb_priv->last_sb mark - it for a descriptor interrupt and complete the urb in - the descriptor interrupt handler. - */ - USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0; - - while ((sub != NULL) && (sub != urb_priv->last_sb)) { - sub = sub->next ? phys_to_virt(sub->next) : 0; - } - if (sub != NULL) { - /* The urb has not been fully processed. */ - urb_priv->urb_state = WAITING_FOR_DESCR_INTR; - } else { - warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub); - etrax_usb_complete_urb(urb, 0); - } - } else { - etrax_usb_complete_urb(urb, 0); - } - } - - } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - - struct usb_iso_packet_descriptor *packet; - - if (urb_priv->urb_state == UNLINK) { - info("Ignoring rx data for urb being unlinked."); - goto skip_out; - } else if (urb_priv->urb_state == NOT_STARTED) { - info("What? Got rx data for urb that isn't started?"); - goto skip_out; - } - - packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter]; - packet->status = 0; - - if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) { - /* We get nodata for empty data transactions, and the rx descriptor's - hw_len field is not valid in that case. We copy 0 bytes however to - stay in synch. */ - packet->actual_length = 0; - } else { - packet->actual_length = myNextRxDesc->hw_len; - /* Make sure the data fits in the buffer. */ - assert(packet->actual_length <= packet->length); - memcpy(urb->transfer_buffer + packet->offset, - phys_to_virt(myNextRxDesc->buf), packet->actual_length); - } - - /* Increment the packet counter. */ - urb_priv->isoc_packet_counter++; - - /* Note that we don't care about the eot field in the rx descriptor's status. - It will always be set for isoc traffic. */ - if (urb->number_of_packets == urb_priv->isoc_packet_counter) { - - /* Out-of-synch diagnostics. */ - curr_fm = (*R_USB_FM_NUMBER & 0x7ff); - if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) { - /* This test is wrong, if there is more than one isoc - in endpoint active it will always calculate wrong - since prev_fm is shared by all endpoints. - - FIXME Make this check per URB using urb->start_frame. - */ - dbg_isoc("Out of synch? Previous frame = %d, current frame = %d", - prev_fm, curr_fm); - - } - prev_fm = curr_fm; - - /* Complete the urb with status OK. */ - etrax_usb_complete_isoc_urb(urb, 0); - } - } - - skip_out: - - /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr - has the same layout as USB_IN_Desc for the relevant fields.) */ - prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc); - - myPrevRxDesc = myNextRxDesc; - myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol); - myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol); - myLastRxDesc = myPrevRxDesc; - - myNextRxDesc->status = 0; - myNextRxDesc = phys_to_virt(myNextRxDesc->next); - } - - restore_flags(flags); - - DBFEXIT; - - return IRQ_HANDLED; -} - - -/* This function will unlink the SB descriptors associated with this urb. */ -static int etrax_remove_from_sb_list(struct urb *urb) -{ - USB_SB_Desc_t *next_sb, *first_sb, *last_sb; - etrax_urb_priv_t *urb_priv; - int i = 0; - - DBFENTER; - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor - doesn't really need to be disabled, it's just that we expect it to be. */ - if (usb_pipetype(urb->pipe) == PIPE_BULK) { - assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) { - assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable))); - } - - first_sb = urb_priv->first_sb; - last_sb = urb_priv->last_sb; - - assert(first_sb); - assert(last_sb); - - while (first_sb != last_sb) { - next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next); - kmem_cache_free(usb_desc_cache, first_sb); - first_sb = next_sb; - i++; - } - kmem_cache_free(usb_desc_cache, last_sb); - i++; - dbg_sb("%d SB descriptors freed", i); - /* Compare i with urb->number_of_packets for Isoc traffic. - Should be same when calling unlink_urb */ - - DBFEXIT; - - return i; -} - -static int etrax_usb_submit_bulk_urb(struct urb *urb) -{ - int epid; - int empty; - unsigned long flags; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - /* Epid allocation, empty check and list add must be protected. - Read about this in etrax_usb_submit_ctrl_urb. */ - - spin_lock_irqsave(&urb_list_lock, flags); - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - spin_unlock_irqrestore(&urb_list_lock, flags); - return -ENOMEM; - } - empty = urb_list_empty(epid); - urb_list_add(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", - usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid); - - /* Mark the urb as being in progress. */ - urb->status = -EINPROGRESS; - - /* Setup the hcpriv data. */ - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - /* This sets rx_offset to 0. */ - urb_priv->urb_state = NOT_STARTED; - urb->hcpriv = urb_priv; - - if (empty) { - etrax_usb_add_to_bulk_sb_list(urb, epid); - } - - DBFEXIT; - - return 0; -} - -static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid) -{ - USB_SB_Desc_t *sb_desc; - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - unsigned long flags; - char maxlen; - - DBFENTER; - - dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb); - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - - sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc != NULL); - - - if (usb_pipeout(urb->pipe)) { - - dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid); - - /* This is probably a sanity check of the bulk transaction length - not being larger than 64 kB. */ - if (urb->transfer_buffer_length > 0xffff) { - panic("urb->transfer_buffer_length > 0xffff"); - } - - sb_desc->sw_len = urb->transfer_buffer_length; - - /* The rem field is don't care if it's not a full-length transfer, so setting - it shouldn't hurt. Also, rem isn't used for OUT traffic. */ - sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - /* The full field is set to yes, even if we don't actually check that this is - a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0). - Setting full prevents the USB controller from sending an empty packet in - that case. However, if URB_ZERO_PACKET was set we want that. */ - if (!(urb->transfer_flags & URB_ZERO_PACKET)) { - sb_desc->command |= IO_STATE(USB_SB_command, full, yes); - } - - sb_desc->buf = virt_to_phys(urb->transfer_buffer); - sb_desc->next = 0; - - } else if (usb_pipein(urb->pipe)) { - - dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid); - - sb_desc->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - - /* The rem field is don't care if it's not a full-length transfer, so setting - it shouldn't hurt. */ - sb_desc->command = - (IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc->buf = 0; - sb_desc->next = 0; - } - - urb_priv->first_sb = sb_desc; - urb_priv->last_sb = sb_desc; - urb_priv->epid = epid; - - urb->hcpriv = urb_priv; - - /* Reset toggle bits and reset error count. */ - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - /* FIXME: Is this a special case since the hold field is checked, - or should we check hold in a lot of other cases as well? */ - if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s", __FUNCTION__); - } - - /* Reset error counters (regardless of which direction this traffic is). */ - *R_USB_EPT_DATA &= - ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | - IO_MASK(R_USB_EPT_DATA, error_count_out)); - - /* Software must preset the toggle bits. */ - if (usb_pipeout(urb->pipe)) { - char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out); - *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle); - } else { - char toggle = - usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); - *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in); - *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle); - } - - /* Assert that the EP descriptor is disabled. */ - assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))); - - /* The reason we set the EP's sub pointer directly instead of - walking the SB list and linking it last in the list is that we only - have one active urb at a time (the rest are queued). */ - - /* Note that we cannot have interrupts running when we have set the SB descriptor - but the EP is not yet enabled. If a bulk eot happens for another EP, we will - find this EP disabled and with a SB != 0, which will make us think that it's done. */ - TxBulkEPList[epid].sub = virt_to_phys(sb_desc); - TxBulkEPList[epid].hw_len = 0; - /* Note that we don't have to fill in the ep_id field since this - was done when we allocated the EP descriptors in init_tx_bulk_ep. */ - - /* Check if the dummy list is already with us (if several urbs were queued). */ - if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) { - - dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d", - (unsigned long)urb, epid); - - /* The last EP in the dummy list already has its next pointer set to - TxBulkEPList[epid].next. */ - - /* We don't need to check if the DMA is at this EP or not before changing the - next pointer, since we will do it in one 32-bit write (EP descriptors are - 32-bit aligned). */ - TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]); - } - /* Enable the EP descr. */ - dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid); - TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* Everything is set up, safe to enable interrupts again. */ - restore_flags(flags); - - /* If the DMA bulk channel isn't running, we need to restart it if it - has stopped at the last EP descriptor (DMA stopped because there was - no more traffic) or if it has stopped at a dummy EP with the intr flag - set (DMA stopped because we were too slow in inserting new traffic). */ - if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { - - USB_EP_Desc_t *ep; - ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP); - dbg_bulk("DMA channel not running in add"); - dbg_bulk("DMA is at 0x%lx", (unsigned long)ep); - - if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) || - (ep->command & 0x8) >> 3) { - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - /* Update/restart the bulk start timer since we just started the channel. */ - mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); - /* Update/restart the bulk eot timer since we just inserted traffic. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - } - } - - DBFEXIT; -} - -static void etrax_usb_complete_bulk_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - unsigned long flags; - - DBFENTER; - - if (status) - warn("Completing bulk urb with status %d.", status); - - dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid); - - /* Update the urb list. */ - urb_list_del(urb, epid); - - /* For an IN pipe, we always set the actual length, regardless of whether there was - an error or not (which means the device driver can use the data if it wants to). */ - if (usb_pipein(urb->pipe)) { - urb->actual_length = urb_priv->rx_offset; - } else { - /* Set actual_length for OUT urbs also; the USB mass storage driver seems - to want that. We wouldn't know of any partial writes if there was an error. */ - if (status == 0) { - urb->actual_length = urb->transfer_buffer_length; - } else { - urb->actual_length = 0; - } - } - - /* FIXME: Is there something of the things below we shouldn't do if there was an error? - Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */ - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - - /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */ - if (usb_pipeout(urb->pipe)) { - char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } else { - char toggle = - IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA); - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), - usb_pipeout(urb->pipe), toggle); - } - restore_flags(flags); - - /* Remember to free the SBs. */ - etrax_remove_from_sb_list(urb); - kfree(urb_priv); - urb->hcpriv = 0; - - /* If there are any more urb's in the list we'd better start sending */ - if (!urb_list_empty(epid)) { - - struct urb *new_urb; - - /* Get the first urb. */ - new_urb = urb_list_first(epid); - assert(new_urb); - - dbg_bulk("More bulk for epid %d", epid); - - etrax_usb_add_to_bulk_sb_list(new_urb, epid); - } - - urb->status = status; - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (usb_pipein(urb->pipe) && - (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { - urb->status = -EREMOTEIO; - } - } - } - - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (urb_list_empty(epid)) { - /* This means that this EP is now free, deconfigure it. */ - etrax_usb_free_epid(epid); - - /* No more traffic; time to clean up. - Must set sub pointer to 0, since we look at the sub pointer when handling - the bulk eot interrupt. */ - - dbg_bulk("No bulk for epid %d", epid); - - TxBulkEPList[epid].sub = 0; - - /* Unlink the dummy list. */ - - dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d", - (unsigned long)urb, epid); - - /* No need to wait for the DMA before changing the next pointer. - The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use - the last one (INVALID_EPID) for actual traffic. */ - TxBulkEPList[epid].next = - virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]); - } - - DBFEXIT; -} - -static int etrax_usb_submit_ctrl_urb(struct urb *urb) -{ - int epid; - int empty; - unsigned long flags; - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */ - - /* Epid allocation, empty check and list add must be protected. - - Epid allocation because if we find an existing epid for this endpoint an urb might be - completed (emptying the list) before we add the new urb to the list, causing the epid - to be de-allocated. We would then start the transfer with an invalid epid -> epid attn. - - Empty check and add because otherwise we might conclude that the list is not empty, - after which it becomes empty before we add the new urb to the list, causing us not to - insert the new traffic into the SB list. */ - - spin_lock_irqsave(&urb_list_lock, flags); - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - spin_unlock_irqrestore(&urb_list_lock, flags); - DBFEXIT; - return -ENOMEM; - } - empty = urb_list_empty(epid); - urb_list_add(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", - (unsigned long)urb, empty ? "empty" : "", epid); - - /* Mark the urb as being in progress. */ - urb->status = -EINPROGRESS; - - /* Setup the hcpriv data. */ - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - /* This sets rx_offset to 0. */ - urb_priv->urb_state = NOT_STARTED; - urb->hcpriv = urb_priv; - - if (empty) { - etrax_usb_add_to_ctrl_sb_list(urb, epid); - } - - DBFEXIT; - - return 0; -} - -static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid) -{ - USB_SB_Desc_t *sb_desc_setup; - USB_SB_Desc_t *sb_desc_data; - USB_SB_Desc_t *sb_desc_status; - - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - - unsigned long flags; - char maxlen; - - DBFENTER; - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - - sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_setup != NULL); - sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_status != NULL); - - /* Initialize the mandatory setup SB descriptor (used only in control transfers) */ - sb_desc_setup->sw_len = 8; - sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, setup) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes)); - - sb_desc_setup->buf = virt_to_phys(urb->setup_packet); - - if (usb_pipeout(urb->pipe)) { - dbg_ctrl("Transfer for epid %d is OUT", epid); - - /* If this Control OUT transfer has an optional data stage we add an OUT token - before the mandatory IN (status) token, hence the reordered SB list */ - - sb_desc_setup->next = virt_to_phys(sb_desc_status); - if (urb->transfer_buffer) { - - dbg_ctrl("This OUT transfer has an extra data stage"); - - sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_data != NULL); - - sb_desc_setup->next = virt_to_phys(sb_desc_data); - - sb_desc_data->sw_len = urb->transfer_buffer_length; - sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes)); - sb_desc_data->buf = virt_to_phys(urb->transfer_buffer); - sb_desc_data->next = virt_to_phys(sb_desc_status); - } - - sb_desc_status->sw_len = 1; - sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc_status->buf = 0; - sb_desc_status->next = 0; - - } else if (usb_pipein(urb->pipe)) { - - dbg_ctrl("Transfer for epid %d is IN", epid); - dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length); - dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen); - - sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(sb_desc_data != NULL); - - sb_desc_setup->next = virt_to_phys(sb_desc_data); - - sb_desc_data->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - dbg_ctrl("sw_len got %d", sb_desc_data->sw_len); - - sb_desc_data->command = - (IO_FIELD(USB_SB_command, rem, - urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes)); - - sb_desc_data->buf = 0; - sb_desc_data->next = virt_to_phys(sb_desc_status); - - /* Read comment at zout_buffer declaration for an explanation to this. */ - sb_desc_status->sw_len = 1; - sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, full, yes) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - sb_desc_status->buf = virt_to_phys(&zout_buffer[0]); - sb_desc_status->next = 0; - } - - urb_priv->first_sb = sb_desc_setup; - urb_priv->last_sb = sb_desc_status; - urb_priv->epid = epid; - - urb_priv->urb_state = STARTED; - - /* Reset toggle bits and reset error count, remember to di and ei */ - /* Warning: it is possible that this locking doesn't work with bottom-halves */ - - save_flags(flags); - cli(); - - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) { - panic("Hold was set in %s", __FUNCTION__); - } - - - /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits - are set to a specific value. Why the difference? Read "Transfer and Toggle Bits - in Designer's Reference, p. 8 - 11. */ - *R_USB_EPT_DATA &= - ~(IO_MASK(R_USB_EPT_DATA, error_count_in) | - IO_MASK(R_USB_EPT_DATA, error_count_out) | - IO_MASK(R_USB_EPT_DATA, t_in) | - IO_MASK(R_USB_EPT_DATA, t_out)); - - /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now - (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */ - restore_flags(flags); - - /* Assert that the EP descriptor is disabled. */ - assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))); - - /* Set up and enable the EP descriptor. */ - TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup); - TxCtrlEPList[epid].hw_len = 0; - TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - - DBFENTER; - - if (status) - warn("Completing ctrl urb with status %d.", status); - - dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb); - - /* Remove this urb from the list. */ - urb_list_del(urb, epid); - - /* For an IN pipe, we always set the actual length, regardless of whether there was - an error or not (which means the device driver can use the data if it wants to). */ - if (usb_pipein(urb->pipe)) { - urb->actual_length = urb_priv->rx_offset; - } - - /* FIXME: Is there something of the things below we shouldn't do if there was an error? - Like, maybe we shouldn't insert more traffic. */ - - /* Remember to free the SBs. */ - etrax_remove_from_sb_list(urb); - kfree(urb_priv); - urb->hcpriv = 0; - - /* If there are any more urbs in the list we'd better start sending. */ - if (!urb_list_empty(epid)) { - struct urb *new_urb; - - /* Get the first urb. */ - new_urb = urb_list_first(epid); - assert(new_urb); - - dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb); - - etrax_usb_add_to_ctrl_sb_list(new_urb, epid); - } - - urb->status = status; - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (usb_pipein(urb->pipe) && - (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) { - urb->status = -EREMOTEIO; - } - } - } - - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (urb_list_empty(epid)) { - /* No more traffic. Time to clean up. */ - etrax_usb_free_epid(epid); - /* Must set sub pointer to 0. */ - dbg_ctrl("No ctrl for epid %d", epid); - TxCtrlEPList[epid].sub = 0; - } - - DBFEXIT; -} - -static int etrax_usb_submit_intr_urb(struct urb *urb) -{ - - int epid; - - DBFENTER; - - if (usb_pipeout(urb->pipe)) { - /* Unsupported transfer type. - We don't support interrupt out traffic. (If we do, we can't support - intervals for neither in or out traffic, but are forced to schedule all - interrupt traffic in one frame.) */ - return -EINVAL; - } - - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - return -ENOMEM; - } - - if (!urb_list_empty(epid)) { - /* There is already a queued urb for this endpoint. */ - etrax_usb_free_epid(epid); - return -ENXIO; - } - - urb->status = -EINPROGRESS; - - dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid); - - urb_list_add(urb, epid); - etrax_usb_add_to_intr_sb_list(urb, epid); - - return 0; - - DBFEXIT; -} - -static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid) -{ - - volatile USB_EP_Desc_t *tmp_ep; - volatile USB_EP_Desc_t *first_ep; - - char maxlen; - int interval; - int i; - - etrax_urb_priv_t *urb_priv; - - DBFENTER; - - maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); - interval = urb->interval; - - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG); - assert(urb_priv != NULL); - urb->hcpriv = urb_priv; - - first_ep = &TxIntrEPList[0]; - - /* Round of the interval to 2^n, it is obvious that this code favours - smaller numbers, but that is actually a good thing */ - /* FIXME: The "rounding error" for larger intervals will be quite - large. For in traffic this shouldn't be a problem since it will only - mean that we "poll" more often. */ - for (i = 0; interval; i++) { - interval = interval >> 1; - } - interval = 1 << (i - 1); - - dbg_intr("Interval rounded to %d", interval); - - tmp_ep = first_ep; - i = 0; - do { - if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) { - if ((i % interval) == 0) { - /* Insert the traffic ep after tmp_ep */ - USB_EP_Desc_t *ep_desc; - USB_SB_Desc_t *sb_desc; - - dbg_intr("Inserting EP for epid %d", epid); - - ep_desc = (USB_EP_Desc_t *) - kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - sb_desc = (USB_SB_Desc_t *) - kmem_cache_alloc(usb_desc_cache, SLAB_FLAG); - assert(ep_desc != NULL); - CHECK_ALIGN(ep_desc); - assert(sb_desc != NULL); - - ep_desc->sub = virt_to_phys(sb_desc); - ep_desc->hw_len = 0; - ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) | - IO_STATE(USB_EP_command, enable, yes)); - - - /* Round upwards the number of packets of size maxlen - that this SB descriptor should receive. */ - sb_desc->sw_len = urb->transfer_buffer_length ? - (urb->transfer_buffer_length - 1) / maxlen + 1 : 0; - sb_desc->next = 0; - sb_desc->buf = 0; - sb_desc->command = - (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) | - IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - ep_desc->next = tmp_ep->next; - tmp_ep->next = virt_to_phys(ep_desc); - } - i++; - } - tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next); - } while (tmp_ep != first_ep); - - - /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */ - urb_priv->epid = epid; - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start); - - DBFEXIT; -} - - - -static void etrax_usb_complete_intr_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - - DBFENTER; - - if (status) - warn("Completing intr urb with status %d.", status); - - dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb); - - urb->status = status; - urb->actual_length = urb_priv->rx_offset; - - dbg_intr("interrupt urb->actual_length = %d", urb->actual_length); - - /* We let any non-zero status from the layer above have precedence. */ - if (status == 0) { - /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length) - is to be treated as an error. */ - if (urb->transfer_flags & URB_SHORT_NOT_OK) { - if (urb->actual_length != - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { - urb->status = -EREMOTEIO; - } - } - } - - /* The driver will resubmit the URB so we need to remove it first */ - etrax_usb_unlink_urb(urb, 0); - if (urb->complete) { - urb->complete(urb, NULL); - } - - DBFEXIT; -} - - -static int etrax_usb_submit_isoc_urb(struct urb *urb) -{ - int epid; - unsigned long flags; - - DBFENTER; - - dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb); - - /* Epid allocation, empty check and list add must be protected. - Read about this in etrax_usb_submit_ctrl_urb. */ - - spin_lock_irqsave(&urb_list_lock, flags); - /* Is there an active epid for this urb ? */ - epid = etrax_usb_setup_epid(urb); - if (epid == -1) { - DBFEXIT; - spin_unlock_irqrestore(&urb_list_lock, flags); - return -ENOMEM; - } - - /* Ok, now we got valid endpoint, lets insert some traffic */ - - urb->status = -EINPROGRESS; - - /* Find the last urb in the URB_List and add this urb after that one. - Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This - is important to make this in "real time" since isochronous traffic is - time sensitive. */ - - dbg_isoc("Adding isoc urb to (possibly empty) list"); - urb_list_add(urb, epid); - etrax_usb_add_to_isoc_sb_list(urb, epid); - spin_unlock_irqrestore(&urb_list_lock, flags); - - DBFEXIT; - - return 0; -} - -static void etrax_usb_check_error_isoc_ep(const int epid) -{ - unsigned long int flags; - int error_code; - __u32 r_usb_ept_data; - - /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof, - bulk_eot and epid_attn interrupts. So we just check the status of - the epid without testing if for it in R_USB_EPID_ATTN. */ - - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO - registers, they are located at the same address and are of the same size. - In other words, this read should be ok for isoc also. */ - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data); - - if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Hold was set for epid %d.", epid); - return; - } - - if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) { - - /* This indicates that the SB list of the ept was completed before - new data was appended to it. This is not an error, but indicates - large system or USB load and could possibly cause trouble for - very timing sensitive USB device drivers so we log it. - */ - info("Isoc. epid %d disabled with no error", epid); - return; - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) { - /* Not really a protocol error, just says that the endpoint gave - a stall response. Note that error_code cannot be stall for isoc. */ - panic("Isoc traffic cannot stall"); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) { - /* Two devices responded to a transaction request. Must be resolved - by software. FIXME: Reset ports? */ - panic("Bus error for epid %d." - " Two devices responded to transaction request", - epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - /* DMA overrun or underrun. */ - warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - - /* It seems that error_code = buffer_error in - R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS - are the same error. */ - } -} - - -static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid) -{ - - int i = 0; - - etrax_urb_priv_t *urb_priv; - USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc; - - DBFENTER; - - prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; - - urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC); - assert(urb_priv != NULL); - - urb->hcpriv = urb_priv; - urb_priv->epid = epid; - - if (usb_pipeout(urb->pipe)) { - - if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n"); - - dbg_isoc("Transfer for epid %d is OUT", epid); - dbg_isoc("%d packets in URB", urb->number_of_packets); - - /* Create one SB descriptor for each packet and link them together. */ - for (i = 0; i < urb->number_of_packets; i++) { - if (!urb->iso_frame_desc[i].length) - continue; - - next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC); - assert(next_sb_desc != NULL); - - if (urb->iso_frame_desc[i].length > 0) { - - next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) | - IO_STATE(USB_SB_command, eot, yes)); - - next_sb_desc->sw_len = urb->iso_frame_desc[i].length; - next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset); - - /* Check if full length transfer. */ - if (urb->iso_frame_desc[i].length == - usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) { - next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes); - } - } else { - dbg_isoc("zero len packet"); - next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) | - IO_STATE(USB_SB_command, tt, zout) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, full, yes)); - - next_sb_desc->sw_len = 1; - next_sb_desc->buf = virt_to_phys(&zout_buffer[0]); - } - - /* First SB descriptor that belongs to this urb */ - if (i == 0) - urb_priv->first_sb = next_sb_desc; - else - prev_sb_desc->next = virt_to_phys(next_sb_desc); - - prev_sb_desc = next_sb_desc; - } - - next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) | - IO_STATE(USB_SB_command, eol, yes)); - next_sb_desc->next = 0; - urb_priv->last_sb = next_sb_desc; - - } else if (usb_pipein(urb->pipe)) { - - dbg_isoc("Transfer for epid %d is IN", epid); - dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length); - dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length); - - /* Note that in descriptors for periodic traffic are not consumed. This means that - the USB controller never propagates in the SB list. In other words, if there already - is an SB descriptor in the list for this EP we don't have to do anything. */ - if (TxIsocEPList[epid].sub == 0) { - dbg_isoc("Isoc traffic not already running, allocating SB"); - - next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC); - assert(next_sb_desc != NULL); - - next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) | - IO_STATE(USB_SB_command, eot, yes) | - IO_STATE(USB_SB_command, eol, yes)); - - next_sb_desc->next = 0; - next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant - for periodic in traffic as long as it is more - than zero. Set to 1 always. */ - next_sb_desc->buf = 0; - - /* The rem field is don't care for isoc traffic, so we don't set it. */ - - /* Only one SB descriptor that belongs to this urb. */ - urb_priv->first_sb = next_sb_desc; - urb_priv->last_sb = next_sb_desc; - - } else { - - dbg_isoc("Isoc traffic already running, just setting first/last_sb"); - - /* Each EP for isoc in will have only one SB descriptor, setup when submitting the - already active urb. Note that even though we may have several first_sb/last_sb - pointing at the same SB descriptor, they are freed only once (when the list has - become empty). */ - urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub); - urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub); - return; - } - - } - - /* Find the spot to insert this urb and add it. */ - if (TxIsocEPList[epid].sub == 0) { - /* First SB descriptor inserted in this list (in or out). */ - dbg_isoc("Inserting SB desc first in list"); - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); - - } else { - /* Isochronous traffic is already running, insert new traffic last (only out). */ - dbg_isoc("Inserting SB desc last in list"); - temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub); - while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) != - IO_STATE(USB_SB_command, eol, yes)) { - assert(temp_sb_desc->next); - temp_sb_desc = phys_to_virt(temp_sb_desc->next); - } - dbg_isoc("Appending list on desc 0x%p", temp_sb_desc); - - /* Next pointer must be set before eol is removed. */ - temp_sb_desc->next = virt_to_phys(urb_priv->first_sb); - /* Clear the previous end of list flag since there is a new in the - added SB descriptor list. */ - temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol); - - if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) { - /* 8.8.5 in Designer's Reference says we should check for and correct - any errors in the EP here. That should not be necessary if epid_attn - is handled correctly, so we assume all is ok. */ - dbg_isoc("EP disabled"); - etrax_usb_check_error_isoc_ep(epid); - - /* The SB list was exhausted. */ - if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) { - /* The new sublist did not get processed before the EP was - disabled. Setup the EP again. */ - dbg_isoc("Set EP sub to new list"); - TxIsocEPList[epid].hw_len = 0; - TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb); - } - } - } - - if (urb->transfer_flags & URB_ISO_ASAP) { - /* The isoc transfer should be started as soon as possible. The start_frame - field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER - with a USB Chief trace shows that the first isoc IN token is sent 2 frames - later. I'm not sure how this affects usage of the start_frame field by the - device driver, or how it affects things when USB_ISO_ASAP is not set, so - therefore there's no compensation for the 2 frame "lag" here. */ - urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff); - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - urb_priv->urb_state = STARTED; - dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame); - } else { - /* Not started yet. */ - urb_priv->urb_state = NOT_STARTED; - dbg_isoc("urb_priv->urb_state set to NOT_STARTED"); - } - - /* We start the DMA sub channel without checking if it's running or not, because: - 1) If it's already running, issuing the start command is a nop. - 2) We avoid a test-and-set race condition. */ - *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start); - - DBFEXIT; -} - -static void etrax_usb_complete_isoc_urb(struct urb *urb, int status) -{ - etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - int epid = urb_priv->epid; - int auto_resubmit = 0; - - DBFENTER; - dbg_isoc("complete urb 0x%p, status %d", urb, status); - - if (status) - warn("Completing isoc urb with status %d.", status); - - if (usb_pipein(urb->pipe)) { - int i; - - /* Make that all isoc packets have status and length set before - completing the urb. */ - for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) { - urb->iso_frame_desc[i].actual_length = 0; - urb->iso_frame_desc[i].status = -EPROTO; - } - - urb_list_del(urb, epid); - - if (!list_empty(&urb_list[epid])) { - ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED; - } else { - unsigned long int flags; - if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - /* The EP was enabled, disable it and wait. */ - TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable); - - /* Ah, the luxury of busy-wait. */ - while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid])); - } - - etrax_remove_from_sb_list(urb); - TxIsocEPList[epid].sub = 0; - TxIsocEPList[epid].hw_len = 0; - - save_flags(flags); - cli(); - etrax_usb_free_epid(epid); - restore_flags(flags); - } - - urb->hcpriv = 0; - kfree(urb_priv); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - } else if (usb_pipeout(urb->pipe)) { - int freed_descr; - - dbg_isoc("Isoc out urb complete 0x%p", urb); - - /* Update the urb list. */ - urb_list_del(urb, epid); - - freed_descr = etrax_remove_from_sb_list(urb); - dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets); - assert(freed_descr == urb->number_of_packets); - urb->hcpriv = 0; - kfree(urb_priv); - - /* Release allocated bandwidth. */ - usb_release_bandwidth(urb->dev, urb, 0); - } - - urb->status = status; - if (urb->complete) { - urb->complete(urb, NULL); - } - - if (auto_resubmit) { - /* Check that urb was not unlinked by the complete callback. */ - if (__urb_list_entry(urb, epid)) { - /* Move this one down the list. */ - urb_list_move_last(urb, epid); - - /* Mark the now first urb as started (may already be). */ - ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED; - - /* Must set this to 0 since this urb is still active after - completion. */ - urb_priv->isoc_packet_counter = 0; - } else { - warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb); - } - } - - DBFEXIT; -} - -static void etrax_usb_complete_urb(struct urb *urb, int status) -{ - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - etrax_usb_complete_bulk_urb(urb, status); - break; - case PIPE_CONTROL: - etrax_usb_complete_ctrl_urb(urb, status); - break; - case PIPE_INTERRUPT: - etrax_usb_complete_intr_urb(urb, status); - break; - case PIPE_ISOCHRONOUS: - etrax_usb_complete_isoc_urb(urb, status); - break; - default: - err("Unknown pipetype"); - } -} - - - -static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc) -{ - usb_interrupt_registers_t *reg; - unsigned long flags; - __u32 irq_mask; - __u8 status; - __u32 epid_attn; - __u16 port_status_1; - __u16 port_status_2; - __u32 fm_number; - - DBFENTER; - - /* Read critical registers into local variables, do kmalloc afterwards. */ - save_flags(flags); - cli(); - - irq_mask = *R_USB_IRQ_MASK_READ; - /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS - must be read before R_USB_EPID_ATTN since reading the latter clears the - ourun and perror fields of R_USB_STATUS. */ - status = *R_USB_STATUS; - - /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */ - epid_attn = *R_USB_EPID_ATTN; - - /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the - port_status interrupt. */ - port_status_1 = *R_USB_RH_PORT_STATUS_1; - port_status_2 = *R_USB_RH_PORT_STATUS_2; - - /* Reading R_USB_FM_NUMBER clears the sof interrupt. */ - /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */ - fm_number = *R_USB_FM_NUMBER; - - restore_flags(flags); - - reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC); - - assert(reg != NULL); - - reg->hc = (etrax_hc_t *)vhc; - - /* Now put register values into kmalloc'd area. */ - reg->r_usb_irq_mask_read = irq_mask; - reg->r_usb_status = status; - reg->r_usb_epid_attn = epid_attn; - reg->r_usb_rh_port_status_1 = port_status_1; - reg->r_usb_rh_port_status_2 = port_status_2; - reg->r_usb_fm_number = fm_number; - - INIT_WORK(®->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg); - schedule_work(®->usb_bh); - - DBFEXIT; - - return IRQ_HANDLED; -} - -static void etrax_usb_hc_interrupt_bottom_half(void *data) -{ - usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data; - __u32 irq_mask = reg->r_usb_irq_mask_read; - - DBFENTER; - - /* Interrupts are handled in order of priority. */ - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) { - etrax_usb_hc_epid_attn_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) { - etrax_usb_hc_port_status_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) { - etrax_usb_hc_ctl_status_interrupt(reg); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) { - etrax_usb_hc_isoc_eof_interrupt(); - } - if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) { - /* Update/restart the bulk start timer since obviously the channel is running. */ - mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL); - /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - - etrax_usb_hc_bulk_eot_interrupt(0); - } - - kmem_cache_free(top_half_reg_cache, reg); - - DBFEXIT; -} - - -void etrax_usb_hc_isoc_eof_interrupt(void) -{ - struct urb *urb; - etrax_urb_priv_t *urb_priv; - int epid; - unsigned long flags; - - DBFENTER; - - /* Do not check the invalid epid (it has a valid sub pointer). */ - for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) { - - /* Do not check the invalid epid (it has a valid sub pointer). */ - if ((epid == DUMMY_EPID) || (epid == INVALID_EPID)) - continue; - - /* Disable interrupts to block the isoc out descriptor interrupt handler - from being called while the isoc EPID list is being checked. - */ - save_flags(flags); - cli(); - - if (TxIsocEPList[epid].sub == 0) { - /* Nothing here to see. */ - restore_flags(flags); - continue; - } - - /* Get the first urb (if any). */ - urb = urb_list_first(epid); - if (urb == 0) { - warn("Ignoring NULL urb"); - restore_flags(flags); - continue; - } - if (usb_pipein(urb->pipe)) { - - /* Sanity check. */ - assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS); - - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - if (urb_priv->urb_state == NOT_STARTED) { - - /* If ASAP is not set and urb->start_frame is the current frame, - start the transfer. */ - if (!(urb->transfer_flags & URB_ISO_ASAP) && - (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) { - - dbg_isoc("Enabling isoc IN EP descr for epid %d", epid); - TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes); - - /* This urb is now active. */ - urb_priv->urb_state = STARTED; - continue; - } - } - } - restore_flags(flags); - } - - DBFEXIT; - -} - -void etrax_usb_hc_bulk_eot_interrupt(int timer_induced) -{ - int epid; - - /* The technique is to run one urb at a time, wait for the eot interrupt at which - point the EP descriptor has been disabled. */ - - DBFENTER; - dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : ""); - - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - - if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) && - (TxBulkEPList[epid].sub != 0)) { - - struct urb *urb; - etrax_urb_priv_t *urb_priv; - unsigned long flags; - __u32 r_usb_ept_data; - - /* Found a disabled EP descriptor which has a non-null sub pointer. - Verify that this ctrl EP descriptor got disabled no errors. - FIXME: Necessary to check error_code? */ - dbg_bulk("for epid %d?", epid); - - /* Get the first urb. */ - urb = urb_list_first(epid); - - /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of - wrong unlinking? */ - if (!urb) { - warn("NULL urb for epid %d", epid); - continue; - } - - assert(urb); - urb_priv = (etrax_urb_priv_t *)urb->hcpriv; - assert(urb_priv); - - /* Sanity checks. */ - assert(usb_pipetype(urb->pipe) == PIPE_BULK); - if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) { - err("bulk endpoint got disabled before reaching last sb"); - } - - /* For bulk IN traffic, there seems to be a race condition between - between the bulk eot and eop interrupts, or rather an uncertainty regarding - the order in which they happen. Normally we expect the eop interrupt from - DMA channel 9 to happen before the eot interrupt. - - Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */ - - if (usb_pipein(urb->pipe)) { - dbg_bulk("in urb, continuing"); - continue; - } - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) == - IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - /* This means that the endpoint has no error, is disabled - and had inserted traffic, i.e. transfer successfully completed. */ - etrax_usb_complete_bulk_urb(urb, 0); - } else { - /* Shouldn't happen. We expect errors to be caught by epid attention. */ - err("Found disabled bulk EP desc, error_code != no_error"); - } - } - } - - /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer. - However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may - not. Also, we might find two disabled EPs when handling an eot interrupt, and then find - none the next time. */ - - DBFEXIT; - -} - -void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg) -{ - /* This function handles the epid attention interrupt. There are a variety of reasons - for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details): - - invalid ep_id - Invalid epid in an EP (EP disabled). - stall - Not strictly an error condition (EP disabled). - 3rd error - Three successive transaction errors (EP disabled). - buffer ourun - Buffer overrun or underrun (EP disabled). - past eof1 - Intr or isoc transaction proceeds past EOF1. - near eof - Intr or isoc transaction would not fit inside the frame. - zout transfer - If zout transfer for a bulk endpoint (EP disabled). - setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */ - - int epid; - - - DBFENTER; - - assert(reg != NULL); - - /* Note that we loop through all epids. We still want to catch errors for - the invalid one, even though we might handle them differently. */ - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - - if (test_bit(epid, (void *)®->r_usb_epid_attn)) { - - struct urb *urb; - __u32 r_usb_ept_data; - unsigned long flags; - int error_code; - - save_flags(flags); - cli(); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); - nop(); - /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO - registers, they are located at the same address and are of the same size. - In other words, this read should be ok for isoc also. */ - r_usb_ept_data = *R_USB_EPT_DATA; - restore_flags(flags); - - /* First some sanity checks. */ - if (epid == INVALID_EPID) { - /* FIXME: What if it became disabled? Could seriously hurt interrupt - traffic. (Use do_intr_recover.) */ - warn("Got epid_attn for INVALID_EPID (%d).", epid); - err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); - err("R_USB_STATUS = 0x%x", reg->r_usb_status); - continue; - } else if (epid == DUMMY_EPID) { - /* We definitely don't care about these ones. Besides, they are - always disabled, so any possible disabling caused by the - epid attention interrupt is irrelevant. */ - warn("Got epid_attn for DUMMY_EPID (%d).", epid); - continue; - } - - /* Get the first urb in the urb list for this epid. We blatantly assume - that only the first urb could have caused the epid attention. - (For bulk and ctrl, only one urb is active at any one time. For intr - and isoc we remove them once they are completed.) */ - urb = urb_list_first(epid); - - if (urb == NULL) { - err("Got epid_attn for epid %i with no urb.", epid); - err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data); - err("R_USB_STATUS = 0x%x", reg->r_usb_status); - continue; - } - - switch (usb_pipetype(urb->pipe)) { - case PIPE_BULK: - warn("Got epid attn for bulk endpoint, epid %d", epid); - break; - case PIPE_CONTROL: - warn("Got epid attn for control endpoint, epid %d", epid); - break; - case PIPE_INTERRUPT: - warn("Got epid attn for interrupt endpoint, epid %d", epid); - break; - case PIPE_ISOCHRONOUS: - warn("Got epid attn for isochronous endpoint, epid %d", epid); - break; - } - - if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) { - if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) { - warn("Hold was set for epid %d.", epid); - continue; - } - } - - /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and - R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data); - } else { - error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data); - } - - /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */ - if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) { - - /* Isoc traffic doesn't have error_count_in/error_count_out. */ - if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) && - (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 || - IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) { - /* 3rd error. */ - warn("3rd error for epid %i", epid); - etrax_usb_complete_urb(urb, -EPROTO); - - } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - - warn("Perror for epid %d", epid); - - if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) { - /* invalid ep_id */ - panic("Perror because of invalid epid." - " Deconfigured too early?"); - } else { - /* past eof1, near eof, zout transfer, setup transfer */ - - /* Dump the urb and the relevant EP descriptor list. */ - - __dump_urb(urb); - __dump_ept_data(epid); - __dump_ep_list(usb_pipetype(urb->pipe)); - - panic("Something wrong with DMA descriptor contents." - " Too much traffic inserted?"); - } - } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - /* buffer ourun */ - panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - } - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) { - /* Not really a protocol error, just says that the endpoint gave - a stall response. Note that error_code cannot be stall for isoc. */ - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { - panic("Isoc traffic cannot stall"); - } - - warn("Stall for epid %d", epid); - etrax_usb_complete_urb(urb, -EPIPE); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) { - /* Two devices responded to a transaction request. Must be resolved - by software. FIXME: Reset ports? */ - panic("Bus error for epid %d." - " Two devices responded to transaction request", - epid); - - } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) { - /* DMA overrun or underrun. */ - warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid); - - /* It seems that error_code = buffer_error in - R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS - are the same error. */ - etrax_usb_complete_urb(urb, -EPROTO); - } - } - } - - DBFEXIT; - -} - -void etrax_usb_bulk_start_timer_func(unsigned long dummy) -{ - - /* We might enable an EP descriptor behind the current DMA position when it's about - to decide that there are no more bulk traffic and it should stop the bulk channel. - Therefore we periodically check if the bulk channel is stopped and there is an - enabled bulk EP descriptor, in which case we start the bulk channel. */ - dbg_bulk("bulk_start_timer timed out."); - - if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) { - int epid; - - dbg_bulk("Bulk DMA channel not running."); - - for (epid = 0; epid < NBR_OF_EPIDS; epid++) { - if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) { - dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n", - epid); - *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); - - /* Restart the bulk eot timer since we just started the bulk channel. */ - mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL); - - /* No need to search any further. */ - break; - } - } - } else { - dbg_bulk("Bulk DMA channel running."); - } -} - -void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg) -{ - etrax_hc_t *hc = reg->hc; - __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2; - - DBFENTER; - - /* The Etrax RH does not include a wPortChange register, so this has to be handled in software - (by saving the old port status value for comparison when the port status interrupt happens). - See section 11.16.2.6.2 in the USB 1.1 spec for details. */ - - dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1); - dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2); - dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1); - dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2); - - /* C_PORT_CONNECTION is set on any transition. */ - hc->rh.wPortChange_1 |= - ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; - - hc->rh.wPortChange_2 |= - ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) != - (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ? - (1 << RH_PORT_CONNECTION) : 0; - - /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when - the port is disabled, not when it's enabled. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ? - (1 << RH_PORT_ENABLE) : 0; - - /* C_PORT_SUSPEND is set to one when the device has transitioned out - of the suspended state, i.e. when suspend goes from one to zero. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ? - (1 << RH_PORT_SUSPEND) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ? - (1 << RH_PORT_SUSPEND) : 0; - - - /* C_PORT_RESET is set when reset processing on this port is complete. */ - hc->rh.wPortChange_1 |= - ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET)) - && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ? - (1 << RH_PORT_RESET) : 0; - - hc->rh.wPortChange_2 |= - ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET)) - && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ? - (1 << RH_PORT_RESET) : 0; - - /* Save the new values for next port status change. */ - hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1; - hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2; - - dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1); - dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2); - - DBFEXIT; - -} - -void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg) -{ - DBFENTER; - - /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB - list for the corresponding epid? */ - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) { - panic("USB controller got ourun."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) { - - /* Before, etrax_usb_do_intr_recover was called on this epid if it was - an interrupt pipe. I don't see how re-enabling all EP descriptors - will help if there was a programming error. */ - panic("USB controller got perror."); - } - - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) { - /* We should never operate in device mode. */ - panic("USB controller in device mode."); - } - - /* These if-statements could probably be nested. */ - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) { - info("USB controller in host mode."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) { - info("USB controller started."); - } - if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) { - info("USB controller running."); - } - - DBFEXIT; - -} - - -static int etrax_rh_submit_urb(struct urb *urb) -{ - struct usb_device *usb_dev = urb->dev; - etrax_hc_t *hc = usb_dev->bus->hcpriv; - unsigned int pipe = urb->pipe; - struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet; - void *data = urb->transfer_buffer; - int leni = urb->transfer_buffer_length; - int len = 0; - int stat = 0; - - __u16 bmRType_bReq; - __u16 wValue; - __u16 wIndex; - __u16 wLength; - - DBFENTER; - - /* FIXME: What is this interrupt urb that is sent to the root hub? */ - if (usb_pipetype (pipe) == PIPE_INTERRUPT) { - dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval); - hc->rh.urb = urb; - hc->rh.send = 1; - /* FIXME: We could probably remove this line since it's done - in etrax_rh_init_int_timer. (Don't remove it from - etrax_rh_init_int_timer though.) */ - hc->rh.interval = urb->interval; - etrax_rh_init_int_timer(urb); - DBFEXIT; - - return 0; - } - - bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8); - wValue = le16_to_cpu(cmd->wValue); - wIndex = le16_to_cpu(cmd->wIndex); - wLength = le16_to_cpu(cmd->wLength); - - dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq); - dbg_rh("wValue : 0x%04x (%d)", wValue, wValue); - dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex); - dbg_rh("wLength : 0x%04x (%d)", wLength, wLength); - - switch (bmRType_bReq) { - - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - *(__u16 *) data = cpu_to_le16 (1); - OK (2); - - case RH_GET_STATUS | RH_INTERFACE: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_ENDPOINT: - *(__u16 *) data = cpu_to_le16 (0); - OK (2); - - case RH_GET_STATUS | RH_CLASS: - *(__u32 *) data = cpu_to_le32 (0); - OK (4); /* hub power ** */ - - case RH_GET_STATUS | RH_OTHER | RH_CLASS: - if (wIndex == 1) { - *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1); - *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1); - } else if (wIndex == 2) { - *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2); - *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2); - } else { - dbg_rh("RH_GET_STATUS whith invalid wIndex!"); - OK(0); - } - - OK(4); - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - OK (0); - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - case (RH_C_HUB_OVER_CURRENT): - OK (0); /* hub power over current ** */ - } - break; - - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - if (wIndex == 1) { - - dbg_rh("trying to do disable port 1"); - - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes); - - while (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes)); - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); - dbg_rh("Port 1 is disabled"); - - } else if (wIndex == 2) { - - dbg_rh("trying to do disable port 2"); - - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes); - - while (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes)); - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); - dbg_rh("Port 2 is disabled"); - - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_SUSPEND): - /* Opposite to suspend should be resume, so we'll do a resume. */ - /* FIXME: USB 1.1, 11.16.2.2 says: - "Clearing the PORT_SUSPEND feature causes a host-initiated resume - on the specified port. If the port is not in the Suspended state, - the hub should treat this request as a functional no-operation." - Shouldn't we check if the port is in a suspended state before - resuming? */ - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - if (wIndex == 1) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, resume) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else if (wIndex == 2) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, resume) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_C_PORT_CONNECTION): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_C_PORT_ENABLE): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE " - "with invalid wIndex == %d!", wIndex); - } - OK (0); - case (RH_C_PORT_SUSPEND): -/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ - OK (0); - case (RH_C_PORT_OVER_CURRENT): - OK (0); /* port power over current ** */ - case (RH_C_PORT_RESET): - if (wIndex == 1) { - hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET); - } else if (wIndex == 2) { - hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET); - } else { - dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET " - "with invalid index == %d!", wIndex); - } - - OK (0); - - } - break; - - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - if (wIndex == 1) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, suspend) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else if (wIndex == 2) { - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, suspend) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - } else { - dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND " - "with invalid wIndex == %d!", wIndex); - } - - OK (0); - case (RH_PORT_RESET): - if (wIndex == 1) { - - port_1_reset: - dbg_rh("Doing reset of port 1"); - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port1) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - - /* We must wait at least 10 ms for the device to recover. - 15 ms should be enough. */ - udelay(15000); - - /* Wait for reset bit to go low (should be done by now). */ - while (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes)); - - /* If the port status is - 1) connected and enabled then there is a device and everything is fine - 2) neither connected nor enabled then there is no device, also fine - 3) connected and not enabled then we try again - (Yes, there are other port status combinations besides these.) */ - - if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { - dbg_rh("Connected device on port 1, but port not enabled?" - " Trying reset again."); - goto port_2_reset; - } - - /* Diagnostic printouts. */ - if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) { - dbg_rh("No connected device on port 1"); - } else if ((hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) && - (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) { - dbg_rh("Connected device on port 1, port 1 enabled"); - } - - } else if (wIndex == 2) { - - port_2_reset: - dbg_rh("Doing reset of port 2"); - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Issue the reset command. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, port2) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, nop); - - /* We must wait at least 10 ms for the device to recover. - 15 ms should be enough. */ - udelay(15000); - - /* Wait for reset bit to go low (should be done by now). */ - while (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes)); - - /* If the port status is - 1) connected and enabled then there is a device and everything is fine - 2) neither connected nor enabled then there is no device, also fine - 3) connected and not enabled then we try again - (Yes, there are other port status combinations besides these.) */ - - if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { - dbg_rh("Connected device on port 2, but port not enabled?" - " Trying reset again."); - goto port_2_reset; - } - - /* Diagnostic printouts. */ - if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) { - dbg_rh("No connected device on port 2"); - } else if ((hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) && - (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) { - dbg_rh("Connected device on port 2, port 2 enabled"); - } - - } else { - dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex); - } - - /* Make sure the controller isn't busy. */ - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* If all enabled ports were disabled the host controller goes down into - started mode, so we need to bring it back into the running state. - (This is safe even if it's already in the running state.) */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - dbg_rh("...Done"); - OK(0); - - case (RH_PORT_POWER): - OK (0); /* port power ** */ - case (RH_PORT_ENABLE): - /* There is no port enable command in the host controller, so if the - port is already enabled, we do nothing. If not, we reset the port - (with an ugly goto). */ - - if (wIndex == 1) { - if (hc->rh.prev_wPortStatus_1 & - IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) { - goto port_1_reset; - } - } else if (wIndex == 2) { - if (hc->rh.prev_wPortStatus_2 & - IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) { - goto port_2_reset; - } - } else { - dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex); - } - OK (0); - } - break; - - case RH_SET_ADDRESS: - hc->rh.devnum = wValue; - dbg_rh("RH address set to: %d", hc->rh.devnum); - OK (0); - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength)); - memcpy (data, root_hub_dev_des, len); - OK (len); - case (0x02): /* configuration descriptor */ - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength)); - memcpy (data, root_hub_config_des, len); - OK (len); - case (0x03): /* string descriptors */ - len = usb_root_hub_string (wValue & 0xff, - 0xff, "ETRAX 100LX", - data, wLength); - if (len > 0) { - OK(min(leni, len)); - } else { - stat = -EPIPE; - } - - } - break; - - case RH_GET_DESCRIPTOR | RH_CLASS: - root_hub_hub_des[2] = hc->rh.numports; - len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength)); - memcpy (data, root_hub_hub_des, len); - OK (len); - - case RH_GET_CONFIGURATION: - *(__u8 *) data = 0x01; - OK (1); - - case RH_SET_CONFIGURATION: - OK (0); - - default: - stat = -EPIPE; - } - - urb->actual_length = len; - urb->status = stat; - urb->dev = NULL; - if (urb->complete) { - urb->complete(urb, NULL); - } - DBFEXIT; - - return 0; -} - -static void -etrax_usb_bulk_eot_timer_func(unsigned long dummy) -{ - /* Because of a race condition in the top half, we might miss a bulk eot. - This timer "simulates" a bulk eot if we don't get one for a while, hopefully - correcting the situation. */ - dbg_bulk("bulk_eot_timer timed out."); - etrax_usb_hc_bulk_eot_interrupt(1); -} - -static void* -etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size, - unsigned mem_flags, dma_addr_t *dma) -{ - return kmalloc(size, mem_flags); -} - -static void -etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma) -{ - kfree(addr); -} - - -static struct device fake_device; - -static int __init etrax_usb_hc_init(void) -{ - static etrax_hc_t *hc; - struct usb_bus *bus; - struct usb_device *usb_rh; - int i; - - DBFENTER; - - info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version); - - hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL); - assert(hc != NULL); - - /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */ - /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate - SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) == - sizeof(USB_SB_Desc_t). */ - - usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, - SLAB_HWCACHE_ALIGN, 0, 0); - assert(usb_desc_cache != NULL); - - top_half_reg_cache = kmem_cache_create("top_half_reg_cache", - sizeof(usb_interrupt_registers_t), - 0, SLAB_HWCACHE_ALIGN, 0, 0); - assert(top_half_reg_cache != NULL); - - isoc_compl_cache = kmem_cache_create("isoc_compl_cache", - sizeof(usb_isoc_complete_data_t), - 0, SLAB_HWCACHE_ALIGN, 0, 0); - assert(isoc_compl_cache != NULL); - - etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations); - hc->bus = bus; - bus->bus_name="ETRAX 100LX"; - bus->hcpriv = hc; - - /* Initialize RH to the default address. - And make sure that we have no status change indication */ - hc->rh.numports = 2; /* The RH has two ports */ - hc->rh.devnum = 1; - hc->rh.wPortChange_1 = 0; - hc->rh.wPortChange_2 = 0; - - /* Also initate the previous values to zero */ - hc->rh.prev_wPortStatus_1 = 0; - hc->rh.prev_wPortStatus_2 = 0; - - /* Initialize the intr-traffic flags */ - /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */ - hc->intr.sleeping = 0; - hc->intr.wq = NULL; - - epid_usage_bitmask = 0; - epid_out_traffic = 0; - - /* Mark the invalid epid as being used. */ - set_bit(INVALID_EPID, (void *)&epid_usage_bitmask); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID); - nop(); - /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */ - *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) | - IO_FIELD(R_USB_EPT_DATA, max_len, 1)); - - /* Mark the dummy epid as being used. */ - set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask); - *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID); - nop(); - *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) | - IO_FIELD(R_USB_EPT_DATA, max_len, 1)); - - /* Initialize the urb list by initiating a head for each list. */ - for (i = 0; i < NBR_OF_EPIDS; i++) { - INIT_LIST_HEAD(&urb_list[i]); - } - spin_lock_init(&urb_list_lock); - - INIT_LIST_HEAD(&urb_unlink_list); - - - /* Initiate the bulk start timer. */ - init_timer(&bulk_start_timer); - bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL; - bulk_start_timer.function = etrax_usb_bulk_start_timer_func; - add_timer(&bulk_start_timer); - - - /* Initiate the bulk eot timer. */ - init_timer(&bulk_eot_timer); - bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL; - bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func; - add_timer(&bulk_eot_timer); - - /* Set up the data structures for USB traffic. Note that this must be done before - any interrupt that relies on sane DMA list occurrs. */ - init_rx_buffers(); - init_tx_bulk_ep(); - init_tx_ctrl_ep(); - init_tx_intr_ep(); - init_tx_isoc_ep(); - - device_initialize(&fake_device); - kobject_set_name(&fake_device.kobj, "etrax_usb"); - kobject_add(&fake_device.kobj); - kobject_uevent(&fake_device.kobj, KOBJ_ADD); - hc->bus->controller = &fake_device; - usb_register_bus(hc->bus); - - *R_IRQ_MASK2_SET = - /* Note that these interrupts are not used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) | - /* Sub channel 1 (ctrl) descr. interrupts are used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) | - IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) | - /* Sub channel 3 (isoc) descr. interrupts are used. */ - IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set); - - /* Note that the dma9_descr interrupt is not used. */ - *R_IRQ_MASK2_SET = - IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) | - IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set); - - /* FIXME: Enable iso_eof only when isoc traffic is running. */ - *R_USB_IRQ_MASK_SET = - IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) | - IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) | - IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) | - IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) | - IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set); - - - if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0, - "ETRAX 100LX built-in USB (HC)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0, - "ETRAX 100LX built-in USB (Rx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0, - "ETRAX 100LX built-in USB (Tx)", hc)) { - err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ); - etrax_usb_hc_cleanup(); - DBFEXIT; - return -1; - } - - /* R_USB_COMMAND: - USB commands in host mode. The fields in this register should all be - written to in one write. Do not read-modify-write one field at a time. A - write to this register will trigger events in the USB controller and an - incomplete command may lead to unpredictable results, and in worst case - even to a deadlock in the controller. - (Note however that the busy field is read-only, so no need to write to it.) */ - - /* Check the busy bit before writing to R_USB_COMMAND. */ - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Reset the USB interface. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, reset); - - /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800), - to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may - allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation. - - While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK - behaviour, it doesn't solve this problem. What happens is that a control transfer will not - be interrupted in its data stage when PSTART happens (the point at which periodic traffic - is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before - PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done, - there may be too little time left for an isochronous transfer, causing an epid attention - interrupt due to perror. The work-around for this is to let the control transfers run at the - end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't - fit into the frame. However, since there will *always* be a control transfer at the beginning - of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer - which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to - this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make - sure that the periodic transfers that are inserted will always fit in the frame. - - The idea was suggested that a control transfer could be split up into several 8 byte transfers, - so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this - hasn't been implemented. - - The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra - for possible bit stuffing. */ - - *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960); - -#ifdef CONFIG_ETRAX_USB_HOST_PORT1 - *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no); -#endif - -#ifdef CONFIG_ETRAX_USB_HOST_PORT2 - *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no); -#endif - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Configure the USB interface as a host controller. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config); - - /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled - sequence of resetting the ports. If we reset both ports now, and there are devices - on both ports, we will get a bus error because both devices will answer the set address - request. */ - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - /* Start processing of USB traffic. */ - *R_USB_COMMAND = - IO_STATE(R_USB_COMMAND, port_sel, nop) | - IO_STATE(R_USB_COMMAND, port_cmd, reset) | - IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run); - - while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy)); - - usb_rh = usb_alloc_dev(NULL, hc->bus, 0); - hc->bus->root_hub = usb_rh; - usb_rh->state = USB_STATE_ADDRESS; - usb_rh->speed = USB_SPEED_FULL; - usb_rh->devnum = 1; - hc->bus->devnum_next = 2; - usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64); - usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE); - usb_new_device(usb_rh); - - DBFEXIT; - - return 0; -} - -static void etrax_usb_hc_cleanup(void) -{ - DBFENTER; - - free_irq(ETRAX_USB_HC_IRQ, NULL); - free_irq(ETRAX_USB_RX_IRQ, NULL); - free_irq(ETRAX_USB_TX_IRQ, NULL); - - usb_deregister_bus(etrax_usb_bus); - - /* FIXME: call kmem_cache_destroy here? */ - - DBFEXIT; -} - -module_init(etrax_usb_hc_init); -module_exit(etrax_usb_hc_cleanup); diff --git a/drivers/usb/host/hc_crisv10.h b/drivers/usb/host/hc_crisv10.h deleted file mode 100644 index 62f7711..0000000 --- a/drivers/usb/host/hc_crisv10.h +++ /dev/null @@ -1,289 +0,0 @@ -#ifndef __LINUX_ETRAX_USB_H -#define __LINUX_ETRAX_USB_H - -#include <linux/types.h> -#include <linux/list.h> - -typedef struct USB_IN_Desc { - volatile __u16 sw_len; - volatile __u16 command; - volatile unsigned long next; - volatile unsigned long buf; - volatile __u16 hw_len; - volatile __u16 status; -} USB_IN_Desc_t; - -typedef struct USB_SB_Desc { - volatile __u16 sw_len; - volatile __u16 command; - volatile unsigned long next; - volatile unsigned long buf; - __u32 dummy; -} USB_SB_Desc_t; - -typedef struct USB_EP_Desc { - volatile __u16 hw_len; - volatile __u16 command; - volatile unsigned long sub; - volatile unsigned long next; - __u32 dummy; -} USB_EP_Desc_t; - -struct virt_root_hub { - int devnum; - void *urb; - void *int_addr; - int send; - int interval; - int numports; - struct timer_list rh_int_timer; - volatile __u16 wPortChange_1; - volatile __u16 wPortChange_2; - volatile __u16 prev_wPortStatus_1; - volatile __u16 prev_wPortStatus_2; -}; - -struct etrax_usb_intr_traffic { - int sleeping; - int error; - struct wait_queue *wq; -}; - -typedef struct etrax_usb_hc { - struct usb_bus *bus; - struct virt_root_hub rh; - struct etrax_usb_intr_traffic intr; -} etrax_hc_t; - -typedef enum { - STARTED, - NOT_STARTED, - UNLINK, - TRANSFER_DONE, - WAITING_FOR_DESCR_INTR -} etrax_usb_urb_state_t; - - - -typedef struct etrax_usb_urb_priv { - /* The first_sb field is used for freeing all SB descriptors belonging - to an urb. The corresponding ep descriptor's sub pointer cannot be - used for this since the DMA advances the sub pointer as it processes - the sb list. */ - USB_SB_Desc_t *first_sb; - /* The last_sb field referes to the last SB descriptor that belongs to - this urb. This is important to know so we can free the SB descriptors - that ranges between first_sb and last_sb. */ - USB_SB_Desc_t *last_sb; - - /* The rx_offset field is used in ctrl and bulk traffic to keep track - of the offset in the urb's transfer_buffer where incoming data should be - copied to. */ - __u32 rx_offset; - - /* Counter used in isochronous transfers to keep track of the - number of packets received/transmitted. */ - __u32 isoc_packet_counter; - - /* This field is used to pass information about the urb's current state between - the various interrupt handlers (thus marked volatile). */ - volatile etrax_usb_urb_state_t urb_state; - - /* Connection between the submitted urb and ETRAX epid number */ - __u8 epid; - - /* The rx_data_list field is used for periodic traffic, to hold - received data for later processing in the the complete_urb functions, - where the data us copied to the urb's transfer_buffer. Basically, we - use this intermediate storage because we don't know when it's safe to - reuse the transfer_buffer (FIXME?). */ - struct list_head rx_data_list; -} etrax_urb_priv_t; - -/* This struct is for passing data from the top half to the bottom half. */ -typedef struct usb_interrupt_registers -{ - etrax_hc_t *hc; - __u32 r_usb_epid_attn; - __u8 r_usb_status; - __u16 r_usb_rh_port_status_1; - __u16 r_usb_rh_port_status_2; - __u32 r_usb_irq_mask_read; - __u32 r_usb_fm_number; - struct work_struct usb_bh; -} usb_interrupt_registers_t; - -/* This struct is for passing data from the isoc top half to the isoc bottom half. */ -typedef struct usb_isoc_complete_data -{ - struct urb *urb; - struct work_struct usb_bh; -} usb_isoc_complete_data_t; - -/* This struct holds data we get from the rx descriptors for DMA channel 9 - for periodic traffic (intr and isoc). */ -typedef struct rx_data -{ - void *data; - int length; - struct list_head list; -} rx_data_t; - -typedef struct urb_entry -{ - struct urb *urb; - struct list_head list; -} urb_entry_t; - -/* --------------------------------------------------------------------------- - Virtual Root HUB - ------------------------------------------------------------------------- */ -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -/* Field definitions for */ - -#define USB_IN_command__eol__BITNR 0 /* command macros */ -#define USB_IN_command__eol__WIDTH 1 -#define USB_IN_command__eol__no 0 -#define USB_IN_command__eol__yes 1 - -#define USB_IN_command__intr__BITNR 3 -#define USB_IN_command__intr__WIDTH 1 -#define USB_IN_command__intr__no 0 -#define USB_IN_command__intr__yes 1 - -#define USB_IN_status__eop__BITNR 1 /* status macros. */ -#define USB_IN_status__eop__WIDTH 1 -#define USB_IN_status__eop__no 0 -#define USB_IN_status__eop__yes 1 - -#define USB_IN_status__eot__BITNR 5 -#define USB_IN_status__eot__WIDTH 1 -#define USB_IN_status__eot__no 0 -#define USB_IN_status__eot__yes 1 - -#define USB_IN_status__error__BITNR 6 -#define USB_IN_status__error__WIDTH 1 -#define USB_IN_status__error__no 0 -#define USB_IN_status__error__yes 1 - -#define USB_IN_status__nodata__BITNR 7 -#define USB_IN_status__nodata__WIDTH 1 -#define USB_IN_status__nodata__no 0 -#define USB_IN_status__nodata__yes 1 - -#define USB_IN_status__epid__BITNR 8 -#define USB_IN_status__epid__WIDTH 5 - -#define USB_EP_command__eol__BITNR 0 -#define USB_EP_command__eol__WIDTH 1 -#define USB_EP_command__eol__no 0 -#define USB_EP_command__eol__yes 1 - -#define USB_EP_command__eof__BITNR 1 -#define USB_EP_command__eof__WIDTH 1 -#define USB_EP_command__eof__no 0 -#define USB_EP_command__eof__yes 1 - -#define USB_EP_command__intr__BITNR 3 -#define USB_EP_command__intr__WIDTH 1 -#define USB_EP_command__intr__no 0 -#define USB_EP_command__intr__yes 1 - -#define USB_EP_command__enable__BITNR 4 -#define USB_EP_command__enable__WIDTH 1 -#define USB_EP_command__enable__no 0 -#define USB_EP_command__enable__yes 1 - -#define USB_EP_command__hw_valid__BITNR 5 -#define USB_EP_command__hw_valid__WIDTH 1 -#define USB_EP_command__hw_valid__no 0 -#define USB_EP_command__hw_valid__yes 1 - -#define USB_EP_command__epid__BITNR 8 -#define USB_EP_command__epid__WIDTH 5 - -#define USB_SB_command__eol__BITNR 0 /* command macros. */ -#define USB_SB_command__eol__WIDTH 1 -#define USB_SB_command__eol__no 0 -#define USB_SB_command__eol__yes 1 - -#define USB_SB_command__eot__BITNR 1 -#define USB_SB_command__eot__WIDTH 1 -#define USB_SB_command__eot__no 0 -#define USB_SB_command__eot__yes 1 - -#define USB_SB_command__intr__BITNR 3 -#define USB_SB_command__intr__WIDTH 1 -#define USB_SB_command__intr__no 0 -#define USB_SB_command__intr__yes 1 - -#define USB_SB_command__tt__BITNR 4 -#define USB_SB_command__tt__WIDTH 2 -#define USB_SB_command__tt__zout 0 -#define USB_SB_command__tt__in 1 -#define USB_SB_command__tt__out 2 -#define USB_SB_command__tt__setup 3 - - -#define USB_SB_command__rem__BITNR 8 -#define USB_SB_command__rem__WIDTH 6 - -#define USB_SB_command__full__BITNR 6 -#define USB_SB_command__full__WIDTH 1 -#define USB_SB_command__full__no 0 -#define USB_SB_command__full__yes 1 - -#endif diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 2718b5d..46873f2 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -1577,7 +1577,7 @@ static int isp116x_remove(struct platform_device *pdev) #define resource_len(r) (((r)->end - (r)->start) + 1) -static int __init isp116x_probe(struct platform_device *pdev) +static int __devinit isp116x_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct isp116x *isp116x; diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 9303464..d849c80 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -18,19 +18,38 @@ #include <asm/mach-types.h> #include <asm/hardware.h> #include <asm/arch/board.h> +#include <asm/arch/cpu.h> #ifndef CONFIG_ARCH_AT91 #error "CONFIG_ARCH_AT91 must be defined." #endif -/* interface and function clocks */ -static struct clk *iclk, *fclk; +/* interface and function clocks; sometimes also an AHB clock */ +static struct clk *iclk, *fclk, *hclk; static int clocked; extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ +static void at91_start_clock(void) +{ + if (cpu_is_at91sam9261()) + clk_enable(hclk); + clk_enable(iclk); + clk_enable(fclk); + clocked = 1; +} + +static void at91_stop_clock(void) +{ + clk_disable(fclk); + clk_disable(iclk); + if (cpu_is_at91sam9261()) + clk_disable(hclk); + clocked = 0; +} + static void at91_start_hc(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -41,9 +60,7 @@ static void at91_start_hc(struct platform_device *pdev) /* * Start the USB clocks. */ - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; + at91_start_clock(); /* * The USB host controller must remain in reset. @@ -66,9 +83,7 @@ static void at91_stop_hc(struct platform_device *pdev) /* * Stop the USB clocks. */ - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } @@ -126,6 +141,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); + if (cpu_is_at91sam9261()) + hclk = clk_get(&pdev->dev, "hck0"); at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -137,6 +154,8 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, /* Error handling */ at91_stop_hc(pdev); + if (cpu_is_at91sam9261()) + clk_put(hclk); clk_put(fclk); clk_put(iclk); @@ -171,9 +190,11 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd, iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + if (cpu_is_at91sam9261()) + clk_put(hclk); clk_put(fclk); clk_put(iclk); - fclk = iclk = NULL; + fclk = iclk = hclk = NULL; dev_set_drvdata(&pdev->dev, NULL); return 0; @@ -280,9 +301,7 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg) */ if (at91_suspend_entering_slow_clock()) { ohci_usb_reset (ohci); - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } return 0; @@ -295,11 +314,8 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) if (device_may_wakeup(&pdev->dev)) disable_irq_wake(hcd->irq); - if (!clocked) { - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; - } + if (!clocked) + at91_start_clock(); return 0; } diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index fa6a7ce..e8bbe8b 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -42,6 +42,9 @@ #include <asm/system.h> #include <asm/unaligned.h> #include <asm/byteorder.h> +#ifdef CONFIG_PPC_PS3 +#include <asm/firmware.h> +#endif #include "../core/hcd.h" @@ -483,9 +486,6 @@ static int ohci_run (struct ohci_hcd *ohci) * or if bus glue did the same (e.g. for PCI add-in cards with * PCI PM support). */ - ohci_dbg (ohci, "resetting from state '%s', control = 0x%x\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - ohci_readl (ohci, &ohci->regs->control)); if ((ohci->hc_control & OHCI_CTRL_RWC) != 0 && !device_may_wakeup(hcd->self.controller)) device_init_wakeup(hcd->self.controller, 1); @@ -741,9 +741,6 @@ static void ohci_stop (struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); - ohci_dbg (ohci, "stop %s controller (state 0x%02x)\n", - hcfs2string (ohci->hc_control & OHCI_CTRL_HCFS), - hcd->state); ohci_dump (ohci, 1); flush_scheduled_work(); @@ -944,9 +941,12 @@ static int __init ohci_hcd_mod_init(void) sizeof (struct ed), sizeof (struct td)); #ifdef PS3_SYSTEM_BUS_DRIVER - retval = ps3_system_bus_driver_register(&PS3_SYSTEM_BUS_DRIVER); - if (retval < 0) - goto error_ps3; + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) { + retval = ps3_system_bus_driver_register( + &PS3_SYSTEM_BUS_DRIVER); + if (retval < 0) + goto error_ps3; + } #endif #ifdef PLATFORM_DRIVER @@ -992,7 +992,8 @@ static int __init ohci_hcd_mod_init(void) error_platform: #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); error_ps3: #endif return retval; @@ -1014,7 +1015,8 @@ static void __exit ohci_hcd_mod_exit(void) platform_driver_unregister(&PLATFORM_DRIVER); #endif #ifdef PS3_SYSTEM_BUS_DRIVER - ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); + if (firmware_has_feature(FW_FEATURE_PS3_LV1)) + ps3_system_bus_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c index b331ac4..7970560 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -20,10 +20,16 @@ /*-------------------------------------------------------------------------*/ +static int broken_suspend(struct usb_hcd *hcd) +{ + device_init_wakeup(&hcd->self.root_hub->dev, 0); + return 0; +} + /* AMD 756, for most chips (early revs), corrupts register * values on read ... so enable the vendor workaround. */ -static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd) +static int ohci_quirk_amd756(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -31,16 +37,14 @@ static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd) ohci_dbg (ohci, "AMD756 erratum 4 workaround\n"); /* also erratum 10 (suspend/resume issues) */ - device_init_wakeup(&hcd->self.root_hub->dev, 0); - - return 0; + return broken_suspend(hcd); } /* Apple's OHCI driver has a lot of bizarre workarounds * for this chip. Evidently control and bulk lists * can get confused. (B&W G3 models, and ...) */ -static int __devinit ohci_quirk_opti(struct usb_hcd *hcd) +static int ohci_quirk_opti(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -53,7 +57,7 @@ static int __devinit ohci_quirk_opti(struct usb_hcd *hcd) * identify the USB (fn2). This quirk might apply to more or * even all NSC stuff. */ -static int __devinit ohci_quirk_ns(struct usb_hcd *hcd) +static int ohci_quirk_ns(struct usb_hcd *hcd) { struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct pci_dev *b; @@ -75,7 +79,7 @@ static int __devinit ohci_quirk_ns(struct usb_hcd *hcd) * delays before control or bulk queues get re-activated * in finish_unlinks() */ -static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd) +static int ohci_quirk_zfmicro(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -88,7 +92,7 @@ static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd) /* Check for Toshiba SCC OHCI which has big endian registers * and little endian in memory data structures */ -static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd) +static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci (hcd); @@ -129,6 +133,18 @@ static const struct pci_device_id ohci_pci_quirks[] = { PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6), .driver_data = (unsigned long)ohci_quirk_toshiba_scc, }, + { + /* Toshiba portege 4000 */ + .vendor = PCI_VENDOR_ID_AL, + .device = 0x5237, + .subvendor = PCI_VENDOR_ID_TOSHIBA_2, + .subdevice = 0x0004, + .driver_data = (unsigned long) broken_suspend, + }, + { + PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152), + .driver_data = (unsigned long) broken_suspend, + }, /* FIXME for some of the early AMD 760 southbridges, OHCI * won't work at all. blacklist them. */ diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c index 5d6c06b..1497371 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -145,7 +145,8 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) return out - buf; } -static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) +static int uhci_show_qh(struct uhci_hcd *uhci, + struct uhci_qh *qh, char *buf, int len, int space) { char *out = buf; int i, nurbs; @@ -190,13 +191,16 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) if (list_empty(&qh->queue)) { out += sprintf(out, "%*s queue is empty\n", space, ""); + if (qh == uhci->skel_async_qh) + out += uhci_show_td(uhci->term_td, out, + len - (out - buf), 0); } else { struct urb_priv *urbp = list_entry(qh->queue.next, struct urb_priv, node); struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - if (cpu_to_le32(td->dma_handle) != (element & ~UHCI_PTR_BITS)) + if (element != LINK_TO_TD(td)) out += sprintf(out, "%*s Element != First TD\n", space, ""); i = nurbs = 0; @@ -220,16 +224,6 @@ static int uhci_show_qh(struct uhci_qh *qh, char *buf, int len, int space) return out - buf; } -static const char * const qh_names[] = { - "skel_unlink_qh", "skel_iso_qh", - "skel_int128_qh", "skel_int64_qh", - "skel_int32_qh", "skel_int16_qh", - "skel_int8_qh", "skel_int4_qh", - "skel_int2_qh", "skel_int1_qh", - "skel_ls_control_qh", "skel_fs_control_qh", - "skel_bulk_qh", "skel_term_qh" -}; - static int uhci_show_sc(int port, unsigned short status, char *buf, int len) { char *out = buf; @@ -352,6 +346,13 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) struct uhci_td *td; struct list_head *tmp, *head; int nframes, nerrs; + __le32 link; + __le32 fsbr_link; + + static const char * const qh_names[] = { + "unlink", "iso", "int128", "int64", "int32", "int16", + "int8", "int4", "int2", "async", "term" + }; out += uhci_show_root_hub_state(uhci, out, len - (out - buf)); out += sprintf(out, "HC status\n"); @@ -374,7 +375,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) nframes = 10; nerrs = 0; for (i = 0; i < UHCI_NUMFRAMES; ++i) { - __le32 link, qh_dma; + __le32 qh_dma; j = 0; td = uhci->frame_cpu[i]; @@ -393,7 +394,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) do { td = list_entry(tmp, struct uhci_td, fl_list); tmp = tmp->next; - if (cpu_to_le32(td->dma_handle) != link) { + if (link != LINK_TO_TD(td)) { if (nframes > 0) out += sprintf(out, " link does " "not match list entry!\n"); @@ -428,25 +429,24 @@ check_link: out += sprintf(out, "Skeleton QHs\n"); + fsbr_link = 0; for (i = 0; i < UHCI_NUM_SKELQH; ++i) { int cnt = 0; qh = uhci->skelqh[i]; - out += sprintf(out, "- %s\n", qh_names[i]); \ - out += uhci_show_qh(qh, out, len - (out - buf), 4); + out += sprintf(out, "- skel_%s_qh\n", qh_names[i]); \ + out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); /* Last QH is the Terminating QH, it's different */ - if (i == UHCI_NUM_SKELQH - 1) { - if (qh->link != UHCI_PTR_TERM) - out += sprintf(out, " bandwidth reclamation on!\n"); - - if (qh_element(qh) != cpu_to_le32(uhci->term_td->dma_handle)) + if (i == SKEL_TERM) { + if (qh_element(qh) != LINK_TO_TD(uhci->term_td)) out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); - - continue; + link = fsbr_link; + if (!link) + link = LINK_TO_QH(uhci->skel_term_qh); + goto check_qh_link; } - j = (i < 9) ? 9 : i+1; /* Next skeleton */ head = &qh->node; tmp = head->next; @@ -454,17 +454,26 @@ check_link: qh = list_entry(tmp, struct uhci_qh, node); tmp = tmp->next; if (++cnt <= 10) - out += uhci_show_qh(qh, out, + out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); + if (!fsbr_link && qh->skel >= SKEL_FSBR) + fsbr_link = LINK_TO_QH(qh); } if ((cnt -= 10) > 0) out += sprintf(out, " Skipped %d QHs\n", cnt); - if (i > 1 && i < UHCI_NUM_SKELQH - 1) { - if (qh->link != - (cpu_to_le32(uhci->skelqh[j]->dma_handle) | UHCI_PTR_QH)) - out += sprintf(out, " last QH not linked to next skeleton!\n"); - } + link = UHCI_PTR_TERM; + if (i <= SKEL_ISO) + ; + else if (i < SKEL_ASYNC) + link = LINK_TO_QH(uhci->skel_async_qh); + else if (!uhci->fsbr_is_on) + ; + else + link = LINK_TO_QH(uhci->skel_term_qh); +check_qh_link: + if (qh->link != link) + out += sprintf(out, " last QH not linked to next skeleton!\n"); } return out - buf; @@ -568,8 +577,8 @@ static const struct file_operations uhci_debug_operations = { static inline void lprintk(char *buf) {} -static inline int uhci_show_qh(struct uhci_qh *qh, char *buf, - int len, int space) +static inline int uhci_show_qh(struct uhci_hcd *uhci, + struct uhci_qh *qh, char *buf, int len, int space) { return 0; } diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index ded4df3..d22da26 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -13,7 +13,7 @@ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu * * Intel documents this fairly well, and as far as I know there * are no royalties or anything like that, but even so there are @@ -107,16 +107,16 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) * interrupt QHs, which will help spread out bandwidth utilization. * * ffs (Find First bit Set) does exactly what we need: - * 1,3,5,... => ffs = 0 => use skel_int2_qh = skelqh[8], - * 2,6,10,... => ffs = 1 => use skel_int4_qh = skelqh[7], etc. + * 1,3,5,... => ffs = 0 => use period-2 QH = skelqh[8], + * 2,6,10,... => ffs = 1 => use period-4 QH = skelqh[7], etc. * ffs >= 7 => not on any high-period queue, so use - * skel_int1_qh = skelqh[9]. + * period-1 QH = skelqh[9]. * Add in UHCI_NUMFRAMES to insure at least one bit is set. */ skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); if (skelnum <= 1) skelnum = 9; - return UHCI_PTR_QH | cpu_to_le32(uhci->skelqh[skelnum]->dma_handle); + return LINK_TO_QH(uhci->skelqh[skelnum]); } #include "uhci-debug.c" @@ -540,16 +540,18 @@ static void uhci_shutdown(struct pci_dev *pdev) * * The hardware doesn't really know any difference * in the queues, but the order does matter for the - * protocols higher up. The order is: + * protocols higher up. The order in which the queues + * are encountered by the hardware is: * - * - any isochronous events handled before any + * - All isochronous events are handled before any * of the queues. We don't do that here, because * we'll create the actual TD entries on demand. - * - The first queue is the interrupt queue. - * - The second queue is the control queue, split into low- and full-speed - * - The third queue is bulk queue. - * - The fourth queue is the bandwidth reclamation queue, which loops back - * to the full-speed control queue. + * - The first queue is the high-period interrupt queue. + * - The second queue is the period-1 interrupt and async + * (low-speed control, full-speed control, then bulk) queue. + * - The third queue is the terminating bandwidth reclamation queue, + * which contains no members, loops back to itself, and is present + * only when FSBR is on and there are no full-speed control or bulk QHs. */ static int uhci_start(struct usb_hcd *hcd) { @@ -626,34 +628,19 @@ static int uhci_start(struct usb_hcd *hcd) } /* - * 8 Interrupt queues; link all higher int queues to int1, - * then link int1 to control and control to bulk + * 8 Interrupt queues; link all higher int queues to int1 = async */ - uhci->skel_int128_qh->link = - uhci->skel_int64_qh->link = - uhci->skel_int32_qh->link = - uhci->skel_int16_qh->link = - uhci->skel_int8_qh->link = - uhci->skel_int4_qh->link = - uhci->skel_int2_qh->link = UHCI_PTR_QH | - cpu_to_le32(uhci->skel_int1_qh->dma_handle); - - uhci->skel_int1_qh->link = UHCI_PTR_QH | - cpu_to_le32(uhci->skel_ls_control_qh->dma_handle); - uhci->skel_ls_control_qh->link = UHCI_PTR_QH | - cpu_to_le32(uhci->skel_fs_control_qh->dma_handle); - uhci->skel_fs_control_qh->link = UHCI_PTR_QH | - cpu_to_le32(uhci->skel_bulk_qh->dma_handle); - uhci->skel_bulk_qh->link = UHCI_PTR_QH | - cpu_to_le32(uhci->skel_term_qh->dma_handle); + for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i) + uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh); + uhci->skel_async_qh->link = UHCI_PTR_TERM; + uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); /* This dummy TD is to work around a bug in Intel PIIX controllers */ uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | - (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); - uhci->term_td->link = cpu_to_le32(uhci->term_td->dma_handle); - - uhci->skel_term_qh->link = UHCI_PTR_TERM; - uhci->skel_term_qh->element = cpu_to_le32(uhci->term_td->dma_handle); + (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); + uhci->term_td->link = UHCI_PTR_TERM; + uhci->skel_async_qh->element = uhci->skel_term_qh->element = + LINK_TO_TD(uhci->term_td); /* * Fill the frame list: make all entries point to the proper diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 74469b5..1b3d234 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -129,11 +129,12 @@ struct uhci_qh { __le32 element; /* Queue element (TD) pointer */ /* Software fields */ + dma_addr_t dma_handle; + struct list_head node; /* Node in the list of QHs */ struct usb_host_endpoint *hep; /* Endpoint information */ struct usb_device *udev; struct list_head queue; /* Queue of urbps for this QH */ - struct uhci_qh *skel; /* Skeleton for this QH */ struct uhci_td *dummy_td; /* Dummy TD to end the queue */ struct uhci_td *post_td; /* Last TD completed */ @@ -149,8 +150,7 @@ struct uhci_qh { int state; /* QH_STATE_xxx; see above */ int type; /* Queue type (control, bulk, etc) */ - - dma_addr_t dma_handle; + int skel; /* Skeleton queue number */ unsigned int initial_toggle:1; /* Endpoint's current toggle value */ unsigned int needs_fixup:1; /* Must fix the TD toggle values */ @@ -171,6 +171,8 @@ static inline __le32 qh_element(struct uhci_qh *qh) { return element; } +#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle)) + /* * Transfer Descriptors @@ -264,6 +266,8 @@ static inline u32 td_status(struct uhci_td *td) { return le32_to_cpu(status); } +#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle)) + /* * Skeleton Queue Headers @@ -272,12 +276,13 @@ static inline u32 td_status(struct uhci_td *td) { /* * The UHCI driver uses QHs with Interrupt, Control and Bulk URBs for * automatic queuing. To make it easy to insert entries into the schedule, - * we have a skeleton of QHs for each predefined Interrupt latency, - * low-speed control, full-speed control, bulk, and terminating QH - * (see explanation for the terminating QH below). + * we have a skeleton of QHs for each predefined Interrupt latency. + * Asynchronous QHs (low-speed control, full-speed control, and bulk) + * go onto the period-1 interrupt list, since they all get accessed on + * every frame. * - * When we want to add a new QH, we add it to the end of the list for the - * skeleton QH. For instance, the schedule list can look like this: + * When we want to add a new QH, we add it to the list starting from the + * appropriate skeleton QH. For instance, the schedule can look like this: * * skel int128 QH * dev 1 interrupt QH @@ -285,50 +290,47 @@ static inline u32 td_status(struct uhci_td *td) { * skel int64 QH * skel int32 QH * ... - * skel int1 QH - * skel low-speed control QH - * dev 5 control QH - * skel full-speed control QH - * skel bulk QH + * skel int1 + async QH + * dev 5 low-speed control QH * dev 1 bulk QH * dev 2 bulk QH - * skel terminating QH * - * The terminating QH is used for 2 reasons: - * - To place a terminating TD which is used to workaround a PIIX bug - * (see Intel errata for explanation), and - * - To loop back to the full-speed control queue for full-speed bandwidth - * reclamation. + * There is a special terminating QH used to keep full-speed bandwidth + * reclamation active when no full-speed control or bulk QHs are linked + * into the schedule. It has an inactive TD (to work around a PIIX bug, + * see the Intel errata) and it points back to itself. * - * There's a special skeleton QH for Isochronous QHs. It never appears - * on the schedule, and Isochronous TDs go on the schedule before the + * There's a special skeleton QH for Isochronous QHs which never appears + * on the schedule. Isochronous TDs go on the schedule before the * the skeleton QHs. The hardware accesses them directly rather than * through their QH, which is used only for bookkeeping purposes. * While the UHCI spec doesn't forbid the use of QHs for Isochronous, * it doesn't use them either. And the spec says that queues never * advance on an error completion status, which makes them totally * unsuitable for Isochronous transfers. + * + * There's also a special skeleton QH used for QHs which are in the process + * of unlinking and so may still be in use by the hardware. It too never + * appears on the schedule. */ -#define UHCI_NUM_SKELQH 14 -#define skel_unlink_qh skelqh[0] -#define skel_iso_qh skelqh[1] -#define skel_int128_qh skelqh[2] -#define skel_int64_qh skelqh[3] -#define skel_int32_qh skelqh[4] -#define skel_int16_qh skelqh[5] -#define skel_int8_qh skelqh[6] -#define skel_int4_qh skelqh[7] -#define skel_int2_qh skelqh[8] -#define skel_int1_qh skelqh[9] -#define skel_ls_control_qh skelqh[10] -#define skel_fs_control_qh skelqh[11] -#define skel_bulk_qh skelqh[12] -#define skel_term_qh skelqh[13] - -/* Find the skelqh entry corresponding to an interval exponent */ -#define UHCI_SKEL_INDEX(exponent) (9 - exponent) - +#define UHCI_NUM_SKELQH 11 +#define SKEL_UNLINK 0 +#define skel_unlink_qh skelqh[SKEL_UNLINK] +#define SKEL_ISO 1 +#define skel_iso_qh skelqh[SKEL_ISO] + /* int128, int64, ..., int1 = 2, 3, ..., 9 */ +#define SKEL_INDEX(exponent) (9 - exponent) +#define SKEL_ASYNC 9 +#define skel_async_qh skelqh[SKEL_ASYNC] +#define SKEL_TERM 10 +#define skel_term_qh skelqh[SKEL_TERM] + +/* The following entries refer to sublists of skel_async_qh */ +#define SKEL_LS_CONTROL 20 +#define SKEL_FS_CONTROL 21 +#define SKEL_FSBR SKEL_FS_CONTROL +#define SKEL_BULK 22 /* * The UHCI controller and root hub diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index bacc25c..8e4427a 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -33,6 +33,9 @@ static __u8 root_hub_hub_des[] = /* status change bits: nonzero writes will clear */ #define RWC_BITS (USBPORTSC_OCC | USBPORTSC_PEC | USBPORTSC_CSC) +/* suspend/resume bits: port suspended or port resuming */ +#define SUSPEND_BITS (USBPORTSC_SUSP | USBPORTSC_RD) + /* A port that either is connected or has a changed-bit set will prevent * us from AUTO_STOPPING. */ @@ -96,8 +99,8 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, int status; int i; - if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) { - CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD); + if (inw(port_addr) & SUSPEND_BITS) { + CLR_RH_PORTSTAT(SUSPEND_BITS); if (test_bit(port, &uhci->resuming_ports)) set_bit(port, &uhci->port_c_suspend); @@ -107,7 +110,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, * Experiments show that some controllers take longer, so * we'll poll for completion. */ for (i = 0; i < 10; ++i) { - if (!(inw(port_addr) & USBPORTSC_RD)) + if (!(inw(port_addr) & SUSPEND_BITS)) break; udelay(1); } @@ -289,7 +292,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, wPortStatus |= USB_PORT_STAT_CONNECTION; if (status & USBPORTSC_PE) { wPortStatus |= USB_PORT_STAT_ENABLE; - if (status & (USBPORTSC_SUSP | USBPORTSC_RD)) + if (status & SUSPEND_BITS) wPortStatus |= USB_PORT_STAT_SUSPEND; } if (status & USBPORTSC_OC) diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index 68e66b3..4aed305 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -13,7 +13,7 @@ * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) - * (C) Copyright 2004-2006 Alan Stern, stern@rowland.harvard.edu + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu */ @@ -45,15 +45,27 @@ static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) */ static void uhci_fsbr_on(struct uhci_hcd *uhci) { + struct uhci_qh *lqh; + + /* The terminating skeleton QH always points back to the first + * FSBR QH. Make the last async QH point to the terminating + * skeleton QH. */ uhci->fsbr_is_on = 1; - uhci->skel_term_qh->link = cpu_to_le32( - uhci->skel_fs_control_qh->dma_handle) | UHCI_PTR_QH; + lqh = list_entry(uhci->skel_async_qh->node.prev, + struct uhci_qh, node); + lqh->link = LINK_TO_QH(uhci->skel_term_qh); } static void uhci_fsbr_off(struct uhci_hcd *uhci) { + struct uhci_qh *lqh; + + /* Remove the link from the last async QH to the terminating + * skeleton QH. */ uhci->fsbr_is_on = 0; - uhci->skel_term_qh->link = UHCI_PTR_TERM; + lqh = list_entry(uhci->skel_async_qh->node.prev, + struct uhci_qh, node); + lqh->link = UHCI_PTR_TERM; } static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) @@ -111,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci) static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) { - if (!list_empty(&td->list)) + if (!list_empty(&td->list)) { dev_warn(uhci_dev(uhci), "td %p still in list!\n", td); - if (!list_empty(&td->fl_list)) + WARN_ON(1); + } + if (!list_empty(&td->fl_list)) { dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td); + WARN_ON(1); + } dma_pool_free(uhci->td_pool, td, td->dma_handle); } @@ -158,11 +174,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci, td->link = ltd->link; wmb(); - ltd->link = cpu_to_le32(td->dma_handle); + ltd->link = LINK_TO_TD(td); } else { td->link = uhci->frame[framenum]; wmb(); - uhci->frame[framenum] = cpu_to_le32(td->dma_handle); + uhci->frame[framenum] = LINK_TO_TD(td); uhci->frame_cpu[framenum] = td; } } @@ -184,7 +200,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, struct uhci_td *ntd; ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); - uhci->frame[td->frame] = cpu_to_le32(ntd->dma_handle); + uhci->frame[td->frame] = LINK_TO_TD(ntd); uhci->frame_cpu[td->frame] = ntd; } } else { @@ -279,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) { WARN_ON(qh->state != QH_STATE_IDLE && qh->udev); - if (!list_empty(&qh->queue)) + if (!list_empty(&qh->queue)) { dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh); + WARN_ON(1); + } list_del(&qh->node); if (qh->udev) { @@ -405,12 +423,66 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) } /* - * Put a QH on the schedule in both hardware and software + * Link an Isochronous QH into its skeleton's list */ -static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +static inline void link_iso(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + list_add_tail(&qh->node, &uhci->skel_iso_qh->node); + + /* Isochronous QHs aren't linked by the hardware */ +} + +/* + * Link a high-period interrupt QH into the schedule at the end of its + * skeleton's list + */ +static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; + list_add_tail(&qh->node, &uhci->skelqh[qh->skel]->node); + + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + qh->link = pqh->link; + wmb(); + pqh->link = LINK_TO_QH(qh); +} + +/* + * Link a period-1 interrupt or async QH into the schedule at the + * correct spot in the async skeleton's list, and update the FSBR link + */ +static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + struct uhci_qh *pqh; + __le32 link_to_new_qh; + + /* Find the predecessor QH for our new one and insert it in the list. + * The list of QHs is expected to be short, so linear search won't + * take too long. */ + list_for_each_entry_reverse(pqh, &uhci->skel_async_qh->node, node) { + if (pqh->skel <= qh->skel) + break; + } + list_add(&qh->node, &pqh->node); + + /* Link it into the schedule */ + qh->link = pqh->link; + wmb(); + link_to_new_qh = LINK_TO_QH(qh); + pqh->link = link_to_new_qh; + + /* If this is now the first FSBR QH, link the terminating skeleton + * QH to it. */ + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) + uhci->skel_term_qh->link = link_to_new_qh; +} + +/* + * Put a QH on the schedule in both hardware and software + */ +static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ WARN_ON(list_empty(&qh->queue)); /* Set the element pointer if it isn't set already. @@ -421,7 +493,7 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - qh->element = cpu_to_le32(td->dma_handle); + qh->element = LINK_TO_TD(td); } /* Treat the queue as if it has just advanced */ @@ -432,36 +504,68 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) return; qh->state = QH_STATE_ACTIVE; - /* Move the QH from its old list to the end of the appropriate + /* Move the QH from its old list to the correct spot in the appropriate * skeleton's list */ if (qh == uhci->next_qh) uhci->next_qh = list_entry(qh->node.next, struct uhci_qh, node); - list_move_tail(&qh->node, &qh->skel->node); + list_del(&qh->node); + + if (qh->skel == SKEL_ISO) + link_iso(uhci, qh); + else if (qh->skel < SKEL_ASYNC) + link_interrupt(uhci, qh); + else + link_async(uhci, qh); +} + +/* + * Unlink a high-period interrupt QH from the schedule + */ +static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ + struct uhci_qh *pqh; - /* Link it into the schedule */ pqh = list_entry(qh->node.prev, struct uhci_qh, node); - qh->link = pqh->link; - wmb(); - pqh->link = UHCI_PTR_QH | cpu_to_le32(qh->dma_handle); + pqh->link = qh->link; + mb(); } /* - * Take a QH off the hardware schedule + * Unlink a period-1 interrupt or async QH from the schedule */ -static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; + __le32 link_to_next_qh = qh->link; + pqh = list_entry(qh->node.prev, struct uhci_qh, node); + pqh->link = link_to_next_qh; + + /* If this was the old first FSBR QH, link the terminating skeleton + * QH to the next (new first FSBR) QH. */ + if (pqh->skel < SKEL_FSBR && qh->skel >= SKEL_FSBR) + uhci->skel_term_qh->link = link_to_next_qh; + mb(); +} + +/* + * Take a QH off the hardware schedule + */ +static void uhci_unlink_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) +{ if (qh->state == QH_STATE_UNLINKING) return; WARN_ON(qh->state != QH_STATE_ACTIVE || !qh->udev); qh->state = QH_STATE_UNLINKING; /* Unlink the QH from the schedule and record when we did it */ - pqh = list_entry(qh->node.prev, struct uhci_qh, node); - pqh->link = qh->link; - mb(); + if (qh->skel == SKEL_ISO) + ; + else if (qh->skel < SKEL_ASYNC) + unlink_interrupt(uhci, qh); + else + unlink_async(uhci, qh); uhci_get_current_frame_number(uhci); qh->unlink_frame = uhci->frame_number; @@ -642,9 +746,11 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci, { struct uhci_td *td, *tmp; - if (!list_empty(&urbp->node)) + if (!list_empty(&urbp->node)) { dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n", urbp->urb); + WARN_ON(1); + } list_for_each_entry_safe(td, tmp, &urbp->td_list, list) { uhci_remove_td_from_urbp(td); @@ -697,6 +803,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, dma_addr_t data = urb->transfer_dma; __le32 *plink; struct urb_priv *urbp = urb->hcpriv; + int skel; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; @@ -737,7 +844,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; @@ -757,7 +864,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); /* * It's IN if the pipe is an output pipe or we're not expecting @@ -784,7 +891,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); @@ -797,11 +904,13 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, * isn't in the CONFIGURED state. */ if (urb->dev->speed == USB_SPEED_LOW || urb->dev->state != USB_STATE_CONFIGURED) - qh->skel = uhci->skel_ls_control_qh; + skel = SKEL_LS_CONTROL; else { - qh->skel = uhci->skel_fs_control_qh; + skel = SKEL_FS_CONTROL; uhci_add_fsbr(uhci, urb); } + if (qh->state != QH_STATE_ACTIVE) + qh->skel = skel; urb->actual_length = -8; /* Account for the SETUP packet */ return 0; @@ -860,7 +969,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); } uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, @@ -888,7 +997,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); uhci_add_td_to_urbp(td, urbp); uhci_fill_td(td, status, @@ -914,7 +1023,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = cpu_to_le32(td->dma_handle); + *plink = LINK_TO_TD(td); uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); @@ -931,7 +1040,7 @@ nomem: return -ENOMEM; } -static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, +static int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, struct uhci_qh *qh) { int ret; @@ -940,7 +1049,8 @@ static inline int uhci_submit_bulk(struct uhci_hcd *uhci, struct urb *urb, if (urb->dev->speed == USB_SPEED_LOW) return -EINVAL; - qh->skel = uhci->skel_bulk_qh; + if (qh->state != QH_STATE_ACTIVE) + qh->skel = SKEL_BULK; ret = uhci_submit_common(uhci, urb, qh); if (ret == 0) uhci_add_fsbr(uhci, urb); @@ -968,7 +1078,7 @@ static int uhci_submit_interrupt(struct uhci_hcd *uhci, struct urb *urb, if (exponent < 0) return -EINVAL; qh->period = 1 << exponent; - qh->skel = uhci->skelqh[UHCI_SKEL_INDEX(exponent)]; + qh->skel = SKEL_INDEX(exponent); /* For now, interrupt phase is fixed by the layout * of the QH lists. */ @@ -1005,7 +1115,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, * the queue at the status stage transaction, which is * the last TD. */ WARN_ON(list_empty(&urbp->td_list)); - qh->element = cpu_to_le32(td->dma_handle); + qh->element = LINK_TO_TD(td); tmp = td->list.prev; ret = -EINPROGRESS; @@ -1069,7 +1179,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (debug > 1 && errbuf) { /* Print the chain for debugging */ - uhci_show_qh(urbp->qh, errbuf, + uhci_show_qh(uhci, urbp->qh, errbuf, ERRBUF_LEN, 0); lprintk(errbuf); } @@ -1216,7 +1326,7 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, qh->iso_status = 0; } - qh->skel = uhci->skel_iso_qh; + qh->skel = SKEL_ISO; if (!qh->bandwidth_reserved) uhci_reserve_bandwidth(uhci, qh); return 0; @@ -1566,8 +1676,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) { /* Detect the Intel bug and work around it */ - if (qh->post_td && qh_element(qh) == - cpu_to_le32(qh->post_td->dma_handle)) { + if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) { qh->element = qh->post_td->link; qh->advance_jiffies = jiffies; ret = 1; diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig index 69a9f3b..a792e42 100644 --- a/drivers/usb/input/Kconfig +++ b/drivers/usb/input/Kconfig @@ -4,151 +4,6 @@ comment "USB Input Devices" depends on USB -config USB_HID - tristate "USB Human Interface Device (full HID) support" - default y - depends on USB && INPUT - select HID - ---help--- - Say Y here if you want full HID support to connect USB keyboards, - mice, joysticks, graphic tablets, or any other HID based devices - to your computer via USB, as well as Uninterruptible Power Supply - (UPS) and monitor control devices. - - You can't use this driver and the HIDBP (Boot Protocol) keyboard - and mouse drivers at the same time. More information is available: - <file:Documentation/input/input.txt>. - - If unsure, say Y. - - To compile this driver as a module, choose M here: the - module will be called usbhid. - -comment "Input core support is needed for USB HID input layer or HIDBP support" - depends on USB_HID && INPUT=n - -config USB_HIDINPUT_POWERBOOK - bool "Enable support for iBook/PowerBook special keys" - default n - depends on USB_HID - help - Say Y here if you want support for the special keys (Fn, Numlock) on - Apple iBooks and PowerBooks. - - If unsure, say N. - -config HID_FF - bool "Force feedback support (EXPERIMENTAL)" - depends on USB_HID && EXPERIMENTAL - help - Say Y here is you want force feedback support for a few HID devices. - See below for a list of supported devices. - - See <file:Documentation/input/ff.txt> for a description of the force - feedback API. - - If unsure, say N. - -config HID_PID - bool "PID device support" - depends on HID_FF - help - Say Y here if you have a PID-compliant device and wish to enable force - feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such - devices. - -config LOGITECH_FF - bool "Logitech devices support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have one of these devices: - - Logitech WingMan Cordless RumblePad - - Logitech WingMan Cordless RumblePad 2 - - Logitech WingMan Force 3D - - Logitech Formula Force EX - - Logitech MOMO Force wheel - - and if you want to enable force feedback for them. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config PANTHERLORD_FF - bool "PantherLord USB/PS2 2in1 Adapter support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a PantherLord USB/PS2 2in1 Adapter and want - to enable force feedback support for it. - -config THRUSTMASTER_FF - bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)" - depends on HID_FF && EXPERIMENTAL - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a THRUSTMASTER FireStore Dual Power 2, - and want to enable force feedback support for it. - Note: if you say N here, this device will still be supported, but without - force feedback. - -config ZEROPLUS_FF - bool "Zeroplus based game controller support" - depends on HID_FF - select INPUT_FF_MEMLESS if USB_HID - help - Say Y here if you have a Zeroplus based game controller and want to - enable force feedback for it. - -config USB_HIDDEV - bool "/dev/hiddev raw HID device support" - depends on USB_HID - help - Say Y here if you want to support HID devices (from the USB - specification standpoint) that aren't strictly user interface - devices, like monitor controls and Uninterruptable Power Supplies. - - This module supports these devices separately using a separate - event interface on /dev/usb/hiddevX (char 180:96 to 180:111). - - If unsure, say Y. - -menu "USB HID Boot Protocol drivers" - depends on USB!=n && USB_HID!=y - -config USB_KBD - tristate "USB HIDBP Keyboard (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB keyboard and prefer - to use the keyboard in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple keyboards. - - To compile this driver as a module, choose M here: the - module will be called usbkbd. - - If even remotely unsure, say N. - -config USB_MOUSE - tristate "USB HIDBP Mouse (simple Boot) support" - depends on USB && INPUT - ---help--- - Say Y here only if you are absolutely sure that you don't want - to use the generic HID driver for your USB mouse and prefer - to use the mouse in its limited Boot Protocol mode instead. - - This is almost certainly not what you want. This is mostly - useful for embedded applications or simple mice. - - To compile this driver as a module, choose M here: the - module will be called usbmouse. - - If even remotely unsure, say N. - -endmenu - config USB_AIPTEK tristate "Aiptek 6000U/8000U tablet support" depends on USB && INPUT diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile index f8c35b6..284a073 100644 --- a/drivers/usb/input/Makefile +++ b/drivers/usb/input/Makefile @@ -4,40 +4,12 @@ # Multipart objects. wacom-objs := wacom_wac.o wacom_sys.o -usbhid-objs := hid-core.o - -# Optional parts of multipart objects. - -ifeq ($(CONFIG_USB_HIDDEV),y) - usbhid-objs += hiddev.o -endif -ifeq ($(CONFIG_HID_PID),y) - usbhid-objs += hid-pidff.o -endif -ifeq ($(CONFIG_LOGITECH_FF),y) - usbhid-objs += hid-lgff.o -endif -ifeq ($(CONFIG_PANTHERLORD_FF),y) - usbhid-objs += hid-plff.o -endif -ifeq ($(CONFIG_THRUSTMASTER_FF),y) - usbhid-objs += hid-tmff.o -endif -ifeq ($(CONFIG_ZEROPLUS_FF),y) - usbhid-objs += hid-zpff.o -endif -ifeq ($(CONFIG_HID_FF),y) - usbhid-objs += hid-ff.o -endif obj-$(CONFIG_USB_AIPTEK) += aiptek.o obj-$(CONFIG_USB_ATI_REMOTE) += ati_remote.o obj-$(CONFIG_USB_ATI_REMOTE2) += ati_remote2.o -obj-$(CONFIG_USB_HID) += usbhid.o -obj-$(CONFIG_USB_KBD) += usbkbd.o obj-$(CONFIG_USB_KBTAB) += kbtab.o obj-$(CONFIG_USB_KEYSPAN_REMOTE) += keyspan_remote.o -obj-$(CONFIG_USB_MOUSE) += usbmouse.o obj-$(CONFIG_USB_TOUCHSCREEN) += usbtouchscreen.o obj-$(CONFIG_USB_POWERMATE) += powermate.o obj-$(CONFIG_USB_WACOM) += wacom.o @@ -45,7 +17,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o obj-$(CONFIG_USB_YEALINK) += yealink.o obj-$(CONFIG_USB_XPAD) += xpad.o obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o -obj-$(CONFIG_USB_GTCO) += gtco.o +obj-$(CONFIG_USB_GTCO) += gtco.o ifeq ($(CONFIG_USB_DEBUG),y) EXTRA_CFLAGS += -DDEBUG diff --git a/drivers/usb/input/gtco.c b/drivers/usb/input/gtco.c index dbd207e..b2ca10f2f 100644 --- a/drivers/usb/input/gtco.c +++ b/drivers/usb/input/gtco.c @@ -223,7 +223,7 @@ static void parse_hid_report_descriptor(struct gtco *device, char * report, __u32 oldval[TAG_GLOB_MAX]; /* Debug stuff */ - char maintype='x'; + char maintype = 'x'; char globtype[12]; int indent = 0; char indentstr[10] = ""; diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c index 4142e36..4f3e9bc 100644 --- a/drivers/usb/input/wacom_wac.c +++ b/drivers/usb/input/wacom_wac.c @@ -163,7 +163,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) } id = STYLUS_DEVICE_ID; - if (data[1] & 0x10) { /* in prox */ + if (data[1] & 0x80) { /* in prox */ switch ((data[1] >> 5) & 3) { @@ -196,9 +196,6 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) wacom_report_abs(wcombo, ABS_DISTANCE, data[7] & 0x3f); break; } - } - - if (data[1] & 0x90) { x = wacom_le16_to_cpu(&data[2]); y = wacom_le16_to_cpu(&data[4]); wacom_report_abs(wcombo, ABS_X, x); @@ -210,19 +207,28 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04); } wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */ - } - else - wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ - - if (data[1] & 0x10) /* only report prox-in when in area */ wacom_report_key(wcombo, wacom->tool[0], 1); - if (!(data[1] & 0x90)) /* report prox-out when physically out */ + } else if (!(data[1] & 0x90)) { + wacom_report_abs(wcombo, ABS_X, 0); + wacom_report_abs(wcombo, ABS_Y, 0); + if (wacom->tool[0] == BTN_TOOL_MOUSE) { + wacom_report_key(wcombo, BTN_LEFT, 0); + wacom_report_key(wcombo, BTN_RIGHT, 0); + wacom_report_abs(wcombo, ABS_DISTANCE, 0); + } else { + wacom_report_abs(wcombo, ABS_PRESSURE, 0); + wacom_report_key(wcombo, BTN_TOUCH, 0); + wacom_report_key(wcombo, BTN_STYLUS, 0); + wacom_report_key(wcombo, BTN_STYLUS2, 0); + } + wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ wacom_report_key(wcombo, wacom->tool[0], 0); - wacom_input_sync(wcombo); + } /* send pad data */ if (wacom->features->type == WACOM_G4) { - if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) { + if (data[7] & 0xf8) { + wacom_input_sync(wcombo); /* sync last event */ wacom->id[1] = 1; wacom->serial[1] = (data[7] & 0xf8); wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); @@ -230,10 +236,15 @@ static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo) rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3); wacom_report_rel(wcombo, REL_WHEEL, rw); wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0); + wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); } else if (wacom->id[1]) { + wacom_input_sync(wcombo); /* sync last event */ wacom->id[1] = 0; + wacom_report_key(wcombo, BTN_0, (data[7] & 0x40)); + wacom_report_key(wcombo, BTN_4, (data[7] & 0x80)); wacom_report_key(wcombo, BTN_TOOL_FINGER, 0); + wacom_report_abs(wcombo, ABS_MISC, 0); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0); } } @@ -304,28 +315,35 @@ static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo) default: /* Unknown tool */ wacom->tool[idx] = BTN_TOOL_PEN; } - /* only large I3 support Lens Cursor */ - if(!((wacom->tool[idx] == BTN_TOOL_LENS) - && ((wacom->features->type == INTUOS3) - || (wacom->features->type == INTUOS3S)))) { - wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */ - wacom_report_key(wcombo, wacom->tool[idx], 1); - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - return 2; - } return 1; } /* Exit report */ if ((data[1] & 0xfe) == 0x80) { - if(!((wacom->tool[idx] == BTN_TOOL_LENS) - && ((wacom->features->type == INTUOS3) - || (wacom->features->type == INTUOS3S)))) { - wacom_report_key(wcombo, wacom->tool[idx], 0); - wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ - wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); - return 2; + wacom_report_abs(wcombo, ABS_X, 0); + wacom_report_abs(wcombo, ABS_Y, 0); + wacom_report_abs(wcombo, ABS_DISTANCE, 0); + if (wacom->tool[idx] >= BTN_TOOL_MOUSE) { + wacom_report_key(wcombo, BTN_LEFT, 0); + wacom_report_key(wcombo, BTN_MIDDLE, 0); + wacom_report_key(wcombo, BTN_RIGHT, 0); + wacom_report_key(wcombo, BTN_SIDE, 0); + wacom_report_key(wcombo, BTN_EXTRA, 0); + wacom_report_abs(wcombo, ABS_THROTTLE, 0); + wacom_report_abs(wcombo, ABS_RZ, 0); + } else { + wacom_report_abs(wcombo, ABS_PRESSURE, 0); + wacom_report_abs(wcombo, ABS_TILT_X, 0); + wacom_report_abs(wcombo, ABS_TILT_Y, 0); + wacom_report_key(wcombo, BTN_STYLUS, 0); + wacom_report_key(wcombo, BTN_STYLUS2, 0); + wacom_report_key(wcombo, BTN_TOUCH, 0); + wacom_report_abs(wcombo, ABS_WHEEL, 0); } + wacom_report_key(wcombo, wacom->tool[idx], 0); + wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */ + wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]); + return 2; } return 0; } @@ -394,6 +412,7 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) wacom_report_key(wcombo, wacom->tool[1], 1); else wacom_report_key(wcombo, wacom->tool[1], 0); + wacom_report_abs(wcombo, ABS_MISC, PAD_DEVICE_ID); wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff); return 1; } @@ -403,6 +422,12 @@ static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo) if (result) return result-1; + /* Only large I3 and I1 & I2 support Lense Cursor */ + if((wacom->tool[idx] == BTN_TOOL_LENS) + && ((wacom->features->type == INTUOS3) + || (wacom->features->type == INTUOS3S))) + return 0; + /* Cintiq doesn't send data when RDY bit isn't set */ if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40)) return 0; @@ -554,11 +579,11 @@ static struct wacom_features wacom_features[] = { { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 63, GRAPHIRE }, { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 63, GRAPHIRE }, { "Wacom PenPartner2", 8, 3250, 2320, 255, 63, GRAPHIRE }, - { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 63, INTUOS }, - { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 63, INTUOS }, - { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 63, INTUOS }, - { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 63, INTUOS }, - { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 63, INTUOS }, + { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, + { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, + { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 31, INTUOS }, + { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 31, INTUOS }, + { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 31, INTUOS }, { "Wacom PL400", 8, 5408, 4056, 255, 0, PL }, { "Wacom PL500", 8, 6144, 4608, 255, 0, PL }, { "Wacom PL600", 8, 6126, 4604, 255, 0, PL }, @@ -571,11 +596,11 @@ static struct wacom_features wacom_features[] = { { "Wacom DTF521", 8, 6282, 4762, 511, 0, PL }, { "Wacom DTF720", 8, 6858, 5506, 511, 0, PL }, { "Wacom Cintiq Partner",8, 20480, 15360, 511, 0, PTU }, - { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 63, INTUOS }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS }, - { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 63, INTUOS }, - { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 63, INTUOS }, - { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 63, INTUOS }, + { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 31, INTUOS }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, + { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 31, INTUOS }, + { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 31, INTUOS }, + { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 31, INTUOS }, { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 63, INTUOS3S }, { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 63, INTUOS3 }, { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 63, INTUOS3 }, @@ -584,7 +609,7 @@ static struct wacom_features wacom_features[] = { { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 63, INTUOS3 }, { "Wacom Intuos3 4x6", 10, 31496, 19685, 1023, 63, INTUOS3S }, { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 63, CINTIQ }, - { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 63, INTUOS }, + { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 31, INTUOS }, { } }; diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h index a1d9ce0..a230222 100644 --- a/drivers/usb/input/wacom_wac.h +++ b/drivers/usb/input/wacom_wac.h @@ -12,6 +12,7 @@ #define STYLUS_DEVICE_ID 0x02 #define CURSOR_DEVICE_ID 0x06 #define ERASER_DEVICE_ID 0x0A +#define PAD_DEVICE_ID 0x0F enum { PENPARTNER = 0, diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index a74bf86..9c7eb61 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -88,6 +88,17 @@ config USB_LCD To compile this driver as a module, choose M here: the module will be called usblcd. +config USB_BERRY_CHARGE + tristate "USB BlackBerry recharge support" + depends on USB + help + Say Y here if you want to connect a BlackBerry device to your + computer's USB port and have it automatically switch to "recharge" + mode. + + To compile this driver as a module, choose M here: the + module will be called berry_charge. + config USB_LED tristate "USB LED driver support" depends on USB @@ -233,6 +244,20 @@ config USB_TRANCEVIBRATOR To compile this driver as a module, choose M here: the module will be called trancevibrator. +config USB_IOWARRIOR + tristate "IO Warrior driver support" + depends on USB + help + Say Y here if you want to support the IO Warrior devices from Code + Mercenaries. This includes support for the following devices: + IO Warrior 40 + IO Warrior 24 + IO Warrior 56 + IO Warrior 24 Power Vampire + + To compile this driver as a module, choose M here: the + module will be called iowarrior. + config USB_TEST tristate "USB testing driver (DEVELOPMENT)" depends on USB && USB_DEVICEFS && EXPERIMENTAL diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 2cba07d..b68e6b7 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -6,12 +6,14 @@ obj-$(CONFIG_USB_ADUTUX) += adutux.o obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o obj-$(CONFIG_USB_AUERSWALD) += auerswald.o +obj-$(CONFIG_USB_BERRY_CHARGE) += berry_charge.o obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o obj-$(CONFIG_USB_CYTHERM) += cytherm.o obj-$(CONFIG_USB_EMI26) += emi26.o obj-$(CONFIG_USB_EMI62) += emi62.o obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o obj-$(CONFIG_USB_IDMOUSE) += idmouse.o +obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o obj-$(CONFIG_USB_LCD) += usblcd.o obj-$(CONFIG_USB_LD) += ldusb.o obj-$(CONFIG_USB_LED) += usbled.o diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c index 75bfab9..77145f9 100644 --- a/drivers/usb/misc/adutux.c +++ b/drivers/usb/misc/adutux.c @@ -285,23 +285,24 @@ static int adu_open(struct inode *inode, struct file *file) /* save device in the file's private structure */ file->private_data = dev; - /* initialize in direction */ - dev->read_buffer_length = 0; - - /* fixup first read by having urb waiting for it */ - usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, - usb_rcvintpipe(dev->udev, - dev->interrupt_in_endpoint->bEndpointAddress), - dev->interrupt_in_buffer, - le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), - adu_interrupt_in_callback, dev, - dev->interrupt_in_endpoint->bInterval); - /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ - dev->read_urb_finished = 0; - usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); - /* we ignore failure */ - /* end of fixup for first read */ + if (dev->open_count == 1) { + /* initialize in direction */ + dev->read_buffer_length = 0; + /* fixup first read by having urb waiting for it */ + usb_fill_int_urb(dev->interrupt_in_urb,dev->udev, + usb_rcvintpipe(dev->udev, + dev->interrupt_in_endpoint->bEndpointAddress), + dev->interrupt_in_buffer, + le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize), + adu_interrupt_in_callback, dev, + dev->interrupt_in_endpoint->bInterval); + /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */ + dev->read_urb_finished = 0; + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + if (retval) + --dev->open_count; + } up(&dev->sem); exit_no_device: @@ -469,7 +470,7 @@ static ssize_t adu_read(struct file *file, __user char *buffer, size_t count, adu_interrupt_in_callback, dev, dev->interrupt_in_endpoint->bInterval); - retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL); + retval = usb_submit_urb(dev->interrupt_in_urb, GFP_ATOMIC); if (!retval) { spin_unlock_irqrestore(&dev->buflock, flags); dbg(2," %s : submitted OK", __FUNCTION__); @@ -539,7 +540,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, size_t bytes_written = 0; size_t bytes_to_write; size_t buffer_size; - int retval = 0; + int retval; int timeout = 0; dbg(2," %s : enter, count = %Zd", __FUNCTION__, count); @@ -547,7 +548,9 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, dev = file->private_data; /* lock this object */ - down_interruptible(&dev->sem); + retval = down_interruptible(&dev->sem); + if (retval) + goto exit_nolock; /* verify that the device wasn't unplugged */ if (dev->udev == NULL || dev->minor == 0) { @@ -575,7 +578,11 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, } up(&dev->sem); timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout); - down_interruptible(&dev->sem); + retval = down_interruptible(&dev->sem); + if (retval) { + retval = bytes_written ? bytes_written : retval; + goto exit_nolock; + } if (timeout > 0) { break; } @@ -637,6 +644,7 @@ static ssize_t adu_write(struct file *file, const __user char *buffer, exit: /* unlock the device */ up(&dev->sem); +exit_nolock: dbg(2," %s : leave, return value %d", __FUNCTION__, retval); diff --git a/drivers/usb/misc/appledisplay.c b/drivers/usb/misc/appledisplay.c index 32f0e3a..cf70c16 100644 --- a/drivers/usb/misc/appledisplay.c +++ b/drivers/usb/misc/appledisplay.c @@ -141,7 +141,7 @@ static int appledisplay_bl_update_status(struct backlight_device *bd) int retval; pdata->msgdata[0] = 0x10; - pdata->msgdata[1] = bd->props->brightness; + pdata->msgdata[1] = bd->props.brightness; retval = usb_control_msg( pdata->udev, @@ -177,11 +177,9 @@ static int appledisplay_bl_get_brightness(struct backlight_device *bd) return pdata->msgdata[1]; } -static struct backlight_properties appledisplay_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops appledisplay_bl_data = { .get_brightness = appledisplay_bl_get_brightness, .update_status = appledisplay_bl_update_status, - .max_brightness = 0xFF }; static void appledisplay_work(struct work_struct *work) @@ -190,11 +188,9 @@ static void appledisplay_work(struct work_struct *work) container_of(work, struct appledisplay, work.work); int retval; - up(&pdata->bd->sem); retval = appledisplay_bl_get_brightness(pdata->bd); if (retval >= 0) - pdata->bd->props->brightness = retval; - down(&pdata->bd->sem); + pdata->bd->props.brightness = retval; /* Poll again in about 125ms if there's still a button pressed */ if (pdata->button_pressed) @@ -281,17 +277,17 @@ static int appledisplay_probe(struct usb_interface *iface, /* Register backlight device */ snprintf(bl_name, sizeof(bl_name), "appledisplay%d", atomic_inc_return(&count_displays) - 1); - pdata->bd = backlight_device_register(bl_name, NULL, - pdata, &appledisplay_bl_data); + pdata->bd = backlight_device_register(bl_name, NULL, pdata, + &appledisplay_bl_data); if (IS_ERR(pdata->bd)) { err("appledisplay: Backlight registration failed"); goto error; } + pdata->bd->props.max_brightness = 0xff; + /* Try to get brightness */ - up(&pdata->bd->sem); brightness = appledisplay_bl_get_brightness(pdata->bd); - down(&pdata->bd->sem); if (brightness < 0) { retval = brightness; @@ -300,9 +296,7 @@ static int appledisplay_probe(struct usb_interface *iface, } /* Set brightness in backlight device */ - up(&pdata->bd->sem); - pdata->bd->props->brightness = brightness; - down(&pdata->bd->sem); + pdata->bd->props.brightness = brightness; /* save our data pointer in the interface device */ usb_set_intfdata(iface, pdata); diff --git a/drivers/usb/misc/berry_charge.c b/drivers/usb/misc/berry_charge.c new file mode 100644 index 0000000..b15f2fd --- /dev/null +++ b/drivers/usb/misc/berry_charge.c @@ -0,0 +1,140 @@ +/* + * USB BlackBerry charging module + * + * Copyright (C) 2007 Greg Kroah-Hartman <gregkh@suse.de> + * + * This program is free software; 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. + * + * Information on how to switch configs was taken by the bcharge.cc file + * created by the barry.sf.net project. + * + * bcharge.cc has the following copyright: + * Copyright (C) 2006, Net Direct Inc. (http://www.netdirect.ca/) + * and is released under the GPLv2. + * + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> + +#define RIM_VENDOR 0x0fca +#define BLACKBERRY 0x0001 + +static int debug; + +#ifdef dbg +#undef dbg +#endif +#define dbg(dev, format, arg...) \ + if (debug) \ + dev_printk(KERN_DEBUG , dev , format , ## arg) + +static struct usb_device_id id_table [] = { + { USB_DEVICE(RIM_VENDOR, BLACKBERRY) }, + { }, /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, id_table); + +static int magic_charge(struct usb_device *udev) +{ + char *dummy_buffer = kzalloc(2, GFP_KERNEL); + int retval; + + if (!dummy_buffer) + return -ENOMEM; + + /* send two magic commands and then set the configuration. The device + * will then reset itself with the new power usage and should start + * charging. */ + + /* Note, with testing, it only seems that the first message is really + * needed (at least for the 8700c), but to be safe, we emulate what + * other operating systems seem to be sending to their device. We + * really need to get some specs for this device to be sure about what + * is going on here. + */ + dbg(&udev->dev, "Sending first magic command\n"); + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0xa5, 0xc0, 0, 1, dummy_buffer, 2, 100); + if (retval != 2) { + dev_err(&udev->dev, "First magic command failed: %d.\n", + retval); + return retval; + } + + dbg(&udev->dev, "Sending second magic command\n"); + retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0xa2, 0x40, 0, 1, dummy_buffer, 0, 100); + if (retval != 0) { + dev_err(&udev->dev, "Second magic command failed: %d.\n", + retval); + return retval; + } + + dbg(&udev->dev, "Calling set_configuration\n"); + retval = usb_driver_set_configuration(udev, 1); + if (retval) + dev_err(&udev->dev, "Set Configuration failed :%d.\n", retval); + + return retval; +} + +static int berry_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + + dbg(&udev->dev, "Power is set to %dmA\n", + udev->actconfig->desc.bMaxPower * 2); + + /* check the power usage so we don't try to enable something that is + * already enabled */ + if ((udev->actconfig->desc.bMaxPower * 2) == 500) { + dbg(&udev->dev, "device is already charging, power is " + "set to %dmA\n", udev->actconfig->desc.bMaxPower * 2); + return -ENODEV; + } + + /* turn the power on */ + magic_charge(udev); + + /* we don't really want to bind to the device, userspace programs can + * handle the syncing just fine, so get outta here. */ + return -ENODEV; +} + +static void berry_disconnect(struct usb_interface *intf) +{ +} + +static struct usb_driver berry_driver = { + .name = "berry_charge", + .probe = berry_probe, + .disconnect = berry_disconnect, + .id_table = id_table, +}; + +static int __init berry_init(void) +{ + return usb_register(&berry_driver); +} + +static void __exit berry_exit(void) +{ + usb_deregister(&berry_driver); +} + +module_init(berry_init); +module_exit(berry_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@suse.de>"); +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c index b63b5f3..d721380 100644 --- a/drivers/usb/misc/cypress_cy7c63.c +++ b/drivers/usb/misc/cypress_cy7c63.c @@ -246,11 +246,13 @@ static void cypress_disconnect(struct usb_interface *interface) struct cypress *dev; dev = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); /* remove device attribute files */ device_remove_file(&interface->dev, &dev_attr_port0); device_remove_file(&interface->dev, &dev_attr_port1); + /* the intfdata can be set to NULL only after the + * device files have been removed */ + usb_set_intfdata(interface, NULL); usb_put_dev(dev->udev); diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 0c1d66d..e2172e5 100644 --- a/drivers/usb/misc/ftdi-elan.c +++ b/drivers/usb/misc/ftdi-elan.c @@ -2304,7 +2304,6 @@ static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi) #define OHCI_QUIRK_SUPERIO 0x02 #define OHCI_QUIRK_INITRESET 0x04 #define OHCI_BIG_ENDIAN 0x08 -#define OHCI_QUIRK_ZFMICRO 0x10 #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR #define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \ OHCI_INTR_WDH) @@ -2905,17 +2904,35 @@ static int __init ftdi_elan_init(void) { int result; printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name, - __TIME__, __DATE__); + __TIME__, __DATE__); init_MUTEX(&ftdi_module_lock); INIT_LIST_HEAD(&ftdi_static_list); status_queue = create_singlethread_workqueue("ftdi-status-control"); + if (!status_queue) + goto err_status_queue; command_queue = create_singlethread_workqueue("ftdi-command-engine"); + if (!command_queue) + goto err_command_queue; respond_queue = create_singlethread_workqueue("ftdi-respond-engine"); + if (!respond_queue) + goto err_respond_queue; result = usb_register(&ftdi_elan_driver); - if (result) + if (result) { + destroy_workqueue(status_queue); + destroy_workqueue(command_queue); + destroy_workqueue(respond_queue); printk(KERN_ERR "usb_register failed. Error number %d\n", - result); + result); + } return result; + + err_respond_queue: + destroy_workqueue(command_queue); + err_command_queue: + destroy_workqueue(status_queue); + err_status_queue: + printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name); + return -ENOMEM; } static void __exit ftdi_elan_exit(void) diff --git a/drivers/usb/misc/iowarrior.c b/drivers/usb/misc/iowarrior.c new file mode 100644 index 0000000..fc51207 --- /dev/null +++ b/drivers/usb/misc/iowarrior.c @@ -0,0 +1,925 @@ +/* + * Native support for the I/O-Warrior USB devices + * + * Copyright (c) 2003-2005 Code Mercenaries GmbH + * written by Christian Lucht <lucht@codemercs.com> + * + * based on + + * usb-skeleton.c by Greg Kroah-Hartman <greg@kroah.com> + * brlvger.c by Stephane Dalton <sdalton@videotron.ca> + * and St�hane Doyon <s.doyon@videotron.ca> + * + * Released under the GPLv2. + */ + +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/poll.h> +#include <linux/version.h> +#include <linux/usb/iowarrior.h> + +/* Version Information */ +#define DRIVER_VERSION "v0.4.0" +#define DRIVER_AUTHOR "Christian Lucht <lucht@codemercs.com>" +#define DRIVER_DESC "USB IO-Warrior driver (Linux 2.6.x)" + +#define USB_VENDOR_ID_CODEMERCS 1984 +/* low speed iowarrior */ +#define USB_DEVICE_ID_CODEMERCS_IOW40 0x1500 +#define USB_DEVICE_ID_CODEMERCS_IOW24 0x1501 +#define USB_DEVICE_ID_CODEMERCS_IOWPV1 0x1511 +#define USB_DEVICE_ID_CODEMERCS_IOWPV2 0x1512 +/* full speed iowarrior */ +#define USB_DEVICE_ID_CODEMERCS_IOW56 0x1503 + +/* Get a minor range for your devices from the usb maintainer */ +#ifdef CONFIG_USB_DYNAMIC_MINORS +#define IOWARRIOR_MINOR_BASE 0 +#else +#define IOWARRIOR_MINOR_BASE 208 // SKELETON_MINOR_BASE 192 + 16, not offical yet +#endif + +/* interrupt input queue size */ +#define MAX_INTERRUPT_BUFFER 16 +/* + maximum number of urbs that are submitted for writes at the same time, + this applies to the IOWarrior56 only! + IOWarrior24 and IOWarrior40 use synchronous usb_control_msg calls. +*/ +#define MAX_WRITES_IN_FLIGHT 4 + +/* Use our own dbg macro */ +#undef dbg +#define dbg( format, arg... ) do { if( debug ) printk( KERN_DEBUG __FILE__ ": " format "\n" , ## arg ); } while ( 0 ) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +/* Module parameters */ +static int debug = 0; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "debug=1 enables debugging messages"); + +static struct usb_driver iowarrior_driver; + +/*--------------*/ +/* data */ +/*--------------*/ + +/* Structure to hold all of our device specific stuff */ +struct iowarrior { + struct mutex mutex; /* locks this structure */ + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface; /* the interface for this device */ + unsigned char minor; /* the starting minor number for this device */ + struct usb_endpoint_descriptor *int_out_endpoint; /* endpoint for reading (needed for IOW56 only) */ + struct usb_endpoint_descriptor *int_in_endpoint; /* endpoint for reading */ + struct urb *int_in_urb; /* the urb for reading data */ + unsigned char *int_in_buffer; /* buffer for data to be read */ + unsigned char serial_number; /* to detect lost packages */ + unsigned char *read_queue; /* size is MAX_INTERRUPT_BUFFER * packet size */ + wait_queue_head_t read_wait; + wait_queue_head_t write_wait; /* wait-queue for writing to the device */ + atomic_t write_busy; /* number of write-urbs submitted */ + atomic_t read_idx; + atomic_t intr_idx; + spinlock_t intr_idx_lock; /* protects intr_idx */ + atomic_t overflow_flag; /* signals an index 'rollover' */ + int present; /* this is 1 as long as the device is connected */ + int opened; /* this is 1 if the device is currently open */ + char chip_serial[9]; /* the serial number string of the chip connected */ + int report_size; /* number of bytes in a report */ + u16 product_id; +}; + +/*--------------*/ +/* globals */ +/*--------------*/ +/* prevent races between open() and disconnect() */ +static DECLARE_MUTEX(disconnect_sem); + +/* + * USB spec identifies 5 second timeouts. + */ +#define GET_TIMEOUT 5 +#define USB_REQ_GET_REPORT 0x01 +//#if 0 +static int usb_get_report(struct usb_device *dev, + struct usb_host_interface *inter, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, (type << 8) + id, + inter->desc.bInterfaceNumber, buf, size, + GET_TIMEOUT*HZ); +} +//#endif + +#define USB_REQ_SET_REPORT 0x09 + +static int usb_set_report(struct usb_interface *intf, unsigned char type, + unsigned char id, void *buf, int size) +{ + return usb_control_msg(interface_to_usbdev(intf), + usb_sndctrlpipe(interface_to_usbdev(intf), 0), + USB_REQ_SET_REPORT, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, + intf->cur_altsetting->desc.bInterfaceNumber, buf, + size, HZ); +} + +/*---------------------*/ +/* driver registration */ +/*---------------------*/ +/* table of devices that work with this driver */ +static struct usb_device_id iowarrior_ids[] = { + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV1)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOWPV2)}, + {USB_DEVICE(USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW56)}, + {} /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, iowarrior_ids); + +/* + * USB callback handler for reading data + */ +static void iowarrior_callback(struct urb *urb) +{ + struct iowarrior *dev = (struct iowarrior *)urb->context; + int intr_idx; + int read_idx; + int aux_idx; + int offset; + int status; + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + return; + default: + goto exit; + } + + spin_lock(&dev->intr_idx_lock); + intr_idx = atomic_read(&dev->intr_idx); + /* aux_idx become previous intr_idx */ + aux_idx = (intr_idx == 0) ? (MAX_INTERRUPT_BUFFER - 1) : (intr_idx - 1); + read_idx = atomic_read(&dev->read_idx); + + /* queue is not empty and it's interface 0 */ + if ((intr_idx != read_idx) + && (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0)) { + /* + 1 for serial number */ + offset = aux_idx * (dev->report_size + 1); + if (!memcmp + (dev->read_queue + offset, urb->transfer_buffer, + dev->report_size)) { + /* equal values on interface 0 will be ignored */ + spin_unlock(&dev->intr_idx_lock); + goto exit; + } + } + + /* aux_idx become next intr_idx */ + aux_idx = (intr_idx == (MAX_INTERRUPT_BUFFER - 1)) ? 0 : (intr_idx + 1); + if (read_idx == aux_idx) { + /* queue full, dropping oldest input */ + read_idx = (++read_idx == MAX_INTERRUPT_BUFFER) ? 0 : read_idx; + atomic_set(&dev->read_idx, read_idx); + atomic_set(&dev->overflow_flag, 1); + } + + /* +1 for serial number */ + offset = intr_idx * (dev->report_size + 1); + memcpy(dev->read_queue + offset, urb->transfer_buffer, + dev->report_size); + *(dev->read_queue + offset + (dev->report_size)) = dev->serial_number++; + + atomic_set(&dev->intr_idx, aux_idx); + spin_unlock(&dev->intr_idx_lock); + /* tell the blocking read about the new data */ + wake_up_interruptible(&dev->read_wait); + +exit: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + dev_err(&dev->interface->dev, "%s - usb_submit_urb failed with result %d", + __FUNCTION__, status); + +} + +/* + * USB Callback handler for write-ops + */ +static void iowarrior_write_callback(struct urb *urb) +{ + struct iowarrior *dev; + dev = (struct iowarrior *)urb->context; + /* sync/async unlink faults aren't errors */ + if (urb->status && + !(urb->status == -ENOENT || + urb->status == -ECONNRESET || urb->status == -ESHUTDOWN)) { + dbg("%s - nonzero write bulk status received: %d", + __func__, urb->status); + } + /* free up our allocated buffer */ + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + /* tell a waiting writer the interrupt-out-pipe is available again */ + atomic_dec(&dev->write_busy); + wake_up_interruptible(&dev->write_wait); +} + +/** + * iowarrior_delete + */ +static inline void iowarrior_delete(struct iowarrior *dev) +{ + dbg("%s - minor %d", __func__, dev->minor); + kfree(dev->int_in_buffer); + usb_free_urb(dev->int_in_urb); + kfree(dev->read_queue); + kfree(dev); +} + +/*---------------------*/ +/* fops implementation */ +/*---------------------*/ + +static int read_index(struct iowarrior *dev) +{ + int intr_idx, read_idx; + + read_idx = atomic_read(&dev->read_idx); + intr_idx = atomic_read(&dev->intr_idx); + + return (read_idx == intr_idx ? -1 : read_idx); +} + +/** + * iowarrior_read + */ +static ssize_t iowarrior_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct iowarrior *dev; + int read_idx; + int offset; + + dev = (struct iowarrior *)file->private_data; + + /* verify that the device wasn't unplugged */ + if (dev == NULL || !dev->present) + return -ENODEV; + + dbg("%s - minor %d, count = %zd", __func__, dev->minor, count); + + /* read count must be packet size (+ time stamp) */ + if ((count != dev->report_size) + && (count != (dev->report_size + 1))) + return -EINVAL; + + /* repeat until no buffer overrun in callback handler occur */ + do { + atomic_set(&dev->overflow_flag, 0); + if ((read_idx = read_index(dev)) == -1) { + /* queue emty */ + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + else { + //next line will return when there is either new data, or the device is unplugged + int r = wait_event_interruptible(dev->read_wait, + (!dev->present + || (read_idx = + read_index + (dev)) != + -1)); + if (r) { + //we were interrupted by a signal + return -ERESTART; + } + if (!dev->present) { + //The device was unplugged + return -ENODEV; + } + if (read_idx == -1) { + // Can this happen ??? + return 0; + } + } + } + + offset = read_idx * (dev->report_size + 1); + if (copy_to_user(buffer, dev->read_queue + offset, count)) { + return -EFAULT; + } + } while (atomic_read(&dev->overflow_flag)); + + read_idx = ++read_idx == MAX_INTERRUPT_BUFFER ? 0 : read_idx; + atomic_set(&dev->read_idx, read_idx); + return count; +} + +/* + * iowarrior_write + */ +static ssize_t iowarrior_write(struct file *file, + const char __user *user_buffer, + size_t count, loff_t *ppos) +{ + struct iowarrior *dev; + int retval = 0; + char *buf = NULL; /* for IOW24 and IOW56 we need a buffer */ + struct urb *int_out_urb = NULL; + + dev = (struct iowarrior *)file->private_data; + + mutex_lock(&dev->mutex); + /* verify that the device wasn't unplugged */ + if (dev == NULL || !dev->present) { + retval = -ENODEV; + goto exit; + } + dbg("%s - minor %d, count = %zd", __func__, dev->minor, count); + /* if count is 0 we're already done */ + if (count == 0) { + retval = 0; + goto exit; + } + /* We only accept full reports */ + if (count != dev->report_size) { + retval = -EINVAL; + goto exit; + } + switch (dev->product_id) { + case USB_DEVICE_ID_CODEMERCS_IOW24: + case USB_DEVICE_ID_CODEMERCS_IOWPV1: + case USB_DEVICE_ID_CODEMERCS_IOWPV2: + case USB_DEVICE_ID_CODEMERCS_IOW40: + /* IOW24 and IOW40 use a synchronous call */ + buf = kmalloc(8, GFP_KERNEL); /* 8 bytes are enough for both products */ + if (!buf) { + retval = -ENOMEM; + goto exit; + } + if (copy_from_user(buf, user_buffer, count)) { + retval = -EFAULT; + kfree(buf); + goto exit; + } + retval = usb_set_report(dev->interface, 2, 0, buf, count); + kfree(buf); + goto exit; + break; + case USB_DEVICE_ID_CODEMERCS_IOW56: + /* The IOW56 uses asynchronous IO and more urbs */ + if (atomic_read(&dev->write_busy) == MAX_WRITES_IN_FLIGHT) { + /* Wait until we are below the limit for submitted urbs */ + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto exit; + } else { + retval = wait_event_interruptible(dev->write_wait, + (!dev->present || (atomic_read (&dev-> write_busy) < MAX_WRITES_IN_FLIGHT))); + if (retval) { + /* we were interrupted by a signal */ + retval = -ERESTART; + goto exit; + } + if (!dev->present) { + /* The device was unplugged */ + retval = -ENODEV; + goto exit; + } + if (!dev->opened) { + /* We were closed while waiting for an URB */ + retval = -ENODEV; + goto exit; + } + } + } + atomic_inc(&dev->write_busy); + int_out_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!int_out_urb) { + retval = -ENOMEM; + dbg("%s Unable to allocate urb ", __func__); + goto error_no_urb; + } + buf = usb_buffer_alloc(dev->udev, dev->report_size, + GFP_KERNEL, &int_out_urb->transfer_dma); + if (!buf) { + retval = -ENOMEM; + dbg("%s Unable to allocate buffer ", __func__); + goto error_no_buffer; + } + usb_fill_int_urb(int_out_urb, dev->udev, + usb_sndintpipe(dev->udev, + dev->int_out_endpoint->bEndpointAddress), + buf, dev->report_size, + iowarrior_write_callback, dev, + dev->int_out_endpoint->bInterval); + int_out_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + if (copy_from_user(buf, user_buffer, count)) { + retval = -EFAULT; + goto error; + } + retval = usb_submit_urb(int_out_urb, GFP_KERNEL); + if (retval) { + dbg("%s submit error %d for urb nr.%d", __func__, + retval, atomic_read(&dev->write_busy)); + goto error; + } + /* submit was ok */ + retval = count; + usb_free_urb(int_out_urb); + goto exit; + break; + default: + /* what do we have here ? An unsupported Product-ID ? */ + dev_err(&dev->interface->dev, "%s - not supported for product=0x%x", + __FUNCTION__, dev->product_id); + retval = -EFAULT; + goto exit; + break; + } +error: + usb_buffer_free(dev->udev, dev->report_size, buf, + int_out_urb->transfer_dma); +error_no_buffer: + usb_free_urb(int_out_urb); +error_no_urb: + atomic_dec(&dev->write_busy); + wake_up_interruptible(&dev->write_wait); +exit: + mutex_unlock(&dev->mutex); + return retval; +} + +/** + * iowarrior_ioctl + */ +static int iowarrior_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct iowarrior *dev = NULL; + __u8 *buffer; + __u8 __user *user_buffer; + int retval; + int io_res; /* checks for bytes read/written and copy_to/from_user results */ + + dev = (struct iowarrior *)file->private_data; + if (dev == NULL) { + return -ENODEV; + } + + buffer = kzalloc(dev->report_size, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + /* lock this object */ + mutex_lock(&dev->mutex); + + /* verify that the device wasn't unplugged */ + if (!dev->present) { + mutex_unlock(&dev->mutex); + return -ENODEV; + } + + dbg("%s - minor %d, cmd 0x%.4x, arg %ld", __func__, dev->minor, cmd, + arg); + + retval = 0; + io_res = 0; + switch (cmd) { + case IOW_WRITE: + if (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW24 || + dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV1 || + dev->product_id == USB_DEVICE_ID_CODEMERCS_IOWPV2 || + dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW40) { + user_buffer = (__u8 __user *)arg; + io_res = copy_from_user(buffer, user_buffer, + dev->report_size); + if (io_res) { + retval = -EFAULT; + } else { + io_res = usb_set_report(dev->interface, 2, 0, + buffer, + dev->report_size); + if (io_res < 0) + retval = io_res; + } + } else { + retval = -EINVAL; + dev_err(&dev->interface->dev, + "ioctl 'IOW_WRITE' is not supported for product=0x%x.", + dev->product_id); + } + break; + case IOW_READ: + user_buffer = (__u8 __user *)arg; + io_res = usb_get_report(dev->udev, + dev->interface->cur_altsetting, 1, 0, + buffer, dev->report_size); + if (io_res < 0) + retval = io_res; + else { + io_res = copy_to_user(user_buffer, buffer, dev->report_size); + if (io_res < 0) + retval = -EFAULT; + } + break; + case IOW_GETINFO: + { + /* Report available information for the device */ + struct iowarrior_info info; + /* needed for power consumption */ + struct usb_config_descriptor *cfg_descriptor = &dev->udev->actconfig->desc; + + /* directly from the descriptor */ + info.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + info.product = dev->product_id; + info.revision = le16_to_cpu(dev->udev->descriptor.bcdDevice); + + /* 0==UNKNOWN, 1==LOW(usb1.1) ,2=FULL(usb1.1), 3=HIGH(usb2.0) */ + info.speed = le16_to_cpu(dev->udev->speed); + info.if_num = dev->interface->cur_altsetting->desc.bInterfaceNumber; + info.report_size = dev->report_size; + + /* serial number string has been read earlier 8 chars or empty string */ + memcpy(info.serial, dev->chip_serial, + sizeof(dev->chip_serial)); + if (cfg_descriptor == NULL) { + info.power = -1; /* no information available */ + } else { + /* the MaxPower is stored in units of 2mA to make it fit into a byte-value */ + info.power = cfg_descriptor->bMaxPower * 2; + } + io_res = copy_to_user((struct iowarrior_info __user *)arg, &info, + sizeof(struct iowarrior_info)); + if (io_res < 0) + retval = -EFAULT; + break; + } + default: + /* return that we did not understand this ioctl call */ + retval = -ENOTTY; + break; + } + + /* unlock the device */ + mutex_unlock(&dev->mutex); + return retval; +} + +/** + * iowarrior_open + */ +static int iowarrior_open(struct inode *inode, struct file *file) +{ + struct iowarrior *dev = NULL; + struct usb_interface *interface; + int subminor; + int retval = 0; + + dbg("%s", __func__); + + subminor = iminor(inode); + + /* prevent disconnects */ + down(&disconnect_sem); + + interface = usb_find_interface(&iowarrior_driver, subminor); + if (!interface) { + err("%s - error, can't find device for minor %d", __FUNCTION__, + subminor); + retval = -ENODEV; + goto out; + } + + dev = usb_get_intfdata(interface); + if (!dev) { + retval = -ENODEV; + goto out; + } + + /* Only one process can open each device, no sharing. */ + if (dev->opened) { + retval = -EBUSY; + goto out; + } + + /* setup interrupt handler for receiving values */ + if ((retval = usb_submit_urb(dev->int_in_urb, GFP_KERNEL)) < 0) { + dev_err(&interface->dev, "Error %d while submitting URB\n", retval); + retval = -EFAULT; + goto out; + } + /* increment our usage count for the driver */ + ++dev->opened; + /* save our object in the file's private structure */ + file->private_data = dev; + retval = 0; + +out: + up(&disconnect_sem); + return retval; +} + +/** + * iowarrior_release + */ +static int iowarrior_release(struct inode *inode, struct file *file) +{ + struct iowarrior *dev; + int retval = 0; + + dev = (struct iowarrior *)file->private_data; + if (dev == NULL) { + return -ENODEV; + } + + dbg("%s - minor %d", __func__, dev->minor); + + /* lock our device */ + mutex_lock(&dev->mutex); + + if (dev->opened <= 0) { + retval = -ENODEV; /* close called more than once */ + mutex_unlock(&dev->mutex); + } else { + dev->opened = 0; /* we're closeing now */ + retval = 0; + if (dev->present) { + /* + The device is still connected so we only shutdown + pending read-/write-ops. + */ + usb_kill_urb(dev->int_in_urb); + wake_up_interruptible(&dev->read_wait); + wake_up_interruptible(&dev->write_wait); + mutex_unlock(&dev->mutex); + } else { + /* The device was unplugged, cleanup resources */ + mutex_unlock(&dev->mutex); + iowarrior_delete(dev); + } + } + return retval; +} + +static unsigned iowarrior_poll(struct file *file, poll_table * wait) +{ + struct iowarrior *dev = file->private_data; + unsigned int mask = 0; + + if (!dev->present) + return POLLERR | POLLHUP; + + poll_wait(file, &dev->read_wait, wait); + poll_wait(file, &dev->write_wait, wait); + + if (!dev->present) + return POLLERR | POLLHUP; + + if (read_index(dev) != -1) + mask |= POLLIN | POLLRDNORM; + + if (atomic_read(&dev->write_busy) < MAX_WRITES_IN_FLIGHT) + mask |= POLLOUT | POLLWRNORM; + return mask; +} + +/* + * File operations needed when we register this driver. + * This assumes that this driver NEEDS file operations, + * of course, which means that the driver is expected + * to have a node in the /dev directory. If the USB + * device were for a network interface then the driver + * would use "struct net_driver" instead, and a serial + * device would use "struct tty_driver". + */ +static struct file_operations iowarrior_fops = { + .owner = THIS_MODULE, + .write = iowarrior_write, + .read = iowarrior_read, + .ioctl = iowarrior_ioctl, + .open = iowarrior_open, + .release = iowarrior_release, + .poll = iowarrior_poll, +}; + +/* + * usb class driver info in order to get a minor number from the usb core, + * and to have the device registered with devfs and the driver core + */ +static struct usb_class_driver iowarrior_class = { + .name = "iowarrior%d", + .fops = &iowarrior_fops, + .minor_base = IOWARRIOR_MINOR_BASE, +}; + +/*---------------------------------*/ +/* probe and disconnect functions */ +/*---------------------------------*/ +/** + * iowarrior_probe + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int iowarrior_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct iowarrior *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and intialize it */ + dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL); + if (dev == NULL) { + dev_err(&interface->dev, "Out of memory"); + return retval; + } + + mutex_init(&dev->mutex); + + atomic_set(&dev->intr_idx, 0); + atomic_set(&dev->read_idx, 0); + spin_lock_init(&dev->intr_idx_lock); + atomic_set(&dev->overflow_flag, 0); + init_waitqueue_head(&dev->read_wait); + atomic_set(&dev->write_busy, 0); + init_waitqueue_head(&dev->write_wait); + + dev->udev = udev; + dev->interface = interface; + + iface_desc = interface->cur_altsetting; + dev->product_id = le16_to_cpu(udev->descriptor.idProduct); + + /* set up the endpoint information */ + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (usb_endpoint_is_int_in(endpoint)) + dev->int_in_endpoint = endpoint; + if (usb_endpoint_is_int_out(endpoint)) + /* this one will match for the IOWarrior56 only */ + dev->int_out_endpoint = endpoint; + } + /* we have to check the report_size often, so remember it in the endianess suitable for our machine */ + dev->report_size = le16_to_cpu(dev->int_in_endpoint->wMaxPacketSize); + if ((dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) && + (dev->product_id == USB_DEVICE_ID_CODEMERCS_IOW56)) + /* IOWarrior56 has wMaxPacketSize different from report size */ + dev->report_size = 7; + + /* create the urb and buffer for reading */ + dev->int_in_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->int_in_urb) { + dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n"); + goto error; + } + dev->int_in_buffer = kmalloc(dev->report_size, GFP_KERNEL); + if (!dev->int_in_buffer) { + dev_err(&interface->dev, "Couldn't allocate int_in_buffer\n"); + goto error; + } + usb_fill_int_urb(dev->int_in_urb, dev->udev, + usb_rcvintpipe(dev->udev, + dev->int_in_endpoint->bEndpointAddress), + dev->int_in_buffer, dev->report_size, + iowarrior_callback, dev, + dev->int_in_endpoint->bInterval); + /* create an internal buffer for interrupt data from the device */ + dev->read_queue = + kmalloc(((dev->report_size + 1) * MAX_INTERRUPT_BUFFER), + GFP_KERNEL); + if (!dev->read_queue) { + dev_err(&interface->dev, "Couldn't allocate read_queue\n"); + goto error; + } + /* Get the serial-number of the chip */ + memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial)); + usb_string(udev, udev->descriptor.iSerialNumber, dev->chip_serial, + sizeof(dev->chip_serial)); + if (strlen(dev->chip_serial) != 8) + memset(dev->chip_serial, 0x00, sizeof(dev->chip_serial)); + + /* Set the idle timeout to 0, if this is interface 0 */ + if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) { + usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x0A, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + 0, NULL, 0, USB_CTRL_SET_TIMEOUT); + } + /* allow device read and ioctl */ + dev->present = 1; + + /* we can register the device now, as it is ready */ + usb_set_intfdata(interface, dev); + + retval = usb_register_dev(interface, &iowarrior_class); + if (retval) { + /* something prevented us from registering this driver */ + dev_err(&interface->dev, "Not able to get a minor for this device.\n"); + usb_set_intfdata(interface, NULL); + goto error; + } + + dev->minor = interface->minor; + + /* let the user know what node this device is now attached to */ + dev_info(&interface->dev, "IOWarrior product=0x%x, serial=%s interface=%d " + "now attached to iowarrior%d\n", dev->product_id, dev->chip_serial, + iface_desc->desc.bInterfaceNumber, dev->minor - IOWARRIOR_MINOR_BASE); + return retval; + +error: + iowarrior_delete(dev); + return retval; +} + +/** + * iowarrior_disconnect + * + * Called by the usb core when the device is removed from the system. + */ +static void iowarrior_disconnect(struct usb_interface *interface) +{ + struct iowarrior *dev; + int minor; + + /* prevent races with open() */ + down(&disconnect_sem); + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + mutex_lock(&dev->mutex); + + minor = dev->minor; + + /* give back our minor */ + usb_deregister_dev(interface, &iowarrior_class); + + /* prevent device read, write and ioctl */ + dev->present = 0; + + mutex_unlock(&dev->mutex); + + if (dev->opened) { + /* There is a process that holds a filedescriptor to the device , + so we only shutdown read-/write-ops going on. + Deleting the device is postponed until close() was called. + */ + usb_kill_urb(dev->int_in_urb); + wake_up_interruptible(&dev->read_wait); + wake_up_interruptible(&dev->write_wait); + } else { + /* no process is using the device, cleanup now */ + iowarrior_delete(dev); + } + up(&disconnect_sem); + + dev_info(&interface->dev, "I/O-Warror #%d now disconnected\n", + minor - IOWARRIOR_MINOR_BASE); +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver iowarrior_driver = { + .name = "iowarrior", + .probe = iowarrior_probe, + .disconnect = iowarrior_disconnect, + .id_table = iowarrior_ids, +}; + +static int __init iowarrior_init(void) +{ + return usb_register(&iowarrior_driver); +} + +static void __exit iowarrior_exit(void) +{ + usb_deregister(&iowarrior_driver); +} + +module_init(iowarrior_init); +module_exit(iowarrior_exit); diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index 788a11e..11555bd 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -62,6 +62,8 @@ #define USB_DEVICE_ID_VERNIER_SKIP 0x0003 #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004 +#define USB_VENDOR_ID_MICROCHIP 0x04d8 +#define USB_DEVICE_ID_PICDEM 0x000c #ifdef CONFIG_USB_DYNAMIC_MINORS #define USB_LD_MINOR_BASE 0 @@ -89,6 +91,7 @@ static struct usb_device_id ld_usb_table [] = { { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, + { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ld_usb_table); diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c index ada2ebc..887ef95 100644 --- a/drivers/usb/misc/usblcd.c +++ b/drivers/usb/misc/usblcd.c @@ -47,6 +47,7 @@ struct usb_lcd { #define to_lcd_dev(d) container_of(d, struct usb_lcd, kref) static struct usb_driver lcd_driver; +static DEFINE_MUTEX(usb_lcd_open_mutex); static void lcd_delete(struct kref *kref) @@ -68,6 +69,7 @@ static int lcd_open(struct inode *inode, struct file *file) subminor = iminor(inode); + mutex_lock(&usb_lcd_open_mutex); interface = usb_find_interface(&lcd_driver, subminor); if (!interface) { err ("USBLCD: %s - error, can't find device for minor %d", @@ -89,6 +91,7 @@ static int lcd_open(struct inode *inode, struct file *file) file->private_data = dev; exit: + mutex_unlock(&usb_lcd_open_mutex); return retval; } @@ -347,7 +350,7 @@ static void lcd_disconnect(struct usb_interface *interface) int minor = interface->minor; /* prevent skel_open() from racing skel_disconnect() */ - lock_kernel(); + mutex_lock(&usb_lcd_open_mutex); dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); @@ -355,7 +358,7 @@ static void lcd_disconnect(struct usb_interface *interface) /* give back our minor */ usb_deregister_dev(interface, &lcd_class); - unlock_kernel(); + mutex_unlock(&usb_lcd_open_mutex); /* decrement our usage count */ kref_put(&dev->kref, lcd_delete); diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index c01dfe6..0af11a6 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -356,8 +356,10 @@ static inline char mon_bin_get_setup(unsigned char *setupb, if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') return '-'; - if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN); + } if (urb->setup_packet == NULL) return 'Z'; @@ -369,7 +371,8 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp, unsigned int offset, struct urb *urb, unsigned int length) { - if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) { + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { mon_dmapeek_vec(rp, offset, urb->transfer_dma, length); return 0; } @@ -440,7 +443,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb, /* We use the fact that usb_pipein() returns 0x80 */ ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; ep->ts_sec = ts.tv_sec; ep->ts_usec = ts.tv_usec; @@ -500,7 +503,7 @@ static void mon_bin_error(void *data, struct urb *urb, int error) /* We use the fact that usb_pipein() returns 0x80 */ ep->epnum = usb_pipeendpoint(urb->pipe) | usb_pipein(urb->pipe); ep->devnum = usb_pipedevice(urb->pipe); - ep->busnum = rp->r.m_bus->u_bus->busnum; + ep->busnum = urb->dev->bus->busnum; ep->id = (unsigned long) urb; ep->status = error; @@ -515,7 +518,6 @@ static void mon_bin_error(void *data, struct urb *urb, int error) static int mon_bin_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_bin *rp; size_t size; int rc; @@ -525,7 +527,7 @@ static int mon_bin_open(struct inode *inode, struct file *file) mutex_unlock(&mon_lock); return -ENODEV; } - if ((ubus = mbus->u_bus) == NULL) { + if (mbus != &mon_bus0 && mbus->u_bus == NULL) { printk(KERN_ERR TAG ": consistency error on open\n"); mutex_unlock(&mon_lock); return -ENODEV; @@ -1165,7 +1167,7 @@ err_dev: return rc; } -void __exit mon_bin_exit(void) +void mon_bin_exit(void) { cdev_del(&mon_bin_cdev); unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR); diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index c9739e7..8a1df2c 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -16,8 +16,6 @@ #include "usb_mon.h" #include "../core/hcd.h" -static void mon_submit(struct usb_bus *ubus, struct urb *urb); -static void mon_complete(struct usb_bus *ubus, struct urb *urb); static void mon_stop(struct mon_bus *mbus); static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus); static void mon_bus_drop(struct kref *r); @@ -25,6 +23,7 @@ static void mon_bus_init(struct usb_bus *ubus); DEFINE_MUTEX(mon_lock); +struct mon_bus mon_bus0; /* Pseudo bus meaning "all buses" */ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ /* @@ -35,22 +34,19 @@ static LIST_HEAD(mon_buses); /* All buses we know: struct mon_bus */ void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r) { unsigned long flags; - struct usb_bus *ubus; + struct list_head *p; spin_lock_irqsave(&mbus->lock, flags); if (mbus->nreaders == 0) { - ubus = mbus->u_bus; - if (ubus->monitored) { - /* - * Something is really broken, refuse to go on and - * possibly corrupt ops pointers or worse. - */ - printk(KERN_ERR TAG ": bus %d is already monitored\n", - ubus->busnum); - spin_unlock_irqrestore(&mbus->lock, flags); - return; + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + struct mon_bus *m1; + m1 = list_entry(p, struct mon_bus, bus_link); + m1->u_bus->monitored = 1; + } + } else { + mbus->u_bus->monitored = 1; } - ubus->monitored = 1; } mbus->nreaders++; list_add_tail(&r->r_link, &mbus->r_list); @@ -80,77 +76,79 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r) /* */ -static void mon_submit(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_submit(struct mon_bus *mbus, struct urb *urb) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_submit(r->r_data, urb); } - spin_unlock_irqrestore(&mbus->lock, flags); return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit(mbus, urb); + mon_bus_submit(&mon_bus0, urb); } /* */ -static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +static void mon_bus_submit_error(struct mon_bus *mbus, struct urb *urb, int error) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; - mbus = ubus->mon_bus; - if (mbus == NULL) - goto out_unlocked; - spin_lock_irqsave(&mbus->lock, flags); - if (mbus->nreaders == 0) - goto out_locked; - mbus->cnt_events++; list_for_each (pos, &mbus->r_list) { r = list_entry(pos, struct mon_reader, r_link); r->rnf_error(r->r_data, urb, error); } - spin_unlock_irqrestore(&mbus->lock, flags); return; +} -out_locked: - spin_unlock_irqrestore(&mbus->lock, flags); -out_unlocked: - return; +static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) +{ + struct mon_bus *mbus; + + if ((mbus = ubus->mon_bus) != NULL) + mon_bus_submit_error(mbus, urb, error); + mon_bus_submit_error(&mon_bus0, urb, error); } /* */ -static void mon_complete(struct usb_bus *ubus, struct urb *urb) +static void mon_bus_complete(struct mon_bus *mbus, struct urb *urb) { - struct mon_bus *mbus; unsigned long flags; struct list_head *pos; struct mon_reader *r; + spin_lock_irqsave(&mbus->lock, flags); + mbus->cnt_events++; + list_for_each (pos, &mbus->r_list) { + r = list_entry(pos, struct mon_reader, r_link); + r->rnf_complete(r->r_data, urb); + } + spin_unlock_irqrestore(&mbus->lock, flags); +} + +static void mon_complete(struct usb_bus *ubus, struct urb *urb) +{ + struct mon_bus *mbus; + mbus = ubus->mon_bus; if (mbus == NULL) { /* @@ -162,13 +160,8 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb) return; } - spin_lock_irqsave(&mbus->lock, flags); - mbus->cnt_events++; - list_for_each (pos, &mbus->r_list) { - r = list_entry(pos, struct mon_reader, r_link); - r->rnf_complete(r->r_data, urb); - } - spin_unlock_irqrestore(&mbus->lock, flags); + mon_bus_complete(mbus, urb); + mon_bus_complete(&mon_bus0, urb); } /* int (*unlink_urb) (struct urb *urb, int status); */ @@ -179,14 +172,26 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb) static void mon_stop(struct mon_bus *mbus) { struct usb_bus *ubus = mbus->u_bus; + struct list_head *p; - /* - * A stop can be called for a dissolved mon_bus in case of - * a reader staying across an rmmod foo_hcd. - */ - if (ubus != NULL) { - ubus->monitored = 0; - mb(); + if (mbus == &mon_bus0) { + list_for_each (p, &mon_buses) { + mbus = list_entry(p, struct mon_bus, bus_link); + /* + * We do not change nreaders here, so rely on mon_lock. + */ + if (mbus->nreaders == 0 && (ubus = mbus->u_bus) != NULL) + ubus->monitored = 0; + } + } else { + /* + * A stop can be called for a dissolved mon_bus in case of + * a reader staying across an rmmod foo_hcd, so test ->u_bus. + */ + if (mon_bus0.nreaders == 0 && (ubus = mbus->u_bus) != NULL) { + ubus->monitored = 0; + mb(); + } } } @@ -199,6 +204,10 @@ static void mon_stop(struct mon_bus *mbus) static void mon_bus_add(struct usb_bus *ubus) { mon_bus_init(ubus); + mutex_lock(&mon_lock); + if (mon_bus0.nreaders != 0) + ubus->monitored = 1; + mutex_unlock(&mon_lock); } /* @@ -250,12 +259,7 @@ static struct usb_mon_operations mon_ops_0 = { static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) { - /* - * Never happens, but... - */ if (ubus->monitored) { - printk(KERN_ERR TAG ": bus %d is dissolved while monitored\n", - ubus->busnum); ubus->monitored = 0; mb(); } @@ -263,6 +267,8 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus) ubus->mon_bus = NULL; mbus->u_bus = NULL; mb(); + + /* We want synchronize_irq() here, but that needs an argument. */ } /* @@ -295,9 +301,8 @@ static void mon_bus_init(struct usb_bus *ubus) */ mbus->u_bus = ubus; ubus->mon_bus = mbus; - mbus->uses_dma = ubus->uses_dma; - mbus->text_inited = mon_text_add(mbus, ubus); + mbus->text_inited = mon_text_add(mbus, ubus->busnum); // mon_bin_add(...) mutex_lock(&mon_lock); @@ -309,6 +314,18 @@ err_alloc: return; } +static void mon_bus0_init(void) +{ + struct mon_bus *mbus = &mon_bus0; + + kref_init(&mbus->ref); + spin_lock_init(&mbus->lock); + INIT_LIST_HEAD(&mbus->r_list); + + mbus->text_inited = mon_text_add(mbus, 0); + // mbus->bin_inited = mon_bin_add(mbus, 0); +} + /* * Search a USB bus by number. Notice that USB bus numbers start from one, * which we may later use to identify "all" with zero. @@ -322,6 +339,9 @@ struct mon_bus *mon_bus_lookup(unsigned int num) struct list_head *p; struct mon_bus *mbus; + if (num == 0) { + return &mon_bus0; + } list_for_each (p, &mon_buses) { mbus = list_entry(p, struct mon_bus, bus_link); if (mbus->u_bus->busnum == num) { @@ -341,6 +361,8 @@ static int __init mon_init(void) if ((rc = mon_bin_init()) != 0) goto err_bin; + mon_bus0_init(); + if (usb_mon_register(&mon_ops_0) != 0) { printk(KERN_NOTICE TAG ": unable to register with the core\n"); rc = -ENODEV; @@ -374,6 +396,7 @@ static void __exit mon_exit(void) usb_mon_deregister(); mutex_lock(&mon_lock); + while (!list_empty(&mon_buses)) { p = mon_buses.next; mbus = list_entry(p, struct mon_bus, bus_link); @@ -397,6 +420,11 @@ static void __exit mon_exit(void) mon_dissolve(mbus, mbus->u_bus); kref_put(&mbus->ref, mon_bus_drop); } + + mbus = &mon_bus0; + if (mbus->text_inited) + mon_text_del(mbus); + mutex_unlock(&mon_lock); mon_text_exit(); diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c index d38a127..ec0cc51 100644 --- a/drivers/usb/mon/mon_text.c +++ b/drivers/usb/mon/mon_text.c @@ -31,9 +31,21 @@ * to a local DoS. But we have to keep to root in order to prevent * password sniffing from HID devices. */ -#define EVENT_MAX (2*PAGE_SIZE / sizeof(struct mon_event_text)) +#define EVENT_MAX (4*PAGE_SIZE / sizeof(struct mon_event_text)) -#define PRINTF_DFL 160 +/* + * Potentially unlimited number; we limit it for similar allocations. + * The usbfs limits this to 128, but we're not quite as generous. + */ +#define ISODESC_MAX 5 + +#define PRINTF_DFL 250 /* with 5 ISOs segs */ + +struct mon_iso_desc { + int status; + unsigned int offset; + unsigned int length; /* Unsigned here, signed in URB. Historic. */ +}; struct mon_event_text { struct list_head e_link; @@ -41,10 +53,16 @@ struct mon_event_text { unsigned int pipe; /* Pipe */ unsigned long id; /* From pointer, most of the time */ unsigned int tstamp; + int busnum; int length; /* Depends on type: xfer length or act length */ int status; + int interval; + int start_frame; + int error_count; char setup_flag; char data_flag; + int numdesc; /* Full number */ + struct mon_iso_desc isodesc[ISODESC_MAX]; unsigned char setup[SETUP_MAX]; unsigned char data[DATA_MAX]; }; @@ -68,6 +86,28 @@ static struct dentry *mon_dir; /* Usually /sys/kernel/debug/usbmon */ static void mon_text_ctor(void *, struct kmem_cache *, unsigned long); +struct mon_text_ptr { + int cnt, limit; + char *pbuf; +}; + +static struct mon_event_text * + mon_text_read_wait(struct mon_reader_text *rp, struct file *file); +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep); + /* * mon_text_submit * mon_text_complete @@ -84,8 +124,10 @@ static inline char mon_text_get_setup(struct mon_event_text *ep, if (!usb_pipecontrol(urb->pipe) || ev_type != 'S') return '-'; - if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) { return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX); + } if (urb->setup_packet == NULL) return 'Z'; /* '0' would be not as pretty. */ @@ -104,10 +146,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, len = DATA_MAX; if (usb_pipein(pipe)) { - if (ev_type == 'S') + if (ev_type != 'C') return '<'; } else { - if (ev_type == 'C') + if (ev_type != 'S') return '>'; } @@ -120,8 +162,10 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb, * contain non-NULL garbage in case the upper level promised to * set DMA for the HCD. */ - if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + if (urb->dev->bus->uses_dma && + (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) { return mon_dmapeek(ep->data, urb->transfer_dma, len); + } if (urb->transfer_buffer == NULL) return 'Z'; /* '0' would be not as pretty. */ @@ -146,6 +190,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, { struct mon_event_text *ep; unsigned int stamp; + struct usb_iso_packet_descriptor *fp; + struct mon_iso_desc *dp; + int i, ndesc; stamp = mon_get_timestamp(); @@ -158,12 +205,36 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb, ep->type = ev_type; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = urb->dev->bus->busnum; ep->tstamp = stamp; ep->length = (ev_type == 'S') ? urb->transfer_buffer_length : urb->actual_length; /* Collecting status makes debugging sense for submits, too */ ep->status = urb->status; + if (usb_pipeint(urb->pipe)) { + ep->interval = urb->interval; + } else if (usb_pipeisoc(urb->pipe)) { + ep->interval = urb->interval; + ep->start_frame = urb->start_frame; + ep->error_count = urb->error_count; + } + ep->numdesc = urb->number_of_packets; + if (usb_pipeisoc(urb->pipe) && urb->number_of_packets > 0) { + if ((ndesc = urb->number_of_packets) > ISODESC_MAX) + ndesc = ISODESC_MAX; + fp = urb->iso_frame_desc; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + dp->status = fp->status; + dp->offset = fp->offset; + dp->length = (ev_type == 'S') ? + fp->length : fp->actual_length; + fp++; + dp++; + } + } + ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus); ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type, rp->r.m_bus); @@ -199,6 +270,7 @@ static void mon_text_error(void *data, struct urb *urb, int error) ep->type = 'E'; ep->pipe = urb->pipe; ep->id = (unsigned long) urb; + ep->busnum = 0; ep->tstamp = 0; ep->length = 0; ep->status = error; @@ -237,13 +309,11 @@ static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp, static int mon_text_open(struct inode *inode, struct file *file) { struct mon_bus *mbus; - struct usb_bus *ubus; struct mon_reader_text *rp; int rc; mutex_lock(&mon_lock); mbus = inode->i_private; - ubus = mbus->u_bus; rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL); if (rp == NULL) { @@ -267,8 +337,7 @@ static int mon_text_open(struct inode *inode, struct file *file) rp->r.rnf_error = mon_text_error; rp->r.rnf_complete = mon_text_complete; - snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum, - (long)rp); + snprintf(rp->slab_name, SLAB_NAME_SZ, "mon_text_%p", rp); rp->e_slab = kmem_cache_create(rp->slab_name, sizeof(struct mon_event_text), sizeof(long), 0, mon_text_ctor, NULL); @@ -300,17 +369,75 @@ err_alloc: * dd if=/dbg/usbmon/0t bs=10 * Also, we do not allow seeks and do not bother advancing the offset. */ -static ssize_t mon_text_read(struct file *file, char __user *buf, +static ssize_t mon_text_read_t(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos) { struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_t(rp, &ptr, ep); + mon_text_read_statset(rp, &ptr, ep); + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static ssize_t mon_text_read_u(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct mon_reader_text *rp = file->private_data; + struct mon_event_text *ep; + struct mon_text_ptr ptr; + + if (IS_ERR(ep = mon_text_read_wait(rp, file))) + return PTR_ERR(ep); + mutex_lock(&rp->printf_lock); + ptr.cnt = 0; + ptr.pbuf = rp->printf_buf; + ptr.limit = rp->printf_size; + + mon_text_read_head_u(rp, &ptr, ep); + if (ep->type == 'E') { + mon_text_read_statset(rp, &ptr, ep); + } else if (usb_pipeisoc(ep->pipe)) { + mon_text_read_isostat(rp, &ptr, ep); + mon_text_read_isodesc(rp, &ptr, ep); + } else if (usb_pipeint(ep->pipe)) { + mon_text_read_intstat(rp, &ptr, ep); + } else { + mon_text_read_statset(rp, &ptr, ep); + } + ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt, + " %d", ep->length); + mon_text_read_data(rp, &ptr, ep); + + if (copy_to_user(buf, rp->printf_buf, ptr.cnt)) + ptr.cnt = -EFAULT; + mutex_unlock(&rp->printf_lock); + kmem_cache_free(rp->e_slab, ep); + return ptr.cnt; +} + +static struct mon_event_text *mon_text_read_wait(struct mon_reader_text *rp, + struct file *file) +{ struct mon_bus *mbus = rp->r.m_bus; DECLARE_WAITQUEUE(waita, current); struct mon_event_text *ep; - int cnt, limit; - char *pbuf; - char udir, utype; - int data_len, i; add_wait_queue(&rp->wait, &waita); set_current_state(TASK_INTERRUPTIBLE); @@ -318,7 +445,7 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, if (file->f_flags & O_NONBLOCK) { set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); - return -EWOULDBLOCK; /* Same as EAGAIN in Linux */ + return ERR_PTR(-EWOULDBLOCK); } /* * We do not count nwaiters, because ->release is supposed @@ -327,17 +454,19 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, schedule(); if (signal_pending(current)) { remove_wait_queue(&rp->wait, &waita); - return -EINTR; + return ERR_PTR(-EINTR); } set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); remove_wait_queue(&rp->wait, &waita); + return ep; +} - mutex_lock(&rp->printf_lock); - cnt = 0; - pbuf = rp->printf_buf; - limit = rp->printf_size; +static void mon_text_read_head_t(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; udir = usb_pipein(ep->pipe) ? 'i' : 'o'; switch (usb_pipetype(ep->pipe)) { @@ -346,13 +475,38 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, case PIPE_CONTROL: utype = 'C'; break; default: /* PIPE_BULK */ utype = 'B'; } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "%lx %u %c %c%c:%03u:%02u", ep->id, ep->tstamp, ep->type, - utype, udir, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); + utype, udir, + usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_head_u(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + char udir, utype; + + udir = usb_pipein(ep->pipe) ? 'i' : 'o'; + switch (usb_pipetype(ep->pipe)) { + case PIPE_ISOCHRONOUS: utype = 'Z'; break; + case PIPE_INTERRUPT: utype = 'I'; break; + case PIPE_CONTROL: utype = 'C'; break; + default: /* PIPE_BULK */ utype = 'B'; + } + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "%lx %u %c %c%c:%d:%03u:%u", + ep->id, ep->tstamp, ep->type, + utype, udir, + ep->busnum, usb_pipedevice(ep->pipe), usb_pipeendpoint(ep->pipe)); +} + +static void mon_text_read_statset(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ if (ep->setup_flag == 0) { /* Setup packet is present and captured */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " s %02x %02x %04x %04x %04x", ep->setup[0], ep->setup[1], @@ -360,40 +514,86 @@ static ssize_t mon_text_read(struct file *file, char __user *buf, (ep->setup[5] << 8) | ep->setup[4], (ep->setup[7] << 8) | ep->setup[6]); } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */ - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c __ __ ____ ____ ____", ep->setup_flag); } else { /* No setup for this kind of URB */ - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->status); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->status); } - cnt += snprintf(pbuf + cnt, limit - cnt, " %d", ep->length); +} + +static void mon_text_read_intstat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d", ep->status, ep->interval); +} + +static void mon_text_read_isostat(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + if (ep->type == 'S') { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d", ep->status, ep->interval, ep->start_frame); + } else { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%d:%d:%d", + ep->status, ep->interval, ep->start_frame, ep->error_count); + } +} + +static void mon_text_read_isodesc(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int ndesc; /* Display this many */ + int i; + const struct mon_iso_desc *dp; + + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d", ep->numdesc); + ndesc = ep->numdesc; + if (ndesc > ISODESC_MAX) + ndesc = ISODESC_MAX; + if (ndesc < 0) + ndesc = 0; + dp = ep->isodesc; + for (i = 0; i < ndesc; i++) { + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " %d:%u:%u", dp->status, dp->offset, dp->length); + dp++; + } +} + +static void mon_text_read_data(struct mon_reader_text *rp, + struct mon_text_ptr *p, const struct mon_event_text *ep) +{ + int data_len, i; if ((data_len = ep->length) > 0) { if (ep->data_flag == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, " ="); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + " ="); if (data_len >= DATA_MAX) data_len = DATA_MAX; for (i = 0; i < data_len; i++) { if (i % 4 == 0) { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, " "); } - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, + p->limit - p->cnt, "%02x", ep->data[i]); } - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, + "\n"); } else { - cnt += snprintf(pbuf + cnt, limit - cnt, + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, " %c\n", ep->data_flag); } } else { - cnt += snprintf(pbuf + cnt, limit - cnt, "\n"); + p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n"); } - - if (copy_to_user(buf, rp->printf_buf, cnt)) - cnt = -EFAULT; - mutex_unlock(&rp->printf_lock); - kmem_cache_free(rp->e_slab, ep); - return cnt; } static int mon_text_release(struct inode *inode, struct file *file) @@ -439,34 +639,46 @@ static int mon_text_release(struct inode *inode, struct file *file) return 0; } -static const struct file_operations mon_fops_text = { +static const struct file_operations mon_fops_text_t = { .owner = THIS_MODULE, .open = mon_text_open, .llseek = no_llseek, - .read = mon_text_read, - /* .write = mon_text_write, */ - /* .poll = mon_text_poll, */ - /* .ioctl = mon_text_ioctl, */ + .read = mon_text_read_t, .release = mon_text_release, }; -int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) +static const struct file_operations mon_fops_text_u = { + .owner = THIS_MODULE, + .open = mon_text_open, + .llseek = no_llseek, + .read = mon_text_read_u, + .release = mon_text_release, +}; + +int mon_text_add(struct mon_bus *mbus, int busnum) { struct dentry *d; enum { NAMESZ = 10 }; char name[NAMESZ]; int rc; - rc = snprintf(name, NAMESZ, "%dt", ubus->busnum); + rc = snprintf(name, NAMESZ, "%dt", busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_t; - d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text); + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_t); if (d == NULL) goto err_create_t; mbus->dent_t = d; - /* XXX The stats do not belong to here (text API), but oh well... */ - rc = snprintf(name, NAMESZ, "%ds", ubus->busnum); + rc = snprintf(name, NAMESZ, "%du", busnum); + if (rc <= 0 || rc >= NAMESZ) + goto err_print_u; + d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text_u); + if (d == NULL) + goto err_create_u; + mbus->dent_u = d; + + rc = snprintf(name, NAMESZ, "%ds", busnum); if (rc <= 0 || rc >= NAMESZ) goto err_print_s; d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat); @@ -478,6 +690,10 @@ int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus) err_create_s: err_print_s: + debugfs_remove(mbus->dent_u); + mbus->dent_u = NULL; +err_create_u: +err_print_u: debugfs_remove(mbus->dent_t); mbus->dent_t = NULL; err_create_t: @@ -487,6 +703,7 @@ err_print_t: void mon_text_del(struct mon_bus *mbus) { + debugfs_remove(mbus->dent_u); debugfs_remove(mbus->dent_t); debugfs_remove(mbus->dent_s); } @@ -520,7 +737,7 @@ int __init mon_text_init(void) return 0; } -void __exit mon_text_exit(void) +void mon_text_exit(void) { debugfs_remove(mon_dir); } diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h index 4f949ce..13d6325 100644 --- a/drivers/usb/mon/usb_mon.h +++ b/drivers/usb/mon/usb_mon.h @@ -22,7 +22,7 @@ struct mon_bus { int text_inited; struct dentry *dent_s; /* Debugging file */ struct dentry *dent_t; /* Text interface file */ - int uses_dma; + struct dentry *dent_u; /* Second text interface file */ /* Ref */ int nreaders; /* Under mon_lock AND mbus->lock */ @@ -52,14 +52,14 @@ void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r); struct mon_bus *mon_bus_lookup(unsigned int num); -int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus); +int /*bool*/ mon_text_add(struct mon_bus *mbus, int busnum); void mon_text_del(struct mon_bus *mbus); // void mon_bin_add(struct mon_bus *); int __init mon_text_init(void); -void __exit mon_text_exit(void); +void mon_text_exit(void); int __init mon_bin_init(void); -void __exit mon_bin_exit(void); +void mon_bin_exit(void); /* * DMA interface. @@ -81,4 +81,6 @@ extern struct mutex mon_lock; extern const struct file_operations mon_fops_stat; +extern struct mon_bus mon_bus0; /* Only for redundant checks */ + #endif /* __USB_MON_H */ diff --git a/drivers/usb/net/Kconfig b/drivers/usb/net/Kconfig index a2b94ef5..3de564b 100644 --- a/drivers/usb/net/Kconfig +++ b/drivers/usb/net/Kconfig @@ -84,6 +84,7 @@ config USB_PEGASUS config USB_RTL8150 tristate "USB RTL8150 based ethernet device support (EXPERIMENTAL)" depends on EXPERIMENTAL + select MII help Say Y here if you have RTL8150 based usb-ethernet adapter. Send me <petkan@users.sourceforge.net> any comments you may have. @@ -98,7 +99,7 @@ config USB_USBNET_MII config USB_USBNET tristate "Multi-purpose USB Networking Framework" - select MII if USBNET_MII != n + select MII if USB_USBNET_MII != n ---help--- This driver supports several kinds of network links over USB, with "minidrivers" built around a common network driver core @@ -185,6 +186,15 @@ config USB_NET_CDCETHER IEEE 802 "local assignment" bit is set in the address, a "usbX" name is used instead. +config USB_NET_DM9601 + tristate "Davicom DM9601 based USB 1.1 10/100 ethernet devices" + depends on USB_USBNET + select CRC32 + select USB_USBNET_MII + help + This option adds support for Davicom DM9601 based USB 1.1 + 10/100 Ethernet adapters. + config USB_NET_GL620A tristate "GeneSys GL620USB-A based cables" depends on USB_USBNET @@ -239,6 +249,7 @@ config USB_NET_RNDIS_HOST config USB_NET_CDC_SUBSET tristate "Simple USB Network Links (CDC Ethernet subset)" depends on USB_USBNET + default y help This driver module supports USB network devices that can work without any device-specific information. Select it if you have @@ -298,6 +309,13 @@ config USB_EPSON2888 Choose this option to support the usb networking links used by some sample firmware from Epson. +config USB_KC2190 + boolean "KT Technology KC2190 based cables (InstaNet)" + depends on USB_NET_CDC_SUBSET && EXPERIMENTAL + help + Choose this option if you're using a host-to-host cable + with one of these chips. + config USB_NET_ZAURUS tristate "Sharp Zaurus (stock ROMs) and compatible" depends on USB_USBNET diff --git a/drivers/usb/net/Makefile b/drivers/usb/net/Makefile index 7b51964..595a539 100644 --- a/drivers/usb/net/Makefile +++ b/drivers/usb/net/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_PEGASUS) += pegasus.o obj-$(CONFIG_USB_RTL8150) += rtl8150.o obj-$(CONFIG_USB_NET_AX8817X) += asix.o obj-$(CONFIG_USB_NET_CDCETHER) += cdc_ether.o +obj-$(CONFIG_USB_NET_DM9601) += dm9601.o obj-$(CONFIG_USB_NET_GL620A) += gl620a.o obj-$(CONFIG_USB_NET_NET1080) += net1080.o obj-$(CONFIG_USB_NET_PLUSB) += plusb.o diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c index bd357e1..d5ef97b 100644 --- a/drivers/usb/net/asix.c +++ b/drivers/usb/net/asix.c @@ -298,7 +298,7 @@ static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb) if (ax_skb) { ax_skb->len = size; ax_skb->data = packet; - ax_skb->tail = packet + size; + skb_set_tail_pointer(ax_skb, size); usbnet_skb_return(dev, ax_skb); } else { return 0; @@ -338,7 +338,7 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, && ((headroom + tailroom) >= (4 + padlen))) { if ((headroom < 4) || (tailroom < padlen)) { skb->data = memmove(skb->head + 4, skb->data, skb->len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); } } else { struct sk_buff *skb2; @@ -351,10 +351,12 @@ static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb, skb_push(skb, 4); packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4); - memcpy(skb->data, &packet_len, sizeof(packet_len)); + cpu_to_le32s(&packet_len); + skb_copy_to_linear_data(skb, &packet_len, sizeof(packet_len)); if ((skb->len % 512) == 0) { - memcpy( skb->tail, &padbytes, sizeof(padbytes)); + cpu_to_le32s(&padbytes); + memcpy(skb_tail_pointer(skb), &padbytes, sizeof(padbytes)); skb_put(skb, sizeof(padbytes)); } return skb; @@ -1393,9 +1395,9 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x07b8, 0x420a), .driver_info = (unsigned long) &hawking_uf200_info, }, { - // Billionton Systems, USB2AR - USB_DEVICE (0x08dd, 0x90ff), - .driver_info = (unsigned long) &ax8817x_info, + // Billionton Systems, USB2AR + USB_DEVICE (0x08dd, 0x90ff), + .driver_info = (unsigned long) &ax8817x_info, }, { // ATEN UC210T USB_DEVICE (0x0557, 0x2009), @@ -1421,9 +1423,13 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x1631, 0x6200), .driver_info = (unsigned long) &ax8817x_info, }, { + // JVC MP-PRX1 Port Replicator + USB_DEVICE (0x04f1, 0x3008), + .driver_info = (unsigned long) &ax8817x_info, +}, { // ASIX AX88772 10/100 - USB_DEVICE (0x0b95, 0x7720), - .driver_info = (unsigned long) &ax88772_info, + USB_DEVICE (0x0b95, 0x7720), + .driver_info = (unsigned long) &ax88772_info, }, { // ASIX AX88178 10/100/1000 USB_DEVICE (0x0b95, 0x1780), diff --git a/drivers/usb/net/catc.c b/drivers/usb/net/catc.c index 4852012..86e90c5 100644 --- a/drivers/usb/net/catc.c +++ b/drivers/usb/net/catc.c @@ -255,7 +255,6 @@ static void catc_rx_done(struct urb *urb) if (!(skb = dev_alloc_skb(pkt_len))) return; - skb->dev = catc->netdev; eth_copy_and_sum(skb, pkt_start + pkt_offset, pkt_len, 0); skb_put(skb, pkt_len); @@ -356,7 +355,7 @@ resubmit: * Transmit routines. */ -static void catc_tx_run(struct catc *catc) +static int catc_tx_run(struct catc *catc) { int status; @@ -374,12 +373,14 @@ static void catc_tx_run(struct catc *catc) catc->tx_ptr = 0; catc->netdev->trans_start = jiffies; + return status; } static void catc_tx_done(struct urb *urb) { struct catc *catc = urb->context; unsigned long flags; + int r; if (urb->status == -ECONNRESET) { dbg("Tx Reset."); @@ -398,10 +399,13 @@ static void catc_tx_done(struct urb *urb) spin_lock_irqsave(&catc->tx_lock, flags); - if (catc->tx_ptr) - catc_tx_run(catc); - else + if (catc->tx_ptr) { + r = catc_tx_run(catc); + if (unlikely(r < 0)) + clear_bit(TX_RUNNING, &catc->flags); + } else { clear_bit(TX_RUNNING, &catc->flags); + } netif_wake_queue(catc->netdev); @@ -412,6 +416,7 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) { struct catc *catc = netdev_priv(netdev); unsigned long flags; + int r = 0; char *tx_buf; spin_lock_irqsave(&catc->tx_lock, flags); @@ -419,11 +424,14 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) catc->tx_ptr = (((catc->tx_ptr - 1) >> 6) + 1) << 6; tx_buf = catc->tx_buf[catc->tx_idx] + catc->tx_ptr; *((u16*)tx_buf) = (catc->is_f5u011) ? cpu_to_be16((u16)skb->len) : cpu_to_le16((u16)skb->len); - memcpy(tx_buf + 2, skb->data, skb->len); + skb_copy_from_linear_data(skb, tx_buf + 2, skb->len); catc->tx_ptr += skb->len + 2; - if (!test_and_set_bit(TX_RUNNING, &catc->flags)) - catc_tx_run(catc); + if (!test_and_set_bit(TX_RUNNING, &catc->flags)) { + r = catc_tx_run(catc); + if (r < 0) + clear_bit(TX_RUNNING, &catc->flags); + } if ((catc->is_f5u011 && catc->tx_ptr) || (catc->tx_ptr >= ((TX_MAX_BURST - 1) * (PKT_SZ + 2)))) @@ -431,8 +439,10 @@ static int catc_hard_start_xmit(struct sk_buff *skb, struct net_device *netdev) spin_unlock_irqrestore(&catc->tx_lock, flags); - catc->stats.tx_bytes += skb->len; - catc->stats.tx_packets++; + if (r >= 0) { + catc->stats.tx_bytes += skb->len; + catc->stats.tx_packets++; + } dev_kfree_skb(skb); diff --git a/drivers/usb/net/cdc_subset.c b/drivers/usb/net/cdc_subset.c index ae8fb06..bc62b01 100644 --- a/drivers/usb/net/cdc_subset.c +++ b/drivers/usb/net/cdc_subset.c @@ -79,13 +79,19 @@ static int always_connected (struct usbnet *dev) * * ALi M5632 driver ... does high speed * + * NOTE that the MS-Windows drivers for this chip use some funky and + * (naturally) undocumented 7-byte prefix to each packet, so this is a + * case where we don't currently interoperate. Also, once you unplug + * one end of the cable, you need to replug the other end too ... since + * chip docs are unavailable, there's no way to reset the relevant state + * short of a power cycle. + * *-------------------------------------------------------------------------*/ static const struct driver_info ali_m5632_info = { .description = "ALi M5632", }; - #endif @@ -159,6 +165,11 @@ static const struct driver_info epson2888_info = { #endif /* CONFIG_USB_EPSON2888 */ +/*------------------------------------------------------------------------- + * + * info from Jonathan McDowell <noodles@earth.li> + * + *-------------------------------------------------------------------------*/ #ifdef CONFIG_USB_KC2190 #define HAVE_HARDWARE static const struct driver_info kc2190_info = { @@ -223,6 +234,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x0402, 0x5632), // ALi defaults .driver_info = (unsigned long) &ali_m5632_info, }, +{ + USB_DEVICE (0x182d,0x207c), // SiteCom CN-124 + .driver_info = (unsigned long) &ali_m5632_info, +}, #endif #ifdef CONFIG_USB_AN2720 @@ -314,13 +329,13 @@ static struct usb_driver cdc_subset_driver = { static int __init cdc_subset_init(void) { - return usb_register(&cdc_subset_driver); + return usb_register(&cdc_subset_driver); } module_init(cdc_subset_init); static void __exit cdc_subset_exit(void) { - usb_deregister(&cdc_subset_driver); + usb_deregister(&cdc_subset_driver); } module_exit(cdc_subset_exit); diff --git a/drivers/usb/net/dm9601.c b/drivers/usb/net/dm9601.c new file mode 100644 index 0000000..a6763860 --- /dev/null +++ b/drivers/usb/net/dm9601.c @@ -0,0 +1,619 @@ +/* + * Davicom DM9601 USB 1.1 10/100Mbps ethernet devices + * + * Peter Korsgaard <jacmet@sunsite.dk> + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +//#define DEBUG + +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/stddef.h> +#include <linux/init.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/ethtool.h> +#include <linux/mii.h> +#include <linux/usb.h> +#include <linux/crc32.h> + +#include "usbnet.h" + +/* datasheet: + http://www.davicom.com.tw/big5/download/Data%20Sheet/DM9601-DS-P01-930914.pdf +*/ + +/* control requests */ +#define DM_READ_REGS 0x00 +#define DM_WRITE_REGS 0x01 +#define DM_READ_MEMS 0x02 +#define DM_WRITE_REG 0x03 +#define DM_WRITE_MEMS 0x05 +#define DM_WRITE_MEM 0x07 + +/* registers */ +#define DM_NET_CTRL 0x00 +#define DM_RX_CTRL 0x05 +#define DM_SHARED_CTRL 0x0b +#define DM_SHARED_ADDR 0x0c +#define DM_SHARED_DATA 0x0d /* low + high */ +#define DM_PHY_ADDR 0x10 /* 6 bytes */ +#define DM_MCAST_ADDR 0x16 /* 8 bytes */ +#define DM_GPR_CTRL 0x1e +#define DM_GPR_DATA 0x1f + +#define DM_MAX_MCAST 64 +#define DM_MCAST_SIZE 8 +#define DM_EEPROM_LEN 256 +#define DM_TX_OVERHEAD 2 /* 2 byte header */ +#define DM_RX_OVERHEAD 7 /* 3 byte header + 4 byte crc tail */ +#define DM_TIMEOUT 1000 + + +static int dm_read(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + devdbg(dev, "dm_read() reg=0x%02x length=%d", reg, length); + return usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + DM_READ_REGS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, reg, data, length, USB_CTRL_SET_TIMEOUT); +} + +static int dm_read_reg(struct usbnet *dev, u8 reg, u8 *value) +{ + return dm_read(dev, reg, 1, value); +} + +static int dm_write(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + devdbg(dev, "dm_write() reg=0x%02x, length=%d", reg, length); + return usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + DM_WRITE_REGS, + USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, + 0, reg, data, length, USB_CTRL_SET_TIMEOUT); +} + +static int dm_write_reg(struct usbnet *dev, u8 reg, u8 value) +{ + devdbg(dev, "dm_write_reg() reg=0x%02x, value=0x%02x", reg, value); + return usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + DM_WRITE_REG, + USB_DIR_OUT | USB_TYPE_VENDOR |USB_RECIP_DEVICE, + value, reg, NULL, 0, USB_CTRL_SET_TIMEOUT); +} + +static void dm_write_async_callback(struct urb *urb) +{ + struct usb_ctrlrequest *req = (struct usb_ctrlrequest *)urb->context; + + if (urb->status < 0) + printk(KERN_DEBUG "dm_write_async_callback() failed with %d", + urb->status); + + kfree(req); + usb_free_urb(urb); +} + +static void dm_write_async(struct usbnet *dev, u8 reg, u16 length, void *data) +{ + struct usb_ctrlrequest *req; + struct urb *urb; + int status; + + devdbg(dev, "dm_write_async() reg=0x%02x length=%d", reg, length); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + deverr(dev, "Error allocating URB in dm_write_async!"); + return; + } + + req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); + if (!req) { + deverr(dev, "Failed to allocate memory for control request"); + usb_free_urb(urb); + return; + } + + req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + req->bRequest = DM_WRITE_REGS; + req->wValue = 0; + req->wIndex = cpu_to_le16(reg); + req->wLength = cpu_to_le16(length); + + usb_fill_control_urb(urb, dev->udev, + usb_sndctrlpipe(dev->udev, 0), + (void *)req, data, length, + dm_write_async_callback, req); + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + deverr(dev, "Error submitting the control message: status=%d", + status); + kfree(req); + usb_free_urb(urb); + } +} + +static void dm_write_reg_async(struct usbnet *dev, u8 reg, u8 value) +{ + struct usb_ctrlrequest *req; + struct urb *urb; + int status; + + devdbg(dev, "dm_write_reg_async() reg=0x%02x value=0x%02x", + reg, value); + + urb = usb_alloc_urb(0, GFP_ATOMIC); + if (!urb) { + deverr(dev, "Error allocating URB in dm_write_async!"); + return; + } + + req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC); + if (!req) { + deverr(dev, "Failed to allocate memory for control request"); + usb_free_urb(urb); + return; + } + + req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + req->bRequest = DM_WRITE_REG; + req->wValue = cpu_to_le16(value); + req->wIndex = cpu_to_le16(reg); + req->wLength = 0; + + usb_fill_control_urb(urb, dev->udev, + usb_sndctrlpipe(dev->udev, 0), + (void *)req, NULL, 0, dm_write_async_callback, req); + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + deverr(dev, "Error submitting the control message: status=%d", + status); + kfree(req); + usb_free_urb(urb); + } +} + +static int dm_read_shared_word(struct usbnet *dev, int phy, u8 reg, u16 *value) +{ + int ret, i; + + mutex_lock(&dev->phy_mutex); + + dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); + dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0xc : 0x4); + + for (i = 0; i < DM_TIMEOUT; i++) { + u8 tmp; + + udelay(1); + ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); + if (ret < 0) + goto out; + + /* ready */ + if ((tmp & 1) == 0) + break; + } + + if (i == DM_TIMEOUT) { + deverr(dev, "%s read timed out!", phy ? "phy" : "eeprom"); + ret = -EIO; + goto out; + } + + dm_write_reg(dev, DM_SHARED_CTRL, 0x0); + ret = dm_read(dev, DM_SHARED_DATA, 2, value); + + devdbg(dev, "read shared %d 0x%02x returned 0x%04x, %d", + phy, reg, *value, ret); + + out: + mutex_unlock(&dev->phy_mutex); + return ret; +} + +static int dm_write_shared_word(struct usbnet *dev, int phy, u8 reg, u16 value) +{ + int ret, i; + + mutex_lock(&dev->phy_mutex); + + ret = dm_write(dev, DM_SHARED_DATA, 2, &value); + if (ret < 0) + goto out; + + dm_write_reg(dev, DM_SHARED_ADDR, phy ? (reg | 0x40) : reg); + dm_write_reg(dev, DM_SHARED_CTRL, phy ? 0x1c : 0x14); + + for (i = 0; i < DM_TIMEOUT; i++) { + u8 tmp; + + udelay(1); + ret = dm_read_reg(dev, DM_SHARED_CTRL, &tmp); + if (ret < 0) + goto out; + + /* ready */ + if ((tmp & 1) == 0) + break; + } + + if (i == DM_TIMEOUT) { + deverr(dev, "%s write timed out!", phy ? "phy" : "eeprom"); + ret = -EIO; + goto out; + } + + dm_write_reg(dev, DM_SHARED_CTRL, 0x0); + +out: + mutex_unlock(&dev->phy_mutex); + return ret; +} + +static int dm_read_eeprom_word(struct usbnet *dev, u8 offset, void *value) +{ + return dm_read_shared_word(dev, 0, offset, value); +} + + + +static int dm9601_get_eeprom_len(struct net_device *dev) +{ + return DM_EEPROM_LEN; +} + +static int dm9601_get_eeprom(struct net_device *net, + struct ethtool_eeprom *eeprom, u8 * data) +{ + struct usbnet *dev = netdev_priv(net); + u16 *ebuf = (u16 *) data; + int i; + + /* access is 16bit */ + if ((eeprom->offset % 2) || (eeprom->len % 2)) + return -EINVAL; + + for (i = 0; i < eeprom->len / 2; i++) { + if (dm_read_eeprom_word(dev, eeprom->offset / 2 + i, + &ebuf[i]) < 0) + return -EINVAL; + } + return 0; +} + +static int dm9601_mdio_read(struct net_device *netdev, int phy_id, int loc) +{ + struct usbnet *dev = netdev_priv(netdev); + + u16 res; + + if (phy_id) { + devdbg(dev, "Only internal phy supported"); + return 0; + } + + dm_read_shared_word(dev, 1, loc, &res); + + devdbg(dev, + "dm9601_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", + phy_id, loc, le16_to_cpu(res)); + + return le16_to_cpu(res); +} + +static void dm9601_mdio_write(struct net_device *netdev, int phy_id, int loc, + int val) +{ + struct usbnet *dev = netdev_priv(netdev); + u16 res = cpu_to_le16(val); + + if (phy_id) { + devdbg(dev, "Only internal phy supported"); + return; + } + + devdbg(dev,"dm9601_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", + phy_id, loc, val); + + dm_write_shared_word(dev, 1, loc, res); +} + +static void dm9601_get_drvinfo(struct net_device *net, + struct ethtool_drvinfo *info) +{ + /* Inherit standard device info */ + usbnet_get_drvinfo(net, info); + info->eedump_len = DM_EEPROM_LEN; +} + +static u32 dm9601_get_link(struct net_device *net) +{ + struct usbnet *dev = netdev_priv(net); + + return mii_link_ok(&dev->mii); +} + +static int dm9601_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ + struct usbnet *dev = netdev_priv(net); + + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); +} + +static struct ethtool_ops dm9601_ethtool_ops = { + .get_drvinfo = dm9601_get_drvinfo, + .get_link = dm9601_get_link, + .get_msglevel = usbnet_get_msglevel, + .set_msglevel = usbnet_set_msglevel, + .get_eeprom_len = dm9601_get_eeprom_len, + .get_eeprom = dm9601_get_eeprom, + .get_settings = usbnet_get_settings, + .set_settings = usbnet_set_settings, + .nway_reset = usbnet_nway_reset, +}; + +static void dm9601_set_multicast(struct net_device *net) +{ + struct usbnet *dev = netdev_priv(net); + /* We use the 20 byte dev->data for our 8 byte filter buffer + * to avoid allocating memory that is tricky to free later */ + u8 *hashes = (u8 *) & dev->data; + u8 rx_ctl = 0x01; + + memset(hashes, 0x00, DM_MCAST_SIZE); + hashes[DM_MCAST_SIZE - 1] |= 0x80; /* broadcast address */ + + if (net->flags & IFF_PROMISC) { + rx_ctl |= 0x02; + } else if (net->flags & IFF_ALLMULTI || net->mc_count > DM_MAX_MCAST) { + rx_ctl |= 0x04; + } else if (net->mc_count) { + struct dev_mc_list *mc_list = net->mc_list; + int i; + + for (i = 0; i < net->mc_count; i++) { + u32 crc = ether_crc(ETH_ALEN, mc_list->dmi_addr) >> 26; + hashes[crc >> 3] |= 1 << (crc & 0x7); + } + } + + dm_write_async(dev, DM_MCAST_ADDR, DM_MCAST_SIZE, hashes); + dm_write_reg_async(dev, DM_RX_CTRL, rx_ctl); +} + +static int dm9601_bind(struct usbnet *dev, struct usb_interface *intf) +{ + int ret; + + ret = usbnet_get_endpoints(dev, intf); + if (ret) + goto out; + + dev->net->do_ioctl = dm9601_ioctl; + dev->net->set_multicast_list = dm9601_set_multicast; + dev->net->ethtool_ops = &dm9601_ethtool_ops; + dev->net->hard_header_len += DM_TX_OVERHEAD; + dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + dev->rx_urb_size = dev->net->mtu + DM_RX_OVERHEAD; + + dev->mii.dev = dev->net; + dev->mii.mdio_read = dm9601_mdio_read; + dev->mii.mdio_write = dm9601_mdio_write; + dev->mii.phy_id_mask = 0x1f; + dev->mii.reg_num_mask = 0x1f; + + /* reset */ + ret = dm_write_reg(dev, DM_NET_CTRL, 1); + udelay(20); + + /* read MAC */ + ret = dm_read(dev, DM_PHY_ADDR, ETH_ALEN, dev->net->dev_addr); + if (ret < 0) { + printk(KERN_ERR "Error reading MAC address\n"); + ret = -ENODEV; + goto out; + } + + + /* power up phy */ + dm_write_reg(dev, DM_GPR_CTRL, 1); + dm_write_reg(dev, DM_GPR_DATA, 0); + + /* receive broadcast packets */ + dm9601_set_multicast(dev->net); + + dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET); + dm9601_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE, + ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP); + mii_nway_restart(&dev->mii); + +out: + return ret; +} + +static int dm9601_rx_fixup(struct usbnet *dev, struct sk_buff *skb) +{ + u8 status; + int len; + + /* format: + b0: rx status + b1: packet length (incl crc) low + b2: packet length (incl crc) high + b3..n-4: packet data + bn-3..bn: ethernet crc + */ + + if (unlikely(skb->len < DM_RX_OVERHEAD)) { + dev_err(&dev->udev->dev, "unexpected tiny rx frame\n"); + return 0; + } + + status = skb->data[0]; + len = (skb->data[1] | (skb->data[2] << 8)) - 4; + + if (unlikely(status & 0xbf)) { + if (status & 0x01) dev->stats.rx_fifo_errors++; + if (status & 0x02) dev->stats.rx_crc_errors++; + if (status & 0x04) dev->stats.rx_frame_errors++; + if (status & 0x20) dev->stats.rx_missed_errors++; + if (status & 0x90) dev->stats.rx_length_errors++; + return 0; + } + + skb_pull(skb, 3); + skb_trim(skb, len); + + return 1; +} + +static struct sk_buff *dm9601_tx_fixup(struct usbnet *dev, struct sk_buff *skb, + gfp_t flags) +{ + int len; + + /* format: + b0: packet length low + b1: packet length high + b3..n: packet data + */ + + if (skb_headroom(skb) < DM_TX_OVERHEAD) { + struct sk_buff *skb2; + + skb2 = skb_copy_expand(skb, DM_TX_OVERHEAD, 0, flags); + dev_kfree_skb_any(skb); + skb = skb2; + if (!skb) + return NULL; + } + + __skb_push(skb, DM_TX_OVERHEAD); + + len = skb->len; + /* usbnet adds padding if length is a multiple of packet size + if so, adjust length value in header */ + if ((len % dev->maxpacket) == 0) + len++; + + skb->data[0] = len; + skb->data[1] = len >> 8; + + return skb; +} + +static void dm9601_status(struct usbnet *dev, struct urb *urb) +{ + int link; + u8 *buf; + + /* format: + b0: net status + b1: tx status 1 + b2: tx status 2 + b3: rx status + b4: rx overflow + b5: rx count + b6: tx count + b7: gpr + */ + + if (urb->actual_length < 8) + return; + + buf = urb->transfer_buffer; + + link = !!(buf[0] & 0x40); + if (netif_carrier_ok(dev->net) != link) { + if (link) { + netif_carrier_on(dev->net); + usbnet_defer_kevent (dev, EVENT_LINK_RESET); + } + else + netif_carrier_off(dev->net); + devdbg(dev, "Link Status is: %d", link); + } +} + +static int dm9601_link_reset(struct usbnet *dev) +{ + struct ethtool_cmd ecmd; + + mii_check_media(&dev->mii, 1, 1); + mii_ethtool_gset(&dev->mii, &ecmd); + + devdbg(dev, "link_reset() speed: %d duplex: %d", + ecmd.speed, ecmd.duplex); + + return 0; +} + +static const struct driver_info dm9601_info = { + .description = "Davicom DM9601 USB Ethernet", + .flags = FLAG_ETHER, + .bind = dm9601_bind, + .rx_fixup = dm9601_rx_fixup, + .tx_fixup = dm9601_tx_fixup, + .status = dm9601_status, + .link_reset = dm9601_link_reset, + .reset = dm9601_link_reset, +}; + +static const struct usb_device_id products[] = { + { + USB_DEVICE(0x07aa, 0x9601), /* Corega FEther USB-TXC */ + .driver_info = (unsigned long)&dm9601_info, + }, + { + USB_DEVICE(0x0a46, 0x9601), /* Davicom USB-100 */ + .driver_info = (unsigned long)&dm9601_info, + }, + { + USB_DEVICE(0x0a46, 0x6688), /* ZT6688 USB NIC */ + .driver_info = (unsigned long)&dm9601_info, + }, + { + USB_DEVICE(0x0a46, 0x0268), /* ShanTou ST268 USB NIC */ + .driver_info = (unsigned long)&dm9601_info, + }, + {}, // END +}; + +MODULE_DEVICE_TABLE(usb, products); + +static struct usb_driver dm9601_driver = { + .name = "dm9601", + .id_table = products, + .probe = usbnet_probe, + .disconnect = usbnet_disconnect, + .suspend = usbnet_suspend, + .resume = usbnet_resume, +}; + +static int __init dm9601_init(void) +{ + return usb_register(&dm9601_driver); +} + +static void __exit dm9601_exit(void) +{ + usb_deregister(&dm9601_driver); +} + +module_init(dm9601_init); +module_exit(dm9601_exit); + +MODULE_AUTHOR("Peter Korsgaard <jacmet@sunsite.dk>"); +MODULE_DESCRIPTION("Davicom DM9601 USB 1.1 ethernet devices"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/net/gl620a.c b/drivers/usb/net/gl620a.c index d257a8e..031cf5c 100644 --- a/drivers/usb/net/gl620a.c +++ b/drivers/usb/net/gl620a.c @@ -157,7 +157,7 @@ genelink_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) if ((headroom < (4 + 4*1)) || (tailroom < padlen)) { skb->data = memmove(skb->head + (4 + 4*1), skb->data, skb->len); - skb->tail = skb->data + skb->len; + skb_set_tail_pointer(skb, skb->len); } } else { struct sk_buff *skb2; diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c index de95268..a0cc05d 100644 --- a/drivers/usb/net/kaweth.c +++ b/drivers/usb/net/kaweth.c @@ -636,8 +636,6 @@ static void kaweth_usb_receive(struct urb *urb) skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */ - skb->dev = net; - eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0); skb_put(skb, pkt_len); diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c index ccebfde..19bf8da 100644 --- a/drivers/usb/net/net1080.c +++ b/drivers/usb/net/net1080.c @@ -520,7 +520,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) skb->data = memmove(skb->head + sizeof (struct nc_header), skb->data, skb->len); - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); goto encapsulate; } } diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c index d48c024..a05fd97 100644 --- a/drivers/usb/net/pegasus.c +++ b/drivers/usb/net/pegasus.c @@ -316,6 +316,7 @@ static int update_eth_regs_async(pegasus_t * pegasus) return ret; } +/* Returns 0 on success, error on failure */ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd) { int i; @@ -574,7 +575,6 @@ static void fill_skb_pool(pegasus_t * pegasus) */ if (pegasus->rx_pool[i] == NULL) return; - pegasus->rx_pool[i]->dev = pegasus->net; skb_reserve(pegasus->rx_pool[i], 2); } } @@ -847,10 +847,6 @@ static void intr_callback(struct urb *urb) * d[0].NO_CARRIER kicks in only with failed TX. * ... so monitoring with MII may be safest. */ - if (d[0] & NO_CARRIER) - netif_carrier_off(net); - else - netif_carrier_on(net); /* bytes 3-4 == rx_lostpkt, reg 2E/2F */ pegasus->stats.rx_missed_errors += ((d[3] & 0x7f) << 8) | d[4]; @@ -883,7 +879,7 @@ static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) netif_stop_queue(net); ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); - memcpy(pegasus->tx_buff + 2, skb->data, skb->len); + skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, usb_sndbulkpipe(pegasus->usb, 2), pegasus->tx_buff, count, @@ -950,7 +946,7 @@ static void set_carrier(struct net_device *net) pegasus_t *pegasus = netdev_priv(net); u16 tmp; - if (!read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) + if (read_mii_word(pegasus, pegasus->phy, MII_BMSR, &tmp)) return; if (tmp & BMSR_LSTATUS) @@ -1408,8 +1404,10 @@ static void pegasus_disconnect(struct usb_interface *intf) unlink_all_urbs(pegasus); free_all_urbs(pegasus); free_skb_pool(pegasus); - if (pegasus->rx_skb) + if (pegasus->rx_skb != NULL) { dev_kfree_skb(pegasus->rx_skb); + pegasus->rx_skb = NULL; + } free_netdev(pegasus->net); } diff --git a/drivers/usb/net/rndis_host.c b/drivers/usb/net/rndis_host.c index 39a21c7..980e4aa 100644 --- a/drivers/usb/net/rndis_host.c +++ b/drivers/usb/net/rndis_host.c @@ -253,6 +253,7 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */ * of that mess as possible. */ #define OID_802_3_PERMANENT_ADDRESS ccpu2(0x01010101) +#define OID_GEN_MAXIMUM_FRAME_SIZE ccpu2(0x00010106) #define OID_GEN_CURRENT_PACKET_FILTER ccpu2(0x0001010e) /* @@ -349,7 +350,7 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) case RNDIS_MSG_INDICATE: { /* fault */ // struct rndis_indicate *msg = (void *)buf; dev_info(&info->control->dev, - "rndis fault indication\n"); + "rndis fault indication\n"); } break; case RNDIS_MSG_KEEPALIVE: { /* ping */ @@ -387,6 +388,71 @@ static int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf) return -ETIMEDOUT; } +/* + * rndis_query: + * + * Performs a query for @oid along with 0 or more bytes of payload as + * specified by @in_len. If @reply_len is not set to -1 then the reply + * length is checked against this value, resulting in an error if it + * doesn't match. + * + * NOTE: Adding a payload exactly or greater than the size of the expected + * response payload is an evident requirement MSFT added for ActiveSync. + * + * The only exception is for OIDs that return a variably sized response, + * in which case no payload should be added. This undocumented (and + * nonsensical!) issue was found by sniffing protocol requests from the + * ActiveSync 4.1 Windows driver. + */ +static int rndis_query(struct usbnet *dev, struct usb_interface *intf, + void *buf, u32 oid, u32 in_len, + void **reply, int *reply_len) +{ + int retval; + union { + void *buf; + struct rndis_msg_hdr *header; + struct rndis_query *get; + struct rndis_query_c *get_c; + } u; + u32 off, len; + + u.buf = buf; + + memset(u.get, 0, sizeof *u.get + in_len); + u.get->msg_type = RNDIS_MSG_QUERY; + u.get->msg_len = cpu_to_le32(sizeof *u.get + in_len); + u.get->oid = oid; + u.get->len = cpu_to_le32(in_len); + u.get->offset = ccpu2(20); + + retval = rndis_command(dev, u.header); + if (unlikely(retval < 0)) { + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) failed, %d\n", + oid, retval); + return retval; + } + + off = le32_to_cpu(u.get_c->offset); + len = le32_to_cpu(u.get_c->len); + if (unlikely((8 + off + len) > CONTROL_BUFFER_SIZE)) + goto response_error; + + if (*reply_len != -1 && len != *reply_len) + goto response_error; + + *reply = (unsigned char *) &u.get_c->request_id + off; + *reply_len = len; + + return retval; + +response_error: + dev_err(&intf->dev, "RNDIS_MSG_QUERY(0x%08x) " + "invalid response - off %d len %d\n", + oid, off, len); + return -EDOM; +} + static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) { int retval; @@ -403,6 +469,8 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) struct rndis_set_c *set_c; } u; u32 tmp; + int reply_len; + unsigned char *bp; /* we can't rely on i/o from stack working, or stack allocation */ u.buf = kmalloc(CONTROL_BUFFER_SIZE, GFP_KERNEL); @@ -421,6 +489,12 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) * TX we'll stick to one Ethernet packet plus RNDIS framing. * For RX we handle drivers that zero-pad to end-of-packet. * Don't let userspace change these settings. + * + * NOTE: there still seems to be wierdness here, as if we need + * to do some more things to make sure WinCE targets accept this. + * They default to jumbograms of 8KB or 16KB, which is absurd + * for such low data rates and which is also more than Linux + * can usually expect to allocate for SKB data... */ net->hard_header_len += sizeof (struct rndis_data_hdr); dev->hard_mtu = net->mtu + net->hard_header_len; @@ -434,7 +508,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) if (unlikely(retval < 0)) { /* it might not even be an RNDIS device!! */ dev_err(&intf->dev, "RNDIS init failed, %d\n", retval); - goto fail_and_release; + goto fail_and_release; } tmp = le32_to_cpu(u.init_c->max_transfer_size); if (tmp < dev->hard_mtu) { @@ -450,34 +524,15 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) dev->hard_mtu, tmp, dev->rx_urb_size, 1 << le32_to_cpu(u.init_c->packet_alignment)); - /* Get designated host ethernet address. - * - * Adding a payload exactly the same size as the expected response - * payload is an evident requirement MSFT added for ActiveSync. - * This undocumented (and nonsensical) issue was found by sniffing - * protocol requests from the ActiveSync 4.1 Windows driver. - */ - memset(u.get, 0, sizeof *u.get + 48); - u.get->msg_type = RNDIS_MSG_QUERY; - u.get->msg_len = ccpu2(sizeof *u.get + 48); - u.get->oid = OID_802_3_PERMANENT_ADDRESS; - u.get->len = ccpu2(48); - u.get->offset = ccpu2(20); - - retval = rndis_command(dev, u.header); - if (unlikely(retval < 0)) { + /* Get designated host ethernet address */ + reply_len = ETH_ALEN; + retval = rndis_query(dev, intf, u.buf, OID_802_3_PERMANENT_ADDRESS, + 48, (void **) &bp, &reply_len); + if (unlikely(retval< 0)) { dev_err(&intf->dev, "rndis get ethaddr, %d\n", retval); goto fail_and_release; } - tmp = le32_to_cpu(u.get_c->offset); - if (unlikely((tmp + 8) > (CONTROL_BUFFER_SIZE - ETH_ALEN) - || u.get_c->len != ccpu2(ETH_ALEN))) { - dev_err(&intf->dev, "rndis ethaddr off %d len %d ?\n", - tmp, le32_to_cpu(u.get_c->len)); - retval = -EDOM; - goto fail_and_release; - } - memcpy(net->dev_addr, tmp + (char *)&u.get_c->request_id, ETH_ALEN); + memcpy(net->dev_addr, bp, ETH_ALEN); /* set a nonzero filter to enable data transfers */ memset(u.set, 0, sizeof *u.set); @@ -502,6 +557,7 @@ static int rndis_bind(struct usbnet *dev, struct usb_interface *intf) fail_and_release: usb_set_intfdata(info->data, NULL); usb_driver_release_interface(driver_of(intf), info->data); + info->data = NULL; fail: kfree(u.buf); return retval; @@ -588,7 +644,7 @@ rndis_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) if (likely((sizeof *hdr) <= room)) { skb->data = memmove(skb->head + sizeof *hdr, skb->data, len); - skb->tail = skb->data + len; + skb_set_tail_pointer(skb, len); goto fill; } } @@ -618,7 +674,7 @@ fill: static const struct driver_info rndis_info = { .description = "RNDIS device", - .flags = FLAG_ETHER | FLAG_FRAMING_RN, + .flags = FLAG_ETHER | FLAG_FRAMING_RN | FLAG_NO_SETINT, .bind = rndis_bind, .unbind = rndis_unbind, .status = rndis_status, diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c index ea153dc..fa598f0 100644 --- a/drivers/usb/net/rtl8150.c +++ b/drivers/usb/net/rtl8150.c @@ -646,7 +646,6 @@ static void fill_skb_pool(rtl8150_t *dev) if (!skb) { return; } - skb->dev = dev->netdev; skb_reserve(skb, 2); dev->rx_skb_pool[i] = skb; } diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c index 43ba61a..f9cd42d 100644 --- a/drivers/usb/net/usbnet.c +++ b/drivers/usb/net/usbnet.c @@ -147,7 +147,7 @@ int usbnet_get_endpoints(struct usbnet *dev, struct usb_interface *intf) if (tmp < 0) return tmp; } - + dev->in = usb_rcvbulkpipe (dev->udev, in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); dev->out = usb_sndbulkpipe (dev->udev, @@ -203,7 +203,6 @@ void usbnet_skb_return (struct usbnet *dev, struct sk_buff *skb) { int status; - skb->dev = dev->net; skb->protocol = eth_type_trans (skb, dev->net); dev->stats.rx_packets++; dev->stats.rx_bytes += skb->len; @@ -327,7 +326,7 @@ static void rx_submit (struct usbnet *dev, struct urb *urb, gfp_t flags) if (netif_running (dev->net) && netif_device_present (dev->net) && !test_bit (EVENT_RX_HALT, &dev->flags)) { - switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ + switch (retval = usb_submit_urb (urb, GFP_ATOMIC)){ case -EPIPE: usbnet_defer_kevent (dev, EVENT_RX_HALT); break; @@ -443,7 +442,7 @@ block: case -EOVERFLOW: dev->stats.rx_over_errors++; // FALLTHROUGH - + default: entry->state = rx_cleanup; dev->stats.rx_errors++; @@ -560,7 +559,7 @@ static int usbnet_stop (struct net_device *net) if (netif_msg_ifdown (dev)) devinfo (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld", - dev->stats.rx_packets, dev->stats.tx_packets, + dev->stats.rx_packets, dev->stats.tx_packets, dev->stats.rx_errors, dev->stats.tx_errors ); @@ -578,7 +577,7 @@ static int usbnet_stop (struct net_device *net) devdbg (dev, "waited for %d urb completions", temp); } dev->wait = NULL; - remove_wait_queue (&unlink_wakeup, &wait); + remove_wait_queue (&unlink_wakeup, &wait); usb_kill_urb(dev->interrupt); @@ -735,8 +734,7 @@ void usbnet_get_drvinfo (struct net_device *net, struct ethtool_drvinfo *info) { struct usbnet *dev = netdev_priv(net); - /* REVISIT don't always return "usbnet" */ - strncpy (info->driver, driver_name, sizeof info->driver); + strncpy (info->driver, dev->driver_name, sizeof info->driver); strncpy (info->version, DRIVER_VERSION, sizeof info->version); strncpy (info->fw_version, dev->driver_info->description, sizeof info->fw_version); @@ -834,7 +832,7 @@ kevent (struct work_struct *work) } if (test_bit (EVENT_LINK_RESET, &dev->flags)) { - struct driver_info *info = dev->driver_info; + struct driver_info *info = dev->driver_info; int retval = 0; clear_bit (EVENT_LINK_RESET, &dev->flags); @@ -1066,7 +1064,7 @@ static void usbnet_bh (unsigned long param) * USB Device Driver support * *-------------------------------------------------------------------------*/ - + // precondition: never called in_interrupt void usbnet_disconnect (struct usb_interface *intf) @@ -1087,7 +1085,7 @@ void usbnet_disconnect (struct usb_interface *intf) intf->dev.driver->name, xdev->bus->bus_name, xdev->devpath, dev->driver_info->description); - + net = dev->net; unregister_netdev (net); @@ -1111,15 +1109,17 @@ int usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) { struct usbnet *dev; - struct net_device *net; + struct net_device *net; struct usb_host_interface *interface; struct driver_info *info; struct usb_device *xdev; int status; + const char *name; + name = udev->dev.driver->name; info = (struct driver_info *) prod->driver_info; if (!info) { - dev_dbg (&udev->dev, "blacklisted by %s\n", driver_name); + dev_dbg (&udev->dev, "blacklisted by %s\n", name); return -ENODEV; } xdev = interface_to_usbdev (udev); @@ -1139,6 +1139,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) dev = netdev_priv(net); dev->udev = xdev; dev->driver_info = info; + dev->driver_name = name; dev->msg_enable = netif_msg_init (msg_level, NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK); skb_queue_head_init (&dev->rxq); @@ -1181,6 +1182,9 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) // NOTE net->name still not usable ... if (info->bind) { status = info->bind (dev, udev); + if (status < 0) + goto out1; + // heuristic: "usb%d" for links we know are two-host, // else "eth%d" when there's reasonable doubt. userspace // can rename the link if it knows better. @@ -1207,12 +1211,12 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod) if (status == 0 && dev->status) status = init_status (dev, udev); if (status < 0) - goto out1; + goto out3; if (!dev->rx_urb_size) dev->rx_urb_size = dev->hard_mtu; dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1); - + SET_NETDEV_DEV(net, &udev->dev); status = register_netdev (net); if (status) @@ -1255,7 +1259,7 @@ EXPORT_SYMBOL_GPL(usbnet_probe); int usbnet_suspend (struct usb_interface *intf, pm_message_t message) { struct usbnet *dev = usb_get_intfdata(intf); - + /* accelerate emptying of the rx and queues, to avoid * having everything error out. */ @@ -1286,7 +1290,7 @@ static int __init usbnet_init(void) < sizeof (struct skb_data)); random_ether_addr(node_id); - return 0; + return 0; } module_init(usbnet_init); diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h index 07c70ab..cbb53e0 100644 --- a/drivers/usb/net/usbnet.h +++ b/drivers/usb/net/usbnet.h @@ -29,6 +29,7 @@ struct usbnet { /* housekeeping */ struct usb_device *udev; struct driver_info *driver_info; + const char *driver_name; wait_queue_head_t *wait; struct mutex phy_mutex; diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index 2f4d303..ba5d1dc 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -423,11 +423,11 @@ config USB_SERIAL_MCT_U232 module will be called mct_u232. config USB_SERIAL_MOS7720 - tristate "USB Moschip 7720 Single Port Serial Driver" + tristate "USB Moschip 7720 Serial Driver" depends on USB_SERIAL ---help--- - Say Y here if you want to use a USB Serial single port adapter from - Moschip Semiconductor Tech. + Say Y here if you want to use USB Serial single and double + port adapters from Moschip Semiconductor Tech. To compile this driver as a module, choose M here: the module will be called mos7720. diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c index 11dad42..b675735 100644 --- a/drivers/usb/serial/aircable.c +++ b/drivers/usb/serial/aircable.c @@ -209,6 +209,7 @@ static void aircable_send(struct usb_serial_port *port) int count, result; struct aircable_private *priv = usb_get_serial_port_data(port); unsigned char* buf; + u16 *dbuf; dbg("%s - port %d", __FUNCTION__, port->number); if (port->write_urb_busy) return; @@ -226,8 +227,8 @@ static void aircable_send(struct usb_serial_port *port) buf[0] = TX_HEADER_0; buf[1] = TX_HEADER_1; - buf[2] = (unsigned char)count; - buf[3] = (unsigned char)(count >> 8); + dbuf = (u16 *)&buf[2]; + *dbuf = cpu_to_le16((u16)count); serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE); memcpy(port->write_urb->transfer_buffer, buf, @@ -434,7 +435,7 @@ static void aircable_write_bulk_callback(struct urb *urb) __FUNCTION__, urb->status); port->write_urb->transfer_buffer_length = 1; port->write_urb->dev = port->serial->dev; - result = usb_submit_urb(port->write_urb, GFP_KERNEL); + result = usb_submit_urb(port->write_urb, GFP_ATOMIC); if (result) dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c index 0af42e3..39a4983 100644 --- a/drivers/usb/serial/airprime.c +++ b/drivers/usb/serial/airprime.c @@ -18,11 +18,6 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */ - { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */ - { USB_DEVICE(0x1410, 0x1130) }, /* Novatel Wireless S720 CDMA/EV-DO */ - { USB_DEVICE(0x1410, 0x2110) }, /* Novatel Wireless U720 CDMA/EV-DO */ - { USB_DEVICE(0x1410, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */ - { USB_DEVICE(0x1410, 0x1100) }, /* ExpressCard34 Qualcomm 3G CDMA */ { USB_DEVICE(0x413c, 0x8115) }, /* Dell Wireless HSDPA 5500 */ { }, }; @@ -44,8 +39,43 @@ struct airprime_private { int outstanding_urbs; int throttled; struct urb *read_urbp[NUM_READ_URBS]; + + /* Settings for the port */ + int rts_state; /* Handshaking pins (outputs) */ + int dtr_state; + int cts_state; /* Handshaking pins (inputs) */ + int dsr_state; + int dcd_state; + int ri_state; }; +static int airprime_send_setup(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct airprime_private *priv; + + dbg("%s", __FUNCTION__); + + if (port->number != 0) + return 0; + + priv = usb_get_serial_port_data(port); + + if (port->tty) { + int val = 0; + if (priv->dtr_state) + val |= 0x01; + if (priv->rts_state) + val |= 0x02; + + return usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 0x22,0x21,val,0,NULL,0,USB_CTRL_SET_TIMEOUT); + } + + return 0; +} + static void airprime_read_bulk_callback(struct urb *urb) { struct usb_serial_port *port = urb->context; @@ -58,11 +88,6 @@ static void airprime_read_bulk_callback(struct urb *urb) if (urb->status) { dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - /* something happened, so free up the memory for this urb */ - if (urb->transfer_buffer) { - kfree (urb->transfer_buffer); - urb->transfer_buffer = NULL; - } return; } usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); @@ -123,6 +148,10 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp) usb_set_serial_port_data(port, priv); } + /* Set some sane defaults */ + priv->rts_state = 1; + priv->dtr_state = 1; + for (i = 0; i < NUM_READ_URBS; ++i) { buffer = kmalloc(buffer_size, GFP_KERNEL); if (!buffer) { @@ -146,6 +175,8 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp) airprime_read_bulk_callback, port); result = usb_submit_urb(urb, GFP_KERNEL); if (result) { + usb_free_urb(urb); + kfree(buffer); dev_err(&port->dev, "%s - failed submitting read urb %d for port %d, error %d\n", __FUNCTION__, i, port->number, result); @@ -154,33 +185,21 @@ static int airprime_open(struct usb_serial_port *port, struct file *filp) /* remember this urb so we can kill it when the port is closed */ priv->read_urbp[i] = urb; } + + airprime_send_setup(port); + goto out; errout: /* some error happened, cancel any submitted urbs and clean up anything that got allocated successfully */ - for ( ; i >= 0; --i) { + while (i-- != 0) { urb = priv->read_urbp[i]; - if (urb) { - /* This urb was submitted successfully. So we have to - cancel it. - Unlinking the urb will invoke read_bulk_callback() - with an error status, so its transfer buffer will - be freed there */ - if (usb_unlink_urb (urb) != -EINPROGRESS) { - /* comments in drivers/usb/core/urb.c say this - can only happen if the urb was never submitted, - or has completed already. - Either way we may have to free the transfer - buffer here. */ - if (urb->transfer_buffer) { - kfree (urb->transfer_buffer); - urb->transfer_buffer = NULL; - } - } - usb_free_urb (urb); - } + buffer = urb->transfer_buffer; + usb_kill_urb (urb); + usb_free_urb (urb); + kfree (buffer); } out: @@ -194,10 +213,14 @@ static void airprime_close(struct usb_serial_port *port, struct file * filp) dbg("%s - port %d", __FUNCTION__, port->number); - /* killing the urb will invoke read_bulk_callback() with an error status, - so the transfer buffer will be freed there */ + priv->rts_state = 0; + priv->dtr_state = 0; + + airprime_send_setup(port); + for (i = 0; i < NUM_READ_URBS; ++i) { usb_kill_urb (priv->read_urbp[i]); + kfree (priv->read_urbp[i]->transfer_buffer); usb_free_urb (priv->read_urbp[i]); } diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c index edd6857..ea2175b 100644 --- a/drivers/usb/serial/ark3116.c +++ b/drivers/usb/serial/ark3116.c @@ -341,7 +341,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) result = usb_serial_generic_open(port, filp); if (result) - return result; + goto err_out; /* open */ ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf); @@ -372,6 +372,7 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp) if (port->tty) ark3116_set_termios(port, &tmp_termios); +err_out: kfree(buf); return result; diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 3ec2487..e831cb7 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -58,17 +58,22 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ + { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ + { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */ + { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */ + { USB_DEVICE(0x10C4, 0x814B) }, /* West Mountain Radio RIGtalk */ { USB_DEVICE(0x10C4, 0x815E) }, /* Helicomm IP-Link 1220-DVM */ { USB_DEVICE(0x10C4, 0x81C8) }, /* Lipowsky Industrie Elektronik GmbH, Baby-JTAG */ { USB_DEVICE(0x10C4, 0x81E2) }, /* Lipowsky Industrie Elektronik GmbH, Baby-LIN */ { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ + { USB_DEVICE(0x10C5, 0xEA61) }, /* Silicon Labs MobiData GPRS USB Modem */ { USB_DEVICE(0x13AD, 0x9999) }, /* Baltech card reader */ { USB_DEVICE(0x16D6, 0x0001) }, /* Jablotron serial interface */ { } /* Terminating Entry */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4695952..95a1805 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -315,6 +315,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_SIO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U232AM_ALT_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_232RL_PID) }, { USB_DEVICE(FTDI_VID, FTDI_8U2232C_PID) }, { USB_DEVICE(FTDI_VID, FTDI_MICRO_CHAMELEON_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RELAIS_PID) }, @@ -341,6 +342,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_PERLE_ULTRAPORT_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PIEGROUP_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TNC_X_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_USBX_707_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2101_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) }, { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) }, @@ -420,6 +422,14 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_ELV_ALC8500_PID) }, { USB_DEVICE(FTDI_VID, FTDI_PYRAMID_PID) }, { USB_DEVICE(FTDI_VID, FTDI_ELV_FHZ1000PC_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_US485_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PICPRO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PCMCIA_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PK1_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_RS232MON_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, /* * These will probably use user-space drivers. Uncomment them if * you need them or use the user-specified vendor/product module @@ -459,6 +469,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FALCOM_VID, FALCOM_TWIST_PID) }, { USB_DEVICE(FALCOM_VID, FALCOM_SAMBA_PID) }, { USB_DEVICE(FTDI_VID, FTDI_SUUNTO_SPORTS_PID) }, + { USB_DEVICE(TTI_VID, TTI_QL355P_PID) }, { USB_DEVICE(FTDI_VID, FTDI_RM_CANVIEW_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USOTL4_PID) }, { USB_DEVICE(BANDB_VID, BANDB_USTL4_PID) }, @@ -513,6 +524,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) }, { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) }, { USB_DEVICE(ELEKTOR_VID, ELEKTOR_FT323R_PID) }, + { USB_DEVICE(TELLDUS_VID, TELLDUS_TELLSTICK_PID) }, { }, /* Optional parameter entry */ { } /* Terminating entry */ }; @@ -532,6 +544,7 @@ static const char *ftdi_chip_name[] = { [FT8U232AM] = "FT8U232AM", [FT232BM] = "FT232BM", [FT2232C] = "FT2232C", + [FT232RL] = "FT232RL", }; @@ -587,6 +600,8 @@ struct ftdi_private { static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id *id); static int ftdi_sio_attach (struct usb_serial *serial); static void ftdi_shutdown (struct usb_serial *serial); +static int ftdi_sio_port_probe (struct usb_serial_port *port); +static int ftdi_sio_port_remove (struct usb_serial_port *port); static int ftdi_open (struct usb_serial_port *port, struct file *filp); static void ftdi_close (struct usb_serial_port *port, struct file *filp); static int ftdi_write (struct usb_serial_port *port, const unsigned char *buf, int count); @@ -621,6 +636,8 @@ static struct usb_serial_driver ftdi_sio_device = { .num_bulk_out = 1, .num_ports = 1, .probe = ftdi_sio_probe, + .port_probe = ftdi_sio_port_probe, + .port_remove = ftdi_sio_port_remove, .open = ftdi_open, .close = ftdi_close, .throttle = ftdi_throttle, @@ -863,6 +880,7 @@ static __u32 get_ftdi_divisor(struct usb_serial_port * port) break; case FT232BM: /* FT232BM chip */ case FT2232C: /* FT2232C chip */ + case FT232RL: if (baud <= 3000000) { div_value = ftdi_232bm_baud_to_divisor(baud); } else { @@ -1005,9 +1023,12 @@ static void ftdi_determine_type(struct usb_serial_port *port) /* (It might be a BM because of the iSerialNumber bug, * but it will still work as an AM device.) */ priv->chip_type = FT8U232AM; - } else { + } else if (version < 0x600) { /* Assume its an FT232BM (or FT245BM) */ priv->chip_type = FT232BM; + } else { + /* Assume its an FT232R */ + priv->chip_type = FT232RL; } info("Detected %s", ftdi_chip_name[priv->chip_type]); } @@ -1023,11 +1044,10 @@ static ssize_t show_latency_timer(struct device *dev, struct device_attribute *a { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_device *udev; + struct usb_device *udev = port->serial->dev; unsigned short latency = 0; int rv = 0; - udev = to_usb_device(dev); dbg("%s",__FUNCTION__); @@ -1051,13 +1071,11 @@ static ssize_t store_latency_timer(struct device *dev, struct device_attribute * { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_device *udev; + struct usb_device *udev = port->serial->dev; char buf[1]; int v = simple_strtoul(valbuf, NULL, 10); int rv = 0; - udev = to_usb_device(dev); - dbg("%s: setting latency timer = %i", __FUNCTION__, v); rv = usb_control_msg(udev, @@ -1082,13 +1100,11 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att { struct usb_serial_port *port = to_usb_serial_port(dev); struct ftdi_private *priv = usb_get_serial_port_data(port); - struct usb_device *udev; + struct usb_device *udev = port->serial->dev; char buf[1]; int v = simple_strtoul(valbuf, NULL, 10); int rv = 0; - udev = to_usb_device(dev); - dbg("%s: setting event char = %i", __FUNCTION__, v); rv = usb_control_msg(udev, @@ -1109,46 +1125,38 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer); static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char); -static int create_sysfs_attrs(struct usb_serial *serial) +static int create_sysfs_attrs(struct usb_serial_port *port) { - struct ftdi_private *priv; - struct usb_device *udev; + struct ftdi_private *priv = usb_get_serial_port_data(port); int retval = 0; dbg("%s",__FUNCTION__); - priv = usb_get_serial_port_data(serial->port[0]); - udev = serial->dev; - /* XXX I've no idea if the original SIO supports the event_char * sysfs parameter, so I'm playing it safe. */ if (priv->chip_type != SIO) { dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]); - retval = device_create_file(&udev->dev, &dev_attr_event_char); + retval = device_create_file(&port->dev, &dev_attr_event_char); if ((!retval) && (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) { - retval = device_create_file(&udev->dev, + retval = device_create_file(&port->dev, &dev_attr_latency_timer); } } return retval; } -static void remove_sysfs_attrs(struct usb_serial *serial) +static void remove_sysfs_attrs(struct usb_serial_port *port) { - struct ftdi_private *priv; - struct usb_device *udev; + struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s",__FUNCTION__); - priv = usb_get_serial_port_data(serial->port[0]); - udev = serial->dev; - /* XXX see create_sysfs_attrs */ if (priv->chip_type != SIO) { - device_remove_file(&udev->dev, &dev_attr_event_char); + device_remove_file(&port->dev, &dev_attr_event_char); if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) { - device_remove_file(&udev->dev, &dev_attr_latency_timer); + device_remove_file(&port->dev, &dev_attr_latency_timer); } } @@ -1168,13 +1176,9 @@ static int ftdi_sio_probe (struct usb_serial *serial, const struct usb_device_id return (0); } -/* attach subroutine */ -static int ftdi_sio_attach (struct usb_serial *serial) +static int ftdi_sio_port_probe(struct usb_serial_port *port) { - struct usb_serial_port *port = serial->port[0]; struct ftdi_private *priv; - struct ftdi_sio_quirk *quirk; - int retval; dbg("%s",__FUNCTION__); @@ -1214,19 +1218,21 @@ static int ftdi_sio_attach (struct usb_serial *serial) kfree(port->bulk_out_buffer); port->bulk_out_buffer = NULL; - usb_set_serial_port_data(serial->port[0], priv); + usb_set_serial_port_data(port, priv); - ftdi_determine_type (serial->port[0]); - retval = create_sysfs_attrs(serial); - if (retval) - dev_err(&serial->dev->dev, "Error creating sysfs files, " - "continuing\n"); + ftdi_determine_type (port); + create_sysfs_attrs(port); + return 0; +} +/* attach subroutine */ +static int ftdi_sio_attach (struct usb_serial *serial) +{ /* Check for device requiring special set up. */ - quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial); - if (quirk && quirk->setup) { + struct ftdi_sio_quirk *quirk = usb_get_serial_data(serial); + + if (quirk && quirk->setup) quirk->setup(serial); - } return 0; } /* ftdi_sio_attach */ @@ -1270,17 +1276,18 @@ static void ftdi_HE_TIRA1_setup (struct usb_serial *serial) * calls __serial_close for each open of the port * shutdown is called then (ie ftdi_shutdown) */ - - static void ftdi_shutdown (struct usb_serial *serial) -{ /* ftdi_shutdown */ +{ + dbg("%s", __FUNCTION__); +} - struct usb_serial_port *port = serial->port[0]; +static int ftdi_sio_port_remove(struct usb_serial_port *port) +{ struct ftdi_private *priv = usb_get_serial_port_data(port); dbg("%s", __FUNCTION__); - remove_sysfs_attrs(serial); + remove_sysfs_attrs(port); /* all open ports are closed at this point * (by usbserial.c:__serial_close, which calls ftdi_close) @@ -1290,8 +1297,9 @@ static void ftdi_shutdown (struct usb_serial *serial) usb_set_serial_port_data(port, NULL); kfree(priv); } -} /* ftdi_shutdown */ + return 0; +} static int ftdi_open (struct usb_serial_port *port, struct file *filp) { /* ftdi_open */ @@ -1426,6 +1434,7 @@ static int ftdi_write (struct usb_serial_port *port, dbg("%s - write limit hit\n", __FUNCTION__); return 0; } + priv->tx_outstanding_urbs++; spin_unlock_irqrestore(&priv->tx_lock, flags); data_offset = priv->write_offset; @@ -1443,14 +1452,15 @@ static int ftdi_write (struct usb_serial_port *port, buffer = kmalloc (transfer_size, GFP_ATOMIC); if (!buffer) { err("%s ran out of kernel memory for urb ...", __FUNCTION__); - return -ENOMEM; + count = -ENOMEM; + goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { err("%s - no more free urbs", __FUNCTION__); - kfree (buffer); - return -ENOMEM; + count = -ENOMEM; + goto error_no_urb; } /* Copy data */ @@ -1492,10 +1502,9 @@ static int ftdi_write (struct usb_serial_port *port, if (status) { err("%s - failed submitting write urb, error %d", __FUNCTION__, status); count = status; - kfree (buffer); + goto error; } else { spin_lock_irqsave(&priv->tx_lock, flags); - ++priv->tx_outstanding_urbs; priv->tx_outstanding_bytes += count; priv->tx_bytes += count; spin_unlock_irqrestore(&priv->tx_lock, flags); @@ -1503,10 +1512,19 @@ static int ftdi_write (struct usb_serial_port *port, /* we are done with this urb, so let the host driver * really free it when it is finished with it */ - usb_free_urb (urb); + usb_free_urb(urb); dbg("%s write returning: %d", __FUNCTION__, count); return count; +error: + usb_free_urb(urb); +error_no_urb: + kfree (buffer); +error_no_buffer: + spin_lock_irqsave(&priv->tx_lock, flags); + priv->tx_outstanding_urbs--; + spin_unlock_irqrestore(&priv->tx_lock, flags); + return count; } /* ftdi_write */ diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h index 7eff1c0..77ad0a0 100644 --- a/drivers/usb/serial/ftdi_sio.h +++ b/drivers/usb/serial/ftdi_sio.h @@ -27,9 +27,11 @@ #define FTDI_8U232AM_PID 0x6001 /* Similar device to SIO above */ #define FTDI_8U232AM_ALT_PID 0x6006 /* FTDI's alternate PID for above */ #define FTDI_8U2232C_PID 0x6010 /* Dual channel device */ +#define FTDI_232RL_PID 0xFBFA /* Product ID for FT232RL */ #define FTDI_RELAIS_PID 0xFA10 /* Relais device from Rudolf Gugler */ #define FTDI_NF_RIC_VID 0x0DCD /* Vendor Id */ #define FTDI_NF_RIC_PID 0x0001 /* Product Id */ +#define FTDI_USBX_707_PID 0xF857 /* ADSTech IR Blaster USBX-707 */ /* www.canusb.com Lawicel CANUSB device */ @@ -339,6 +341,12 @@ #define FTDI_SUUNTO_SPORTS_PID 0xF680 /* Suunto Sports instrument */ /* + * TTi (Thurlby Thandar Instruments) + */ +#define TTI_VID 0x103E /* Vendor Id */ +#define TTI_QL355P_PID 0x03E8 /* TTi QL355P power supply */ + +/* * Definitions for B&B Electronics products. */ #define BANDB_VID 0x0856 /* B&B Electronics Vendor ID */ @@ -491,6 +499,25 @@ #define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */ #define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */ +/* + * Telldus Technologies + */ +#define TELLDUS_VID 0x1781 /* Vendor ID */ +#define TELLDUS_TELLSTICK_PID 0x0C30 /* RF control dongle 433 MHz using FT232RL */ + +/* + * IBS elektronik product ids + * Submitted by Thomas Schleusener + */ +#define FTDI_IBS_US485_PID 0xff38 /* IBS US485 (USB<-->RS422/485 interface) */ +#define FTDI_IBS_PICPRO_PID 0xff39 /* IBS PIC-Programmer */ +#define FTDI_IBS_PCMCIA_PID 0xff3a /* IBS Card reader for PCMCIA SRAM-cards */ +#define FTDI_IBS_PK1_PID 0xff3b /* IBS PK1 - Particel counter */ +#define FTDI_IBS_RS232MON_PID 0xff3c /* IBS RS232 - Monitor */ +#define FTDI_IBS_APP70_PID 0xff3d /* APP 70 (dust monitoring system) */ +#define FTDI_IBS_PEDO_PID 0xff3e /* IBS PEDO-Modem (RF modem 868.35 MHz) */ +#define FTDI_IBS_PROD_PID 0xff3f /* future device */ + /* Commands */ #define FTDI_SIO_RESET 0 /* Reset the port */ #define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ @@ -614,6 +641,7 @@ typedef enum { FT8U232AM = 2, FT232BM = 3, FT2232C = 4, + FT232RL = 5, } ftdi_chip_type_t; typedef enum { diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 601e064..4f8282a 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -20,13 +20,14 @@ #include <linux/usb/serial.h> #include <asm/uaccess.h> -static int generic_probe(struct usb_interface *interface, - const struct usb_device_id *id); - static int debug; #ifdef CONFIG_USB_SERIAL_GENERIC + +static int generic_probe(struct usb_interface *interface, + const struct usb_device_id *id); + static __u16 vendor = 0x05f9; static __u16 product = 0xffff; @@ -66,6 +67,8 @@ struct usb_serial_driver usb_serial_generic_device = { .num_bulk_out = NUM_DONT_CARE, .num_ports = 1, .shutdown = usb_serial_generic_shutdown, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, }; static int generic_probe(struct usb_interface *interface, @@ -115,6 +118,7 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; int result = 0; + unsigned long flags; dbg("%s - port %d", __FUNCTION__, port->number); @@ -124,7 +128,13 @@ int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp) if (port->tty) port->tty->low_latency = 1; - /* if we have a bulk interrupt, start reading from it */ + /* clear the throttle flags */ + spin_lock_irqsave(&port->lock, flags); + port->throttled = 0; + port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + + /* if we have a bulk endpoint, start reading from it */ if (serial->num_bulk_in) { /* Start reading from the device */ usb_fill_bulk_urb (port->read_urb, serial->dev, @@ -253,31 +263,22 @@ int usb_serial_generic_chars_in_buffer (struct usb_serial_port *port) return (chars); } -void usb_serial_generic_read_bulk_callback (struct urb *urb) +/* Push data to tty layer and resubmit the bulk read URB */ +static void flush_and_resubmit_read_urb (struct usb_serial_port *port) { - struct usb_serial_port *port = (struct usb_serial_port *)urb->context; struct usb_serial *serial = port->serial; - struct tty_struct *tty; - unsigned char *data = urb->transfer_buffer; + struct urb *urb = port->read_urb; + struct tty_struct *tty = port->tty; int result; - dbg("%s - port %d", __FUNCTION__, port->number); - - if (urb->status) { - dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - return; - } - - usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); - - tty = port->tty; + /* Push data to tty */ if (tty && urb->actual_length) { tty_buffer_request_room(tty, urb->actual_length); - tty_insert_flip_string(tty, data, urb->actual_length); - tty_flip_buffer_push(tty); + tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length); + tty_flip_buffer_push(tty); /* is this allowed from an URB callback ? */ } - /* Continue trying to always read */ + /* Continue reading from device */ usb_fill_bulk_urb (port->read_urb, serial->dev, usb_rcvbulkpipe (serial->dev, port->bulk_in_endpointAddress), @@ -290,6 +291,40 @@ void usb_serial_generic_read_bulk_callback (struct urb *urb) if (result) dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result); } + +void usb_serial_generic_read_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + unsigned char *data = urb->transfer_buffer; + int is_throttled; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + if (urb->status) { + dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); + return; + } + + usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data); + + /* Throttle the device if requested by tty */ + if (urb->actual_length) { + spin_lock_irqsave(&port->lock, flags); + is_throttled = port->throttled = port->throttle_req; + spin_unlock_irqrestore(&port->lock, flags); + if (is_throttled) { + /* Let the received data linger in the read URB; + * usb_serial_generic_unthrottle() will pick it + * up later. */ + dbg("%s - throttling device", __FUNCTION__); + return; + } + } + + /* Handle data and continue reading from device */ + flush_and_resubmit_read_urb(port); +} EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback); void usb_serial_generic_write_bulk_callback (struct urb *urb) @@ -308,6 +343,38 @@ void usb_serial_generic_write_bulk_callback (struct urb *urb) } EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback); +void usb_serial_generic_throttle (struct usb_serial_port *port) +{ + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* Set the throttle request flag. It will be picked up + * by usb_serial_generic_read_bulk_callback(). */ + spin_lock_irqsave(&port->lock, flags); + port->throttle_req = 1; + spin_unlock_irqrestore(&port->lock, flags); +} + +void usb_serial_generic_unthrottle (struct usb_serial_port *port) +{ + int was_throttled; + unsigned long flags; + + dbg("%s - port %d", __FUNCTION__, port->number); + + /* Clear the throttle flags */ + spin_lock_irqsave(&port->lock, flags); + was_throttled = port->throttled; + port->throttled = port->throttle_req = 0; + spin_unlock_irqrestore(&port->lock, flags); + + if (was_throttled) { + /* Handle pending data and resume reading from device */ + flush_and_resubmit_read_urb(port); + } +} + void usb_serial_generic_shutdown (struct usb_serial *serial) { int i; diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index 6a26a2e..18f74ac 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -111,7 +111,7 @@ struct edgeport_port { struct TxFifo txfifo; /* transmit fifo -- size will be maxTxCredits */ struct urb *write_urb; /* write URB for this port */ - char write_in_progress; /* TRUE while a write URB is outstanding */ + bool write_in_progress; /* 'true' while a write URB is outstanding */ spinlock_t ep_lock; __u8 shadowLCR; /* last LCR value received */ @@ -123,11 +123,11 @@ struct edgeport_port { __u8 validDataMask; __u32 baudRate; - char open; - char openPending; - char commandPending; - char closePending; - char chaseResponsePending; + bool open; + bool openPending; + bool commandPending; + bool closePending; + bool chaseResponsePending; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t wait_open; /* for handling sleeping while waiting for open to finish */ @@ -156,7 +156,7 @@ struct edgeport_serial { __u8 bulk_in_endpoint; /* the bulk in endpoint handle */ unsigned char * bulk_in_buffer; /* the buffer we use for the bulk in endpoint */ struct urb * read_urb; /* our bulk read urb */ - int read_in_progress; + bool read_in_progress; spinlock_t es_lock; __u8 bulk_out_endpoint; /* the bulk out endpoint handle */ @@ -212,7 +212,7 @@ static int debug; static int low_latency = 1; /* tty low latency flag, on by default */ -static int CmdUrbs = 0; /* Number of outstanding Command Write Urbs */ +static atomic_t CmdUrbs; /* Number of outstanding Command Write Urbs */ /* local function prototypes */ @@ -631,14 +631,14 @@ static void edge_interrupt_callback (struct urb *urb) if (edge_serial->rxBytesAvail > 0 && !edge_serial->read_in_progress) { dbg("%s - posting a read", __FUNCTION__); - edge_serial->read_in_progress = TRUE; + edge_serial->read_in_progress = true; /* we have pending bytes on the bulk in pipe, send a request */ edge_serial->read_urb->dev = edge_serial->serial->dev; result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); if (result) { dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __FUNCTION__, result); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } } spin_unlock(&edge_serial->es_lock); @@ -695,13 +695,13 @@ static void edge_bulk_in_callback (struct urb *urb) if (urb->status) { dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; return; } if (urb->actual_length == 0) { dbg("%s - read bulk callback with no data", __FUNCTION__); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; return; } @@ -725,10 +725,10 @@ static void edge_bulk_in_callback (struct urb *urb) status = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC); if (status) { dev_err(&urb->dev->dev, "%s - usb_submit_urb(read bulk) failed, status = %d\n", __FUNCTION__, status); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } } else { - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; } spin_unlock(&edge_serial->es_lock); @@ -759,7 +759,7 @@ static void edge_bulk_out_data_callback (struct urb *urb) } // Release the Write URB - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; // Check if more data needs to be sent send_more_port_data((struct edgeport_serial *)(usb_get_serial_data(edge_port->port->serial)), edge_port); @@ -779,8 +779,8 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) dbg("%s", __FUNCTION__); - CmdUrbs--; - dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs); + atomic_dec(&CmdUrbs); + dbg("%s - FREE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs)); /* clean up the transfer buffer */ @@ -802,7 +802,7 @@ static void edge_bulk_out_cmd_callback (struct urb *urb) tty_wakeup(tty); /* we have completed the command */ - edge_port->commandPending = FALSE; + edge_port->commandPending = false; wake_up(&edge_port->wait_command); } @@ -868,7 +868,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) port0->bulk_in_buffer, edge_serial->read_urb->transfer_buffer_length, edge_bulk_in_callback, edge_serial); - edge_serial->read_in_progress = FALSE; + edge_serial->read_in_progress = false; /* start interrupt read for this edgeport * this interrupt will continue as long as the edgeport is connected */ @@ -890,26 +890,26 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) /* initialize our port settings */ edge_port->txCredits = 0; /* Can't send any data yet */ edge_port->shadowMCR = MCR_MASTER_IE; /* Must always set this bit to enable ints! */ - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; /* send a open port command */ - edge_port->openPending = TRUE; - edge_port->open = FALSE; + edge_port->openPending = true; + edge_port->open = false; response = send_iosp_ext_cmd (edge_port, IOSP_CMD_OPEN_PORT, 0); if (response < 0) { dev_err(&port->dev, "%s - error sending open port command\n", __FUNCTION__); - edge_port->openPending = FALSE; + edge_port->openPending = false; return -ENODEV; } /* now wait for the port to be completely opened */ - wait_event_timeout(edge_port->wait_open, (edge_port->openPending != TRUE), OPEN_TIMEOUT); + wait_event_timeout(edge_port->wait_open, !edge_port->openPending, OPEN_TIMEOUT); - if (edge_port->open == FALSE) { + if (!edge_port->open) { /* open timed out */ dbg("%s - open timedout", __FUNCTION__); - edge_port->openPending = FALSE; + edge_port->openPending = false; return -ENODEV; } @@ -928,7 +928,7 @@ static int edge_open (struct usb_serial_port *port, struct file * filp) /* Allocate a URB for the write */ edge_port->write_urb = usb_alloc_urb (0, GFP_KERNEL); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; if (!edge_port->write_urb) { dbg("%s - no memory", __FUNCTION__); @@ -966,7 +966,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port) lastCredits = edge_port->txCredits; // Did we get our Chase response - if (edge_port->chaseResponsePending == FALSE) { + if (!edge_port->chaseResponsePending) { dbg("%s - Got Chase Response", __FUNCTION__); // did we get all of our credit back? @@ -985,7 +985,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port) // No activity.. count down. loop--; if (loop == 0) { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; dbg("%s - Chase TIMEOUT", __FUNCTION__); return; } @@ -1068,13 +1068,13 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) // block until tx is empty block_until_tx_empty(edge_port); - edge_port->closePending = TRUE; + edge_port->closePending = true; if ((!edge_serial->is_epic) || ((edge_serial->is_epic) && (edge_serial->epic_descriptor.Supports.IOSPChase))) { /* flush and chase */ - edge_port->chaseResponsePending = TRUE; + edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); @@ -1082,7 +1082,7 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) // block until chase finished block_until_chase_response(edge_port); } else { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; } } @@ -1094,10 +1094,10 @@ static void edge_close (struct usb_serial_port *port, struct file * filp) send_iosp_ext_cmd (edge_port, IOSP_CMD_CLOSE_PORT, 0); } - //port->close = TRUE; - edge_port->closePending = FALSE; - edge_port->open = FALSE; - edge_port->openPending = FALSE; + //port->close = true; + edge_port->closePending = false; + edge_port->open = false; + edge_port->openPending = false; usb_kill_urb(edge_port->write_urb); @@ -1247,7 +1247,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge } // lock this write - edge_port->write_in_progress = TRUE; + edge_port->write_in_progress = true; // get a pointer to the write_urb urb = edge_port->write_urb; @@ -1261,7 +1261,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge buffer = kmalloc (count+2, GFP_ATOMIC); if (buffer == NULL) { dev_err(&edge_port->port->dev, "%s - no more kernel memory...\n", __FUNCTION__); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; goto exit_send; } buffer[0] = IOSP_BUILD_DATA_HDR1 (edge_port->port->number - edge_port->port->serial->minor, count); @@ -1301,7 +1301,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial, struct edge if (status) { /* something went wrong */ dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write bulk) failed, status = %d, data lost\n", __FUNCTION__, status); - edge_port->write_in_progress = FALSE; + edge_port->write_in_progress = false; /* revert the credits as something bad happened. */ edge_port->txCredits += count; @@ -1332,7 +1332,7 @@ static int edge_write_room (struct usb_serial_port *port) if (edge_port == NULL) return -ENODEV; - if (edge_port->closePending == TRUE) + if (edge_port->closePending) return -ENODEV; dbg("%s - port %d", __FUNCTION__, port->number); @@ -1371,7 +1371,7 @@ static int edge_chars_in_buffer (struct usb_serial_port *port) if (edge_port == NULL) return -ENODEV; - if (edge_port->closePending == TRUE) + if (edge_port->closePending) return -ENODEV; if (!edge_port->open) { @@ -1762,7 +1762,7 @@ static void edge_break (struct usb_serial_port *port, int break_state) ((edge_serial->is_epic) && (edge_serial->epic_descriptor.Supports.IOSPChase))) { /* flush and chase */ - edge_port->chaseResponsePending = TRUE; + edge_port->chaseResponsePending = true; dbg("%s - Sending IOSP_CMD_CHASE_PORT", __FUNCTION__); status = send_iosp_ext_cmd (edge_port, IOSP_CMD_CHASE_PORT, 0); @@ -1770,7 +1770,7 @@ static void edge_break (struct usb_serial_port *port, int break_state) // block until chase finished block_until_chase_response(edge_port); } else { - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; } } @@ -1952,13 +1952,13 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 // Also, we currently clear flag and close the port regardless of content of above's Byte3. // We could choose to do something else when Byte3 says Timeout on Chase from Edgeport, // like wait longer in block_until_chase_response, but for now we don't. - edge_port->chaseResponsePending = FALSE; + edge_port->chaseResponsePending = false; wake_up (&edge_port->wait_chase); return; case IOSP_EXT_STATUS_RX_CHECK_RSP: dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============\n", __FUNCTION__, edge_serial->rxPort, byte3 ); - //Port->RxCheckRsp = TRUE; + //Port->RxCheckRsp = true; return; } } @@ -1974,8 +1974,8 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 change_port_settings (edge_port, edge_port->port->tty->termios); /* we have completed the open */ - edge_port->openPending = FALSE; - edge_port->open = TRUE; + edge_port->openPending = false; + edge_port->open = true; wake_up(&edge_port->wait_open); return; } @@ -1983,7 +1983,7 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 // If port is closed, silently discard all rcvd status. We can // have cases where buffered status is received AFTER the close // port command is sent to the Edgeport. - if ((!edge_port->open ) || (edge_port->closePending)) { + if (!edge_port->open || edge_port->closePending) { return; } @@ -1991,14 +1991,14 @@ static void process_rcvd_status (struct edgeport_serial *edge_serial, __u8 byte2 // Not currently sent by Edgeport case IOSP_STATUS_LSR: dbg("%s - Port %u LSR Status = %02x", __FUNCTION__, edge_serial->rxPort, byte2); - handle_new_lsr (edge_port, FALSE, byte2, 0); + handle_new_lsr(edge_port, false, byte2, 0); break; case IOSP_STATUS_LSR_DATA: dbg("%s - Port %u LSR Status = %02x, Data = %02x", __FUNCTION__, edge_serial->rxPort, byte2, byte3); // byte2 is LSR Register // byte3 is broken data byte - handle_new_lsr (edge_port, TRUE, byte2, byte3); + handle_new_lsr(edge_port, true, byte2, byte3); break; // // case IOSP_EXT_4_STATUS: @@ -2317,14 +2317,14 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer if (!urb) return -ENOMEM; - CmdUrbs++; - dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, CmdUrbs); + atomic_inc(&CmdUrbs); + dbg("%s - ALLOCATE URB %p (outstanding %d)", __FUNCTION__, urb, atomic_read(&CmdUrbs)); usb_fill_bulk_urb (urb, edge_serial->serial->dev, usb_sndbulkpipe(edge_serial->serial->dev, edge_serial->bulk_out_endpoint), buffer, length, edge_bulk_out_cmd_callback, edge_port); - edge_port->commandPending = TRUE; + edge_port->commandPending = true; status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { @@ -2332,16 +2332,16 @@ static int write_cmd_usb (struct edgeport_port *edge_port, unsigned char *buffer dev_err(&edge_port->port->dev, "%s - usb_submit_urb(write command) failed, status = %d\n", __FUNCTION__, status); usb_kill_urb(urb); usb_free_urb(urb); - CmdUrbs--; + atomic_dec(&CmdUrbs); return status; } // wait for command to finish timeout = COMMAND_TIMEOUT; #if 0 - wait_event (&edge_port->wait_command, (edge_port->commandPending == FALSE)); + wait_event (&edge_port->wait_command, !edge_port->commandPending); - if (edge_port->commandPending == TRUE) { + if (edge_port->commandPending) { /* command timed out */ dbg("%s - command timed out", __FUNCTION__); status = -EINVAL; @@ -2524,8 +2524,8 @@ static void change_port_settings (struct edgeport_port *edge_port, struct ktermi dbg("%s - port %d", __FUNCTION__, edge_port->port->number); - if ((!edge_port->open) && - (!edge_port->openPending)) { + if (!edge_port->open && + !edge_port->openPending) { dbg("%s - port not opened", __FUNCTION__); return; } @@ -2836,9 +2836,9 @@ static int edge_startup (struct usb_serial *serial) struct usb_device *dev; int i, j; int response; - int interrupt_in_found; - int bulk_in_found; - int bulk_out_found; + bool interrupt_in_found; + bool bulk_in_found; + bool bulk_out_found; static __u32 descriptor[3] = { EDGE_COMPATIBILITY_MASK0, EDGE_COMPATIBILITY_MASK1, EDGE_COMPATIBILITY_MASK2 }; @@ -2936,14 +2936,14 @@ static int edge_startup (struct usb_serial *serial) if (edge_serial->is_epic) { /* EPIC thing, set up our interrupt polling now and our read urb, so * that the device knows it really is connected. */ - interrupt_in_found = bulk_in_found = bulk_out_found = FALSE; + interrupt_in_found = bulk_in_found = bulk_out_found = false; for (i = 0; i < serial->interface->altsetting[0].desc.bNumEndpoints; ++i) { struct usb_endpoint_descriptor *endpoint; int buffer_size; endpoint = &serial->interface->altsetting[0].endpoint[i].desc; buffer_size = le16_to_cpu(endpoint->wMaxPacketSize); - if ((!interrupt_in_found) && + if (!interrupt_in_found && (usb_endpoint_is_int_in(endpoint))) { /* we found a interrupt in endpoint */ dbg("found interrupt in"); @@ -2972,10 +2972,10 @@ static int edge_startup (struct usb_serial *serial) edge_serial, endpoint->bInterval); - interrupt_in_found = TRUE; + interrupt_in_found = true; } - if ((!bulk_in_found) && + if (!bulk_in_found && (usb_endpoint_is_bulk_in(endpoint))) { /* we found a bulk in endpoint */ dbg("found bulk in"); @@ -3001,19 +3001,19 @@ static int edge_startup (struct usb_serial *serial) endpoint->wMaxPacketSize, edge_bulk_in_callback, edge_serial); - bulk_in_found = TRUE; + bulk_in_found = true; } - if ((!bulk_out_found) && + if (!bulk_out_found && (usb_endpoint_is_bulk_out(endpoint))) { /* we found a bulk out endpoint */ dbg("found bulk out"); edge_serial->bulk_out_endpoint = endpoint->bEndpointAddress; - bulk_out_found = TRUE; + bulk_out_found = true; } } - if ((!interrupt_in_found) || (!bulk_in_found) || (!bulk_out_found)) { + if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) { err ("Error - the proper endpoints were not found!"); return -ENODEV; } @@ -3083,6 +3083,7 @@ static int __init edgeport_init(void) retval = usb_register(&io_driver); if (retval) goto failed_usb_register; + atomic_set(&CmdUrbs, 0); info(DRIVER_DESC " " DRIVER_VERSION); return 0; diff --git a/drivers/usb/serial/io_edgeport.h b/drivers/usb/serial/io_edgeport.h index 29a913a..cb201c1 100644 --- a/drivers/usb/serial/io_edgeport.h +++ b/drivers/usb/serial/io_edgeport.h @@ -19,12 +19,6 @@ #define MAX_RS232_PORTS 8 /* Max # of RS-232 ports per device */ /* typedefs that the insideout headers need */ -#ifndef TRUE - #define TRUE (1) -#endif -#ifndef FALSE - #define FALSE (0) -#endif #ifndef LOW8 #define LOW8(a) ((unsigned char)(a & 0xff)) #endif diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c index a408184..4df0ec7 100644 --- a/drivers/usb/serial/ipaq.c +++ b/drivers/usb/serial/ipaq.c @@ -247,12 +247,15 @@ static struct usb_device_id ipaq_id_table [] = { { USB_DEVICE(0x04AD, 0x0301) }, /* USB Sync 0301 */ { USB_DEVICE(0x04AD, 0x0302) }, /* USB Sync 0302 */ { USB_DEVICE(0x04AD, 0x0303) }, /* USB Sync 0303 */ + { USB_DEVICE(0x04AD, 0x0306) }, /* GPS Pocket PC USB Sync */ + { USB_DEVICE(0x04B7, 0x0531) }, /* MyGuide 7000 XL USB Sync */ { USB_DEVICE(0x04C5, 0x1058) }, /* FUJITSU USB Sync */ { USB_DEVICE(0x04C5, 0x1079) }, /* FUJITSU USB Sync */ { USB_DEVICE(0x04DA, 0x2500) }, /* Panasonic USB Sync */ { USB_DEVICE(0x04DD, 0x9102) }, /* SHARP WS003SH USB Modem */ { USB_DEVICE(0x04DD, 0x9121) }, /* SHARP WS004SH USB Modem */ { USB_DEVICE(0x04DD, 0x9123) }, /* SHARP WS007SH USB Modem */ + { USB_DEVICE(0x04DD, 0x9151) }, /* SHARP S01SH USB Modem */ { USB_DEVICE(0x04E8, 0x5F00) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F01) }, /* Samsung NEXiO USB Sync */ { USB_DEVICE(0x04E8, 0x5F02) }, /* Samsung NEXiO USB Sync */ diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c index b2097c4..7b085f3 100644 --- a/drivers/usb/serial/kl5kusb105.c +++ b/drivers/usb/serial/kl5kusb105.c @@ -238,7 +238,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, if (rc < 0) err("Reading line status failed (error = %d)", rc); else { - status = status_buf[0] + (status_buf[1]<<8); + status = le16_to_cpu(*(u16 *)status_buf); info("%s - read status %x %x", __FUNCTION__, status_buf[0], status_buf[1]); @@ -257,7 +257,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port, static int klsi_105_startup (struct usb_serial *serial) { struct klsi_105_private *priv; - int i; + int i, j; /* check if we support the product id (see keyspan.c) * FIXME @@ -265,12 +265,12 @@ static int klsi_105_startup (struct usb_serial *serial) /* allocate the private data structure */ for (i=0; i<serial->num_ports; i++) { - int j; priv = kmalloc(sizeof(struct klsi_105_private), GFP_KERNEL); if (!priv) { dbg("%skmalloc for klsi_105_private failed.", __FUNCTION__); - return -ENOMEM; + i--; + goto err_cleanup; } /* set initial values for control structures */ priv->cfg.pktlen = 5; @@ -292,15 +292,14 @@ static int klsi_105_startup (struct usb_serial *serial) priv->write_urb_pool[j] = urb; if (urb == NULL) { err("No more urbs???"); - continue; + goto err_cleanup; } - urb->transfer_buffer = NULL; urb->transfer_buffer = kmalloc (URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { err("%s - out of memory for urb buffers.", __FUNCTION__); - continue; + goto err_cleanup; } } @@ -308,7 +307,20 @@ static int klsi_105_startup (struct usb_serial *serial) init_waitqueue_head(&serial->port[i]->write_wait); } - return (0); + return 0; + +err_cleanup: + for (; i >= 0; i--) { + priv = usb_get_serial_port_data(serial->port[i]); + for (j=0; j < NUM_URBS; j++) { + if (priv->write_urb_pool[j]) { + kfree(priv->write_urb_pool[j]->transfer_buffer); + usb_free_urb(priv->write_urb_pool[j]); + } + } + usb_set_serial_port_data(serial->port[i], NULL); + } + return -ENOMEM; } /* klsi_105_startup */ diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c index 4cd839b..3db1adc 100644 --- a/drivers/usb/serial/mct_u232.c +++ b/drivers/usb/serial/mct_u232.c @@ -438,17 +438,21 @@ static int mct_u232_open (struct usb_serial_port *port, struct file *filp) if (retval) { err("usb_submit_urb(read bulk) failed pipe 0x%x err %d", port->read_urb->pipe, retval); - goto exit; + goto error; } port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL); - if (retval) + if (retval) { + usb_kill_urb(port->read_urb); err(" usb_submit_urb(read int) failed pipe 0x%x err %d", port->interrupt_in_urb->pipe, retval); - -exit: + goto error; + } return 0; + +error: + return retval; } /* mct_u232_open */ diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 2d588fb..b563e2a 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -103,11 +103,9 @@ static void mos7720_interrupt_callback(struct urb *urb) { int result; int length; - __u32 *data; - unsigned int status; + __u8 *data; __u8 sp1; __u8 sp2; - __u8 st; dbg("%s"," : Entering\n"); @@ -141,18 +139,19 @@ static void mos7720_interrupt_callback(struct urb *urb) * Byte 2 IIR Port 2 (port.number is 1) * Byte 3 -------------- * Byte 4 FIFO status for both */ - if (length && length > 4) { + + /* the above description is inverted + * oneukum 2007-03-14 */ + + if (unlikely(length != 4)) { dbg("Wrong data !!!"); return; } - status = *data; - - sp1 = (status & 0xff000000)>>24; - sp2 = (status & 0x00ff0000)>>16; - st = status & 0x000000ff; + sp1 = data[3]; + sp2 = data[2]; - if ((sp1 & 0x01) || (sp2 & 0x01)) { + if ((sp1 | sp2) & 0x01) { /* No Interrupt Pending in both the ports */ dbg("No Interrupt !!!"); } else { @@ -333,6 +332,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) int response; int port_number; char data; + int allocated_urbs = 0; int j; serial = port->serial; @@ -353,7 +353,7 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { - urb = usb_alloc_urb(0,GFP_ATOMIC); + urb = usb_alloc_urb(0,GFP_KERNEL); mos7720_port->write_urb_pool[j] = urb; if (urb == NULL) { @@ -365,10 +365,16 @@ static int mos7720_open(struct usb_serial_port *port, struct file * filp) GFP_KERNEL); if (!urb->transfer_buffer) { err("%s-out of memory for urb buffers.", __FUNCTION__); + usb_free_urb(mos7720_port->write_urb_pool[j]); + mos7720_port->write_urb_pool[j] = NULL; continue; } + allocated_urbs++; } + if (!allocated_urbs) + return -ENOMEM; + /* Initialize MCS7720 -- Write Init values to corresponding Registers * * Register Index @@ -526,7 +532,7 @@ static int mos7720_chars_in_buffer(struct usb_serial_port *port) } for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status == -EINPROGRESS) chars += URB_TRANSFER_BUFFER_SIZE; } dbg("%s - returns %d", __FUNCTION__, chars); @@ -629,7 +635,7 @@ static int mos7720_write_room(struct usb_serial_port *port) } for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) room += URB_TRANSFER_BUFFER_SIZE; } @@ -664,7 +670,7 @@ static int mos7720_write(struct usb_serial_port *port, urb = NULL; for (i = 0; i < NUM_URBS; ++i) { - if (mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (mos7720_port->write_urb_pool[i] && mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) { urb = mos7720_port->write_urb_pool[i]; dbg("URB:%d",i); break; @@ -1628,6 +1634,7 @@ static struct usb_serial_driver moschip7720_2port_driver = { .chars_in_buffer = mos7720_chars_in_buffer, .break_ctl = mos7720_break, .read_bulk_callback = mos7720_bulk_in_callback, + .read_int_callback = mos7720_interrupt_callback, }; static int __init moschip7720_init(void) diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index c6cca85..2366e7b 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -176,9 +176,12 @@ struct moschip_port { int port_num; /*Actual port number in the device(1,2,etc) */ struct urb *write_urb; /* write URB for this port */ struct urb *read_urb; /* read URB for this port */ + struct urb *int_urb; __u8 shadowLCR; /* last LCR value received */ __u8 shadowMCR; /* last MCR value received */ char open; + char open_ports; + char zombie; wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */ int delta_msr_cond; @@ -191,17 +194,17 @@ struct moschip_port { __u8 DcrRegOffset; //for processing control URBS in interrupt context struct urb *control_urb; + struct usb_ctrlrequest *dr; char *ctrl_buf; int MsrLsr; + spinlock_t pool_lock; struct urb *write_urb_pool[NUM_URBS]; + char busy[NUM_URBS]; }; static int debug; -static int mos7840_num_ports; //this says the number of ports in the device -static int mos7840_num_open_ports; - /* * mos7840_set_reg_sync @@ -254,7 +257,7 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg, struct usb_device *dev = port->serial->dev; val = val & 0x00ff; // For the UART control registers, the application number need to be Or'ed - if (mos7840_num_ports == 4) { + if (port->serial->num_ports == 4) { val |= (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; @@ -294,7 +297,7 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg, //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); /*Wval is same as application number */ - if (mos7840_num_ports == 4) { + if (port->serial->num_ports == 4) { Wval = (((__u16) port->number - (__u16) (port->serial->minor)) + 1) << 8; @@ -352,7 +355,7 @@ static inline struct moschip_port *mos7840_get_port_private(struct return (struct moschip_port *)usb_get_serial_port_data(port); } -static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) +static void mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) { struct moschip_port *mos7840_port; struct async_icount *icount; @@ -366,22 +369,24 @@ static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr) /* update input line counters */ if (new_msr & MOS_MSR_DELTA_CTS) { icount->cts++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_DSR) { icount->dsr++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_CD) { icount->dcd++; + smp_wmb(); } if (new_msr & MOS_MSR_DELTA_RI) { icount->rng++; + smp_wmb(); } } - - return 0; } -static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) +static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) { struct async_icount *icount; @@ -400,18 +405,20 @@ static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr) icount = &port->icount; if (new_lsr & SERIAL_LSR_BI) { icount->brk++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_OE) { icount->overrun++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_PE) { icount->parity++; + smp_wmb(); } if (new_lsr & SERIAL_LSR_FE) { icount->frame++; + smp_wmb(); } - - return 0; } /************************************************************************/ @@ -426,12 +433,15 @@ static void mos7840_control_callback(struct urb *urb) unsigned char *data; struct moschip_port *mos7840_port; __u8 regval = 0x0; + int result = 0; if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); return; } + mos7840_port = (struct moschip_port *)urb->context; + switch (urb->status) { case 0: /* success */ @@ -449,8 +459,6 @@ static void mos7840_control_callback(struct urb *urb) goto exit; } - mos7840_port = (struct moschip_port *)urb->context; - dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length); dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__, mos7840_port->MsrLsr, mos7840_port->port_num); @@ -462,21 +470,26 @@ static void mos7840_control_callback(struct urb *urb) else if (mos7840_port->MsrLsr == 1) mos7840_handle_new_lsr(mos7840_port, regval); - exit: - return; +exit: + spin_lock(&mos7840_port->pool_lock); + if (!mos7840_port->zombie) + result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC); + spin_unlock(&mos7840_port->pool_lock); + if (result) { + dev_err(&urb->dev->dev, + "%s - Error %d submitting interrupt urb\n", + __FUNCTION__, result); + } } static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg, __u16 * val) { struct usb_device *dev = mcs->port->serial->dev; - struct usb_ctrlrequest *dr = NULL; - unsigned char *buffer = NULL; - int ret = 0; - buffer = (__u8 *) mcs->ctrl_buf; + struct usb_ctrlrequest *dr = mcs->dr; + unsigned char *buffer = mcs->ctrl_buf; + int ret; -// dr=(struct usb_ctrlrequest *)(buffer); - dr = (void *)(buffer + 2); dr->bRequestType = MCS_RD_RTYPE; dr->bRequest = MCS_RDREQ; dr->wValue = cpu_to_le16(Wval); //0; @@ -506,8 +519,8 @@ static void mos7840_interrupt_callback(struct urb *urb) __u16 Data; unsigned char *data; __u8 sp[5], st; - int i; - __u16 wval; + int i, rv = 0; + __u16 wval, wreg = 0; dbg("%s", " : Entering\n"); if (!urb) { @@ -569,31 +582,34 @@ static void mos7840_interrupt_callback(struct urb *urb) dbg("Serial Port %d: Receiver status error or ", i); dbg("address bit detected in 9-bit mode\n"); mos7840_port->MsrLsr = 1; - mos7840_get_reg(mos7840_port, wval, - LINE_STATUS_REGISTER, - &Data); + wreg = LINE_STATUS_REGISTER; break; case SERIAL_IIR_MS: dbg("Serial Port %d: Modem status change\n", i); mos7840_port->MsrLsr = 0; - mos7840_get_reg(mos7840_port, wval, - MODEM_STATUS_REGISTER, - &Data); + wreg = MODEM_STATUS_REGISTER; break; } + spin_lock(&mos7840_port->pool_lock); + if (!mos7840_port->zombie) { + rv = mos7840_get_reg(mos7840_port, wval, wreg, &Data); + } else { + spin_unlock(&mos7840_port->pool_lock); + return; + } + spin_unlock(&mos7840_port->pool_lock); } } } - exit: + if (!(rv < 0)) /* the completion handler for the control urb will resubmit */ + return; +exit: result = usb_submit_urb(urb, GFP_ATOMIC); if (result) { dev_err(&urb->dev->dev, "%s - Error %d submitting interrupt urb\n", __FUNCTION__, result); } - - return; - } static int mos7840_port_paranoia_check(struct usb_serial_port *port, @@ -634,7 +650,8 @@ static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port, if (!port || mos7840_port_paranoia_check(port, function) || mos7840_serial_paranoia_check(port->serial, function)) { - /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */ + /* then say that we don't have a valid usb_serial thing, which will + * end up genrating -ENODEV return values */ return NULL; } @@ -699,6 +716,7 @@ static void mos7840_bulk_in_callback(struct urb *urb) tty_flip_buffer_push(tty); } mos7840_port->icount.rx += urb->actual_length; + smp_wmb(); dbg("mos7840_port->icount.rx is %d:\n", mos7840_port->icount.rx); } @@ -708,15 +726,14 @@ static void mos7840_bulk_in_callback(struct urb *urb) return; } - if (mos7840_port->read_urb->status != -EINPROGRESS) { - mos7840_port->read_urb->dev = serial->dev; - status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + mos7840_port->read_urb->dev = serial->dev; - if (status) { - dbg(" usb_submit_urb(read bulk) failed, status = %d", - status); - } + status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC); + + if (status) { + dbg(" usb_submit_urb(read bulk) failed, status = %d", + status); } } @@ -730,17 +747,28 @@ static void mos7840_bulk_out_data_callback(struct urb *urb) { struct moschip_port *mos7840_port; struct tty_struct *tty; + int i; + if (!urb) { dbg("%s", "Invalid Pointer !!!!:\n"); return; } + mos7840_port = (struct moschip_port *)urb->context; + spin_lock(&mos7840_port->pool_lock); + for (i = 0; i < NUM_URBS; i++) { + if (urb == mos7840_port->write_urb_pool[i]) { + mos7840_port->busy[i] = 0; + break; + } + } + spin_unlock(&mos7840_port->pool_lock); + if (urb->status) { dbg("nonzero write bulk status received:%d\n", urb->status); return; } - mos7840_port = (struct moschip_port *)urb->context; if (!mos7840_port) { dbg("%s", "NULL mos7840_port pointer \n"); return; @@ -792,13 +820,13 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) __u16 Data; int status; struct moschip_port *mos7840_port; + struct moschip_port *port0; if (mos7840_port_paranoia_check(port, __FUNCTION__)) { dbg("%s", "Port Paranoia failed \n"); return -ENODEV; } - mos7840_num_open_ports++; serial = port->serial; if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) { @@ -807,16 +835,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) } mos7840_port = mos7840_get_port_private(port); + port0 = mos7840_get_port_private(serial->port[0]); - if (mos7840_port == NULL) + if (mos7840_port == NULL || port0 == NULL) return -ENODEV; usb_clear_halt(serial->dev, port->write_urb->pipe); usb_clear_halt(serial->dev, port->read_urb->pipe); + port0->open_ports++; /* Initialising the write urb pool */ for (j = 0; j < NUM_URBS; ++j) { - urb = usb_alloc_urb(0, GFP_ATOMIC); + urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->write_urb_pool[j] = urb; if (urb == NULL) { @@ -824,10 +854,10 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) continue; } - urb->transfer_buffer = NULL; - urb->transfer_buffer = - kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); + urb->transfer_buffer = kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL); if (!urb->transfer_buffer) { + usb_free_urb(urb); + mos7840_port->write_urb_pool[j] = NULL; err("%s-out of memory for urb buffers.", __FUNCTION__); continue; } @@ -879,9 +909,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) } Data |= 0x08; //Driver done bit Data |= 0x20; //rx_disable - status = 0; - status = - mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); + status = mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data); if (status < 0) { dbg("writing Controlreg failed\n"); return -1; @@ -893,7 +921,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) //////////////////////////////////// Data = 0x00; - status = 0; status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data); if (status < 0) { dbg("disableing interrupts failed\n"); @@ -901,7 +928,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) } // Set FIFO_CONTROL_REGISTER to the default value Data = 0x00; - status = 0; status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); if (status < 0) { dbg("Writing FIFO_CONTROL_REGISTER failed\n"); @@ -909,7 +935,6 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) } Data = 0xcf; - status = 0; status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data); if (status < 0) { dbg("Writing FIFO_CONTROL_REGISTER failed\n"); @@ -917,22 +942,18 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) } Data = 0x03; - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); mos7840_port->shadowLCR = Data; Data = 0x0b; - status = 0; status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data); mos7840_port->shadowMCR = Data; Data = 0x00; - status = 0; status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data); mos7840_port->shadowLCR = Data; Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80 - status = 0; status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data); Data = 0x0c; @@ -999,7 +1020,7 @@ static int mos7840_open(struct usb_serial_port *port, struct file *filp) /* Check to see if we've set up our endpoint info yet * * (can't set it up in mos7840_startup as the structures * * were not set up at that time.) */ - if (mos7840_num_open_ports == 1) { + if (port0->open_ports == 1) { if (serial->port[0]->interrupt_in_buffer == NULL) { /* set up interrupt urb */ @@ -1097,6 +1118,7 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) { int i; int chars = 0; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " mos7840_chars_in_buffer:entering ..........."); @@ -1112,13 +1134,15 @@ static int mos7840_chars_in_buffer(struct usb_serial_port *port) return -1; } + spin_lock_irqsave(&mos7840_port->pool_lock,flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) { + if (mos7840_port->busy[i]) { chars += URB_TRANSFER_BUFFER_SIZE; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock,flags); dbg("%s - returns %d", __FUNCTION__, chars); - return (chars); + return chars; } @@ -1172,6 +1196,7 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial; struct moschip_port *mos7840_port; + struct moschip_port *port0; int j; __u16 Data; @@ -1189,10 +1214,10 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) } mos7840_port = mos7840_get_port_private(port); + port0 = mos7840_get_port_private(serial->port[0]); - if (mos7840_port == NULL) { + if (mos7840_port == NULL || port0 == NULL) return; - } for (j = 0; j < NUM_URBS; ++j) usb_kill_urb(mos7840_port->write_urb_pool[j]); @@ -1234,12 +1259,13 @@ static void mos7840_close(struct usb_serial_port *port, struct file *filp) } // if(mos7840_port->ctrl_buf != NULL) // kfree(mos7840_port->ctrl_buf); - mos7840_num_open_ports--; + port0->open_ports--; dbg("mos7840_num_open_ports in close%d:in port%d\n", - mos7840_num_open_ports, port->number); - if (mos7840_num_open_ports == 0) { + port0->open_ports, port->number); + if (port0->open_ports == 0) { if (serial->port[0]->interrupt_in_urb) { dbg("%s", "Shutdown interrupt_in_urb\n"); + usb_kill_urb(serial->port[0]->interrupt_in_urb); } } @@ -1368,6 +1394,7 @@ static int mos7840_write_room(struct usb_serial_port *port) { int i; int room = 0; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " mos7840_write_room:entering ..........."); @@ -1384,14 +1411,17 @@ static int mos7840_write_room(struct usb_serial_port *port) return -1; } + spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (!mos7840_port->busy[i]) { room += URB_TRANSFER_BUFFER_SIZE; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); + room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1; dbg("%s - returns %d", __FUNCTION__, room); - return (room); + return room; } @@ -1410,6 +1440,7 @@ static int mos7840_write(struct usb_serial_port *port, int i; int bytes_sent = 0; int transfer_size; + unsigned long flags; struct moschip_port *mos7840_port; struct usb_serial *serial; @@ -1476,13 +1507,16 @@ static int mos7840_write(struct usb_serial_port *port, /* try to find a free urb in the list */ urb = NULL; + spin_lock_irqsave(&mos7840_port->pool_lock, flags); for (i = 0; i < NUM_URBS; ++i) { - if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) { + if (!mos7840_port->busy[i]) { + mos7840_port->busy[i] = 1; urb = mos7840_port->write_urb_pool[i]; dbg("\nURB:%d", i); break; } } + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); if (urb == NULL) { dbg("%s - no more free urbs", __FUNCTION__); @@ -1518,6 +1552,7 @@ static int mos7840_write(struct usb_serial_port *port, status = usb_submit_urb(urb, GFP_ATOMIC); if (status) { + mos7840_port->busy[i] = 0; err("%s - usb_submit_urb(write bulk) failed with status = %d", __FUNCTION__, status); bytes_sent = status; @@ -1525,6 +1560,7 @@ static int mos7840_write(struct usb_serial_port *port, } bytes_sent = transfer_size; mos7840_port->icount.tx += transfer_size; + smp_wmb(); dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx); exit: @@ -2490,6 +2526,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, if (signal_pending(current)) return -ERESTARTSYS; cnow = mos7840_port->icount; + smp_rmb(); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ @@ -2506,6 +2543,7 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, case TIOCGICOUNT: cnow = mos7840_port->icount; + smp_rmb(); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; @@ -2535,19 +2573,18 @@ static int mos7840_ioctl(struct usb_serial_port *port, struct file *file, static int mos7840_calc_num_ports(struct usb_serial *serial) { + int mos7840_num_ports = 0; dbg("numberofendpoints: %d \n", (int)serial->interface->cur_altsetting->desc.bNumEndpoints); dbg("numberofendpoints: %d \n", (int)serial->interface->altsetting->desc.bNumEndpoints); if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) { - mos7840_num_ports = 2; - serial->type->num_ports = 2; + mos7840_num_ports = serial->num_ports = 2; } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) { - mos7840_num_ports = 4; - serial->type->num_bulk_in = 4; - serial->type->num_bulk_out = 4; - serial->type->num_ports = 4; + serial->num_bulk_in = 4; + serial->num_bulk_out = 4; + mos7840_num_ports = serial->num_ports = 4; } return mos7840_num_ports; @@ -2583,7 +2620,9 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL); if (mos7840_port == NULL) { err("%s - Out of memory", __FUNCTION__); - return -ENOMEM; + status = -ENOMEM; + i--; /* don't follow NULL pointer cleaning up */ + goto error; } /* Initialize all port interrupt end point to port 0 int endpoint * @@ -2591,6 +2630,7 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port->port = serial->port[i]; mos7840_set_port_private(serial->port[i], mos7840_port); + spin_lock_init(&mos7840_port->pool_lock); mos7840_port->port_num = ((serial->port[i]->number - (serial->port[i]->serial->minor)) + @@ -2601,22 +2641,22 @@ static int mos7840_startup(struct usb_serial *serial) mos7840_port->ControlRegOffset = 0x1; mos7840_port->DcrRegOffset = 0x4; } else if ((mos7840_port->port_num == 2) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0x8; mos7840_port->ControlRegOffset = 0x9; mos7840_port->DcrRegOffset = 0x16; } else if ((mos7840_port->port_num == 2) - && (mos7840_num_ports == 2)) { + && (serial->num_ports == 2)) { mos7840_port->SpRegOffset = 0xa; mos7840_port->ControlRegOffset = 0xb; mos7840_port->DcrRegOffset = 0x19; } else if ((mos7840_port->port_num == 3) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0xa; mos7840_port->ControlRegOffset = 0xb; mos7840_port->DcrRegOffset = 0x19; } else if ((mos7840_port->port_num == 4) - && (mos7840_num_ports == 4)) { + && (serial->num_ports == 4)) { mos7840_port->SpRegOffset = 0xc; mos7840_port->ControlRegOffset = 0xd; mos7840_port->DcrRegOffset = 0x1c; @@ -2701,21 +2741,19 @@ static int mos7840_startup(struct usb_serial *serial) dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status); Data = 0x20; - status = 0; status = mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER, Data); if (status < 0) { dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n", status); - break; + goto error; } else dbg("CLK_MULTI_REGISTER Writing success status%d\n", status); //write value 0x0 to scratchpad register Data = 0x00; - status = 0; status = mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER, Data); @@ -2729,7 +2767,7 @@ static int mos7840_startup(struct usb_serial *serial) //Zero Length flag register if ((mos7840_port->port_num != 1) - && (mos7840_num_ports == 2)) { + && (serial->num_ports == 2)) { Data = 0xff; status = 0; @@ -2770,14 +2808,17 @@ static int mos7840_startup(struct usb_serial *serial) i + 1, status); } - mos7840_port->control_urb = usb_alloc_urb(0, GFP_ATOMIC); + mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL); mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL); - + mos7840_port->dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); + if (!mos7840_port->control_urb || !mos7840_port->ctrl_buf || !mos7840_port->dr) { + status = -ENOMEM; + goto error; + } } //Zero Length flag enable Data = 0x0f; - status = 0; status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data); if (status < 0) { dbg("Writing ZLP_REG5 failed status-0x%x\n", status); @@ -2789,6 +2830,17 @@ static int mos7840_startup(struct usb_serial *serial) usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ); return 0; +error: + for (/* nothing */; i >= 0; i--) { + mos7840_port = mos7840_get_port_private(serial->port[i]); + + kfree(mos7840_port->dr); + kfree(mos7840_port->ctrl_buf); + usb_free_urb(mos7840_port->control_urb); + kfree(mos7840_port); + serial->port[i] = NULL; + } + return status; } /**************************************************************************** @@ -2799,6 +2851,7 @@ static int mos7840_startup(struct usb_serial *serial) static void mos7840_shutdown(struct usb_serial *serial) { int i; + unsigned long flags; struct moschip_port *mos7840_port; dbg("%s \n", " shutdown :entering.........."); @@ -2814,8 +2867,12 @@ static void mos7840_shutdown(struct usb_serial *serial) for (i = 0; i < serial->num_ports; ++i) { mos7840_port = mos7840_get_port_private(serial->port[i]); - kfree(mos7840_port->ctrl_buf); + spin_lock_irqsave(&mos7840_port->pool_lock, flags); + mos7840_port->zombie = 1; + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); usb_kill_urb(mos7840_port->control_urb); + kfree(mos7840_port->ctrl_buf); + kfree(mos7840_port->dr); kfree(mos7840_port); mos7840_set_port_private(serial->port[i], NULL); } diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c index 0216ac1..4adfab9 100644 --- a/drivers/usb/serial/omninet.c +++ b/drivers/usb/serial/omninet.c @@ -69,6 +69,7 @@ static void omninet_write_bulk_callback (struct urb *urb); static int omninet_write (struct usb_serial_port *port, const unsigned char *buf, int count); static int omninet_write_room (struct usb_serial_port *port); static void omninet_shutdown (struct usb_serial *serial); +static int omninet_attach (struct usb_serial *serial); static struct usb_device_id id_table [] = { { USB_DEVICE(ZYXEL_VENDOR_ID, ZYXEL_OMNINET_ID) }, @@ -99,6 +100,7 @@ static struct usb_serial_driver zyxel_omninet_device = { .num_bulk_in = 1, .num_bulk_out = 2, .num_ports = 1, + .attach = omninet_attach, .open = omninet_open, .close = omninet_close, .write = omninet_write, @@ -145,22 +147,30 @@ struct omninet_data __u8 od_outseq; // Sequence number for bulk_out URBs }; +static int omninet_attach (struct usb_serial *serial) +{ + struct omninet_data *od; + struct usb_serial_port *port = serial->port[0]; + + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + if( !od ) { + err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data)); + return -ENOMEM; + } + usb_set_serial_port_data(port, od); + return 0; +} + static int omninet_open (struct usb_serial_port *port, struct file *filp) { struct usb_serial *serial = port->serial; struct usb_serial_port *wport; - struct omninet_data *od; + struct omninet_data *od = usb_get_serial_port_data(port); int result = 0; dbg("%s - port %d", __FUNCTION__, port->number); od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); - if( !od ) { - err("%s- kmalloc(%Zd) failed.", __FUNCTION__, sizeof(struct omninet_data)); - return -ENOMEM; - } - - usb_set_serial_port_data(port, od); wport = serial->port[1]; wport->tty = port->tty; @@ -170,24 +180,17 @@ static int omninet_open (struct usb_serial_port *port, struct file *filp) port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length, omninet_read_bulk_callback, port); result = usb_submit_urb(port->read_urb, GFP_KERNEL); - if (result) + if (result) { err("%s - failed submitting read urb, error %d", __FUNCTION__, result); + } return result; } static void omninet_close (struct usb_serial_port *port, struct file * filp) { - struct usb_serial *serial = port->serial; - struct usb_serial_port *wport; - dbg("%s - port %d", __FUNCTION__, port->number); - - wport = serial->port[1]; - usb_kill_urb(wport->write_urb); usb_kill_urb(port->read_urb); - - kfree(usb_get_serial_port_data(port)); } @@ -326,7 +329,12 @@ static void omninet_write_bulk_callback (struct urb *urb) static void omninet_shutdown (struct usb_serial *serial) { + struct usb_serial_port *wport = serial->port[1]; + struct usb_serial_port *port = serial->port[0]; dbg ("%s", __FUNCTION__); + + usb_kill_urb(wport->write_urb); + kfree(usb_get_serial_port_data(port)); } diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index ced9f32..8c3f55b 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -67,54 +67,115 @@ static int option_tiocmset(struct usb_serial_port *port, struct file *file, static int option_send_setup(struct usb_serial_port *port); /* Vendor and product IDs */ -#define OPTION_VENDOR_ID 0x0AF0 -#define HUAWEI_VENDOR_ID 0x12D1 -#define AUDIOVOX_VENDOR_ID 0x0F3D -#define NOVATELWIRELESS_VENDOR_ID 0x1410 -#define ANYDATA_VENDOR_ID 0x16d5 - -#define OPTION_PRODUCT_OLD 0x5000 -#define OPTION_PRODUCT_FUSION 0x6000 -#define OPTION_PRODUCT_FUSION2 0x6300 -#define OPTION_PRODUCT_COBRA 0x6500 -#define OPTION_PRODUCT_COBRA2 0x6600 -#define OPTION_PRODUCT_GTMAX36 0x6701 -#define HUAWEI_PRODUCT_E600 0x1001 -#define HUAWEI_PRODUCT_E220 0x1003 -#define AUDIOVOX_PRODUCT_AIRCARD 0x0112 -#define NOVATELWIRELESS_PRODUCT_U740 0x1400 -#define ANYDATA_PRODUCT_ID 0x6501 +#define OPTION_VENDOR_ID 0x0AF0 +#define OPTION_PRODUCT_COLT 0x5000 +#define OPTION_PRODUCT_RICOLA 0x6000 +#define OPTION_PRODUCT_RICOLA_LIGHT 0x6100 +#define OPTION_PRODUCT_RICOLA_QUAD 0x6200 +#define OPTION_PRODUCT_RICOLA_QUAD_LIGHT 0x6300 +#define OPTION_PRODUCT_RICOLA_NDIS 0x6050 +#define OPTION_PRODUCT_RICOLA_NDIS_LIGHT 0x6150 +#define OPTION_PRODUCT_RICOLA_NDIS_QUAD 0x6250 +#define OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT 0x6350 +#define OPTION_PRODUCT_COBRA 0x6500 +#define OPTION_PRODUCT_COBRA_BUS 0x6501 +#define OPTION_PRODUCT_VIPER 0x6600 +#define OPTION_PRODUCT_VIPER_BUS 0x6601 +#define OPTION_PRODUCT_GT_MAX_READY 0x6701 +#define OPTION_PRODUCT_GT_MAX 0x6711 +#define OPTION_PRODUCT_FUJI_MODEM_LIGHT 0x6721 +#define OPTION_PRODUCT_FUJI_MODEM_GT 0x6741 +#define OPTION_PRODUCT_FUJI_MODEM_EX 0x6761 +#define OPTION_PRODUCT_FUJI_NETWORK_LIGHT 0x6731 +#define OPTION_PRODUCT_FUJI_NETWORK_GT 0x6751 +#define OPTION_PRODUCT_FUJI_NETWORK_EX 0x6771 +#define OPTION_PRODUCT_KOI_MODEM 0x6800 +#define OPTION_PRODUCT_KOI_NETWORK 0x6811 +#define OPTION_PRODUCT_SCORPION_MODEM 0x6901 +#define OPTION_PRODUCT_SCORPION_NETWORK 0x6911 +#define OPTION_PRODUCT_ETNA_MODEM 0x7001 +#define OPTION_PRODUCT_ETNA_NETWORK 0x7011 +#define OPTION_PRODUCT_ETNA_MODEM_LITE 0x7021 +#define OPTION_PRODUCT_ETNA_MODEM_GT 0x7041 +#define OPTION_PRODUCT_ETNA_MODEM_EX 0x7061 +#define OPTION_PRODUCT_ETNA_NETWORK_LITE 0x7031 +#define OPTION_PRODUCT_ETNA_NETWORK_GT 0x7051 +#define OPTION_PRODUCT_ETNA_NETWORK_EX 0x7071 +#define OPTION_PRODUCT_ETNA_KOI_MODEM 0x7100 +#define OPTION_PRODUCT_ETNA_KOI_NETWORK 0x7111 + +#define HUAWEI_VENDOR_ID 0x12D1 +#define HUAWEI_PRODUCT_E600 0x1001 +#define HUAWEI_PRODUCT_E220 0x1003 + +#define NOVATELWIRELESS_VENDOR_ID 0x1410 + +#define ANYDATA_VENDOR_ID 0x16d5 +#define ANYDATA_PRODUCT_ID 0x6501 + +#define BANDRICH_VENDOR_ID 0x1A8D +#define BANDRICH_PRODUCT_C100_1 0x1002 +#define BANDRICH_PRODUCT_C100_2 0x1003 + +#define DELL_VENDOR_ID 0x413C static struct usb_device_id option_ids[] = { - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_LIGHT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_QUAD_LIGHT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_LIGHT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA_NDIS_QUAD_LIGHT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA_BUS) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_VIPER_BUS) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX_READY) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GT_MAX) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_LIGHT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_GT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_MODEM_EX) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_LIGHT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_GT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUJI_NETWORK_EX) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_MODEM) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_KOI_NETWORK) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_MODEM) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_SCORPION_NETWORK) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_LITE) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_GT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_MODEM_EX) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_LITE) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_GT) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_NETWORK_EX) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_MODEM) }, + { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_ETNA_KOI_NETWORK) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) }, - { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1100) }, /* Novatel Merlin XS620/S640 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1110) }, /* Novatel Merlin S620 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1120) }, /* Novatel Merlin EX720 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1130) }, /* Novatel Merlin S720 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1400) }, /* Novatel U730 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1410) }, /* Novatel U740 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1420) }, /* Novatel EU870 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel Merlin XU870 HSDPA/3G */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x1430) }, /* Novatel XU870 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2100) }, /* Novatel EV620 CDMA/EV-DO */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2110) }, /* Novatel Merlin ES620 / Merlin ES720 / Ovation U720 */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */ + { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, + { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, + { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, + { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */ { } /* Terminating entry */ }; - -static struct usb_device_id option_ids1[] = { - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_OLD) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_FUSION2) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COBRA2) }, - { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_GTMAX36) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600) }, - { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220) }, - { USB_DEVICE(AUDIOVOX_VENDOR_ID, AUDIOVOX_PRODUCT_AIRCARD) }, - { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID,NOVATELWIRELESS_PRODUCT_U740) }, - { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) }, - { } /* Terminating entry */ -}; - MODULE_DEVICE_TABLE(usb, option_ids); static struct usb_driver option_driver = { @@ -136,7 +197,7 @@ static struct usb_serial_driver option_1port_device = { }, .description = "GSM modem (1-port)", .usb_driver = &option_driver, - .id_table = option_ids1, + .id_table = option_ids, .num_interrupt_in = NUM_DONT_CARE, .num_bulk_in = NUM_DONT_CARE, .num_bulk_out = NUM_DONT_CARE, @@ -539,12 +600,6 @@ static int option_open(struct usb_serial_port *port, struct file *filp) return (0); } -static inline void stop_urb(struct urb *urb) -{ - if (urb && urb->status == -EINPROGRESS) - usb_kill_urb(urb); -} - static void option_close(struct usb_serial_port *port, struct file *filp) { int i; @@ -562,9 +617,9 @@ static void option_close(struct usb_serial_port *port, struct file *filp) /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - stop_urb(portdata->in_urbs[i]); + usb_kill_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) - stop_urb(portdata->out_urbs[i]); + usb_kill_urb(portdata->out_urbs[i]); } port->tty = NULL; } @@ -695,9 +750,9 @@ static void option_shutdown(struct usb_serial *serial) port = serial->port[i]; portdata = usb_get_serial_port_data(port); for (j = 0; j < N_IN_URB; j++) - stop_urb(portdata->in_urbs[j]); + usb_kill_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) - stop_urb(portdata->out_urbs[j]); + usb_kill_urb(portdata->out_urbs[j]); } /* Now free them */ diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c index 6c083d4..83dfae9 100644 --- a/drivers/usb/serial/pl2303.c +++ b/drivers/usb/serial/pl2303.c @@ -83,6 +83,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) }, { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) }, { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) }, + { USB_DEVICE(WS002IN_VENDOR_ID, WS002IN_PRODUCT_ID) }, { } /* Terminating entry */ }; diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h index 65a50396..f9a71d0 100644 --- a/drivers/usb/serial/pl2303.h +++ b/drivers/usb/serial/pl2303.h @@ -97,3 +97,8 @@ /* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */ #define HUAWEI_VENDOR_ID 0x12d1 #define HUAWEI_PRODUCT_ID 0x1001 + +/* Willcom WS002IN Data Driver (by NetIndex Inc.) */ +#define WS002IN_VENDOR_ID 0x11f6 +#define WS002IN_PRODUCT_ID 0x2001 + diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index ecedd83..644607d 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -456,12 +456,6 @@ static int sierra_open(struct usb_serial_port *port, struct file *filp) return (0); } -static inline void stop_urb(struct urb *urb) -{ - if (urb && urb->status == -EINPROGRESS) - usb_kill_urb(urb); -} - static void sierra_close(struct usb_serial_port *port, struct file *filp) { int i; @@ -479,9 +473,9 @@ static void sierra_close(struct usb_serial_port *port, struct file *filp) /* Stop reading/writing urbs */ for (i = 0; i < N_IN_URB; i++) - stop_urb(portdata->in_urbs[i]); + usb_unlink_urb(portdata->in_urbs[i]); for (i = 0; i < N_OUT_URB; i++) - stop_urb(portdata->out_urbs[i]); + usb_unlink_urb(portdata->out_urbs[i]); } port->tty = NULL; } @@ -583,17 +577,26 @@ static void sierra_shutdown(struct usb_serial *serial) /* Stop reading/writing urbs */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; + if (!port) + continue; portdata = usb_get_serial_port_data(port); + if (!portdata) + continue; + for (j = 0; j < N_IN_URB; j++) - stop_urb(portdata->in_urbs[j]); + usb_unlink_urb(portdata->in_urbs[j]); for (j = 0; j < N_OUT_URB; j++) - stop_urb(portdata->out_urbs[j]); + usb_unlink_urb(portdata->out_urbs[j]); } /* Now free them */ for (i = 0; i < serial->num_ports; ++i) { port = serial->port[i]; + if (!port) + continue; portdata = usb_get_serial_port_data(port); + if (!portdata) + continue; for (j = 0; j < N_IN_URB; j++) { if (portdata->in_urbs[j]) { @@ -612,6 +615,8 @@ static void sierra_shutdown(struct usb_serial *serial) /* Now free per port private data */ for (i = 0; i < serial->num_ports; i++) { port = serial->port[i]; + if (!port) + continue; kfree(usb_get_serial_port_data(port)); } } diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 6bf22a2..7639022 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -99,9 +99,12 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po continue; *minor = i; + j = 0; dbg("%s - minor base = %d", __FUNCTION__, *minor); - for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) + for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) { serial_table[i] = serial; + serial->port[j++]->number = i; + } spin_unlock(&table_lock); return serial; } @@ -826,7 +829,6 @@ int usb_serial_probe(struct usb_interface *interface, num_ports = type->num_ports; } - serial->minor = minor; serial->num_ports = num_ports; serial->num_bulk_in = num_bulk_in; serial->num_bulk_out = num_bulk_out; @@ -847,7 +849,6 @@ int usb_serial_probe(struct usb_interface *interface, port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL); if (!port) goto probe_error; - port->number = i + serial->minor; port->serial = serial; spin_lock_init(&port->lock); mutex_init(&port->mutex); @@ -980,6 +981,7 @@ int usb_serial_probe(struct usb_interface *interface, dev_err(&interface->dev, "No more free serial devices\n"); goto probe_error; } + serial->minor = minor; /* register all of the individual ports with the driver core */ for (i = 0; i < num_ports; ++i) { @@ -1034,9 +1036,6 @@ probe_error: kfree(port->interrupt_out_buffer); } - /* return the minor range that this device had */ - return_serial (serial); - /* free up any memory that we allocated */ for (i = 0; i < serial->num_port_pointers; ++i) kfree(serial->port[i]); diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c index 2f59ff2..ffbe601 100644 --- a/drivers/usb/serial/visor.c +++ b/drivers/usb/serial/visor.c @@ -384,19 +384,21 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf, dbg("%s - write limit hit\n", __FUNCTION__); return 0; } + priv->outstanding_urbs++; spin_unlock_irqrestore(&priv->lock, flags); buffer = kmalloc (count, GFP_ATOMIC); if (!buffer) { dev_err(&port->dev, "out of memory\n"); - return -ENOMEM; + count = -ENOMEM; + goto error_no_buffer; } urb = usb_alloc_urb(0, GFP_ATOMIC); if (!urb) { dev_err(&port->dev, "no more free urbs\n"); - kfree (buffer); - return -ENOMEM; + count = -ENOMEM; + goto error_no_urb; } memcpy (buffer, buf, count); @@ -415,19 +417,27 @@ static int visor_write (struct usb_serial_port *port, const unsigned char *buf, dev_err(&port->dev, "%s - usb_submit_urb(write bulk) failed with status = %d\n", __FUNCTION__, status); count = status; - kfree (buffer); + goto error; } else { spin_lock_irqsave(&priv->lock, flags); - ++priv->outstanding_urbs; priv->bytes_out += count; spin_unlock_irqrestore(&priv->lock, flags); } /* we are done with this urb, so let the host driver * really free it when it is finished with it */ - usb_free_urb (urb); + usb_free_urb(urb); return count; +error: + usb_free_urb(urb); +error_no_urb: + kfree(buffer); +error_no_buffer: + spin_lock_irqsave(&priv->lock, flags); + --priv->outstanding_urbs; + spin_unlock_irqrestore(&priv->lock, flags); + return count; } diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c index bf16e9e..27c5f8f 100644 --- a/drivers/usb/serial/whiteheat.c +++ b/drivers/usb/serial/whiteheat.c @@ -1109,7 +1109,7 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * command_port = port->serial->port[COMMAND_PORT]; command_info = usb_get_serial_port_data(command_port); spin_lock_irqsave(&command_info->lock, flags); - command_info->command_finished = FALSE; + command_info->command_finished = false; transfer_buffer = (__u8 *)command_port->write_urb->transfer_buffer; transfer_buffer[0] = command; @@ -1124,12 +1124,12 @@ static int firm_send_command (struct usb_serial_port *port, __u8 command, __u8 * spin_unlock_irqrestore(&command_info->lock, flags); /* wait for the command to complete */ - wait_event_interruptible_timeout(command_info->wait_command, - (command_info->command_finished != FALSE), COMMAND_TIMEOUT); + wait_event_interruptible_timeout(command_info->wait_command, + (bool)command_info->command_finished, COMMAND_TIMEOUT); spin_lock_irqsave(&command_info->lock, flags); - if (command_info->command_finished == FALSE) { + if (command_info->command_finished == false) { dbg("%s - command timed out.", __FUNCTION__); retval = -ETIMEDOUT; goto exit; diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h index d714eff..f160797 100644 --- a/drivers/usb/serial/whiteheat.h +++ b/drivers/usb/serial/whiteheat.h @@ -20,10 +20,6 @@ #define __LINUX_USB_SERIAL_WHITEHEAT_H -#define FALSE 0 -#define TRUE 1 - - /* WhiteHEAT commands */ #define WHITEHEAT_OPEN 1 /* open the port */ #define WHITEHEAT_CLOSE 2 /* close the port */ diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c index 599ad10..06d1107 100644 --- a/drivers/usb/storage/libusual.c +++ b/drivers/usb/storage/libusual.c @@ -117,6 +117,7 @@ EXPORT_SYMBOL_GPL(usb_usual_check_type); static int usu_probe(struct usb_interface *intf, const struct usb_device_id *id) { + int rc; unsigned long type; struct task_struct* task; unsigned long flags; @@ -135,7 +136,7 @@ static int usu_probe(struct usb_interface *intf, task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type); if (IS_ERR(task)) { - int rc = PTR_ERR(task); + rc = PTR_ERR(task); printk(KERN_WARNING "libusual: " "Unable to start the thread for %s: %d\n", bias_names[type], rc); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 70234f5..e227f64 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -153,6 +153,12 @@ static int slave_configure(struct scsi_device *sdev) if (us->flags & US_FL_FIX_CAPACITY) sdev->fix_capacity = 1; + /* A few disks have two indistinguishable version, one of + * which reports the correct capacity and the other does not. + * The sd driver has to guess which is the case. */ + if (us->flags & US_FL_CAPACITY_HEURISTICS) + sdev->guess_capacity = 1; + /* Some devices report a SCSI revision level above 2 but are * unable to handle the REPORT LUNS command (for which * support is mandatory at level 3). Since we already have diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index f49a62f..8b3145ab 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -146,6 +146,13 @@ UNUSUAL_DEV( 0x0420, 0x0001, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), +/* Reported by Andrew Nayenko <relan@bk.ru> */ +UNUSUAL_DEV( 0x0421, 0x0019, 0x0592, 0x0592, + "Nokia", + "Nokia 6288", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_MAX_SECTORS_64 ), + /* Reported by Mario Rettig <mariorettig@web.de> */ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100, "Nokia", @@ -320,6 +327,13 @@ UNUSUAL_DEV( 0x04b0, 0x040d, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by Emil Larsson <emil@swip.net> */ +UNUSUAL_DEV( 0x04b0, 0x0411, 0x0100, 0x0100, + "NIKON", + "NIKON DSC D80", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* BENQ DC5330 * Reported by Manuel Fombuena <mfombuena@ya.com> and * Frank Copeland <fjc@thingy.apana.org.au> */ @@ -1101,6 +1115,15 @@ UNUSUAL_DEV( 0x08bd, 0x1100, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_SINGLE_LUN), +/* Submitted by Dylan Taft <d13f00l@gmail.com> + * US_FL_IGNORE_RESIDUE Needed + */ +UNUSUAL_DEV( 0x08ca, 0x3103, 0x0100, 0x0100, + "AIPTEK", + "Aiptek USB Keychain MP3 Player", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_IGNORE_RESIDUE), + /* Entry needed for flags. Moreover, all devices with this ID use * bulk-only transport, but _some_ falsely report Control/Bulk instead. * One example is "Trumpion Digital Research MYMP3". @@ -1311,12 +1334,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_NO_WP_DETECT ), -/* Reported by Jan Mate <mate@fiit.stuba.sk> */ +/* Reported by Jan Mate <mate@fiit.stuba.sk> + * and by Soeren Sonnenburg <kernel@nn7.de> */ UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000, "Sony Ericsson", "P990i", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY ), + US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), /* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000, @@ -1347,15 +1371,6 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_IGNORE_RESIDUE ), -/* This prevents the kernel from detecting the virtual cd-drive with the - * Windows drivers. <johann.wilhelm@student.tugraz.at> -*/ -UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff, - "HUAWEI", - "E220 USB-UMTS Install", - US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_IGNORE_DEVICE), - /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */ UNUSUAL_DEV( 0x132b, 0x000b, 0x0001, 0x0001, "Minolta", @@ -1394,6 +1409,16 @@ UNUSUAL_DEV( 0x22b8, 0x3010, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ), +/* + * Patch by Pete Zaitcev <zaitcev@redhat.com> + * Report by Mark Patton. Red Hat bz#208928. + */ +UNUSUAL_DEV( 0x22b8, 0x4810, 0x0001, 0x0001, + "Motorola", + "RAZR V3i", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Reported by Radovan Garabik <garabik@kassiopeia.juls.savba.sk> */ UNUSUAL_DEV( 0x2735, 0x100b, 0x0000, 0x9999, "MPIO", @@ -1423,7 +1448,7 @@ UNUSUAL_DEV( 0xed06, 0x4500, 0x0001, 0x0001, "DataStor", "USB4500 FW1.04", US_SC_DEVICE, US_PR_DEVICE, NULL, - US_FL_FIX_CAPACITY), + US_FL_CAPACITY_HEURISTICS), /* Control/Bulk transport for all SubClass values */ USUAL_DEV(US_SC_RBC, US_PR_CB, USB_US_TYPE_STOR), diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 7e7ec29..8e898e3 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -55,7 +55,7 @@ #include <linux/slab.h> #include <linux/kthread.h> #include <linux/mutex.h> -#include <linux/utsrelease.h> +#include <linux/utsname.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -547,7 +547,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id) idesc->bInterfaceSubClass, idesc->bInterfaceProtocol, msgs[msg], - UTS_RELEASE); + utsname()->release); } return 0; diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c index 296b091..8432bf1 100644 --- a/drivers/usb/usb-skeleton.c +++ b/drivers/usb/usb-skeleton.c @@ -34,18 +34,25 @@ static struct usb_device_id skel_table [] = { }; MODULE_DEVICE_TABLE(usb, skel_table); +/* to prevent a race between open and disconnect */ +static DEFINE_MUTEX(skel_open_lock); + /* Get a minor range for your devices from the usb maintainer */ #define USB_SKEL_MINOR_BASE 192 /* our private defines. if this grows any larger, use your own .h file */ #define MAX_TRANSFER (PAGE_SIZE - 512) +/* MAX_TRANSFER is chosen so that the VM is not stressed by + allocations > PAGE_SIZE and the number of packets in a page + is an integer 512 is the largest possible packet on EHCI */ #define WRITES_IN_FLIGHT 8 +/* arbitrarily chosen */ /* Structure to hold all of our device specific stuff */ struct usb_skel { - struct usb_device *dev; /* the usb device for this device */ - struct usb_interface *interface; /* the interface for this device */ + struct usb_device *udev; /* the usb device for this device */ + struct usb_interface *interface; /* the interface for this device */ struct semaphore limit_sem; /* limiting the number of writes in progress */ unsigned char *bulk_in_buffer; /* the buffer to receive data */ size_t bulk_in_size; /* the size of the receive buffer */ @@ -76,8 +83,10 @@ static int skel_open(struct inode *inode, struct file *file) subminor = iminor(inode); + mutex_lock(&skel_open_lock); interface = usb_find_interface(&skel_driver, subminor); if (!interface) { + mutex_unlock(&skel_open_lock); err ("%s - error, can't find device for minor %d", __FUNCTION__, subminor); retval = -ENODEV; @@ -86,17 +95,22 @@ static int skel_open(struct inode *inode, struct file *file) dev = usb_get_intfdata(interface); if (!dev) { + mutex_unlock(&skel_open_lock); retval = -ENODEV; goto exit; } + /* increment our usage count for the device */ + kref_get(&dev->kref); + /* now we can drop the lock */ + mutex_unlock(&skel_open_lock); + /* prevent the device from being autosuspended */ retval = usb_autopm_get_interface(interface); - if (retval) + if (retval) { + kref_put(&dev->kref, skel_delete); goto exit; - - /* increment our usage count for the device */ - kref_get(&dev->kref); + } /* save our object in the file's private structure */ file->private_data = dev; @@ -199,12 +213,6 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou goto exit; } - mutex_lock(&dev->io_mutex); - if (!dev->interface) { /* disconnect() was called */ - retval = -ENODEV; - goto error; - } - /* create a urb, and a buffer for it, and copy the data to the urb */ urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { @@ -223,6 +231,14 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou goto error; } + /* this lock makes sure we don't submit URBs to gone devices */ + mutex_lock(&dev->io_mutex); + if (!dev->interface) { /* disconnect() was called */ + mutex_unlock(&dev->io_mutex); + retval = -ENODEV; + goto error; + } + /* initialize the urb properly */ usb_fill_bulk_urb(urb, dev->udev, usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr), @@ -231,6 +247,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou /* send the data out the bulk port */ retval = usb_submit_urb(urb, GFP_KERNEL); + mutex_unlock(&dev->io_mutex); if (retval) { err("%s - failed submitting write urb, error %d", __FUNCTION__, retval); goto error; @@ -239,7 +256,7 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou /* release our reference to this urb, the USB core will eventually free it entirely */ usb_free_urb(urb); - mutex_unlock(&dev->io_mutex); + return writesize; error: @@ -247,7 +264,6 @@ error: usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma); usb_free_urb(urb); } - mutex_unlock(&dev->io_mutex); up(&dev->limit_sem); exit: @@ -342,6 +358,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i error: if (dev) + /* this frees allocated memory */ kref_put(&dev->kref, skel_delete); return retval; } @@ -352,20 +369,21 @@ static void skel_disconnect(struct usb_interface *interface) int minor = interface->minor; /* prevent skel_open() from racing skel_disconnect() */ - lock_kernel(); + mutex_lock(&skel_open_lock); dev = usb_get_intfdata(interface); usb_set_intfdata(interface, NULL); /* give back our minor */ usb_deregister_dev(interface, &skel_class); + mutex_unlock(&skel_open_lock); /* prevent more I/O from starting */ mutex_lock(&dev->io_mutex); dev->interface = NULL; mutex_unlock(&dev->io_mutex); - unlock_kernel(); + /* decrement our usage count */ kref_put(&dev->kref, skel_delete); @@ -378,6 +396,7 @@ static struct usb_driver skel_driver = { .probe = skel_probe, .disconnect = skel_disconnect, .id_table = skel_table, + .supports_autosuspend = 1, }; static int __init usb_skel_init(void) diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 8874cf2..b1cb72c 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -4,20 +4,7 @@ menu "Graphics support" -config FIRMWARE_EDID - bool "Enable firmware EDID" - default y - ---help--- - This enables access to the EDID transferred from the firmware. - On the i386, this is from the Video BIOS. Enable this if DDC/I2C - transfers do not work for your driver and if you are using - nvidiafb, i810fb or savagefb. - - In general, choosing Y for this option is safe. If you - experience extremely long delays while booting before you get - something on your display, try setting this to N. Matrox cards in - combination with certain motherboards and monitors are known to - suffer from this problem. +source "drivers/video/backlight/Kconfig" config FB tristate "Support for frame buffer devices" @@ -53,9 +40,27 @@ config FB (e.g. an accelerated X server) and that are not frame buffer device-aware may cause unexpected results. If unsure, say N. +config FIRMWARE_EDID + bool "Enable firmware EDID" + depends on FB + default n + ---help--- + This enables access to the EDID transferred from the firmware. + On the i386, this is from the Video BIOS. Enable this if DDC/I2C + transfers do not work for your driver and if you are using + nvidiafb, i810fb or savagefb. + + In general, choosing Y for this option is safe. If you + experience extremely long delays while booting before you get + something on your display, try setting this to N. Matrox cards in + combination with certain motherboards and monitors are known to + suffer from this problem. + config FB_DDC tristate - depends on FB && I2C && I2C_ALGOBIT + depends on FB + select I2C_ALGOBIT + select I2C default n config FB_CFB_FILLRECT @@ -134,6 +139,9 @@ config FB_TILEBLITTING This is particularly important to one driver, matroxfb. If unsure, say N. +comment "Frame buffer hardware drivers" + depends on FB + config FB_CIRRUS tristate "Cirrus Logic support" depends on FB && (ZORRO || PCI) @@ -388,7 +396,7 @@ config FB_ATARI config FB_OF bool "Open Firmware frame buffer device support" - depends on (FB = y) && (PPC64 || PPC_OF) + depends on (FB = y) && (PPC64 || PPC_OF) && (!PPC_PSERIES || PCI) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -669,8 +677,7 @@ config FB_S1D13XXX config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI - select I2C_ALGOBIT if FB_NVIDIA_I2C - select I2C if FB_NVIDIA_I2C + select FB_BACKLIGHT if FB_NVIDIA_BACKLIGHT select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -688,6 +695,7 @@ config FB_NVIDIA config FB_NVIDIA_I2C bool "Enable DDC Support" depends on FB_NVIDIA + select FB_DDC help This enables I2C support for nVidia Chipsets. This is used only for getting EDID information from the attached display @@ -699,8 +707,7 @@ config FB_NVIDIA_I2C config FB_NVIDIA_BACKLIGHT bool "Support for backlight control" - depends on FB_NVIDIA && PMAC_BACKLIGHT - select FB_BACKLIGHT + depends on FB_NVIDIA default y help Say Y here if you want to control the backlight of your display. @@ -708,9 +715,7 @@ config FB_NVIDIA_BACKLIGHT config FB_RIVA tristate "nVidia Riva support" depends on FB && PCI - select I2C_ALGOBIT if FB_RIVA_I2C - select I2C if FB_RIVA_I2C - select FB_DDC if FB_RIVA_I2C + select FB_BACKLIGHT if FB_RIVA_BACKLIGHT select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -727,6 +732,7 @@ config FB_RIVA config FB_RIVA_I2C bool "Enable DDC Support" depends on FB_RIVA + select FB_DDC help This enables I2C support for nVidia Chipsets. This is used only for getting EDID information from the attached display @@ -747,8 +753,7 @@ config FB_RIVA_DEBUG config FB_RIVA_BACKLIGHT bool "Support for backlight control" - depends on FB_RIVA && PMAC_BACKLIGHT - select FB_BACKLIGHT + depends on FB_RIVA default y help Say Y here if you want to control the backlight of your display. @@ -798,8 +803,6 @@ config FB_I810_GTF config FB_I810_I2C bool "Enable DDC Support" depends on FB_I810 && FB_I810_GTF - select I2C - select I2C_ALGOBIT select FB_DDC help @@ -808,8 +811,6 @@ config FB_INTEL depends on FB && EXPERIMENTAL && PCI && X86 select AGP select AGP_INTEL - select I2C_ALGOBIT if FB_INTEL_I2C - select I2C if FB_INTEL_I2C select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -842,6 +843,7 @@ config FB_INTEL_DEBUG config FB_INTEL_I2C bool "DDC/I2C for Intel framebuffer support" depends on FB_INTEL + select FB_DDC default y help Say Y here if you want DDC/I2C support for your on-board Intel graphics. @@ -920,8 +922,8 @@ config FB_MATROX_G config FB_MATROX_I2C tristate "Matrox I2C support" - depends on FB_MATROX && I2C - select I2C_ALGOBIT + depends on FB_MATROX + select FB_DDC ---help--- This drivers creates I2C buses which are needed for accessing the DDC (I2C) bus present on all Matroxes, an I2C bus which @@ -989,9 +991,7 @@ config FB_MATROX_MULTIHEAD config FB_RADEON tristate "ATI Radeon display support" depends on FB && PCI - select I2C_ALGOBIT if FB_RADEON_I2C - select I2C if FB_RADEON_I2C - select FB_DDC if FB_RADEON_I2C + select FB_BACKLIGHT if FB_RADEON_BACKLIGHT select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1015,14 +1015,14 @@ config FB_RADEON config FB_RADEON_I2C bool "DDC/I2C for ATI Radeon support" depends on FB_RADEON + select FB_DDC default y help Say Y here if you want DDC/I2C support for your Radeon board. config FB_RADEON_BACKLIGHT bool "Support for backlight control" - depends on FB_RADEON && PMAC_BACKLIGHT - select FB_BACKLIGHT + depends on FB_RADEON default y help Say Y here if you want to control the backlight of your display. @@ -1042,6 +1042,7 @@ config FB_ATY128 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_BACKLIGHT if FB_ATY128_BACKLIGHT select FB_MACMODES if PPC_PMAC help This driver supports graphics boards with the ATI Rage128 chips. @@ -1053,8 +1054,7 @@ config FB_ATY128 config FB_ATY128_BACKLIGHT bool "Support for backlight control" - depends on FB_ATY128 && PMAC_BACKLIGHT - select FB_BACKLIGHT + depends on FB_ATY128 default y help Say Y here if you want to control the backlight of your display. @@ -1065,6 +1065,7 @@ config FB_ATY select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT + select FB_BACKLIGHT if FB_ATY_BACKLIGHT select FB_MACMODES if PPC help This driver supports graphics boards with the ATI Mach64 chips. @@ -1103,8 +1104,7 @@ config FB_ATY_GX config FB_ATY_BACKLIGHT bool "Support for backlight control" - depends on FB_ATY && PMAC_BACKLIGHT - select FB_BACKLIGHT + depends on FB_ATY default y help Say Y here if you want to control the backlight of your display. @@ -1123,9 +1123,6 @@ config FB_S3 config FB_SAVAGE tristate "S3 Savage support" depends on FB && PCI && EXPERIMENTAL - select I2C_ALGOBIT if FB_SAVAGE_I2C - select I2C if FB_SAVAGE_I2C - select FB_DDC if FB_SAVAGE_I2C select FB_MODE_HELPERS select FB_CFB_FILLRECT select FB_CFB_COPYAREA @@ -1142,6 +1139,7 @@ config FB_SAVAGE config FB_SAVAGE_I2C bool "Enable DDC2 Support" depends on FB_SAVAGE + select FB_DDC help This enables I2C support for S3 Savage Chipsets. This is used only for getting EDID information from the attached display @@ -1322,7 +1320,7 @@ config FB_AU1100 config FB_AU1200 bool "Au1200 LCD Driver" - depends on FB && MIPS && SOC_AU1200 + depends on (FB = y) && MIPS && SOC_AU1200 select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1472,7 +1470,7 @@ config FB_G364 config FB_68328 bool "Motorola 68328 native frame buffer support" - depends on FB && (M68328 || M68EZ328 || M68VZ328) + depends on (FB = y) && (M68328 || M68EZ328 || M68VZ328) select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1573,6 +1571,24 @@ config FB_S3C2410_DEBUG Turn on debugging messages. Note that you can set/unset at run time through sysfs +config FB_SM501 + tristate "Silicon Motion SM501 framebuffer support" + depends on FB && MFD_SM501 + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + ---help--- + Frame buffer driver for the CRT and LCD controllers in the Silicon + Motion SM501. + + This driver is also available as a module ( = code which can be + inserted and removed from the running kernel whenever you want). The + module will be called sm501fb. If you want to compile it as a module, + say M here and read <file:Documentation/modules.txt>. + + If unsure, say N. + + config FB_PNX4008_DUM tristate "Display Update Module support on Philips PNX4008 board" depends on FB && ARCH_PNX4008 @@ -1600,8 +1616,7 @@ config FB_IBM_GXT4500 config FB_PS3 bool "PS3 GPU framebuffer driver" - depends on FB && PPC_PS3 - select PS3_PS3AV + depends on (FB = y) && PS3_PS3AV select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT @@ -1639,6 +1654,7 @@ config FB_VIRTUAL the vfb_enable=1 option. If unsure, say N. + if VT source "drivers/video/console/Kconfig" endif @@ -1647,9 +1663,5 @@ if FB || SGI_NEWPORT_CONSOLE source "drivers/video/logo/Kconfig" endif -if SYSFS - source "drivers/video/backlight/Kconfig" -endif - endmenu diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 6801edf..760305c 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -12,7 +12,7 @@ fb-objs := $(fb-y) obj-$(CONFIG_VT) += console/ obj-$(CONFIG_LOGO) += logo/ -obj-$(CONFIG_SYSFS) += backlight/ +obj-y += backlight/ obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o @@ -98,6 +98,7 @@ obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o obj-$(CONFIG_FB_PS3) += ps3fb.o +obj-$(CONFIG_FB_SM501) += sm501fb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c index 2e976ff..e86d7e0 100644 --- a/drivers/video/aty/aty128fb.c +++ b/drivers/video/aty/aty128fb.c @@ -357,6 +357,12 @@ static int default_lcd_on __devinitdata = 1; static int mtrr = 1; #endif +#ifdef CONFIG_PMAC_BACKLIGHT +static int backlight __devinitdata = 1; +#else +static int backlight __devinitdata = 0; +#endif + /* PLL constants */ struct aty128_constants { u32 ref_clk; @@ -1652,6 +1658,9 @@ static int __devinit aty128fb_setup(char *options) } else if (!strncmp(this_opt, "crt:", 4)) { default_crt_on = simple_strtoul(this_opt+4, NULL, 0); continue; + } else if (!strncmp(this_opt, "backlight:", 10)) { + backlight = simple_strtoul(this_opt+10, NULL, 0); + continue; } #ifdef CONFIG_MTRR if(!strncmp(this_opt, "nomtrr", 6)) { @@ -1695,9 +1704,6 @@ static int __devinit aty128fb_setup(char *options) #ifdef CONFIG_FB_ATY128_BACKLIGHT #define MAX_LEVEL 0xFF -static struct backlight_properties aty128_bl_data; - -/* Call with fb_info->bl_mutex held */ static int aty128_bl_get_level_brightness(struct aty128fb_par *par, int level) { @@ -1705,6 +1711,7 @@ static int aty128_bl_get_level_brightness(struct aty128fb_par *par, int atylevel; /* Get and convert the value */ + /* No locking of bl_curve since we read a single value */ atylevel = MAX_LEVEL - (info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL); @@ -1724,19 +1731,18 @@ static int aty128_bl_get_level_brightness(struct aty128fb_par *par, /* That one prevents proper CRT output with LCD off */ #undef BACKLIGHT_DAC_OFF -/* Call with fb_info->bl_mutex held */ -static int __aty128_bl_update_status(struct backlight_device *bd) +static int aty128_bl_update_status(struct backlight_device *bd) { struct aty128fb_par *par = class_get_devdata(&bd->class_dev); unsigned int reg = aty_ld_le32(LVDS_GEN_CNTL); int level; - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK || + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK || !par->lcd_on) level = 0; else - level = bd->props->brightness; + level = bd->props.brightness; reg |= LVDS_BL_MOD_EN | LVDS_BLON; if (level > 0) { @@ -1778,43 +1784,22 @@ static int __aty128_bl_update_status(struct backlight_device *bd) return 0; } -static int aty128_bl_update_status(struct backlight_device *bd) -{ - struct aty128fb_par *par = class_get_devdata(&bd->class_dev); - struct fb_info *info = pci_get_drvdata(par->pdev); - int ret; - - mutex_lock(&info->bl_mutex); - ret = __aty128_bl_update_status(bd); - mutex_unlock(&info->bl_mutex); - - return ret; -} - static int aty128_bl_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties aty128_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops aty128_bl_data = { .get_brightness = aty128_bl_get_brightness, .update_status = aty128_bl_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; static void aty128_bl_set_power(struct fb_info *info, int power) { - mutex_lock(&info->bl_mutex); - if (info->bl_dev) { - down(&info->bl_dev->sem); - info->bl_dev->props->power = power; - __aty128_bl_update_status(info->bl_dev); - up(&info->bl_dev->sem); + info->bl_dev->props.power = power; + backlight_update_status(info->bl_dev); } - - mutex_unlock(&info->bl_mutex); } static void aty128_bl_init(struct aty128fb_par *par) @@ -1841,25 +1826,15 @@ static void aty128_bl_init(struct aty128fb_par *par) goto error; } - mutex_lock(&info->bl_mutex); info->bl_dev = bd; fb_bl_default_curve(info, 0, 63 * FB_BACKLIGHT_MAX / MAX_LEVEL, 219 * FB_BACKLIGHT_MAX / MAX_LEVEL); - mutex_unlock(&info->bl_mutex); - down(&bd->sem); - bd->props->brightness = aty128_bl_data.max_brightness; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); -#endif + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + bd->props.brightness = bd->props.max_brightness; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("aty128: Backlight initialized (%s)\n", name); @@ -1869,31 +1844,10 @@ error: return; } -static void aty128_bl_exit(struct aty128fb_par *par) +static void aty128_bl_exit(struct backlight_device *bd) { - struct fb_info *info = pci_get_drvdata(par->pdev); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); -#endif - - mutex_lock(&info->bl_mutex); - if (info->bl_dev) { -#ifdef CONFIG_PMAC_BACKLIGHT - if (pmac_backlight == info->bl_dev) - pmac_backlight = NULL; -#endif - - backlight_device_unregister(info->bl_dev); - info->bl_dev = NULL; - - printk("aty128: Backlight unloaded\n"); - } - mutex_unlock(&info->bl_mutex); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_unlock(&pmac_backlight_mutex); -#endif + backlight_device_unregister(bd); + printk("aty128: Backlight unloaded\n"); } #endif /* CONFIG_FB_ATY128_BACKLIGHT */ @@ -2040,7 +1994,8 @@ static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_i par->lock_blank = 0; #ifdef CONFIG_FB_ATY128_BACKLIGHT - aty128_bl_init(par); + if (backlight) + aty128_bl_init(par); #endif if (register_framebuffer(info) < 0) @@ -2180,11 +2135,12 @@ static void __devexit aty128_remove(struct pci_dev *pdev) par = info->par; + unregister_framebuffer(info); + #ifdef CONFIG_FB_ATY128_BACKLIGHT - aty128_bl_exit(par); + aty128_bl_exit(info->bl_dev); #endif - unregister_framebuffer(info); #ifdef CONFIG_MTRR if (par->mtrr.vram_valid) mtrr_del(par->mtrr.vram, info->fix.smem_start, @@ -2214,11 +2170,6 @@ static int aty128fb_blank(int blank, struct fb_info *fb) if (par->lock_blank || par->asleep) return 0; -#ifdef CONFIG_FB_ATY128_BACKLIGHT - if (machine_is(powermac) && blank) - aty128_bl_set_power(fb, FB_BLANK_POWERDOWN); -#endif - if (blank & FB_BLANK_VSYNC_SUSPEND) state |= 2; if (blank & FB_BLANK_HSYNC_SUSPEND) @@ -2233,11 +2184,6 @@ static int aty128fb_blank(int blank, struct fb_info *fb) aty128_set_lcd_enable(par, par->lcd_on && !blank); } -#ifdef CONFIG_FB_ATY128_BACKLIGHT - if (machine_is(powermac) && !blank) - aty128_bl_set_power(fb, FB_BLANK_UNBLANK); -#endif - return 0; } diff --git a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h index f72faff..dc62f8e 100644 --- a/drivers/video/aty/atyfb.h +++ b/drivers/video/aty/atyfb.h @@ -284,7 +284,8 @@ static inline void aty_st_8(int regindex, u8 val, const struct atyfb_par *par) #endif } -#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) +#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \ +defined (CONFIG_FB_ATY_GENERIC_LCD) || defined (CONFIG_FB_ATY_BACKLIGHT) extern void aty_st_lcd(int index, u32 val, const struct atyfb_par *par); extern u32 aty_ld_lcd(int index, const struct atyfb_par *par); #endif diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 301612c..8514f2a 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c @@ -131,7 +131,8 @@ #define PRINTKI(fmt, args...) printk(KERN_INFO "atyfb: " fmt, ## args) #define PRINTKE(fmt, args...) printk(KERN_ERR "atyfb: " fmt, ## args) -#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || defined (CONFIG_FB_ATY_GENERIC_LCD) +#if defined(CONFIG_PM) || defined(CONFIG_PMAC_BACKLIGHT) || \ +defined (CONFIG_FB_ATY_GENERIC_LCD) || defined(CONFIG_FB_ATY_BACKLIGHT) static const u32 lt_lcd_regs[] = { CONFIG_PANEL_LG, LCD_GEN_CNTL_LG, @@ -308,6 +309,12 @@ static int xclk; static int comp_sync __devinitdata = -1; static char *mode; +#ifdef CONFIG_PMAC_BACKLIGHT +static int backlight __devinitdata = 1; +#else +static int backlight __devinitdata = 0; +#endif + #ifdef CONFIG_PPC static int default_vmode __devinitdata = VMODE_CHOOSE; static int default_cmode __devinitdata = CMODE_CHOOSE; @@ -2114,15 +2121,13 @@ static int atyfb_pci_resume(struct pci_dev *pdev) #ifdef CONFIG_FB_ATY_BACKLIGHT #define MAX_LEVEL 0xFF -static struct backlight_properties aty_bl_data; - -/* Call with fb_info->bl_mutex held */ static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) { struct fb_info *info = pci_get_drvdata(par->pdev); int atylevel; /* Get and convert the value */ + /* No locking of bl_curve since we read a single value */ atylevel = info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_LEVEL; if (atylevel < 0) @@ -2133,18 +2138,17 @@ static int aty_bl_get_level_brightness(struct atyfb_par *par, int level) return atylevel; } -/* Call with fb_info->bl_mutex held */ -static int __aty_bl_update_status(struct backlight_device *bd) +static int aty_bl_update_status(struct backlight_device *bd) { struct atyfb_par *par = class_get_devdata(&bd->class_dev); unsigned int reg = aty_ld_lcd(LCD_MISC_CNTL, par); int level; - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; else - level = bd->props->brightness; + level = bd->props.brightness; reg |= (BLMOD_EN | BIASMOD_EN); if (level > 0) { @@ -2159,45 +2163,16 @@ static int __aty_bl_update_status(struct backlight_device *bd) return 0; } -static int aty_bl_update_status(struct backlight_device *bd) -{ - struct atyfb_par *par = class_get_devdata(&bd->class_dev); - struct fb_info *info = pci_get_drvdata(par->pdev); - int ret; - - mutex_lock(&info->bl_mutex); - ret = __aty_bl_update_status(bd); - mutex_unlock(&info->bl_mutex); - - return ret; -} - static int aty_bl_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties aty_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops aty_bl_data = { .get_brightness = aty_bl_get_brightness, .update_status = aty_bl_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; -static void aty_bl_set_power(struct fb_info *info, int power) -{ - mutex_lock(&info->bl_mutex); - - if (info->bl_dev) { - down(&info->bl_dev->sem); - info->bl_dev->props->power = power; - __aty_bl_update_status(info->bl_dev); - up(&info->bl_dev->sem); - } - - mutex_unlock(&info->bl_mutex); -} - static void aty_bl_init(struct atyfb_par *par) { struct fb_info *info = pci_get_drvdata(par->pdev); @@ -2218,25 +2193,15 @@ static void aty_bl_init(struct atyfb_par *par) goto error; } - mutex_lock(&info->bl_mutex); info->bl_dev = bd; fb_bl_default_curve(info, 0, 0x3F * FB_BACKLIGHT_MAX / MAX_LEVEL, 0xFF * FB_BACKLIGHT_MAX / MAX_LEVEL); - mutex_unlock(&info->bl_mutex); - - down(&bd->sem); - bd->props->brightness = aty_bl_data.max_brightness; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); -#endif + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + bd->props.brightness = bd->props.max_brightness; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("aty: Backlight initialized (%s)\n", name); @@ -2246,30 +2211,10 @@ error: return; } -static void aty_bl_exit(struct atyfb_par *par) +static void aty_bl_exit(struct backlight_device *bd) { - struct fb_info *info = pci_get_drvdata(par->pdev); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); -#endif - - mutex_lock(&info->bl_mutex); - if (info->bl_dev) { -#ifdef CONFIG_PMAC_BACKLIGHT - if (pmac_backlight == info->bl_dev) - pmac_backlight = NULL; -#endif - - backlight_device_unregister(info->bl_dev); - - printk("aty: Backlight unloaded\n"); - } - mutex_unlock(&info->bl_mutex); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_unlock(&pmac_backlight_mutex); -#endif + backlight_device_unregister(bd); + printk("aty: Backlight unloaded\n"); } #endif /* CONFIG_FB_ATY_BACKLIGHT */ @@ -2637,7 +2582,7 @@ static int __devinit aty_init(struct fb_info *info) | (USE_F32KHZ | TRISTATE_MEM_EN), par); } else #endif - if (M64_HAS(MOBIL_BUS)) { + if (M64_HAS(MOBIL_BUS) && backlight) { #ifdef CONFIG_FB_ATY_BACKLIGHT aty_bl_init (par); #endif @@ -2814,8 +2759,6 @@ static int atyfb_blank(int blank, struct fb_info *info) return 0; #ifdef CONFIG_FB_ATY_BACKLIGHT - if (machine_is(powermac) && blank > FB_BLANK_NORMAL) - aty_bl_set_power(info, FB_BLANK_POWERDOWN); #elif defined(CONFIG_FB_ATY_GENERIC_LCD) if (par->lcd_table && blank > FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { @@ -2846,8 +2789,6 @@ static int atyfb_blank(int blank, struct fb_info *info) aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par); #ifdef CONFIG_FB_ATY_BACKLIGHT - if (machine_is(powermac) && blank <= FB_BLANK_NORMAL) - aty_bl_set_power(info, FB_BLANK_UNBLANK); #elif defined(CONFIG_FB_ATY_GENERIC_LCD) if (par->lcd_table && blank <= FB_BLANK_NORMAL && (aty_ld_lcd(LCD_GEN_CNTL, par) & LCD_ON)) { @@ -2958,7 +2899,7 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, struct fb_info *info, unsigned long addr) { struct atyfb_par *par = info->par; - struct pcidev_cookie *pcp; + struct device_node *dp; char prop[128]; int node, len, i, j, ret; u32 mem, chip_id; @@ -3096,8 +3037,8 @@ static int __devinit atyfb_setup_sparc(struct pci_dev *pdev, node = 0; } - pcp = pdev->sysdata; - if (node == pcp->prom_node->node) { + dp = pci_device_to_OF_node(pdev); + if (node == dp->node) { struct fb_var_screeninfo *var = &default_var; unsigned int N, P, Q, M, T, R; u32 v_total, h_total; @@ -3726,13 +3667,13 @@ static void __devexit atyfb_remove(struct fb_info *info) aty_set_crtc(par, &saved_crtc); par->pll_ops->set_pll(info, &saved_pll); + unregister_framebuffer(info); + #ifdef CONFIG_FB_ATY_BACKLIGHT if (M64_HAS(MOBIL_BUS)) - aty_bl_exit(par); + aty_bl_exit(info->bl_dev); #endif - unregister_framebuffer(info); - #ifdef CONFIG_MTRR if (par->mtrr_reg >= 0) { mtrr_del(par->mtrr_reg, 0, 0); @@ -3823,6 +3764,8 @@ static int __init atyfb_setup(char *options) xclk = simple_strtoul(this_opt+5, NULL, 0); else if (!strncmp(this_opt, "comp_sync:", 10)) comp_sync = simple_strtoul(this_opt+10, NULL, 0); + else if (!strncmp(this_opt, "backlight:", 10)) + backlight = simple_strtoul(this_opt+10, NULL, 0); #ifdef CONFIG_PPC else if (!strncmp(this_opt, "vmode:", 6)) { unsigned int vmode = diff --git a/drivers/video/aty/mach64_ct.c b/drivers/video/aty/mach64_ct.c index f3b487b..1fdcfdb 100644 --- a/drivers/video/aty/mach64_ct.c +++ b/drivers/video/aty/mach64_ct.c @@ -598,7 +598,6 @@ static void aty_resume_pll_ct(const struct fb_info *info, struct atyfb_par *par = info->par; if (par->mclk_per != par->xclk_per) { - int i; /* * This disables the sclk, crashes the computer as reported: * aty_st_pll_ct(SPLL_CNTL2, 3, info); @@ -614,7 +613,7 @@ static void aty_resume_pll_ct(const struct fb_info *info, * helps for Rage Mobilities that sometimes crash when * we switch to sclk. (Daniel Mantione, 13-05-2003) */ - for (i=0;i<=0x1ffff;i++); + udelay(500); } aty_st_pll_ct(PLL_REF_DIV, pll->ct.pll_ref_div, par); diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c index 3abfd4a..0be25fa 100644 --- a/drivers/video/aty/radeon_backlight.c +++ b/drivers/video/aty/radeon_backlight.c @@ -19,8 +19,6 @@ #define MAX_RADEON_LEVEL 0xFF -static struct backlight_properties radeon_bl_data; - struct radeon_bl_privdata { struct radeonfb_info *rinfo; uint8_t negative; @@ -29,17 +27,13 @@ struct radeon_bl_privdata { static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata, int level) { - struct fb_info *info = pdata->rinfo->info; int rlevel; - mutex_lock(&info->bl_mutex); - /* Get and convert the value */ + /* No locking of bl_curve since we read a single value */ rlevel = pdata->rinfo->info->bl_curve[level] * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL; - mutex_unlock(&info->bl_mutex); - if (rlevel < 0) rlevel = 0; else if (rlevel > MAX_RADEON_LEVEL) @@ -65,11 +59,11 @@ static int radeon_bl_update_status(struct backlight_device *bd) * backlight. This provides some greater power saving and the display * is useless without backlight anyway. */ - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; else - level = bd->props->brightness; + level = bd->props.brightness; del_timer_sync(&rinfo->lvds_timer); radeon_engine_idle(); @@ -130,14 +124,12 @@ static int radeon_bl_update_status(struct backlight_device *bd) static int radeon_bl_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties radeon_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops radeon_bl_data = { .get_brightness = radeon_bl_get_brightness, .update_status = radeon_bl_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; void radeonfb_bl_init(struct radeonfb_info *rinfo) @@ -188,25 +180,15 @@ void radeonfb_bl_init(struct radeonfb_info *rinfo) machine_is_compatible("PowerBook6,5"); #endif - mutex_lock(&rinfo->info->bl_mutex); rinfo->info->bl_dev = bd; fb_bl_default_curve(rinfo->info, 0, 63 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL, 217 * FB_BACKLIGHT_MAX / MAX_RADEON_LEVEL); - mutex_unlock(&rinfo->info->bl_mutex); - down(&bd->sem); - bd->props->brightness = radeon_bl_data.max_brightness; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); -#endif + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + bd->props.brightness = bd->props.max_brightness; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("radeonfb: Backlight initialized (%s)\n", name); @@ -219,29 +201,16 @@ error: void radeonfb_bl_exit(struct radeonfb_info *rinfo) { -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); -#endif + struct backlight_device *bd = rinfo->info->bl_dev; - mutex_lock(&rinfo->info->bl_mutex); - if (rinfo->info->bl_dev) { + if (bd) { struct radeon_bl_privdata *pdata; -#ifdef CONFIG_PMAC_BACKLIGHT - if (pmac_backlight == rinfo->info->bl_dev) - pmac_backlight = NULL; -#endif - - pdata = class_get_devdata(&rinfo->info->bl_dev->class_dev); - backlight_device_unregister(rinfo->info->bl_dev); + pdata = class_get_devdata(&bd->class_dev); + backlight_device_unregister(bd); kfree(pdata); rinfo->info->bl_dev = NULL; printk("radeonfb: Backlight unloaded\n"); } - mutex_unlock(&rinfo->info->bl_mutex); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_unlock(&pmac_backlight_mutex); -#endif } diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 0ed577e..a4b3fd1 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -268,6 +268,11 @@ static int nomtrr = 0; #endif static int force_sleep; static int ignore_devlist; +#ifdef CONFIG_PMAC_BACKLIGHT +static int backlight = 1; +#else +static int backlight = 0; +#endif /* * prototypes @@ -405,7 +410,7 @@ static int __devinit radeon_find_mem_vbios(struct radeonfb_info *rinfo) } #endif -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Read XTAL (ref clock), SCLK and MCLK from Open Firmware device * tree. Hopefully, ATI OF driver is kind enough to fill these @@ -435,7 +440,7 @@ static int __devinit radeon_read_xtal_OF (struct radeonfb_info *rinfo) return 0; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ /* * Read PLL infos from chip registers @@ -640,7 +645,7 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) rinfo->pll.ref_div = INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Retrieve PLL infos from Open Firmware first */ @@ -648,7 +653,7 @@ static void __devinit radeon_get_pllinfo(struct radeonfb_info *rinfo) printk(KERN_INFO "radeonfb: Retrieved PLL infos from Open Firmware\n"); goto found; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ /* * Check out if we have an X86 which gave us some PLL informations @@ -1026,8 +1031,7 @@ int radeon_screen_blank(struct radeonfb_info *rinfo, int blank, int mode_switch) break; } - /* let fbcon do a soft blank for us */ - return (blank == FB_BLANK_NORMAL) ? -EINVAL : 0; + return 0; } static int radeonfb_blank (int blank, struct fb_info *info) @@ -2227,7 +2231,7 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, rinfo->family == CHIP_FAMILY_RS200) rinfo->errata |= CHIP_ERRATA_PLL_DELAY; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* On PPC, we obtain the OF device-node pointer to the firmware * data for this chip */ @@ -2236,6 +2240,8 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, printk(KERN_WARNING "radeonfb (%s): Cannot match card to OF node !\n", pci_name(rinfo->pdev)); +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ +#ifdef CONFIG_PPC_OF /* On PPC, the firmware sets up a memory mapping that tends * to cause lockups when enabling the engine. We reconfigure * the card internal memory mappings properly @@ -2349,7 +2355,8 @@ static int __devinit radeonfb_pci_register (struct pci_dev *pdev, MTRR_TYPE_WRCOMB, 1); #endif - radeonfb_bl_init(rinfo); + if (backlight) + radeonfb_bl_init(rinfo); printk ("radeonfb (%s): %s\n", pci_name(rinfo->pdev), rinfo->name); @@ -2393,7 +2400,6 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev) if (!rinfo) return; - radeonfb_bl_exit(rinfo); radeonfb_pm_exit(rinfo); if (rinfo->mon1_EDID) @@ -2420,6 +2426,8 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev) unregister_framebuffer(info); + radeonfb_bl_exit(rinfo); + iounmap(rinfo->mmio_base); iounmap(rinfo->fb_base); @@ -2469,6 +2477,8 @@ static int __init radeonfb_setup (char *options) force_dfp = 1; } else if (!strncmp(this_opt, "panel_yres:", 11)) { panel_yres = simple_strtoul((this_opt+11), NULL, 0); + } else if (!strncmp(this_opt, "backlight:", 10)) { + backlight = simple_strtoul(this_opt+10, NULL, 0); #ifdef CONFIG_MTRR } else if (!strncmp(this_opt, "nomtrr", 6)) { nomtrr = 1; diff --git a/drivers/video/aty/radeon_monitor.c b/drivers/video/aty/radeon_monitor.c index 38c7dbf..737b5c0 100644 --- a/drivers/video/aty/radeon_monitor.c +++ b/drivers/video/aty/radeon_monitor.c @@ -52,7 +52,7 @@ static char *radeon_get_mon_name(int type) } -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) /* * Try to find monitor informations & EDID data out of the Open Firmware * device-tree. This also contains some "hacks" to work around a few machine @@ -156,7 +156,7 @@ static int __devinit radeon_probe_OF_head(struct radeonfb_info *rinfo, int head_ } return MT_NONE; } -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ static int __devinit radeon_get_panel_info_BIOS(struct radeonfb_info *rinfo) @@ -495,11 +495,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, * Old single head cards */ if (!rinfo->has_CRTC2) { -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = @@ -544,11 +544,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, /* * Probe primary head (DVI or laptop internal panel) */ -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_OF_head(rinfo, 0, &rinfo->mon1_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || CONFIG_SPARC */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon1_type == MT_NONE) rinfo->mon1_type = radeon_probe_i2c_connector(rinfo, ddc_dvi, @@ -572,11 +572,11 @@ void __devinit radeon_probe_screens(struct radeonfb_info *rinfo, /* * Probe secondary head (mostly VGA, can be DVI) */ -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) if (rinfo->mon2_type == MT_NONE) rinfo->mon2_type = radeon_probe_OF_head(rinfo, 1, &rinfo->mon2_EDID); -#endif /* CONFIG_PPC_OF */ +#endif /* CONFIG_PPC_OF || defined(CONFIG_SPARC) */ #ifdef CONFIG_FB_RADEON_I2C if (rinfo->mon2_type == MT_NONE) rinfo->mon2_type = radeon_probe_i2c_connector(rinfo, ddc_vga, diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c index 9a2b0d6..c411293 100644 --- a/drivers/video/aty/radeon_pm.c +++ b/drivers/video/aty/radeon_pm.c @@ -1262,7 +1262,7 @@ static void radeon_pm_full_reset_sdram(struct radeonfb_info *rinfo) /* This is the code for the Aluminium PowerBooks M10 / iBooks M11 */ if (rinfo->family == CHIP_FAMILY_RV350) { u32 sdram_mode_reg = rinfo->save_regs[35]; - static u32 default_mrtable[] = + static const u32 default_mrtable[] = { 0x21320032, 0x21321000, 0xa1321000, 0x21321000, 0xffffffff, 0x21320032, 0xa1320032, 0x21320032, 0xffffffff, diff --git a/drivers/video/aty/radeonfb.h b/drivers/video/aty/radeonfb.h index d5ff224..3190003 100644 --- a/drivers/video/aty/radeonfb.h +++ b/drivers/video/aty/radeonfb.h @@ -16,7 +16,7 @@ #include <asm/io.h> -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) #include <asm/prom.h> #endif @@ -292,7 +292,7 @@ struct radeonfb_info { unsigned long fb_local_base; struct pci_dev *pdev; -#ifdef CONFIG_PPC_OF +#if defined(CONFIG_PPC_OF) || defined(CONFIG_SPARC) struct device_node *of_node; #endif diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 02f1529..47d15b5 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -19,11 +19,6 @@ config BACKLIGHT_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. -config BACKLIGHT_DEVICE - bool - depends on BACKLIGHT_CLASS_DEVICE - default y - config LCD_CLASS_DEVICE tristate "Lowlevel LCD controls" depends on BACKLIGHT_LCD_SUPPORT @@ -37,14 +32,9 @@ config LCD_CLASS_DEVICE To have support for your specific LCD panel you will have to select the proper drivers which depend on this option. -config LCD_DEVICE - bool - depends on LCD_CLASS_DEVICE - default y - config BACKLIGHT_CORGI tristate "Sharp Corgi Backlight Driver (SL Series)" - depends on BACKLIGHT_DEVICE && PXA_SHARPSL + depends on BACKLIGHT_CLASS_DEVICE && PXA_SHARPSL default y help If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the @@ -52,7 +42,7 @@ config BACKLIGHT_CORGI config BACKLIGHT_LOCOMO tristate "Sharp LOCOMO LCD/Backlight Driver" - depends on BACKLIGHT_DEVICE && SHARP_LOCOMO + depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO default y help If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to @@ -60,9 +50,16 @@ config BACKLIGHT_LOCOMO config BACKLIGHT_HP680 tristate "HP Jornada 680 Backlight Driver" - depends on BACKLIGHT_DEVICE && SH_HP6XX + depends on BACKLIGHT_CLASS_DEVICE && SH_HP6XX default y help If you have a HP Jornada 680, say y to enable the backlight driver. +config BACKLIGHT_PROGEAR + tristate "Frontpath ProGear Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && PCI && X86 + default n + help + If you have a Frontpath ProGear say Y to enable the + backlight driver. diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 65e5553..0c3ce46f 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 9601bfe..c65e81f 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -14,6 +14,9 @@ #include <linux/err.h> #include <linux/fb.h> +#ifdef CONFIG_PMAC_BACKLIGHT +#include <asm/backlight.h> +#endif #if defined(CONFIG_FB) || (defined(CONFIG_FB_MODULE) && \ defined(CONFIG_BACKLIGHT_CLASS_DEVICE_MODULE)) @@ -28,19 +31,18 @@ static int fb_notifier_callback(struct notifier_block *self, struct fb_event *evdata = data; /* If we aren't interested in this event, skip it immediately ... */ - if (event != FB_EVENT_BLANK) + if (event != FB_EVENT_BLANK && event != FB_EVENT_CONBLANK) return 0; bd = container_of(self, struct backlight_device, fb_notif); - down(&bd->sem); - if (bd->props) - if (!bd->props->check_fb || - bd->props->check_fb(evdata->info)) { - bd->props->fb_blank = *(int *)evdata->data; - if (likely(bd->props && bd->props->update_status)) - bd->props->update_status(bd); + mutex_lock(&bd->ops_lock); + if (bd->ops) + if (!bd->ops->check_fb || + bd->ops->check_fb(evdata->info)) { + bd->props.fb_blank = *(int *)evdata->data; + backlight_update_status(bd); } - up(&bd->sem); + mutex_unlock(&bd->ops_lock); return 0; } @@ -69,15 +71,9 @@ static inline void backlight_unregister_fb(struct backlight_device *bd) static ssize_t backlight_show_power(struct class_device *cdev, char *buf) { - int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); - down(&bd->sem); - if (likely(bd->props)) - rc = sprintf(buf, "%d\n", bd->props->power); - up(&bd->sem); - - return rc; + return sprintf(buf, "%d\n", bd->props.power); } static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count) @@ -93,30 +89,23 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, if (size != count) return -EINVAL; - down(&bd->sem); - if (likely(bd->props)) { + mutex_lock(&bd->ops_lock); + if (bd->ops) { pr_debug("backlight: set power to %d\n", power); - bd->props->power = power; - if (likely(bd->props->update_status)) - bd->props->update_status(bd); + bd->props.power = power; + backlight_update_status(bd); rc = count; } - up(&bd->sem); + mutex_unlock(&bd->ops_lock); return rc; } static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf) { - int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); - down(&bd->sem); - if (likely(bd->props)) - rc = sprintf(buf, "%d\n", bd->props->brightness); - up(&bd->sem); - - return rc; + return sprintf(buf, "%d\n", bd->props.brightness); } static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count) @@ -132,35 +121,28 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char if (size != count) return -EINVAL; - down(&bd->sem); - if (likely(bd->props)) { - if (brightness > bd->props->max_brightness) + mutex_lock(&bd->ops_lock); + if (bd->ops) { + if (brightness > bd->props.max_brightness) rc = -EINVAL; else { pr_debug("backlight: set brightness to %d\n", brightness); - bd->props->brightness = brightness; - if (likely(bd->props->update_status)) - bd->props->update_status(bd); + bd->props.brightness = brightness; + backlight_update_status(bd); rc = count; } } - up(&bd->sem); + mutex_unlock(&bd->ops_lock); return rc; } static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf) { - int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); - down(&bd->sem); - if (likely(bd->props)) - rc = sprintf(buf, "%d\n", bd->props->max_brightness); - up(&bd->sem); - - return rc; + return sprintf(buf, "%d\n", bd->props.max_brightness); } static ssize_t backlight_show_actual_brightness(struct class_device *cdev, @@ -169,10 +151,10 @@ static ssize_t backlight_show_actual_brightness(struct class_device *cdev, int rc = -ENXIO; struct backlight_device *bd = to_backlight_device(cdev); - down(&bd->sem); - if (likely(bd->props && bd->props->get_brightness)) - rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd)); - up(&bd->sem); + mutex_lock(&bd->ops_lock); + if (bd->ops && bd->ops->get_brightness) + rc = sprintf(buf, "%d\n", bd->ops->get_brightness(bd)); + mutex_unlock(&bd->ops_lock); return rc; } @@ -211,7 +193,7 @@ static const struct class_device_attribute bl_class_device_attributes[] = { * respective framebuffer device). * @devdata: an optional pointer to be stored in the class_device. The * methods may retrieve it by using class_get_devdata(&bd->class_dev). - * @bp: the backlight properties structure. + * @ops: the backlight operations structure. * * Creates and registers new backlight class_device. Returns either an * ERR_PTR() or a pointer to the newly allocated device. @@ -219,39 +201,42 @@ static const struct class_device_attribute bl_class_device_attributes[] = { struct backlight_device *backlight_device_register(const char *name, struct device *dev, void *devdata, - struct backlight_properties *bp) + struct backlight_ops *ops) { int i, rc; struct backlight_device *new_bd; pr_debug("backlight_device_alloc: name=%s\n", name); - new_bd = kmalloc(sizeof(struct backlight_device), GFP_KERNEL); - if (unlikely(!new_bd)) + new_bd = kzalloc(sizeof(struct backlight_device), GFP_KERNEL); + if (!new_bd) return ERR_PTR(-ENOMEM); - init_MUTEX(&new_bd->sem); - new_bd->props = bp; - memset(&new_bd->class_dev, 0, sizeof(new_bd->class_dev)); + mutex_init(&new_bd->update_lock); + mutex_init(&new_bd->ops_lock); + new_bd->ops = ops; new_bd->class_dev.class = &backlight_class; new_bd->class_dev.dev = dev; strlcpy(new_bd->class_dev.class_id, name, KOBJ_NAME_LEN); class_set_devdata(&new_bd->class_dev, devdata); rc = class_device_register(&new_bd->class_dev); - if (unlikely(rc)) { -error: kfree(new_bd); + if (rc) { + kfree(new_bd); return ERR_PTR(rc); } rc = backlight_register_fb(new_bd); - if (unlikely(rc)) - goto error; + if (rc) { + class_device_unregister(&new_bd->class_dev); + return ERR_PTR(rc); + } + for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) { rc = class_device_create_file(&new_bd->class_dev, &bl_class_device_attributes[i]); - if (unlikely(rc)) { + if (rc) { while (--i >= 0) class_device_remove_file(&new_bd->class_dev, &bl_class_device_attributes[i]); @@ -261,6 +246,13 @@ error: kfree(new_bd); } } +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (!pmac_backlight) + pmac_backlight = new_bd; + mutex_unlock(&pmac_backlight_mutex); +#endif + return new_bd; } EXPORT_SYMBOL(backlight_device_register); @@ -280,13 +272,20 @@ void backlight_device_unregister(struct backlight_device *bd) pr_debug("backlight_device_unregister: name=%s\n", bd->class_dev.class_id); +#ifdef CONFIG_PMAC_BACKLIGHT + mutex_lock(&pmac_backlight_mutex); + if (pmac_backlight == bd) + pmac_backlight = NULL; + mutex_unlock(&pmac_backlight_mutex); +#endif + for (i = 0; i < ARRAY_SIZE(bl_class_device_attributes); i++) class_device_remove_file(&bd->class_dev, &bl_class_device_attributes[i]); - down(&bd->sem); - bd->props = NULL; - up(&bd->sem); + mutex_lock(&bd->ops_lock); + bd->ops = NULL; + mutex_unlock(&bd->ops_lock); backlight_unregister_fb(bd); diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c index fde1d95..ce00e18 100644 --- a/drivers/video/backlight/corgi_bl.c +++ b/drivers/video/backlight/corgi_bl.c @@ -22,7 +22,6 @@ #include <asm/hardware/sharpsl_pm.h> static int corgibl_intensity; -static DEFINE_MUTEX(bl_mutex); static struct backlight_properties corgibl_data; static struct backlight_device *corgi_backlight_device; static struct corgibl_machinfo *bl_machinfo; @@ -34,20 +33,18 @@ static unsigned long corgibl_flags; static int corgibl_send_intensity(struct backlight_device *bd) { void (*corgi_kick_batt)(void); - int intensity = bd->props->brightness; + int intensity = bd->props.brightness; - if (bd->props->power != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK) intensity = 0; - if (bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.fb_blank != FB_BLANK_UNBLANK) intensity = 0; if (corgibl_flags & CORGIBL_SUSPENDED) intensity = 0; if (corgibl_flags & CORGIBL_BATTLOW) intensity &= bl_machinfo->limit_mask; - mutex_lock(&bl_mutex); bl_machinfo->set_bl_intensity(intensity); - mutex_unlock(&bl_mutex); corgibl_intensity = intensity; @@ -61,17 +58,21 @@ static int corgibl_send_intensity(struct backlight_device *bd) } #ifdef CONFIG_PM -static int corgibl_suspend(struct platform_device *dev, pm_message_t state) +static int corgibl_suspend(struct platform_device *pdev, pm_message_t state) { + struct backlight_device *bd = platform_get_drvdata(pdev); + corgibl_flags |= CORGIBL_SUSPENDED; - corgibl_send_intensity(corgi_backlight_device); + backlight_update_status(bd); return 0; } -static int corgibl_resume(struct platform_device *dev) +static int corgibl_resume(struct platform_device *pdev) { + struct backlight_device *bd = platform_get_drvdata(pdev); + corgibl_flags &= ~CORGIBL_SUSPENDED; - corgibl_send_intensity(corgi_backlight_device); + backlight_update_status(bd); return 0; } #else @@ -84,12 +85,6 @@ static int corgibl_get_intensity(struct backlight_device *bd) return corgibl_intensity; } -static int corgibl_set_intensity(struct backlight_device *bd) -{ - corgibl_send_intensity(corgi_backlight_device); - return 0; -} - /* * Called when the battery is low to limit the backlight intensity. * If limit==0 clear any limit, otherwise limit the intensity @@ -100,15 +95,14 @@ void corgibl_limit_intensity(int limit) corgibl_flags |= CORGIBL_BATTLOW; else corgibl_flags &= ~CORGIBL_BATTLOW; - corgibl_send_intensity(corgi_backlight_device); + backlight_update_status(corgi_backlight_device); } EXPORT_SYMBOL(corgibl_limit_intensity); -static struct backlight_properties corgibl_data = { - .owner = THIS_MODULE, +static struct backlight_ops corgibl_ops = { .get_brightness = corgibl_get_intensity, - .update_status = corgibl_set_intensity, + .update_status = corgibl_send_intensity, }; static int corgibl_probe(struct platform_device *pdev) @@ -116,30 +110,34 @@ static int corgibl_probe(struct platform_device *pdev) struct corgibl_machinfo *machinfo = pdev->dev.platform_data; bl_machinfo = machinfo; - corgibl_data.max_brightness = machinfo->max_intensity; if (!machinfo->limit_mask) machinfo->limit_mask = -1; corgi_backlight_device = backlight_device_register ("corgi-bl", - &pdev->dev, NULL, &corgibl_data); + &pdev->dev, NULL, &corgibl_ops); if (IS_ERR (corgi_backlight_device)) return PTR_ERR (corgi_backlight_device); - corgibl_data.power = FB_BLANK_UNBLANK; - corgibl_data.brightness = machinfo->default_intensity; - corgibl_send_intensity(corgi_backlight_device); + platform_set_drvdata(pdev, corgi_backlight_device); + + corgi_backlight_device->props.max_brightness = machinfo->max_intensity; + corgi_backlight_device->props.power = FB_BLANK_UNBLANK; + corgi_backlight_device->props.brightness = machinfo->default_intensity; + backlight_update_status(corgi_backlight_device); printk("Corgi Backlight Driver Initialized.\n"); return 0; } -static int corgibl_remove(struct platform_device *dev) +static int corgibl_remove(struct platform_device *pdev) { + struct backlight_device *bd = platform_get_drvdata(pdev); + corgibl_data.power = 0; corgibl_data.brightness = 0; - corgibl_send_intensity(corgi_backlight_device); + backlight_update_status(bd); - backlight_device_unregister(corgi_backlight_device); + backlight_device_unregister(bd); printk("Corgi Backlight Driver Unloaded\n"); return 0; diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c index c07d820..0899fcc 100644 --- a/drivers/video/backlight/hp680_bl.c +++ b/drivers/video/backlight/hp680_bl.c @@ -28,17 +28,16 @@ static int hp680bl_suspended; static int current_intensity = 0; static DEFINE_SPINLOCK(bl_lock); -static struct backlight_device *hp680_backlight_device; static void hp680bl_send_intensity(struct backlight_device *bd) { unsigned long flags; u16 v; - int intensity = bd->props->brightness; + int intensity = bd->props.brightness; - if (bd->props->power != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK) intensity = 0; - if (bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.fb_blank != FB_BLANK_UNBLANK) intensity = 0; if (hp680bl_suspended) intensity = 0; @@ -66,17 +65,21 @@ static void hp680bl_send_intensity(struct backlight_device *bd) #ifdef CONFIG_PM -static int hp680bl_suspend(struct platform_device *dev, pm_message_t state) +static int hp680bl_suspend(struct platform_device *pdev, pm_message_t state) { + struct backlight_device *bd = platform_get_drvdata(pdev); + hp680bl_suspended = 1; - hp680bl_send_intensity(hp680_backlight_device); + hp680bl_send_intensity(bd); return 0; } -static int hp680bl_resume(struct platform_device *dev) +static int hp680bl_resume(struct platform_device *pdev) { + struct backlight_device *bd = platform_get_drvdata(pdev); + hp680bl_suspended = 0; - hp680bl_send_intensity(hp680_backlight_device); + hp680bl_send_intensity(bd); return 0; } #else @@ -95,33 +98,38 @@ static int hp680bl_get_intensity(struct backlight_device *bd) return current_intensity; } -static struct backlight_properties hp680bl_data = { - .owner = THIS_MODULE, - .max_brightness = HP680_MAX_INTENSITY, +static struct backlight_ops hp680bl_ops = { .get_brightness = hp680bl_get_intensity, .update_status = hp680bl_set_intensity, }; -static int __init hp680bl_probe(struct platform_device *dev) +static int __init hp680bl_probe(struct platform_device *pdev) { - hp680_backlight_device = backlight_device_register ("hp680-bl", - &dev->dev, NULL, &hp680bl_data); - if (IS_ERR (hp680_backlight_device)) - return PTR_ERR (hp680_backlight_device); + struct backlight_device *bd; + + bd = backlight_device_register ("hp680-bl", &pdev->dev, NULL, + &hp680bl_ops); + if (IS_ERR(bd)) + return PTR_ERR(bd); - hp680_backlight_device->props->brightness = HP680_DEFAULT_INTENSITY; - hp680bl_send_intensity(hp680_backlight_device); + platform_set_drvdata(pdev, bd); + + bd->props.max_brightness = HP680_MAX_INTENSITY; + bd->props.brightness = HP680_DEFAULT_INTENSITY; + hp680bl_send_intensity(bd); return 0; } -static int hp680bl_remove(struct platform_device *dev) +static int hp680bl_remove(struct platform_device *pdev) { + struct backlight_device *bd = platform_get_drvdata(pdev); + hp680bl_data.brightness = 0; hp680bl_data.power = 0; - hp680bl_send_intensity(hp680_backlight_device); + hp680bl_send_intensity(bd); - backlight_device_unregister(hp680_backlight_device); + backlight_device_unregister(bd); return 0; } diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index f6e0416..6ef8f0a 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -31,11 +31,11 @@ static int fb_notifier_callback(struct notifier_block *self, return 0; ld = container_of(self, struct lcd_device, fb_notif); - down(&ld->sem); - if (ld->props) - if (!ld->props->check_fb || ld->props->check_fb(evdata->info)) - ld->props->set_power(ld, *(int *)evdata->data); - up(&ld->sem); + mutex_lock(&ld->ops_lock); + if (ld->ops) + if (!ld->ops->check_fb || ld->ops->check_fb(evdata->info)) + ld->ops->set_power(ld, *(int *)evdata->data); + mutex_unlock(&ld->ops_lock); return 0; } @@ -66,12 +66,12 @@ static ssize_t lcd_show_power(struct class_device *cdev, char *buf) int rc; struct lcd_device *ld = to_lcd_device(cdev); - down(&ld->sem); - if (likely(ld->props && ld->props->get_power)) - rc = sprintf(buf, "%d\n", ld->props->get_power(ld)); + mutex_lock(&ld->ops_lock); + if (ld->ops && ld->ops->get_power) + rc = sprintf(buf, "%d\n", ld->ops->get_power(ld)); else rc = -ENXIO; - up(&ld->sem); + mutex_unlock(&ld->ops_lock); return rc; } @@ -89,13 +89,13 @@ static ssize_t lcd_store_power(struct class_device *cdev, const char *buf, size_ if (size != count) return -EINVAL; - down(&ld->sem); - if (likely(ld->props && ld->props->set_power)) { + mutex_lock(&ld->ops_lock); + if (ld->ops && ld->ops->set_power) { pr_debug("lcd: set power to %d\n", power); - ld->props->set_power(ld, power); + ld->ops->set_power(ld, power); rc = count; } - up(&ld->sem); + mutex_unlock(&ld->ops_lock); return rc; } @@ -105,10 +105,10 @@ static ssize_t lcd_show_contrast(struct class_device *cdev, char *buf) int rc = -ENXIO; struct lcd_device *ld = to_lcd_device(cdev); - down(&ld->sem); - if (likely(ld->props && ld->props->get_contrast)) - rc = sprintf(buf, "%d\n", ld->props->get_contrast(ld)); - up(&ld->sem); + mutex_lock(&ld->ops_lock); + if (ld->ops && ld->ops->get_contrast) + rc = sprintf(buf, "%d\n", ld->ops->get_contrast(ld)); + mutex_unlock(&ld->ops_lock); return rc; } @@ -126,28 +126,22 @@ static ssize_t lcd_store_contrast(struct class_device *cdev, const char *buf, si if (size != count) return -EINVAL; - down(&ld->sem); - if (likely(ld->props && ld->props->set_contrast)) { + mutex_lock(&ld->ops_lock); + if (ld->ops && ld->ops->set_contrast) { pr_debug("lcd: set contrast to %d\n", contrast); - ld->props->set_contrast(ld, contrast); + ld->ops->set_contrast(ld, contrast); rc = count; } - up(&ld->sem); + mutex_unlock(&ld->ops_lock); return rc; } static ssize_t lcd_show_max_contrast(struct class_device *cdev, char *buf) { - int rc = -ENXIO; struct lcd_device *ld = to_lcd_device(cdev); - down(&ld->sem); - if (likely(ld->props)) - rc = sprintf(buf, "%d\n", ld->props->max_contrast); - up(&ld->sem); - - return rc; + return sprintf(buf, "%d\n", ld->props.max_contrast); } static void lcd_class_release(struct class_device *dev) @@ -180,45 +174,46 @@ static const struct class_device_attribute lcd_class_device_attributes[] = { * respective framebuffer device). * @devdata: an optional pointer to be stored in the class_device. The * methods may retrieve it by using class_get_devdata(ld->class_dev). - * @lp: the lcd properties structure. + * @ops: the lcd operations structure. * * Creates and registers a new lcd class_device. Returns either an ERR_PTR() * or a pointer to the newly allocated device. */ struct lcd_device *lcd_device_register(const char *name, void *devdata, - struct lcd_properties *lp) + struct lcd_ops *ops) { int i, rc; struct lcd_device *new_ld; pr_debug("lcd_device_register: name=%s\n", name); - new_ld = kmalloc(sizeof(struct lcd_device), GFP_KERNEL); - if (unlikely(!new_ld)) + new_ld = kzalloc(sizeof(struct lcd_device), GFP_KERNEL); + if (!new_ld) return ERR_PTR(-ENOMEM); - init_MUTEX(&new_ld->sem); - new_ld->props = lp; - memset(&new_ld->class_dev, 0, sizeof(new_ld->class_dev)); + mutex_init(&new_ld->ops_lock); + mutex_init(&new_ld->update_lock); + new_ld->ops = ops; new_ld->class_dev.class = &lcd_class; strlcpy(new_ld->class_dev.class_id, name, KOBJ_NAME_LEN); class_set_devdata(&new_ld->class_dev, devdata); rc = class_device_register(&new_ld->class_dev); - if (unlikely(rc)) { -error: kfree(new_ld); + if (rc) { + kfree(new_ld); return ERR_PTR(rc); } rc = lcd_register_fb(new_ld); - - if (unlikely(rc)) - goto error; + if (rc) { + class_device_unregister(&new_ld->class_dev); + return ERR_PTR(rc); + } for (i = 0; i < ARRAY_SIZE(lcd_class_device_attributes); i++) { rc = class_device_create_file(&new_ld->class_dev, &lcd_class_device_attributes[i]); - if (unlikely(rc)) { + if (rc) { while (--i >= 0) class_device_remove_file(&new_ld->class_dev, &lcd_class_device_attributes[i]); @@ -251,9 +246,9 @@ void lcd_device_unregister(struct lcd_device *ld) class_device_remove_file(&ld->class_dev, &lcd_class_device_attributes[i]); - down(&ld->sem); - ld->props = NULL; - up(&ld->sem); + mutex_lock(&ld->ops_lock); + ld->ops = NULL; + mutex_unlock(&ld->ops_lock); lcd_unregister_fb(ld); class_device_unregister(&ld->class_dev); } diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c index fc812d9..6b488b8 100644 --- a/drivers/video/backlight/locomolcd.c +++ b/drivers/video/backlight/locomolcd.c @@ -112,11 +112,11 @@ static int current_intensity; static int locomolcd_set_intensity(struct backlight_device *bd) { - int intensity = bd->props->brightness; + int intensity = bd->props.brightness; - if (bd->props->power != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK) intensity = 0; - if (bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.fb_blank != FB_BLANK_UNBLANK) intensity = 0; if (locomolcd_flags & LOCOMOLCD_SUSPENDED) intensity = 0; @@ -141,11 +141,9 @@ static int locomolcd_get_intensity(struct backlight_device *bd) return current_intensity; } -static struct backlight_properties locomobl_data = { - .owner = THIS_MODULE, +static struct backlight_ops locomobl_data = { .get_brightness = locomolcd_get_intensity, .update_status = locomolcd_set_intensity, - .max_brightness = 4, }; #ifdef CONFIG_PM @@ -190,7 +188,8 @@ static int locomolcd_probe(struct locomo_dev *ldev) return PTR_ERR (locomolcd_bl_device); /* Set up frontlight so that screen is readable */ - locomobl_data.brightness = 2; + locomolcd_bl_device->props.max_brightness = 4, + locomolcd_bl_device->props.brightness = 2; locomolcd_set_intensity(locomolcd_bl_device); return 0; @@ -200,8 +199,8 @@ static int locomolcd_remove(struct locomo_dev *dev) { unsigned long flags; - locomobl_data.brightness = 0; - locomobl_data.power = 0; + locomolcd_bl_device->props.brightness = 0; + locomolcd_bl_device->props.power = 0; locomolcd_set_intensity(locomolcd_bl_device); backlight_device_unregister(locomolcd_bl_device); diff --git a/drivers/video/backlight/progear_bl.c b/drivers/video/backlight/progear_bl.c new file mode 100644 index 0000000..836ab4d --- /dev/null +++ b/drivers/video/backlight/progear_bl.c @@ -0,0 +1,153 @@ +/* + * Backlight Driver for Frontpath ProGear HX1050+ + * + * Copyright (c) 2006 Marcin Juszkiewicz + * + * Based on Progear LCD driver by M Schacht + * <mschacht at alumni dot washington dot edu> + * + * Based on Sharp's Corgi Backlight Driver + * Based on Backlight Driver for HP Jornada 680 + * + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> +#include <linux/fb.h> +#include <linux/backlight.h> +#include <linux/pci.h> +#include <asm/uaccess.h> + +#define PMU_LPCR 0xB0 +#define SB_MPS1 0x61 +#define HW_LEVEL_MAX 0x77 +#define HW_LEVEL_MIN 0x4f + +static struct pci_dev *pmu_dev = NULL; +static struct pci_dev *sb_dev = NULL; + +static int progearbl_set_intensity(struct backlight_device *bd) +{ + int intensity = bd->props.brightness; + + if (bd->props.power != FB_BLANK_UNBLANK) + intensity = 0; + if (bd->props.fb_blank != FB_BLANK_UNBLANK) + intensity = 0; + + pci_write_config_byte(pmu_dev, PMU_LPCR, intensity + HW_LEVEL_MIN); + + return 0; +} + +static int progearbl_get_intensity(struct backlight_device *bd) +{ + u8 intensity; + pci_read_config_byte(pmu_dev, PMU_LPCR, &intensity); + + return intensity - HW_LEVEL_MIN; +} + +static struct backlight_ops progearbl_ops = { + .get_brightness = progearbl_get_intensity, + .update_status = progearbl_set_intensity, +}; + +static int progearbl_probe(struct platform_device *pdev) +{ + u8 temp; + struct backlight_device *progear_backlight_device; + + pmu_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, NULL); + if (!pmu_dev) { + printk("ALI M7101 PMU not found.\n"); + return -ENODEV; + } + + sb_dev = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + if (!sb_dev) { + printk("ALI 1533 SB not found.\n"); + pci_dev_put(pmu_dev); + return -ENODEV; + } + + /* Set SB_MPS1 to enable brightness control. */ + pci_read_config_byte(sb_dev, SB_MPS1, &temp); + pci_write_config_byte(sb_dev, SB_MPS1, temp | 0x20); + + progear_backlight_device = backlight_device_register("progear-bl", + &pdev->dev, NULL, + &progearbl_ops); + if (IS_ERR(progear_backlight_device)) + return PTR_ERR(progear_backlight_device); + + platform_set_drvdata(pdev, progear_backlight_device); + + progear_backlight_device->props.power = FB_BLANK_UNBLANK; + progear_backlight_device->props.brightness = HW_LEVEL_MAX - HW_LEVEL_MIN; + progear_backlight_device->props.max_brightness = HW_LEVEL_MAX - HW_LEVEL_MIN; + progearbl_set_intensity(progear_backlight_device); + + return 0; +} + +static int progearbl_remove(struct platform_device *pdev) +{ + struct backlight_device *bd = platform_get_drvdata(pdev); + backlight_device_unregister(bd); + + return 0; +} + +static struct platform_driver progearbl_driver = { + .probe = progearbl_probe, + .remove = progearbl_remove, + .driver = { + .name = "progear-bl", + }, +}; + +static struct platform_device *progearbl_device; + +static int __init progearbl_init(void) +{ + int ret = platform_driver_register(&progearbl_driver); + + if (!ret) { + progearbl_device = platform_device_alloc("progear-bl", -1); + if (!progearbl_device) + return -ENOMEM; + + ret = platform_device_add(progearbl_device); + + if (ret) { + platform_device_put(progearbl_device); + platform_driver_unregister(&progearbl_driver); + } + } + + return ret; +} + +static void __exit progearbl_exit(void) +{ + pci_dev_put(pmu_dev); + pci_dev_put(sb_dev); + + platform_device_unregister(progearbl_device); + platform_driver_unregister(&progearbl_driver); +} + +module_init(progearbl_init); +module_exit(progearbl_exit); + +MODULE_AUTHOR("Marcin Juszkiewicz <linux@hrw.one.pl>"); +MODULE_DESCRIPTION("ProGear Backlight Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/bw2.c b/drivers/video/bw2.c index 9bb6257..b0b2e40 100644 --- a/drivers/video/bw2.c +++ b/drivers/video/bw2.c @@ -186,8 +186,7 @@ static int bw2_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) * Initialisation */ -static void -bw2_init_fix(struct fb_info *info, int linebytes) +static void __devinit bw2_init_fix(struct fb_info *info, int linebytes) { strlcpy(info->fix.id, "bwtwo", sizeof(info->fix.id)); @@ -199,43 +198,44 @@ bw2_init_fix(struct fb_info *info, int linebytes) info->fix.accel = FB_ACCEL_SUN_BWTWO; } -static u8 bw2regs_1600[] __initdata = { +static u8 bw2regs_1600[] __devinitdata = { 0x14, 0x8b, 0x15, 0x28, 0x16, 0x03, 0x17, 0x13, 0x18, 0x7b, 0x19, 0x05, 0x1a, 0x34, 0x1b, 0x2e, 0x1c, 0x00, 0x1d, 0x0a, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x21, 0 }; -static u8 bw2regs_ecl[] __initdata = { +static u8 bw2regs_ecl[] __devinitdata = { 0x14, 0x65, 0x15, 0x1e, 0x16, 0x04, 0x17, 0x0c, 0x18, 0x5e, 0x19, 0x03, 0x1a, 0xa7, 0x1b, 0x23, 0x1c, 0x00, 0x1d, 0x08, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x20, 0 }; -static u8 bw2regs_analog[] __initdata = { +static u8 bw2regs_analog[] __devinitdata = { 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x03, 0x17, 0x13, 0x18, 0xb0, 0x19, 0x03, 0x1a, 0xa6, 0x1b, 0x22, 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x20, 0 }; -static u8 bw2regs_76hz[] __initdata = { +static u8 bw2regs_76hz[] __devinitdata = { 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f, 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a, 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x24, 0 }; -static u8 bw2regs_66hz[] __initdata = { +static u8 bw2regs_66hz[] __devinitdata = { 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14, 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24, 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x20, 0 }; -static void bw2_do_default_mode(struct bw2_par *par, struct fb_info *info, - int *linebytes) +static void __devinit bw2_do_default_mode(struct bw2_par *par, + struct fb_info *info, + int *linebytes) { u8 status, mon; u8 *p; diff --git a/drivers/video/cg14.c b/drivers/video/cg14.c index ec6a51a..b071bb6 100644 --- a/drivers/video/cg14.c +++ b/drivers/video/cg14.c @@ -354,7 +354,8 @@ static int cg14_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) * Initialisation */ -static void cg14_init_fix(struct fb_info *info, int linebytes, struct device_node *dp) +static void __devinit cg14_init_fix(struct fb_info *info, int linebytes, + struct device_node *dp) { const char *name = dp->name; @@ -368,7 +369,7 @@ static void cg14_init_fix(struct fb_info *info, int linebytes, struct device_nod info->fix.accel = FB_ACCEL_SUN_CG14; } -static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __initdata = { +static struct sbus_mmap_map __cg14_mmap_map[CG14_MMAP_ENTRIES] __devinitdata = { { .voff = CG14_REGS, .poff = 0x80000000, diff --git a/drivers/video/cg3.c b/drivers/video/cg3.c index ada6f7e3..f042428 100644 --- a/drivers/video/cg3.c +++ b/drivers/video/cg3.c @@ -186,8 +186,7 @@ static int cg3_setcolreg(unsigned regno, * @blank_mode: the blank mode we want. * @info: frame buffer structure that represents a single frame buffer */ -static int -cg3_blank(int blank, struct fb_info *info) +static int cg3_blank(int blank, struct fb_info *info) { struct cg3_par *par = (struct cg3_par *) info->par; struct cg3_regs __iomem *regs = par->regs; @@ -251,8 +250,8 @@ static int cg3_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg) * Initialisation */ -static void -cg3_init_fix(struct fb_info *info, int linebytes, struct device_node *dp) +static void __devinit cg3_init_fix(struct fb_info *info, int linebytes, + struct device_node *dp) { strlcpy(info->fix.id, dp->name, sizeof(info->fix.id)); @@ -264,10 +263,10 @@ cg3_init_fix(struct fb_info *info, int linebytes, struct device_node *dp) info->fix.accel = FB_ACCEL_SUN_CGTHREE; } -static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var, - struct device_node *dp) +static void __devinit cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var, + struct device_node *dp) { - char *params; + const char *params; char *p; int ww, hh; @@ -287,36 +286,36 @@ static void cg3_rdi_maybe_fixup_var(struct fb_var_screeninfo *var, } } -static u8 cg3regvals_66hz[] __initdata = { /* 1152 x 900, 66 Hz */ +static u8 cg3regvals_66hz[] __devinitdata = { /* 1152 x 900, 66 Hz */ 0x14, 0xbb, 0x15, 0x2b, 0x16, 0x04, 0x17, 0x14, 0x18, 0xae, 0x19, 0x03, 0x1a, 0xa8, 0x1b, 0x24, 0x1c, 0x01, 0x1d, 0x05, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x20, 0 }; -static u8 cg3regvals_76hz[] __initdata = { /* 1152 x 900, 76 Hz */ +static u8 cg3regvals_76hz[] __devinitdata = { /* 1152 x 900, 76 Hz */ 0x14, 0xb7, 0x15, 0x27, 0x16, 0x03, 0x17, 0x0f, 0x18, 0xae, 0x19, 0x03, 0x1a, 0xae, 0x1b, 0x2a, 0x1c, 0x01, 0x1d, 0x09, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x24, 0 }; -static u8 cg3regvals_rdi[] __initdata = { /* 640 x 480, cgRDI */ +static u8 cg3regvals_rdi[] __devinitdata = { /* 640 x 480, cgRDI */ 0x14, 0x70, 0x15, 0x20, 0x16, 0x08, 0x17, 0x10, 0x18, 0x06, 0x19, 0x02, 0x1a, 0x31, 0x1b, 0x51, 0x1c, 0x06, 0x1d, 0x0c, 0x1e, 0xff, 0x1f, 0x01, 0x10, 0x22, 0 }; -static u8 *cg3_regvals[] __initdata = { +static u8 *cg3_regvals[] __devinitdata = { cg3regvals_66hz, cg3regvals_76hz, cg3regvals_rdi }; -static u_char cg3_dacvals[] __initdata = { +static u_char cg3_dacvals[] __devinitdata = { 4, 0xff, 5, 0x00, 6, 0x70, 7, 0x00, 0 }; -static void cg3_do_default_mode(struct cg3_par *par) +static void __devinit cg3_do_default_mode(struct cg3_par *par) { enum cg3_type type; u8 *p; @@ -433,7 +432,8 @@ static int __devinit cg3_init_one(struct of_device *op) return 0; } -static int __devinit cg3_probe(struct of_device *dev, const struct of_device_id *match) +static int __devinit cg3_probe(struct of_device *dev, + const struct of_device_id *match) { struct of_device *op = to_of_device(&dev->dev); diff --git a/drivers/video/chipsfb.c b/drivers/video/chipsfb.c index 73cb426..af313bf1 100644 --- a/drivers/video/chipsfb.c +++ b/drivers/video/chipsfb.c @@ -145,26 +145,6 @@ static int chipsfb_set_par(struct fb_info *info) static int chipsfb_blank(int blank, struct fb_info *info) { -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - - if (pmac_backlight) { - /* used to disable backlight only for blank > 1, but it seems - * useful at blank = 1 too (saves battery, extends backlight - * life) - */ - down(&pmac_backlight->sem); - if (blank) - pmac_backlight->props->power = FB_BLANK_POWERDOWN; - else - pmac_backlight->props->power = FB_BLANK_UNBLANK; - pmac_backlight->props->update_status(pmac_backlight); - up(&pmac_backlight->sem); - } - - mutex_unlock(&pmac_backlight_mutex); -#endif /* CONFIG_PMAC_BACKLIGHT */ - return 1; /* get fb_blank to set the colormap to all black */ } @@ -415,10 +395,8 @@ chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) /* turn on the backlight */ mutex_lock(&pmac_backlight_mutex); if (pmac_backlight) { - down(&pmac_backlight->sem); - pmac_backlight->props->power = FB_BLANK_UNBLANK; - pmac_backlight->props->update_status(pmac_backlight); - up(&pmac_backlight->sem); + pmac_backlight->props.power = FB_BLANK_UNBLANK; + backlight_update_status(pmac_backlight); } mutex_unlock(&pmac_backlight_mutex); #endif /* CONFIG_PMAC_BACKLIGHT */ diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index be3f2c3..0429fd2 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -2233,6 +2233,8 @@ static int fbcon_switch(struct vc_data *vc) static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, int blank) { + struct fb_event event; + if (blank) { unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; @@ -2243,6 +2245,11 @@ static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); vc->vc_video_erase_char = oldc; } + + + event.info = info; + event.data = ␣ + fb_notifier_call_chain(FB_EVENT_CONBLANK, &event); } static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) diff --git a/drivers/video/controlfb.c b/drivers/video/controlfb.c index fd60dba..8b76273 100644 --- a/drivers/video/controlfb.c +++ b/drivers/video/controlfb.c @@ -179,12 +179,14 @@ MODULE_LICENSE("GPL"); int init_module(void) { struct device_node *dp; + int ret = -ENXIO; - dp = find_devices("control"); + dp = of_find_node_by_name(NULL, "control"); if (dp != 0 && !control_of_init(dp)) - return 0; + ret = 0; + of_node_put(dp); - return -ENXIO; + return ret; } void cleanup_module(void) @@ -589,16 +591,18 @@ static int __init control_init(void) { struct device_node *dp; char *option = NULL; + int ret = -ENXIO; if (fb_get_options("controlfb", &option)) return -ENODEV; control_setup(option); - dp = find_devices("control"); + dp = of_find_node_by_name(NULL, "control"); if (dp != 0 && !control_of_init(dp)) - return 0; + ret = 0; + of_node_put(dp); - return -ENXIO; + return ret; } module_init(control_init); diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c index 818fb09..40c80c8 100644 --- a/drivers/video/fbsysfs.c +++ b/drivers/video/fbsysfs.c @@ -59,7 +59,7 @@ struct fb_info *framebuffer_alloc(size_t size, struct device *dev) info->device = dev; #ifdef CONFIG_FB_BACKLIGHT - mutex_init(&info->bl_mutex); + mutex_init(&info->bl_curve_mutex); #endif return info; @@ -445,10 +445,10 @@ static ssize_t store_bl_curve(struct device *device, /* If there has been an error in the input data, we won't * reach this loop. */ - mutex_lock(&fb_info->bl_mutex); + mutex_lock(&fb_info->bl_curve_mutex); for (i = 0; i < FB_BACKLIGHT_LEVELS; ++i) fb_info->bl_curve[i] = tmp_curve[i]; - mutex_unlock(&fb_info->bl_mutex); + mutex_unlock(&fb_info->bl_curve_mutex); return count; } @@ -466,7 +466,7 @@ static ssize_t show_bl_curve(struct device *device, if (!fb_info || !fb_info->bl_dev) return -ENODEV; - mutex_lock(&fb_info->bl_mutex); + mutex_lock(&fb_info->bl_curve_mutex); for (i = 0; i < FB_BACKLIGHT_LEVELS; i += 8) len += snprintf(&buf[len], PAGE_SIZE, "%02x %02x %02x %02x %02x %02x %02x %02x\n", @@ -478,7 +478,7 @@ static ssize_t show_bl_curve(struct device *device, fb_info->bl_curve[i + 5], fb_info->bl_curve[i + 6], fb_info->bl_curve[i + 7]); - mutex_unlock(&fb_info->bl_mutex); + mutex_unlock(&fb_info->bl_curve_mutex); return len; } @@ -552,6 +552,8 @@ void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) { unsigned int i, flat, count, range = (max - min); + mutex_lock(&fb_info->bl_curve_mutex); + fb_info->bl_curve[0] = off; for (flat = 1; flat < (FB_BACKLIGHT_LEVELS / 16); ++flat) @@ -560,6 +562,8 @@ void fb_bl_default_curve(struct fb_info *fb_info, u8 off, u8 min, u8 max) count = FB_BACKLIGHT_LEVELS * 15 / 16; for (i = 0; i < count; ++i) fb_info->bl_curve[flat + i] = min + (range * (i + 1) / count); + + mutex_unlock(&fb_info->bl_curve_mutex); } EXPORT_SYMBOL_GPL(fb_bl_default_curve); #endif diff --git a/drivers/video/ffb.c b/drivers/video/ffb.c index 15854ae..1d4e835 100644 --- a/drivers/video/ffb.c +++ b/drivers/video/ffb.c @@ -336,14 +336,30 @@ struct ffb_dac { u32 value2; }; +#define FFB_DAC_UCTRL 0x1001 /* User Control */ +#define FFB_DAC_UCTRL_MANREV 0x00000f00 /* 4-bit Manufacturing Revision */ +#define FFB_DAC_UCTRL_MANREV_SHIFT 8 +#define FFB_DAC_TGEN 0x6000 /* Timing Generator */ +#define FFB_DAC_TGEN_VIDE 0x00000001 /* Video Enable */ +#define FFB_DAC_DID 0x8000 /* Device Identification */ +#define FFB_DAC_DID_PNUM 0x0ffff000 /* Device Part Number */ +#define FFB_DAC_DID_PNUM_SHIFT 12 +#define FFB_DAC_DID_REV 0xf0000000 /* Device Revision */ +#define FFB_DAC_DID_REV_SHIFT 28 + +#define FFB_DAC_CUR_CTRL 0x100 +#define FFB_DAC_CUR_CTRL_P0 0x00000001 +#define FFB_DAC_CUR_CTRL_P1 0x00000002 + struct ffb_par { spinlock_t lock; struct ffb_fbc __iomem *fbc; struct ffb_dac __iomem *dac; u32 flags; -#define FFB_FLAG_AFB 0x00000001 -#define FFB_FLAG_BLANKED 0x00000002 +#define FFB_FLAG_AFB 0x00000001 /* AFB m3 or m6 */ +#define FFB_FLAG_BLANKED 0x00000002 /* screen is blanked */ +#define FFB_FLAG_INVCURSOR 0x00000004 /* DAC has inverted cursor logic */ u32 fg_cache __attribute__((aligned (8))); u32 bg_cache; @@ -354,7 +370,6 @@ struct ffb_par { unsigned long physbase; unsigned long fbsize; - int dac_rev; int board_type; }; @@ -426,11 +441,12 @@ static void ffb_switch_from_graph(struct ffb_par *par) FFBWait(par); /* Disable cursor. */ - upa_writel(0x100, &dac->type2); - if (par->dac_rev <= 2) + upa_writel(FFB_DAC_CUR_CTRL, &dac->type2); + if (par->flags & FFB_FLAG_INVCURSOR) upa_writel(0, &dac->value2); else - upa_writel(3, &dac->value2); + upa_writel((FFB_DAC_CUR_CTRL_P0 | + FFB_DAC_CUR_CTRL_P1), &dac->value2); spin_unlock_irqrestore(&par->lock, flags); } @@ -664,18 +680,18 @@ ffb_blank(int blank, struct fb_info *info) struct ffb_par *par = (struct ffb_par *) info->par; struct ffb_dac __iomem *dac = par->dac; unsigned long flags; - u32 tmp; + u32 val; + int i; spin_lock_irqsave(&par->lock, flags); FFBWait(par); + upa_writel(FFB_DAC_TGEN, &dac->type); + val = upa_readl(&dac->value); switch (blank) { case FB_BLANK_UNBLANK: /* Unblanking */ - upa_writel(0x6000, &dac->type); - tmp = (upa_readl(&dac->value) | 0x1); - upa_writel(0x6000, &dac->type); - upa_writel(tmp, &dac->value); + val |= FFB_DAC_TGEN_VIDE; par->flags &= ~FFB_FLAG_BLANKED; break; @@ -683,13 +699,16 @@ ffb_blank(int blank, struct fb_info *info) case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */ case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */ case FB_BLANK_POWERDOWN: /* Poweroff */ - upa_writel(0x6000, &dac->type); - tmp = (upa_readl(&dac->value) & ~0x1); - upa_writel(0x6000, &dac->type); - upa_writel(tmp, &dac->value); + val &= ~FFB_DAC_TGEN_VIDE; par->flags |= FFB_FLAG_BLANKED; break; } + upa_writel(FFB_DAC_TGEN, &dac->type); + upa_writel(val, &dac->value); + for (i = 0; i < 10; i++) { + upa_writel(FFB_DAC_TGEN, &dac->type); + upa_readl(&dac->value); + } spin_unlock_irqrestore(&par->lock, flags); @@ -894,6 +913,7 @@ static int ffb_init_one(struct of_device *op) struct ffb_dac __iomem *dac; struct all_info *all; int err; + u32 dac_pnum, dac_rev, dac_mrev; all = kzalloc(sizeof(*all), GFP_KERNEL); if (!all) @@ -948,17 +968,31 @@ static int ffb_init_one(struct of_device *op) if ((upa_readl(&fbc->ucsr) & FFB_UCSR_ALL_ERRORS) != 0) upa_writel(FFB_UCSR_ALL_ERRORS, &fbc->ucsr); - ffb_switch_from_graph(&all->par); - dac = all->par.dac; - upa_writel(0x8000, &dac->type); - all->par.dac_rev = upa_readl(&dac->value) >> 0x1c; + upa_writel(FFB_DAC_DID, &dac->type); + dac_pnum = upa_readl(&dac->value); + dac_rev = (dac_pnum & FFB_DAC_DID_REV) >> FFB_DAC_DID_REV_SHIFT; + dac_pnum = (dac_pnum & FFB_DAC_DID_PNUM) >> FFB_DAC_DID_PNUM_SHIFT; + + upa_writel(FFB_DAC_UCTRL, &dac->type); + dac_mrev = upa_readl(&dac->value); + dac_mrev = (dac_mrev & FFB_DAC_UCTRL_MANREV) >> + FFB_DAC_UCTRL_MANREV_SHIFT; /* Elite3D has different DAC revision numbering, and no DAC revisions - * have the reversed meaning of cursor enable. + * have the reversed meaning of cursor enable. Otherwise, Pacifica 1 + * ramdacs with manufacturing revision less than 3 have inverted + * cursor logic. We identify Pacifica 1 as not Pacifica 2, the + * latter having a part number value of 0x236e. */ - if (all->par.flags & FFB_FLAG_AFB) - all->par.dac_rev = 10; + if ((all->par.flags & FFB_FLAG_AFB) || dac_pnum == 0x236e) { + all->par.flags &= ~FFB_FLAG_INVCURSOR; + } else { + if (dac_mrev < 3) + all->par.flags |= FFB_FLAG_INVCURSOR; + } + + ffb_switch_from_graph(&all->par); /* Unblank it just to be sure. When there are multiple * FFB/AFB cards in the system, or it is not the OBP @@ -993,10 +1027,12 @@ static int ffb_init_one(struct of_device *op) dev_set_drvdata(&op->dev, all); - printk("%s: %s at %016lx, type %d, DAC revision %d\n", + printk("%s: %s at %016lx, type %d, " + "DAC pnum[%x] rev[%d] manuf_rev[%d]\n", dp->full_name, ((all->par.flags & FFB_FLAG_AFB) ? "AFB" : "FFB"), - all->par.physbase, all->par.board_type, all->par.dac_rev); + all->par.physbase, all->par.board_type, + dac_pnum, dac_rev, dac_mrev); return 0; } diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c index ab1b8fe..7e76019 100644 --- a/drivers/video/i810/i810_main.c +++ b/drivers/video/i810/i810_main.c @@ -142,7 +142,7 @@ static int hsync2 __devinitdata; static int vsync1 __devinitdata; static int vsync2 __devinitdata; static int xres __devinitdata; -static int yres __devinitdata; +static int yres; static int vyres __devinitdata; static int sync __devinitdata; static int extvga __devinitdata; diff --git a/drivers/video/igafb.c b/drivers/video/igafb.c index 90592fb..eb1a481 100644 --- a/drivers/video/igafb.c +++ b/drivers/video/igafb.c @@ -44,8 +44,8 @@ #include <asm/io.h> -#ifdef __sparc__ -#include <asm/pbm.h> +#ifdef CONFIG_SPARC +#include <asm/prom.h> #include <asm/pcic.h> #endif @@ -96,7 +96,7 @@ struct fb_var_screeninfo default_var = { .vmode = FB_VMODE_NONINTERLACED }; -#ifdef __sparc__ +#ifdef CONFIG_SPARC struct fb_var_screeninfo default_var_1024x768 __initdata = { /* 1024x768, 75 Hz, Non-Interlaced (78.75 MHz dotclock) */ .xres = 1024, @@ -188,7 +188,7 @@ static inline void iga_outb(struct iga_par *par, unsigned char val, pci_outb(par, val, reg+1); } -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ /* * Very important functionality for the JavaEngine1 computer: @@ -217,7 +217,7 @@ static void iga_blank_border(struct iga_par *par) iga_outb(par, 0, IGA_EXT_CNTRL, IGA_IDX_OVERSCAN_COLOR + i); } -#ifdef __sparc__ +#ifdef CONFIG_SPARC static int igafb_mmap(struct fb_info *info, struct vm_area_struct *vma) { @@ -271,7 +271,7 @@ static int igafb_mmap(struct fb_info *info, vma->vm_flags |= VM_IO; return 0; } -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ static int igafb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, @@ -323,7 +323,7 @@ static struct fb_ops igafb_ops = { .fb_fillrect = cfb_fillrect, .fb_copyarea = cfb_copyarea, .fb_imageblit = cfb_imageblit, -#ifdef __sparc__ +#ifdef CONFIG_SPARC .fb_mmap = igafb_mmap, #endif }; @@ -424,7 +424,7 @@ int __init igafb_init(void) par->frame_buffer_phys = addr & PCI_BASE_ADDRESS_MEM_MASK; -#ifdef __sparc__ +#ifdef CONFIG_SPARC /* * The following is sparc specific and this is why: * @@ -477,8 +477,8 @@ int __init igafb_init(void) * Set default vmode and cmode from PROM properties. */ { - struct pcidev_cookie *cookie = pdev->sysdata; - int node = cookie->prom_node; + struct device_node *dp = pci_device_to_OF_node(pdev); + int node = dp->node; int width = prom_getintdefault(node, "width", 1024); int height = prom_getintdefault(node, "height", 768); int depth = prom_getintdefault(node, "depth", 8); @@ -534,7 +534,7 @@ int __init igafb_init(void) kfree(info); } -#ifdef __sparc__ +#ifdef CONFIG_SPARC /* * Add /dev/fb mmap values. */ @@ -552,7 +552,7 @@ int __init igafb_init(void) par->mmap_map[1].size = PAGE_SIZE * 2; /* X wants 2 pages */ par->mmap_map[1].prot_mask = SRMMU_CACHE; par->mmap_map[1].prot_flag = SRMMU_WRITE; -#endif /* __sparc__ */ +#endif /* CONFIG_SPARC */ return 0; } diff --git a/drivers/video/nvidia/nv_backlight.c b/drivers/video/nvidia/nv_backlight.c index df934bd..43f62d8 100644 --- a/drivers/video/nvidia/nv_backlight.c +++ b/drivers/video/nvidia/nv_backlight.c @@ -12,15 +12,15 @@ #include <linux/backlight.h> #include <linux/fb.h> #include <linux/pci.h> -#include "nv_local.h" -#include "nv_type.h" -#include "nv_proto.h" #ifdef CONFIG_PMAC_BACKLIGHT #include <asm/backlight.h> -#include <asm/machdep.h> #endif +#include "nv_local.h" +#include "nv_type.h" +#include "nv_proto.h" + /* We do not have any information about which values are allowed, thus * we used safe values. */ @@ -28,9 +28,6 @@ #define MAX_LEVEL 0x534 #define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX) -static struct backlight_properties nvidia_bl_data; - -/* Call with fb_info->bl_mutex held */ static int nvidia_bl_get_level_brightness(struct nvidia_par *par, int level) { @@ -38,6 +35,7 @@ static int nvidia_bl_get_level_brightness(struct nvidia_par *par, int nlevel; /* Get and convert the value */ + /* No locking of bl_curve since we read a single value */ nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP; if (nlevel < 0) @@ -50,8 +48,7 @@ static int nvidia_bl_get_level_brightness(struct nvidia_par *par, return nlevel; } -/* Call with fb_info->bl_mutex held */ -static int __nvidia_bl_update_status(struct backlight_device *bd) +static int nvidia_bl_update_status(struct backlight_device *bd) { struct nvidia_par *par = class_get_devdata(&bd->class_dev); u32 tmp_pcrt, tmp_pmc, fpcontrol; @@ -60,11 +57,11 @@ static int __nvidia_bl_update_status(struct backlight_device *bd) if (!par->FlatPanel) return 0; - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; else - level = bd->props->brightness; + level = bd->props.brightness; tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF; tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC; @@ -85,45 +82,16 @@ static int __nvidia_bl_update_status(struct backlight_device *bd) return 0; } -static int nvidia_bl_update_status(struct backlight_device *bd) -{ - struct nvidia_par *par = class_get_devdata(&bd->class_dev); - struct fb_info *info = pci_get_drvdata(par->pci_dev); - int ret; - - mutex_lock(&info->bl_mutex); - ret = __nvidia_bl_update_status(bd); - mutex_unlock(&info->bl_mutex); - - return ret; -} - static int nvidia_bl_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties nvidia_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops nvidia_bl_ops = { .get_brightness = nvidia_bl_get_brightness, .update_status = nvidia_bl_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; -void nvidia_bl_set_power(struct fb_info *info, int power) -{ - mutex_lock(&info->bl_mutex); - - if (info->bl_dev) { - down(&info->bl_dev->sem); - info->bl_dev->props->power = power; - __nvidia_bl_update_status(info->bl_dev); - up(&info->bl_dev->sem); - } - - mutex_unlock(&info->bl_mutex); -} - void nvidia_bl_init(struct nvidia_par *par) { struct fb_info *info = pci_get_drvdata(par->pci_dev); @@ -141,32 +109,22 @@ void nvidia_bl_init(struct nvidia_par *par) snprintf(name, sizeof(name), "nvidiabl%d", info->node); - bd = backlight_device_register(name, info->dev, par, &nvidia_bl_data); + bd = backlight_device_register(name, info->dev, par, &nvidia_bl_ops); if (IS_ERR(bd)) { info->bl_dev = NULL; printk(KERN_WARNING "nvidia: Backlight registration failed\n"); goto error; } - mutex_lock(&info->bl_mutex); info->bl_dev = bd; fb_bl_default_curve(info, 0, 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL, 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL); - mutex_unlock(&info->bl_mutex); - down(&bd->sem); - bd->props->brightness = nvidia_bl_data.max_brightness; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); -#endif + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + bd->props.brightness = bd->props.max_brightness; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("nvidia: Backlight initialized (%s)\n", name); @@ -179,25 +137,8 @@ error: void nvidia_bl_exit(struct nvidia_par *par) { struct fb_info *info = pci_get_drvdata(par->pci_dev); + struct backlight_device *bd = info->bl_dev; -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); -#endif - - mutex_lock(&info->bl_mutex); - if (info->bl_dev) { -#ifdef CONFIG_PMAC_BACKLIGHT - if (pmac_backlight == info->bl_dev) - pmac_backlight = NULL; -#endif - - backlight_device_unregister(info->bl_dev); - - printk("nvidia: Backlight unloaded\n"); - } - mutex_unlock(&info->bl_mutex); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_unlock(&pmac_backlight_mutex); -#endif + backlight_device_unregister(bd); + printk("nvidia: Backlight unloaded\n"); } diff --git a/drivers/video/nvidia/nv_proto.h b/drivers/video/nvidia/nv_proto.h index 43058d0..ff5c410 100644 --- a/drivers/video/nvidia/nv_proto.h +++ b/drivers/video/nvidia/nv_proto.h @@ -67,11 +67,9 @@ extern int nvidiafb_sync(struct fb_info *info); #ifdef CONFIG_FB_NVIDIA_BACKLIGHT extern void nvidia_bl_init(struct nvidia_par *par); extern void nvidia_bl_exit(struct nvidia_par *par); -extern void nvidia_bl_set_power(struct fb_info *info, int power); #else static inline void nvidia_bl_init(struct nvidia_par *par) {} static inline void nvidia_bl_exit(struct nvidia_par *par) {} -static inline void nvidia_bl_set_power(struct fb_info *info, int power) {} #endif #endif /* __NV_PROTO_H__ */ diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c index 8e5b484..b97ec69 100644 --- a/drivers/video/nvidia/nvidia.c +++ b/drivers/video/nvidia/nvidia.c @@ -83,6 +83,11 @@ static int bpp __devinitdata = 8; #ifdef CONFIG_MTRR static int nomtrr __devinitdata = 0; #endif +#ifdef CONFIG_PMAC_BACKLIGHT +static int backlight __devinitdata = 1; +#else +static int backlight __devinitdata = 0; +#endif static char *mode_option __devinitdata = NULL; @@ -938,8 +943,6 @@ static int nvidiafb_blank(int blank, struct fb_info *info) NVWriteSeq(par, 0x01, tmp); NVWriteCrtc(par, 0x1a, vesa); - nvidia_bl_set_power(info, blank); - NVTRACE_LEAVE(); return 0; @@ -1313,7 +1316,10 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd, nvidia_save_vga(par, &par->SavedReg); pci_set_drvdata(pd, info); - nvidia_bl_init(par); + + if (backlight) + nvidia_bl_init(par); + if (register_framebuffer(info) < 0) { printk(KERN_ERR PFX "error registering nVidia framebuffer\n"); goto err_out_iounmap_fb; @@ -1352,9 +1358,10 @@ static void __devexit nvidiafb_remove(struct pci_dev *pd) NVTRACE_ENTER(); + unregister_framebuffer(info); + nvidia_bl_exit(par); - unregister_framebuffer(info); #ifdef CONFIG_MTRR if (par->mtrr.vram_valid) mtrr_del(par->mtrr.vram, info->fix.smem_start, @@ -1409,6 +1416,8 @@ static int __devinit nvidiafb_setup(char *options) paneltweak = simple_strtoul(this_opt+11, NULL, 0); } else if (!strncmp(this_opt, "vram:", 5)) { vram = simple_strtoul(this_opt+5, NULL, 0); + } else if (!strncmp(this_opt, "backlight:", 10)) { + backlight = simple_strtoul(this_opt+10, NULL, 0); #ifdef CONFIG_MTRR } else if (!strncmp(this_opt, "nomtrr", 6)) { nomtrr = 1; diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c index f2e9b74..d7ece8d 100644 --- a/drivers/video/riva/fbdev.c +++ b/drivers/video/riva/fbdev.c @@ -215,6 +215,11 @@ static int noaccel __devinitdata = 0; #ifdef CONFIG_MTRR static int nomtrr __devinitdata = 0; #endif +#ifdef CONFIG_PMAC_BACKLIGHT +static int backlight __devinitdata = 1; +#else +static int backlight __devinitdata = 0; +#endif static char *mode_option __devinitdata = NULL; static int strictmode = 0; @@ -280,9 +285,6 @@ static const struct riva_regs reg_template = { #define MAX_LEVEL 0x534 #define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX) -static struct backlight_properties riva_bl_data; - -/* Call with fb_info->bl_mutex held */ static int riva_bl_get_level_brightness(struct riva_par *par, int level) { @@ -290,6 +292,7 @@ static int riva_bl_get_level_brightness(struct riva_par *par, int nlevel; /* Get and convert the value */ + /* No locking on bl_curve since accessing a single value */ nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP; if (nlevel < 0) @@ -302,18 +305,17 @@ static int riva_bl_get_level_brightness(struct riva_par *par, return nlevel; } -/* Call with fb_info->bl_mutex held */ -static int __riva_bl_update_status(struct backlight_device *bd) +static int riva_bl_update_status(struct backlight_device *bd) { struct riva_par *par = class_get_devdata(&bd->class_dev); U032 tmp_pcrt, tmp_pmc; int level; - if (bd->props->power != FB_BLANK_UNBLANK || - bd->props->fb_blank != FB_BLANK_UNBLANK) + if (bd->props.power != FB_BLANK_UNBLANK || + bd->props.fb_blank != FB_BLANK_UNBLANK) level = 0; else - level = bd->props->brightness; + level = bd->props.brightness; tmp_pmc = par->riva.PMC[0x10F0/4] & 0x0000FFFF; tmp_pcrt = par->riva.PCRTC0[0x081C/4] & 0xFFFFFFFC; @@ -328,45 +330,16 @@ static int __riva_bl_update_status(struct backlight_device *bd) return 0; } -static int riva_bl_update_status(struct backlight_device *bd) -{ - struct riva_par *par = class_get_devdata(&bd->class_dev); - struct fb_info *info = pci_get_drvdata(par->pdev); - int ret; - - mutex_lock(&info->bl_mutex); - ret = __riva_bl_update_status(bd); - mutex_unlock(&info->bl_mutex); - - return ret; -} - static int riva_bl_get_brightness(struct backlight_device *bd) { - return bd->props->brightness; + return bd->props.brightness; } -static struct backlight_properties riva_bl_data = { - .owner = THIS_MODULE, +static struct backlight_ops riva_bl_ops = { .get_brightness = riva_bl_get_brightness, .update_status = riva_bl_update_status, - .max_brightness = (FB_BACKLIGHT_LEVELS - 1), }; -static void riva_bl_set_power(struct fb_info *info, int power) -{ - mutex_lock(&info->bl_mutex); - - if (info->bl_dev) { - down(&info->bl_dev->sem); - info->bl_dev->props->power = power; - __riva_bl_update_status(info->bl_dev); - up(&info->bl_dev->sem); - } - - mutex_unlock(&info->bl_mutex); -} - static void riva_bl_init(struct riva_par *par) { struct fb_info *info = pci_get_drvdata(par->pdev); @@ -384,32 +357,22 @@ static void riva_bl_init(struct riva_par *par) snprintf(name, sizeof(name), "rivabl%d", info->node); - bd = backlight_device_register(name, info->dev, par, &riva_bl_data); + bd = backlight_device_register(name, info->dev, par, &riva_bl_ops); if (IS_ERR(bd)) { info->bl_dev = NULL; printk(KERN_WARNING "riva: Backlight registration failed\n"); goto error; } - mutex_lock(&info->bl_mutex); info->bl_dev = bd; fb_bl_default_curve(info, 0, MIN_LEVEL * FB_BACKLIGHT_MAX / MAX_LEVEL, FB_BACKLIGHT_MAX); - mutex_unlock(&info->bl_mutex); - down(&bd->sem); - bd->props->brightness = riva_bl_data.max_brightness; - bd->props->power = FB_BLANK_UNBLANK; - bd->props->update_status(bd); - up(&bd->sem); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); - if (!pmac_backlight) - pmac_backlight = bd; - mutex_unlock(&pmac_backlight_mutex); -#endif + bd->props.max_brightness = FB_BACKLIGHT_LEVELS - 1; + bd->props.brightness = bd->props.max_brightness; + bd->props.power = FB_BLANK_UNBLANK; + backlight_update_status(bd); printk("riva: Backlight initialized (%s)\n", name); @@ -419,35 +382,16 @@ error: return; } -static void riva_bl_exit(struct riva_par *par) +static void riva_bl_exit(struct fb_info *info) { - struct fb_info *info = pci_get_drvdata(par->pdev); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_lock(&pmac_backlight_mutex); -#endif - - mutex_lock(&info->bl_mutex); - if (info->bl_dev) { -#ifdef CONFIG_PMAC_BACKLIGHT - if (pmac_backlight == info->bl_dev) - pmac_backlight = NULL; -#endif - - backlight_device_unregister(info->bl_dev); + struct backlight_device *bd = info->bl_dev; - printk("riva: Backlight unloaded\n"); - } - mutex_unlock(&info->bl_mutex); - -#ifdef CONFIG_PMAC_BACKLIGHT - mutex_unlock(&pmac_backlight_mutex); -#endif + backlight_device_unregister(bd); + printk("riva: Backlight unloaded\n"); } #else static inline void riva_bl_init(struct riva_par *par) {} -static inline void riva_bl_exit(struct riva_par *par) {} -static inline void riva_bl_set_power(struct fb_info *info, int power) {} +static inline void riva_bl_exit(struct fb_info *info) {} #endif /* CONFIG_FB_RIVA_BACKLIGHT */ /* ------------------------------------------------------------------------- * @@ -1348,8 +1292,6 @@ static int rivafb_blank(int blank, struct fb_info *info) SEQout(par, 0x01, tmp); CRTCout(par, 0x1a, vesa); - riva_bl_set_power(info, blank); - NVTRACE_LEAVE(); return 0; @@ -2120,7 +2062,10 @@ static int __devinit rivafb_probe(struct pci_dev *pd, info->monspecs.modedb = NULL; pci_set_drvdata(pd, info); - riva_bl_init(info->par); + + if (backlight) + riva_bl_init(info->par); + ret = register_framebuffer(info); if (ret < 0) { printk(KERN_ERR PFX @@ -2166,14 +2111,15 @@ static void __exit rivafb_remove(struct pci_dev *pd) NVTRACE_ENTER(); - riva_bl_exit(par); - #ifdef CONFIG_FB_RIVA_I2C riva_delete_i2c_busses(par); kfree(par->EDID); #endif unregister_framebuffer(info); + + riva_bl_exit(info); + #ifdef CONFIG_MTRR if (par->mtrr.vram_valid) mtrr_del(par->mtrr.vram, info->fix.smem_start, @@ -2217,6 +2163,8 @@ static int __init rivafb_setup(char *options) forceCRTC = -1; } else if (!strncmp(this_opt, "flatpanel", 9)) { flatpanel = 1; + } else if (!strncmp(this_opt, "backlight:", 10)) { + backlight = simple_strtoul(this_opt+10, NULL, 0); #ifdef CONFIG_MTRR } else if (!strncmp(this_opt, "nomtrr", 6)) { nomtrr = 1; diff --git a/drivers/video/s3fb.c b/drivers/video/s3fb.c index 3162c37..3091b20 100644 --- a/drivers/video/s3fb.c +++ b/drivers/video/s3fb.c @@ -1000,11 +1000,12 @@ err_enable_device: static void __devexit s3_pci_remove(struct pci_dev *dev) { struct fb_info *info = pci_get_drvdata(dev); - struct s3fb_info *par = info->par; if (info) { #ifdef CONFIG_MTRR + struct s3fb_info *par = info->par; + if (par->mtrr_reg >= 0) { mtrr_del(par->mtrr_reg, 0, 0); par->mtrr_reg = -1; @@ -1134,11 +1135,11 @@ static int __init s3fb_setup(char *options) if (!*opt) continue; #ifdef CONFIG_MTRR - else if (!strcmp(opt, "mtrr:")) + else if (!strncmp(opt, "mtrr:", 5)) mtrr = simple_strtoul(opt + 5, NULL, 0); #endif - else if (!strcmp(opt, "fasttext:")) - mtrr = simple_strtoul(opt + 9, NULL, 0); + else if (!strncmp(opt, "fasttext:", 9)) + fasttext = simple_strtoul(opt + 9, NULL, 0); else mode = opt; } diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c index 4afa305..0166ec2 100644 --- a/drivers/video/savage/savagefb_driver.c +++ b/drivers/video/savage/savagefb_driver.c @@ -384,6 +384,19 @@ SavageSetup2DEngine(struct savagefb_par *par) BCI_SEND(0); BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); BCI_SEND(GlobalBitmapDescriptor); + + /* + * I don't know why, sending this twice fixes the intial black screen, + * prevents X from crashing at least in Toshiba laptops with SavageIX. + * --Tony + */ + par->bci_ptr = 0; + par->SavageWaitFifo(par, 4); + + BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1); + BCI_SEND(0); + BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2); + BCI_SEND(GlobalBitmapDescriptor); } static void savagefb_set_clip(struct fb_info *info) @@ -496,7 +509,7 @@ static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1, #ifdef SAVAGEFB_DEBUG /* This function is used to debug, it prints out the contents of s3 regs */ -static void SavagePrintRegs(void) +static void SavagePrintRegs(struct savagefb_par *par) { unsigned char i; int vgaCRIndex = 0x3d4; @@ -1525,7 +1538,7 @@ static int savagefb_set_par(struct fb_info *info) savagefb_set_fix(info); savagefb_set_clip(info); - SavagePrintRegs(); + SavagePrintRegs(par); return 0; } @@ -2155,7 +2168,6 @@ static int __devinit savagefb_probe(struct pci_dev* dev, int video_len; DBG("savagefb_probe"); - SavagePrintRegs(); info = framebuffer_alloc(sizeof(struct savagefb_par), &dev->dev); if (!info) diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c new file mode 100644 index 0000000..0a44c44 --- /dev/null +++ b/drivers/video/sm501fb.c @@ -0,0 +1,1786 @@ +/* linux/drivers/video/sm501fb.c + * + * Copyright (c) 2006 Simtec Electronics + * Vincent Sanders <vince@simtec.co.uk> + * Ben Dooks <ben@simtec.co.uk> + * + * 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. + * + * Framebuffer driver for the Silicon Motion SM501 + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/mm.h> +#include <linux/tty.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/wait.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/div64.h> + +#ifdef CONFIG_PM +#include <linux/pm.h> +#endif + +#include <linux/sm501.h> +#include <linux/sm501-regs.h> + +#define NR_PALETTE 256 + +enum sm501_controller { + HEAD_CRT = 0, + HEAD_PANEL = 1, +}; + +/* SM501 memory adress */ +struct sm501_mem { + unsigned long size; + unsigned long sm_addr; + void __iomem *k_addr; +}; + +/* private data that is shared between all frambuffers* */ +struct sm501fb_info { + struct device *dev; + struct fb_info *fb[2]; /* fb info for both heads */ + struct resource *fbmem_res; /* framebuffer resource */ + struct resource *regs_res; /* registers resource */ + struct sm501_platdata_fb *pdata; /* our platform data */ + + int irq; + int swap_endian; /* set to swap rgb=>bgr */ + void __iomem *regs; /* remapped registers */ + void __iomem *fbmem; /* remapped framebuffer */ + size_t fbmem_len; /* length of remapped region */ +}; + +/* per-framebuffer private data */ +struct sm501fb_par { + u32 pseudo_palette[16]; + + enum sm501_controller head; + struct sm501_mem cursor; + struct sm501_mem screen; + struct fb_ops ops; + + void *store_fb; + void *store_cursor; + void __iomem *cursor_regs; + struct sm501fb_info *info; +}; + +/* Helper functions */ + +static inline int h_total(struct fb_var_screeninfo *var) +{ + return var->xres + var->left_margin + + var->right_margin + var->hsync_len; +} + +static inline int v_total(struct fb_var_screeninfo *var) +{ + return var->yres + var->upper_margin + + var->lower_margin + var->vsync_len; +} + +/* sm501fb_sync_regs() + * + * This call is mainly for PCI bus systems where we need to + * ensure that any writes to the bus are completed before the + * next phase, or after completing a function. +*/ + +static inline void sm501fb_sync_regs(struct sm501fb_info *info) +{ + readl(info->regs); +} + +/* sm501_alloc_mem + * + * This is an attempt to lay out memory for the two framebuffers and + * everything else + * + * |fbmem_res->start fbmem_res->end| + * | | + * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K | + * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-| + * + * The "spare" space is for the 2d engine data + * the fixed is space for the cursors (2x1Kbyte) + * + * we need to allocate memory for the 2D acceleration engine + * command list and the data for the engine to deal with. + * + * - all allocations must be 128bit aligned + * - cursors are 64x64x2 bits (1Kbyte) + * + */ + +#define SM501_MEMF_CURSOR (1) +#define SM501_MEMF_PANEL (2) +#define SM501_MEMF_CRT (4) +#define SM501_MEMF_ACCEL (8) + +static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, + unsigned int why, size_t size) +{ + unsigned int ptr = 0; + + switch (why) { + case SM501_MEMF_CURSOR: + ptr = inf->fbmem_len - size; + inf->fbmem_len = ptr; + break; + + case SM501_MEMF_PANEL: + ptr = inf->fbmem_len - size; + if (ptr < inf->fb[0]->fix.smem_len) + return -ENOMEM; + + break; + + case SM501_MEMF_CRT: + ptr = 0; + break; + + case SM501_MEMF_ACCEL: + ptr = inf->fb[0]->fix.smem_len; + + if ((ptr + size) > + (inf->fb[1]->fix.smem_start - inf->fbmem_res->start)) + return -ENOMEM; + break; + + default: + return -EINVAL; + } + + mem->size = size; + mem->sm_addr = ptr; + mem->k_addr = inf->fbmem + ptr; + + dev_dbg(inf->dev, "%s: result %08lx, %p - %u, %zd\n", + __func__, mem->sm_addr, mem->k_addr, why, size); + + return 0; +} + +/* sm501fb_ps_to_hz + * + * Converts a period in picoseconds to Hz. + * + * Note, we try to keep this in Hz to minimise rounding with + * the limited PLL settings on the SM501. +*/ + +static unsigned long sm501fb_ps_to_hz(unsigned long psvalue) +{ + unsigned long long numerator=1000000000000ULL; + + /* 10^12 / picosecond period gives frequency in Hz */ + do_div(numerator, psvalue); + return (unsigned long)numerator; +} + +/* sm501fb_hz_to_ps is identical to the oposite transform */ + +#define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x) + +/* sm501fb_setup_gamma + * + * Programs a linear 1.0 gamma ramp in case the gamma + * correction is enabled without programming anything else. +*/ + +static void sm501fb_setup_gamma(struct sm501fb_info *fbi, + unsigned long palette) +{ + unsigned long value = 0; + int offset; + + /* set gamma values */ + for (offset = 0; offset < 256 * 4; offset += 4) { + writel(value, fbi->regs + palette + offset); + value += 0x010101; /* Advance RGB by 1,1,1.*/ + } +} + +/* sm501fb_check_var + * + * check common variables for both panel and crt +*/ + +static int sm501fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *sm = par->info; + unsigned long tmp; + + /* check we can fit these values into the registers */ + + if (var->hsync_len > 255 || var->vsync_len > 255) + return -EINVAL; + + if ((var->xres + var->right_margin) >= 4096) + return -EINVAL; + + if ((var->yres + var->lower_margin) > 2048) + return -EINVAL; + + /* hard limits of device */ + + if (h_total(var) > 4096 || v_total(var) > 2048) + return -EINVAL; + + /* check our line length is going to be 128 bit aligned */ + + tmp = (var->xres * var->bits_per_pixel) / 8; + if ((tmp & 15) != 0) + return -EINVAL; + + /* check the virtual size */ + + if (var->xres_virtual > 4096 || var->yres_virtual > 2048) + return -EINVAL; + + /* can cope with 8,16 or 32bpp */ + + if (var->bits_per_pixel <= 8) + var->bits_per_pixel = 8; + else if (var->bits_per_pixel <= 16) + var->bits_per_pixel = 16; + else if (var->bits_per_pixel == 24) + var->bits_per_pixel = 32; + + /* set r/g/b positions and validate bpp */ + switch(var->bits_per_pixel) { + case 8: + var->red.length = var->bits_per_pixel; + var->red.offset = 0; + var->green.length = var->bits_per_pixel; + var->green.offset = 0; + var->blue.length = var->bits_per_pixel; + var->blue.offset = 0; + var->transp.length = 0; + + break; + + case 16: + if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; + } else { + var->blue.offset = 11; + var->green.offset = 5; + var->red.offset = 0; + } + + var->red.length = 5; + var->green.length = 6; + var->blue.length = 5; + var->transp.length = 0; + break; + + case 32: + if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { + var->transp.offset = 0; + var->red.offset = 8; + var->green.offset = 16; + var->blue.offset = 24; + } else { + var->transp.offset = 24; + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + } + + var->red.length = 8; + var->green.length = 8; + var->blue.length = 8; + var->transp.length = 0; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * sm501fb_check_var_crt(): + * + * check the parameters for the CRT head, and either bring them + * back into range, or return -EINVAL. +*/ + +static int sm501fb_check_var_crt(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return sm501fb_check_var(var, info); +} + +/* sm501fb_check_var_pnl(): + * + * check the parameters for the CRT head, and either bring them + * back into range, or return -EINVAL. +*/ + +static int sm501fb_check_var_pnl(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return sm501fb_check_var(var, info); +} + +/* sm501fb_set_par_common + * + * set common registers for framebuffers +*/ + +static int sm501fb_set_par_common(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long pixclock; /* pixelclock in Hz */ + unsigned long sm501pixclock; /* pixelclock the 501 can achive in Hz */ + unsigned int mem_type; + unsigned int clock_type; + unsigned int head_addr; + + dev_dbg(fbi->dev, "%s: %dx%d, bpp = %d, virtual %dx%d\n", + __func__, var->xres, var->yres, var->bits_per_pixel, + var->xres_virtual, var->yres_virtual); + + switch (par->head) { + case HEAD_CRT: + mem_type = SM501_MEMF_CRT; + clock_type = SM501_CLOCK_V2XCLK; + head_addr = SM501_DC_CRT_FB_ADDR; + break; + + case HEAD_PANEL: + mem_type = SM501_MEMF_PANEL; + clock_type = SM501_CLOCK_P2XCLK; + head_addr = SM501_DC_PANEL_FB_ADDR; + break; + + default: + mem_type = 0; /* stop compiler warnings */ + head_addr = 0; + clock_type = 0; + } + + switch (var->bits_per_pixel) { + case 8: + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + + case 16: + info->fix.visual = FB_VISUAL_DIRECTCOLOR; + break; + + case 32: + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + } + + /* allocate fb memory within 501 */ + info->fix.line_length = (var->xres_virtual * var->bits_per_pixel)/8; + info->fix.smem_len = info->fix.line_length * var->yres_virtual; + + dev_dbg(fbi->dev, "%s: line length = %u\n", __func__, + info->fix.line_length); + + if (sm501_alloc_mem(fbi, &par->screen, mem_type, + info->fix.smem_len)) { + dev_err(fbi->dev, "no memory available\n"); + return -ENOMEM; + } + + info->fix.smem_start = fbi->fbmem_res->start + par->screen.sm_addr; + + info->screen_base = fbi->fbmem + par->screen.sm_addr; + info->screen_size = info->fix.smem_len; + + /* set start of framebuffer to the screen */ + + writel(par->screen.sm_addr | SM501_ADDR_FLIP, fbi->regs + head_addr); + + /* program CRT clock */ + + pixclock = sm501fb_ps_to_hz(var->pixclock); + + sm501pixclock = sm501_set_clock(fbi->dev->parent, clock_type, + pixclock); + + /* update fb layer with actual clock used */ + var->pixclock = sm501fb_hz_to_ps(sm501pixclock); + + dev_dbg(fbi->dev, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, " + "sm501pixclock = %lu, error = %ld%%\n", + __func__, var->pixclock, pixclock, sm501pixclock, + ((pixclock - sm501pixclock)*100)/pixclock); + + return 0; +} + +/* sm501fb_set_par_geometry + * + * set the geometry registers for specified framebuffer. +*/ + +static void sm501fb_set_par_geometry(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned long reg; + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_H_TOT; + else + base += SM501_DC_PANEL_H_TOT; + + /* set framebuffer width and display width */ + + reg = info->fix.line_length; + reg |= ((var->xres * var->bits_per_pixel)/8) << 16; + + writel(reg, fbi->regs + (par->head == HEAD_CRT ? + SM501_DC_CRT_FB_OFFSET : SM501_DC_PANEL_FB_OFFSET)); + + /* program horizontal total */ + + reg = (h_total(var) - 1) << 16; + reg |= (var->xres - 1); + + writel(reg, base + SM501_OFF_DC_H_TOT); + + /* program horizontal sync */ + + reg = var->hsync_len << 16; + reg |= var->xres + var->right_margin - 1; + + writel(reg, base + SM501_OFF_DC_H_SYNC); + + /* program vertical total */ + + reg = (v_total(var) - 1) << 16; + reg |= (var->yres - 1); + + writel(reg, base + SM501_OFF_DC_V_TOT); + + /* program vertical sync */ + reg = var->vsync_len << 16; + reg |= var->yres + var->lower_margin - 1; + + writel(reg, base + SM501_OFF_DC_V_SYNC); +} + +/* sm501fb_pan_crt + * + * pan the CRT display output within an virtual framebuffer +*/ + +static int sm501fb_pan_crt(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned int bytes_pixel = var->bits_per_pixel / 8; + unsigned long reg; + unsigned long xoffs; + + xoffs = var->xoffset * bytes_pixel; + + reg = readl(fbi->regs + SM501_DC_CRT_CONTROL); + + reg &= ~SM501_DC_CRT_CONTROL_PIXEL_MASK; + reg |= ((xoffs & 15) / bytes_pixel) << 4; + writel(reg, fbi->regs + SM501_DC_CRT_CONTROL); + + reg = (par->screen.sm_addr + xoffs + + var->yoffset * info->fix.line_length); + writel(reg | SM501_ADDR_FLIP, fbi->regs + SM501_DC_CRT_FB_ADDR); + + sm501fb_sync_regs(fbi); + return 0; +} + +/* sm501fb_pan_pnl + * + * pan the panel display output within an virtual framebuffer +*/ + +static int sm501fb_pan_pnl(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long reg; + + reg = var->xoffset | (var->xres_virtual << 16); + writel(reg, fbi->regs + SM501_DC_PANEL_FB_WIDTH); + + reg = var->yoffset | (var->yres_virtual << 16); + writel(reg, fbi->regs + SM501_DC_PANEL_FB_HEIGHT); + + sm501fb_sync_regs(fbi); + return 0; +} + +/* sm501fb_set_par_crt + * + * Set the CRT video mode from the fb_info structure +*/ + +static int sm501fb_set_par_crt(struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + struct fb_var_screeninfo *var = &info->var; + unsigned long control; /* control register */ + int ret; + + /* activate new configuration */ + + dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); + + /* enable CRT DAC - note 0 is on!*/ + sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); + + control = readl(fbi->regs + SM501_DC_CRT_CONTROL); + + control &= (SM501_DC_CRT_CONTROL_PIXEL_MASK | + SM501_DC_CRT_CONTROL_GAMMA | + SM501_DC_CRT_CONTROL_BLANK | + SM501_DC_CRT_CONTROL_SEL | + SM501_DC_CRT_CONTROL_CP | + SM501_DC_CRT_CONTROL_TVP); + + /* set the sync polarities before we check data source */ + + if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) + control |= SM501_DC_CRT_CONTROL_HSP; + + if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) + control |= SM501_DC_CRT_CONTROL_VSP; + + if ((control & SM501_DC_CRT_CONTROL_SEL) == 0) { + /* the head is displaying panel data... */ + + sm501_alloc_mem(fbi, &par->screen, SM501_MEMF_CRT, 0); + goto out_update; + } + + ret = sm501fb_set_par_common(info, var); + if (ret) { + dev_err(fbi->dev, "failed to set common parameters\n"); + return ret; + } + + sm501fb_pan_crt(var, info); + sm501fb_set_par_geometry(info, var); + + control |= SM501_FIFO_3; /* fill if >3 free slots */ + + switch(var->bits_per_pixel) { + case 8: + control |= SM501_DC_CRT_CONTROL_8BPP; + break; + + case 16: + control |= SM501_DC_CRT_CONTROL_16BPP; + break; + + case 32: + control |= SM501_DC_CRT_CONTROL_32BPP; + sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); + break; + + default: + BUG(); + } + + control |= SM501_DC_CRT_CONTROL_SEL; /* CRT displays CRT data */ + control |= SM501_DC_CRT_CONTROL_TE; /* enable CRT timing */ + control |= SM501_DC_CRT_CONTROL_ENABLE; /* enable CRT plane */ + + out_update: + dev_dbg(fbi->dev, "new control is %08lx\n", control); + + writel(control, fbi->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(fbi); + + return 0; +} + +static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) +{ + unsigned long control; + void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; + + control = readl(ctrl_reg); + + if (to && (control & SM501_DC_PANEL_CONTROL_VDD) == 0) { + /* enable panel power */ + + control |= SM501_DC_PANEL_CONTROL_VDD; /* FPVDDEN */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control |= SM501_DC_PANEL_CONTROL_DATA; /* DATA */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control |= SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); + + } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { + /* disable panel power */ + + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_DATA; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + + control &= ~SM501_DC_PANEL_CONTROL_VDD; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } + + sm501fb_sync_regs(fbi); +} + +/* sm501fb_set_par_pnl + * + * Set the panel video mode from the fb_info structure +*/ + +static int sm501fb_set_par_pnl(struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + struct fb_var_screeninfo *var = &info->var; + unsigned long control; + unsigned long reg; + int ret; + + dev_dbg(fbi->dev, "%s(%p)\n", __func__, info); + + /* activate this new configuration */ + + ret = sm501fb_set_par_common(info, var); + if (ret) + return ret; + + sm501fb_pan_pnl(var, info); + sm501fb_set_par_geometry(info, var); + + /* update control register */ + + control = readl(fbi->regs + SM501_DC_PANEL_CONTROL); + control &= (SM501_DC_PANEL_CONTROL_GAMMA | + SM501_DC_PANEL_CONTROL_VDD | + SM501_DC_PANEL_CONTROL_DATA | + SM501_DC_PANEL_CONTROL_BIAS | + SM501_DC_PANEL_CONTROL_FPEN | + SM501_DC_PANEL_CONTROL_CP | + SM501_DC_PANEL_CONTROL_CK | + SM501_DC_PANEL_CONTROL_HP | + SM501_DC_PANEL_CONTROL_VP | + SM501_DC_PANEL_CONTROL_HPD | + SM501_DC_PANEL_CONTROL_VPD); + + control |= SM501_FIFO_3; /* fill if >3 free slots */ + + switch(var->bits_per_pixel) { + case 8: + control |= SM501_DC_PANEL_CONTROL_8BPP; + break; + + case 16: + control |= SM501_DC_PANEL_CONTROL_16BPP; + break; + + case 32: + control |= SM501_DC_PANEL_CONTROL_32BPP; + sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); + break; + + default: + BUG(); + } + + writel(0x0, fbi->regs + SM501_DC_PANEL_PANNING_CONTROL); + + /* panel plane top left and bottom right location */ + + writel(0x00, fbi->regs + SM501_DC_PANEL_TL_LOC); + + reg = var->xres - 1; + reg |= (var->yres - 1) << 16; + + writel(reg, fbi->regs + SM501_DC_PANEL_BR_LOC); + + /* program panel control register */ + + control |= SM501_DC_PANEL_CONTROL_TE; /* enable PANEL timing */ + control |= SM501_DC_PANEL_CONTROL_EN; /* enable PANEL gfx plane */ + + if ((var->sync & FB_SYNC_HOR_HIGH_ACT) == 0) + control |= SM501_DC_PANEL_CONTROL_HSP; + + if ((var->sync & FB_SYNC_VERT_HIGH_ACT) == 0) + control |= SM501_DC_PANEL_CONTROL_VSP; + + writel(control, fbi->regs + SM501_DC_PANEL_CONTROL); + sm501fb_sync_regs(fbi); + + /* power the panel up */ + sm501fb_panel_power(fbi, 1); + return 0; +} + + +/* chan_to_field + * + * convert a colour value into a field position + * + * from pxafb.c +*/ + +static inline unsigned int chan_to_field(unsigned int chan, + struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/* sm501fb_setcolreg + * + * set the colour mapping for modes that support palettised data +*/ + +static int sm501fb_setcolreg(unsigned regno, + unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned int val; + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_PALETTE; + else + base += SM501_DC_PANEL_PALETTE; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + /* true-colour, use pseuo-palette */ + + if (regno < 16) { + u32 *pal = par->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = (red >> 8) << 16; + val |= (green >> 8) << 8; + val |= blue >> 8; + + writel(val, base + (regno * 4)); + } + + break; + + default: + return 1; /* unknown type */ + } + + return 0; +} + +/* sm501fb_blank_pnl + * + * Blank or un-blank the panel interface +*/ + +static int sm501fb_blank_pnl(int blank_mode, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + + dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + sm501fb_panel_power(fbi, 0); + break; + + case FB_BLANK_UNBLANK: + sm501fb_panel_power(fbi, 1); + break; + + case FB_BLANK_NORMAL: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + } + + return 0; +} + +/* sm501fb_blank_crt + * + * Blank or un-blank the crt interface +*/ + +static int sm501fb_blank_crt(int blank_mode, struct fb_info *info) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + unsigned long ctrl; + + dev_dbg(fbi->dev, "%s(mode=%d, %p)\n", __func__, blank_mode, info); + + ctrl = readl(fbi->regs + SM501_DC_CRT_CONTROL); + + switch (blank_mode) { + case FB_BLANK_POWERDOWN: + ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; + sm501_misc_control(fbi->dev->parent, SM501_MISC_DAC_POWER, 0); + + case FB_BLANK_NORMAL: + ctrl |= SM501_DC_CRT_CONTROL_BLANK; + break; + + case FB_BLANK_UNBLANK: + ctrl &= ~SM501_DC_CRT_CONTROL_BLANK; + ctrl |= SM501_DC_CRT_CONTROL_ENABLE; + sm501_misc_control(fbi->dev->parent, 0, SM501_MISC_DAC_POWER); + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + default: + return 1; + + } + + writel(ctrl, fbi->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(fbi); + + return 0; +} + +/* sm501fb_cursor + * + * set or change the hardware cursor parameters +*/ + +static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct sm501fb_par *par = info->par; + struct sm501fb_info *fbi = par->info; + void __iomem *base = fbi->regs; + unsigned long hwc_addr; + unsigned long fg, bg; + + dev_dbg(fbi->dev, "%s(%p,%p)\n", __func__, info, cursor); + + if (par->head == HEAD_CRT) + base += SM501_DC_CRT_HWC_BASE; + else + base += SM501_DC_PANEL_HWC_BASE; + + /* check not being asked to exceed capabilities */ + + if (cursor->image.width > 64) + return -EINVAL; + + if (cursor->image.height > 64) + return -EINVAL; + + if (cursor->image.depth > 1) + return -EINVAL; + + hwc_addr = readl(base + SM501_OFF_HWC_ADDR); + + if (cursor->enable) + writel(hwc_addr | SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); + else + writel(hwc_addr & ~SM501_HWC_EN, base + SM501_OFF_HWC_ADDR); + + /* set data */ + if (cursor->set & FB_CUR_SETPOS) { + unsigned int x = cursor->image.dx; + unsigned int y = cursor->image.dy; + + if (x >= 2048 || y >= 2048 ) + return -EINVAL; + + dev_dbg(fbi->dev, "set position %d,%d\n", x, y); + + //y += cursor->image.height; + + writel(x | (y << 16), base + SM501_OFF_HWC_LOC); + } + + if (cursor->set & FB_CUR_SETCMAP) { + unsigned int bg_col = cursor->image.bg_color; + unsigned int fg_col = cursor->image.fg_color; + + dev_dbg(fbi->dev, "%s: update cmap (%08x,%08x)\n", + __func__, bg_col, fg_col); + + bg = ((info->cmap.red[bg_col] & 0xF8) << 8) | + ((info->cmap.green[bg_col] & 0xFC) << 3) | + ((info->cmap.blue[bg_col] & 0xF8) >> 3); + + fg = ((info->cmap.red[fg_col] & 0xF8) << 8) | + ((info->cmap.green[fg_col] & 0xFC) << 3) | + ((info->cmap.blue[fg_col] & 0xF8) >> 3); + + dev_dbg(fbi->dev, "fgcol %08x, bgcol %08x\n", fg, bg); + + writel(bg, base + SM501_OFF_HWC_COLOR_1_2); + writel(fg, base + SM501_OFF_HWC_COLOR_3); + } + + if (cursor->set & FB_CUR_SETSIZE || + cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { + /* SM501 cursor is a two bpp 64x64 bitmap this routine + * clears it to transparent then combines the cursor + * shape plane with the colour plane to set the + * cursor */ + int x, y; + const unsigned char *pcol = cursor->image.data; + const unsigned char *pmsk = cursor->mask; + void __iomem *dst = par->cursor.k_addr; + unsigned char dcol = 0; + unsigned char dmsk = 0; + unsigned int op; + + dev_dbg(fbi->dev, "%s: setting shape (%d,%d)\n", + __func__, cursor->image.width, cursor->image.height); + + for (op = 0; op < (64*64*2)/8; op+=4) + writel(0x0, dst + op); + + for (y = 0; y < cursor->image.height; y++) { + for (x = 0; x < cursor->image.width; x++) { + if ((x % 8) == 0) { + dcol = *pcol++; + dmsk = *pmsk++; + } else { + dcol >>= 1; + dmsk >>= 1; + } + + if (dmsk & 1) { + op = (dcol & 1) ? 1 : 3; + op <<= ((x % 4) * 2); + + op |= readb(dst + (x / 4)); + writeb(op, dst + (x / 4)); + } + } + dst += (64*2)/8; + } + } + + sm501fb_sync_regs(fbi); /* ensure cursor data flushed */ + return 0; +} + +/* sm501fb_crtsrc_show + * + * device attribute code to show where the crt output is sourced from +*/ + +static ssize_t sm501fb_crtsrc_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + unsigned long ctrl; + + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + ctrl &= SM501_DC_CRT_CONTROL_SEL; + + return snprintf(buf, PAGE_SIZE, "%s\n", ctrl ? "crt" : "panel"); +} + +/* sm501fb_crtsrc_show + * + * device attribute code to set where the crt output is sourced from +*/ + +static ssize_t sm501fb_crtsrc_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + enum sm501_controller head; + unsigned long ctrl; + + if (len < 1) + return -EINVAL; + + if (strnicmp(buf, "crt", 3) == 0) + head = HEAD_CRT; + else if (strnicmp(buf, "panel", 5) == 0) + head = HEAD_PANEL; + else + return -EINVAL; + + dev_info(dev, "setting crt source to head %d\n", head); + + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + + if (head == HEAD_CRT) { + ctrl |= SM501_DC_CRT_CONTROL_SEL; + ctrl |= SM501_DC_CRT_CONTROL_ENABLE; + ctrl |= SM501_DC_CRT_CONTROL_TE; + } else { + ctrl &= ~SM501_DC_CRT_CONTROL_SEL; + ctrl &= ~SM501_DC_CRT_CONTROL_ENABLE; + ctrl &= ~SM501_DC_CRT_CONTROL_TE; + } + + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + sm501fb_sync_regs(info); + + return len; +} + +/* Prepare the device_attr for registration with sysfs later */ +static DEVICE_ATTR(crt_src, 0666, sm501fb_crtsrc_show, sm501fb_crtsrc_store); + +/* sm501fb_show_regs + * + * show the primary sm501 registers +*/ +static int sm501fb_show_regs(struct sm501fb_info *info, char *ptr, + unsigned int start, unsigned int len) +{ + void __iomem *mem = info->regs; + char *buf = ptr; + unsigned int reg; + + for (reg = start; reg < (len + start); reg += 4) + ptr += sprintf(ptr, "%08x = %08x\n", reg, readl(mem + reg)); + + return ptr - buf; +} + +/* sm501fb_debug_show_crt + * + * show the crt control and cursor registers +*/ + +static ssize_t sm501fb_debug_show_crt(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + char *ptr = buf; + + ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_CONTROL, 0x40); + ptr += sm501fb_show_regs(info, ptr, SM501_DC_CRT_HWC_BASE, 0x10); + + return ptr - buf; +} + +static DEVICE_ATTR(fbregs_crt, 0444, sm501fb_debug_show_crt, NULL); + +/* sm501fb_debug_show_pnl + * + * show the panel control and cursor registers +*/ + +static ssize_t sm501fb_debug_show_pnl(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sm501fb_info *info = dev_get_drvdata(dev); + char *ptr = buf; + + ptr += sm501fb_show_regs(info, ptr, 0x0, 0x40); + ptr += sm501fb_show_regs(info, ptr, SM501_DC_PANEL_HWC_BASE, 0x10); + + return ptr - buf; +} + +static DEVICE_ATTR(fbregs_pnl, 0444, sm501fb_debug_show_pnl, NULL); + +/* framebuffer ops */ + +static struct fb_ops sm501fb_ops_crt = { + .owner = THIS_MODULE, + .fb_check_var = sm501fb_check_var_crt, + .fb_set_par = sm501fb_set_par_crt, + .fb_blank = sm501fb_blank_crt, + .fb_setcolreg = sm501fb_setcolreg, + .fb_pan_display = sm501fb_pan_crt, + .fb_cursor = sm501fb_cursor, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static struct fb_ops sm501fb_ops_pnl = { + .owner = THIS_MODULE, + .fb_check_var = sm501fb_check_var_pnl, + .fb_set_par = sm501fb_set_par_pnl, + .fb_pan_display = sm501fb_pan_pnl, + .fb_blank = sm501fb_blank_pnl, + .fb_setcolreg = sm501fb_setcolreg, + .fb_cursor = sm501fb_cursor, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +/* sm501fb_info_alloc + * + * creates and initialises an sm501fb_info structure +*/ + +static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt, + struct fb_info *fbinfo_pnl) +{ + struct sm501fb_info *info; + struct sm501fb_par *par; + + info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); + if (info) { + /* set the references back */ + + par = fbinfo_crt->par; + par->info = info; + par->head = HEAD_CRT; + fbinfo_crt->pseudo_palette = &par->pseudo_palette; + + par = fbinfo_pnl->par; + par->info = info; + par->head = HEAD_PANEL; + fbinfo_pnl->pseudo_palette = &par->pseudo_palette; + + /* store the two fbs into our info */ + info->fb[HEAD_CRT] = fbinfo_crt; + info->fb[HEAD_PANEL] = fbinfo_pnl; + } + + return info; +} + +/* sm501_init_cursor + * + * initialise hw cursor parameters +*/ + +static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) +{ + struct sm501fb_par *par = fbi->par; + struct sm501fb_info *info = par->info; + int ret; + + par->cursor_regs = info->regs + reg_base; + + ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024); + if (ret < 0) + return ret; + + /* initialise the colour registers */ + + writel(par->cursor.sm_addr, par->cursor_regs + SM501_OFF_HWC_ADDR); + + writel(0x00, par->cursor_regs + SM501_OFF_HWC_LOC); + writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_1_2); + writel(0x00, par->cursor_regs + SM501_OFF_HWC_COLOR_3); + sm501fb_sync_regs(info); + + return 0; +} + +/* sm501fb_info_start + * + * fills the par structure claiming resources and remapping etc. +*/ + +static int sm501fb_start(struct sm501fb_info *info, + struct platform_device *pdev) +{ + struct resource *res; + struct device *dev; + int ret; + + info->dev = dev = &pdev->dev; + platform_set_drvdata(pdev, info); + + info->irq = ret = platform_get_irq(pdev, 0); + if (ret < 0) { + /* we currently do not use the IRQ */ + dev_warn(dev, "no irq for device\n"); + } + + /* allocate, reserve and remap resources for registers */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res == NULL) { + dev_err(dev, "no resource definition for registers\n"); + ret = -ENOENT; + goto err_release; + } + + info->regs_res = request_mem_region(res->start, + res->end - res->start, + pdev->name); + + if (info->regs_res == NULL) { + dev_err(dev, "cannot claim registers\n"); + ret = -ENXIO; + goto err_release; + } + + info->regs = ioremap(res->start, (res->end - res->start)+1); + if (info->regs == NULL) { + dev_err(dev, "cannot remap registers\n"); + ret = -ENXIO; + goto err_regs_res; + } + + /* allocate, reserve resources for framebuffer */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 2); + if (res == NULL) { + dev_err(dev, "no memory resource defined\n"); + ret = -ENXIO; + goto err_regs_map; + } + + info->fbmem_res = request_mem_region(res->start, + (res->end - res->start)+1, + pdev->name); + if (info->fbmem_res == NULL) { + dev_err(dev, "cannot claim framebuffer\n"); + ret = -ENXIO; + goto err_regs_map; + } + + info->fbmem = ioremap(res->start, (res->end - res->start)+1); + if (info->fbmem == NULL) { + dev_err(dev, "cannot remap framebuffer\n"); + goto err_mem_res; + } + + info->fbmem_len = (res->end - res->start)+1; + + /* enable display controller */ + sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); + + /* setup cursors */ + + sm501_init_cursor(info->fb[HEAD_CRT], SM501_DC_CRT_HWC_ADDR); + sm501_init_cursor(info->fb[HEAD_PANEL], SM501_DC_PANEL_HWC_ADDR); + + return 0; /* everything is setup */ + + err_mem_res: + release_resource(info->fbmem_res); + kfree(info->fbmem_res); + + err_regs_map: + iounmap(info->regs); + + err_regs_res: + release_resource(info->regs_res); + kfree(info->regs_res); + + err_release: + return ret; +} + +static void sm501fb_stop(struct sm501fb_info *info) +{ + /* disable display controller */ + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); + + iounmap(info->fbmem); + release_resource(info->fbmem_res); + kfree(info->fbmem_res); + + iounmap(info->regs); + release_resource(info->regs_res); + kfree(info->regs_res); +} + +static void sm501fb_info_release(struct sm501fb_info *info) +{ + kfree(info); +} + +static int sm501fb_init_fb(struct fb_info *fb, + enum sm501_controller head, + const char *fbname) +{ + struct sm501_platdata_fbsub *pd; + struct sm501fb_par *par = fb->par; + struct sm501fb_info *info = par->info; + unsigned long ctrl; + unsigned int enable; + int ret; + + switch (head) { + case HEAD_CRT: + pd = info->pdata->fb_crt; + ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + enable = (ctrl & SM501_DC_CRT_CONTROL_ENABLE) ? 1 : 0; + + /* ensure we set the correct source register */ + if (info->pdata->fb_route != SM501_FB_CRT_PANEL) { + ctrl |= SM501_DC_CRT_CONTROL_SEL; + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + } + + break; + + case HEAD_PANEL: + pd = info->pdata->fb_pnl; + ctrl = readl(info->regs + SM501_DC_PANEL_CONTROL); + enable = (ctrl & SM501_DC_PANEL_CONTROL_EN) ? 1 : 0; + break; + + default: + pd = NULL; /* stop compiler warnings */ + ctrl = 0; + enable = 0; + BUG(); + } + + dev_info(info->dev, "fb %s %sabled at start\n", + fbname, enable ? "en" : "dis"); + + /* check to see if our routing allows this */ + + if (head == HEAD_CRT && info->pdata->fb_route == SM501_FB_CRT_PANEL) { + ctrl &= ~SM501_DC_CRT_CONTROL_SEL; + writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); + enable = 0; + } + + strlcpy(fb->fix.id, fbname, sizeof(fb->fix.id)); + + memcpy(&par->ops, + (head == HEAD_CRT) ? &sm501fb_ops_crt : &sm501fb_ops_pnl, + sizeof(struct fb_ops)); + + /* update ops dependant on what we've been passed */ + + if ((pd->flags & SM501FB_FLAG_USE_HWCURSOR) == 0) + par->ops.fb_cursor = NULL; + + fb->fbops = &par->ops; + fb->flags = FBINFO_FLAG_DEFAULT | + FBINFO_HWACCEL_XPAN | FBINFO_HWACCEL_YPAN; + + /* fixed data */ + + fb->fix.type = FB_TYPE_PACKED_PIXELS; + fb->fix.type_aux = 0; + fb->fix.xpanstep = 1; + fb->fix.ypanstep = 1; + fb->fix.ywrapstep = 0; + fb->fix.accel = FB_ACCEL_NONE; + + /* screenmode */ + + fb->var.nonstd = 0; + fb->var.activate = FB_ACTIVATE_NOW; + fb->var.accel_flags = 0; + fb->var.vmode = FB_VMODE_NONINTERLACED; + fb->var.bits_per_pixel = 16; + + if (enable && (pd->flags & SM501FB_FLAG_USE_INIT_MODE) && 0) { + /* TODO read the mode from the current display */ + + } else { + if (pd->def_mode) { + dev_info(info->dev, "using supplied mode\n"); + fb_videomode_to_var(&fb->var, pd->def_mode); + + fb->var.bits_per_pixel = pd->def_bpp ? pd->def_bpp : 8; + fb->var.xres_virtual = fb->var.xres; + fb->var.yres_virtual = fb->var.yres; + } else { + ret = fb_find_mode(&fb->var, fb, + NULL, NULL, 0, NULL, 8); + + if (ret == 0 || ret == 4) { + dev_err(info->dev, + "failed to get initial mode\n"); + return -EINVAL; + } + } + } + + /* initialise and set the palette */ + fb_alloc_cmap(&fb->cmap, NR_PALETTE, 0); + fb_set_cmap(&fb->cmap, fb); + + ret = (fb->fbops->fb_check_var)(&fb->var, fb); + if (ret) + dev_err(info->dev, "check_var() failed on initial setup?\n"); + + /* ensure we've activated our new configuration */ + (fb->fbops->fb_set_par)(fb); + + return 0; +} + +/* default platform data if none is supplied (ie, PCI device) */ + +static struct sm501_platdata_fbsub sm501fb_pdata_crt = { + .flags = (SM501FB_FLAG_USE_INIT_MODE | + SM501FB_FLAG_USE_HWCURSOR | + SM501FB_FLAG_USE_HWACCEL | + SM501FB_FLAG_DISABLE_AT_EXIT), + +}; + +static struct sm501_platdata_fbsub sm501fb_pdata_pnl = { + .flags = (SM501FB_FLAG_USE_INIT_MODE | + SM501FB_FLAG_USE_HWCURSOR | + SM501FB_FLAG_USE_HWACCEL | + SM501FB_FLAG_DISABLE_AT_EXIT), +}; + +static struct sm501_platdata_fb sm501fb_def_pdata = { + .fb_route = SM501_FB_OWN, + .fb_crt = &sm501fb_pdata_crt, + .fb_pnl = &sm501fb_pdata_pnl, +}; + +static char driver_name_crt[] = "sm501fb-crt"; +static char driver_name_pnl[] = "sm501fb-panel"; + +static int __init sm501fb_probe(struct platform_device *pdev) +{ + struct sm501fb_info *info; + struct device *dev = &pdev->dev; + struct fb_info *fbinfo_crt; + struct fb_info *fbinfo_pnl; + int ret; + + /* allocate our framebuffers */ + + fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev); + if (fbinfo_crt == NULL) { + dev_err(dev, "cannot allocate crt framebuffer\n"); + return -ENOMEM; + } + + fbinfo_pnl = framebuffer_alloc(sizeof(struct sm501fb_par), dev); + if (fbinfo_pnl == NULL) { + dev_err(dev, "cannot allocate panel framebuffer\n"); + ret = -ENOMEM; + goto fbinfo_crt_alloc_fail; + } + + info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl); + if (info == NULL) { + dev_err(dev, "cannot allocate par\n"); + ret = -ENOMEM; + goto sm501fb_alloc_fail; + } + + if (dev->parent->platform_data) { + struct sm501_platdata *pd = dev->parent->platform_data; + info->pdata = pd->fb; + } + + if (info->pdata == NULL) { + dev_info(dev, "using default configuration data\n"); + info->pdata = &sm501fb_def_pdata; + } + + /* start the framebuffers */ + + ret = sm501fb_start(info, pdev); + if (ret) { + dev_err(dev, "cannot initialise SM501\n"); + goto sm501fb_start_fail; + } + + /* CRT framebuffer setup */ + + ret = sm501fb_init_fb(fbinfo_crt, HEAD_CRT, driver_name_crt); + if (ret) { + dev_err(dev, "cannot initialise CRT fb\n"); + goto sm501fb_start_fail; + } + + /* Panel framebuffer setup */ + + ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl); + if (ret) { + dev_err(dev, "cannot initialise Panel fb\n"); + goto sm501fb_start_fail; + } + + /* register framebuffers */ + + ret = register_framebuffer(fbinfo_crt); + if (ret < 0) { + dev_err(dev, "failed to register CRT fb (%d)\n", ret); + goto register_crt_fail; + } + + ret = register_framebuffer(fbinfo_pnl); + if (ret < 0) { + dev_err(dev, "failed to register panel fb (%d)\n", ret); + goto register_pnl_fail; + } + + dev_info(dev, "fb%d: %s frame buffer device\n", + fbinfo_crt->node, fbinfo_crt->fix.id); + + dev_info(dev, "fb%d: %s frame buffer device\n", + fbinfo_pnl->node, fbinfo_pnl->fix.id); + + /* create device files */ + + ret = device_create_file(dev, &dev_attr_crt_src); + if (ret) + goto crtsrc_fail; + + ret = device_create_file(dev, &dev_attr_fbregs_pnl); + if (ret) + goto fbregs_pnl_fail; + + ret = device_create_file(dev, &dev_attr_fbregs_crt); + if (ret) + goto fbregs_crt_fail; + + /* we registered, return ok */ + return 0; + + fbregs_crt_fail: + device_remove_file(dev, &dev_attr_fbregs_pnl); + + fbregs_pnl_fail: + device_remove_file(dev, &dev_attr_crt_src); + + crtsrc_fail: + unregister_framebuffer(fbinfo_pnl); + + register_pnl_fail: + unregister_framebuffer(fbinfo_crt); + + register_crt_fail: + sm501fb_stop(info); + + sm501fb_start_fail: + sm501fb_info_release(info); + + sm501fb_alloc_fail: + framebuffer_release(fbinfo_pnl); + + fbinfo_crt_alloc_fail: + framebuffer_release(fbinfo_crt); + + return ret; +} + + +/* + * Cleanup + */ +static int sm501fb_remove(struct platform_device *pdev) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + struct fb_info *fbinfo_crt = info->fb[0]; + struct fb_info *fbinfo_pnl = info->fb[1]; + + device_remove_file(&pdev->dev, &dev_attr_fbregs_crt); + device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl); + device_remove_file(&pdev->dev, &dev_attr_crt_src); + + unregister_framebuffer(fbinfo_crt); + unregister_framebuffer(fbinfo_pnl); + + sm501fb_stop(info); + sm501fb_info_release(info); + + framebuffer_release(fbinfo_pnl); + framebuffer_release(fbinfo_crt); + + return 0; +} + +#ifdef CONFIG_PM + +static int sm501fb_suspend_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + struct sm501fb_par *par = fbi->par; + + if (par->screen.size == 0) + return 0; + + /* backup copies in case chip is powered down over suspend */ + + par->store_fb = vmalloc(par->screen.size); + if (par->store_fb == NULL) { + dev_err(info->dev, "no memory to store screen\n"); + return -ENOMEM; + } + + par->store_cursor = vmalloc(par->cursor.size); + if (par->store_cursor == NULL) { + dev_err(info->dev, "no memory to store cursor\n"); + goto err_nocursor; + } + + memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); + memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); + + /* blank the relevant interface to ensure unit power minimised */ + (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); + + return 0; + + err_nocursor: + vfree(par->store_fb); + + return -ENOMEM; + +} + +static void sm501fb_resume_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + struct sm501fb_par *par = fbi->par; + + if (par->screen.size == 0) + return; + + /* re-activate the configuration */ + + (par->ops.fb_set_par)(fbi); + + /* restore the data */ + + memcpy_toio(par->screen.k_addr, par->store_fb, par->screen.size); + memcpy_toio(par->cursor.k_addr, par->store_cursor, par->cursor.size); + + vfree(par->store_fb); + vfree(par->store_cursor); +} + + +/* suspend and resume support */ + +static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + + sm501fb_suspend_fb(info, HEAD_CRT); + sm501fb_suspend_fb(info, HEAD_PANEL); + + /* turn off the clocks, in case the device is not powered down */ + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 0); + + return 0; +} + +static int sm501fb_resume(struct platform_device *pdev) +{ + struct sm501fb_info *info = platform_get_drvdata(pdev); + + sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1); + + sm501fb_resume_fb(info, HEAD_CRT); + sm501fb_resume_fb(info, HEAD_PANEL); + + return 0; +} + +#else +#define sm501fb_suspend NULL +#define sm501fb_resume NULL +#endif + +static struct platform_driver sm501fb_driver = { + .probe = sm501fb_probe, + .remove = sm501fb_remove, + .suspend = sm501fb_suspend, + .resume = sm501fb_resume, + .driver = { + .name = "sm501-fb", + .owner = THIS_MODULE, + }, +}; + +static int __devinit sm501fb_init(void) +{ + return platform_driver_register(&sm501fb_driver); +} + +static void __exit sm501fb_cleanup(void) +{ + platform_driver_unregister(&sm501fb_driver); +} + +module_init(sm501fb_init); +module_exit(sm501fb_cleanup); + +MODULE_AUTHOR("Ben Dooks, Vincent Sanders"); +MODULE_DESCRIPTION("SM501 Framebuffer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c index 59cd1e7..62fa550 100644 --- a/drivers/video/sstfb.c +++ b/drivers/video/sstfb.c @@ -257,6 +257,7 @@ static void __sst_dac_write(u8 __iomem *vbase, u8 reg, u8 val) r_dprintk("sst_dac_write(%#x, %#x)\n", reg, val); reg &= 0x07; __sst_write(vbase, DAC_DATA,(((u32)reg << 8)) | (u32)val); + __sst_wait_idle(vbase); } /* indexed access to ti/att dacs */ |